fix(start-work): use Atlas list key in command config

This commit is contained in:
YeonGyu-Kim
2026-04-01 17:45:16 -07:00
parent f4b8e1c365
commit 51d9685571
6 changed files with 214 additions and 6 deletions
+43
View File
@@ -95,6 +95,49 @@ describe("createChatMessageHandler - /start-work integration", () => {
})
})
describe("createChatMessageHandler - /ulw-loop raw slash fallback", () => {
test("starts ultrawork loop when /ulw-loop arrives through chat.message without native command expansion", async () => {
// given
const startLoopCalls: Array<{
sessionID: string
prompt: string
options: Record<string, unknown>
}> = []
const args = createMockHandlerArgs()
args.hooks.autoSlashCommand = createAutoSlashCommandHook({ skills: [] })
args.hooks.ralphLoop = {
startLoop: (sessionID: string, prompt: string, options?: Record<string, unknown>) => {
startLoopCalls.push({ sessionID, prompt, options: options ?? {} })
return true
},
cancelLoop: () => true,
}
const handler = createChatMessageHandler(args)
const input = createMockInput("sisyphus")
const output: ChatMessageHandlerOutput = {
message: {},
parts: [{ type: "text", text: '/ulw-loop "Ship feature" --strategy=continue' }],
}
// when
await handler(input, output)
// then
expect(startLoopCalls).toEqual([
{
sessionID: "test-session",
prompt: "Ship feature",
options: {
ultrawork: true,
maxIterations: undefined,
completionPromise: undefined,
strategy: "continue",
},
},
])
})
})
function createMockInput(agent?: string, model?: { providerID: string; modelID: string }) {
return {
sessionID: "test-session",
+40 -4
View File
@@ -25,6 +25,10 @@ type StartWorkHookOutput = { parts: Array<{ type: string; text?: string }> }
type SessionModelOverride = { providerID: string; modelID: string }
type RawLoopCommand =
| { command: "ralph-loop" | "ulw-loop"; args: string }
| { command: "cancel-ralph"; args: "" }
function isStartWorkHookOutput(value: unknown): value is StartWorkHookOutput {
if (typeof value !== "object" || value === null) return false
const record = value as Record<string, unknown>
@@ -84,6 +88,33 @@ function getStoredMainSessionModel(
return getSessionModel(input.sessionID)
}
function parseRawLoopSlashCommand(promptText: string): RawLoopCommand | null {
const trimmed = promptText.trim()
if (!trimmed.startsWith("/")) {
return null
}
const cancelMatch = trimmed.match(/^\/cancel-ralph(?:\s+.*)?$/i)
if (cancelMatch) {
return { command: "cancel-ralph", args: "" }
}
const loopMatch = trimmed.match(/^\/(ralph-loop|ulw-loop)\s*([\s\S]*)$/i)
if (!loopMatch) {
return null
}
const command = loopMatch[1]?.toLowerCase()
const args = loopMatch[2]?.trim() ?? ""
if (command === "ralph-loop" || command === "ulw-loop") {
return { command, args }
}
return null
}
export function createChatMessageHandler(args: {
ctx: PluginContext
pluginConfig: OhMyOpenCodeConfig
@@ -201,19 +232,24 @@ export function createChatMessageHandler(args: {
const isCancelRalphTemplate = promptText.includes(
"Cancel the currently active Ralph Loop",
)
const rawLoopCommand =
!isRalphLoopTemplate && !isUlwLoopTemplate && !isCancelRalphTemplate
? parseRawLoopSlashCommand(promptText)
: null
if (isRalphLoopTemplate || isUlwLoopTemplate) {
if (isRalphLoopTemplate || isUlwLoopTemplate || rawLoopCommand?.command === "ralph-loop" || rawLoopCommand?.command === "ulw-loop") {
const taskMatch = promptText.match(/<user-task>\s*([\s\S]*?)\s*<\/user-task>/i)
const rawTask = taskMatch?.[1]?.trim() || ""
const rawTask = taskMatch?.[1]?.trim() || rawLoopCommand?.args || ""
const parsedArguments = parseRalphLoopArguments(rawTask)
const ultrawork = isUlwLoopTemplate || rawLoopCommand?.command === "ulw-loop"
hooks.ralphLoop.startLoop(input.sessionID, parsedArguments.prompt, {
ultrawork: isUlwLoopTemplate,
ultrawork,
maxIterations: parsedArguments.maxIterations,
completionPromise: parsedArguments.completionPromise,
strategy: parsedArguments.strategy,
})
} else if (isCancelRalphTemplate) {
} else if (isCancelRalphTemplate || rawLoopCommand?.command === "cancel-ralph") {
hooks.ralphLoop.cancelLoop(input.sessionID)
}
}