Add /ulw-loop command for ultrawork mode loop (#867)

* feat(ralph-loop): add ultrawork field to RalphLoopState

* feat(ralph-loop): persist ultrawork field in storage

* feat(ralph-loop): accept ultrawork option in startLoop

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* feat(ralph-loop): prepend ultrawork keyword when mode active

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>

* feat(ralph-loop): custom toast for ultrawork mode

* feat(ralph-loop): add /ulw-loop command for ultrawork mode

* fix(ralph-loop): add non-null assertion for type safety

* fix(ralph-loop): mirror argument parsing in ulw-loop handler

- Parse quoted prompts and strip flags from task text
- Support --max-iterations and --completion-promise options
- Add default prompt for empty input
- Fixes behavior inconsistency with /ralph-loop

---------

Co-authored-by: justsisyphus <sisyphus-dev-ai@users.noreply.github.com>
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
YeonGyu-Kim
2026-01-17 19:56:50 +09:00
committed by GitHub
parent 36b665ed89
commit d13e8411f0
8 changed files with 176 additions and 16 deletions
+17 -5
View File
@@ -61,7 +61,7 @@ export interface RalphLoopHook {
startLoop: (
sessionID: string,
prompt: string,
options?: { maxIterations?: number; completionPromise?: string }
options?: { maxIterations?: number; completionPromise?: string; ultrawork?: boolean }
) => boolean
cancelLoop: (sessionID: string) => boolean
getState: () => RalphLoopState | null
@@ -150,7 +150,7 @@ export function createRalphLoopHook(
const startLoop = (
sessionID: string,
prompt: string,
loopOptions?: { maxIterations?: number; completionPromise?: string }
loopOptions?: { maxIterations?: number; completionPromise?: string; ultrawork?: boolean }
): boolean => {
const state: RalphLoopState = {
active: true,
@@ -158,6 +158,7 @@ export function createRalphLoopHook(
max_iterations:
loopOptions?.maxIterations ?? config?.default_max_iterations ?? DEFAULT_MAX_ITERATIONS,
completion_promise: loopOptions?.completionPromise ?? DEFAULT_COMPLETION_PROMISE,
ultrawork: loopOptions?.ultrawork,
started_at: new Date().toISOString(),
prompt,
session_id: sessionID,
@@ -251,11 +252,18 @@ export function createRalphLoopHook(
})
clearState(ctx.directory, stateDir)
const title = state.ultrawork
? "ULTRAWORK LOOP COMPLETE!"
: "Ralph Loop Complete!"
const message = state.ultrawork
? `JUST ULW ULW! Task completed after ${state.iteration} iteration(s)`
: `Task completed after ${state.iteration} iteration(s)`
await ctx.client.tui
.showToast({
body: {
title: "Ralph Loop Complete!",
message: `Task completed after ${state.iteration} iteration(s)`,
title,
message,
variant: "success",
duration: 5000,
},
@@ -304,6 +312,10 @@ export function createRalphLoopHook(
.replace("{{PROMISE}}", newState.completion_promise)
.replace("{{PROMPT}}", newState.prompt)
const finalPrompt = newState.ultrawork
? `ultrawork ${continuationPrompt}`
: continuationPrompt
await ctx.client.tui
.showToast({
body: {
@@ -346,7 +358,7 @@ export function createRalphLoopHook(
body: {
...(agent !== undefined ? { agent } : {}),
...(model !== undefined ? { model } : {}),
parts: [{ type: "text", text: continuationPrompt }],
parts: [{ type: "text", text: finalPrompt }],
},
query: { directory: ctx.directory },
})