feat(hooks): add /stop-continuation command to halt all continuation mechanisms (#1316)
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-opencode) Co-authored-by: justsisyphus <justsisyphus@users.noreply.github.com> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
parent
ddfbdbb84e
commit
e63c568c4f
@ -80,7 +80,8 @@
|
|||||||
"prometheus-md-only",
|
"prometheus-md-only",
|
||||||
"sisyphus-junior-notepad",
|
"sisyphus-junior-notepad",
|
||||||
"start-work",
|
"start-work",
|
||||||
"atlas"
|
"atlas",
|
||||||
|
"stop-continuation-guard"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -88,6 +88,7 @@ export const HookNameSchema = z.enum([
|
|||||||
"sisyphus-junior-notepad",
|
"sisyphus-junior-notepad",
|
||||||
"start-work",
|
"start-work",
|
||||||
"atlas",
|
"atlas",
|
||||||
|
"stop-continuation-guard",
|
||||||
])
|
])
|
||||||
|
|
||||||
export const BuiltinCommandNameSchema = z.enum([
|
export const BuiltinCommandNameSchema = z.enum([
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import type { CommandDefinition } from "../claude-code-command-loader"
|
|||||||
import type { BuiltinCommandName, BuiltinCommands } from "./types"
|
import type { BuiltinCommandName, BuiltinCommands } from "./types"
|
||||||
import { INIT_DEEP_TEMPLATE } from "./templates/init-deep"
|
import { INIT_DEEP_TEMPLATE } from "./templates/init-deep"
|
||||||
import { RALPH_LOOP_TEMPLATE, CANCEL_RALPH_TEMPLATE } from "./templates/ralph-loop"
|
import { RALPH_LOOP_TEMPLATE, CANCEL_RALPH_TEMPLATE } from "./templates/ralph-loop"
|
||||||
|
import { STOP_CONTINUATION_TEMPLATE } from "./templates/stop-continuation"
|
||||||
import { REFACTOR_TEMPLATE } from "./templates/refactor"
|
import { REFACTOR_TEMPLATE } from "./templates/refactor"
|
||||||
import { START_WORK_TEMPLATE } from "./templates/start-work"
|
import { START_WORK_TEMPLATE } from "./templates/start-work"
|
||||||
|
|
||||||
@ -70,6 +71,12 @@ $ARGUMENTS
|
|||||||
</user-request>`,
|
</user-request>`,
|
||||||
argumentHint: "[plan-name]",
|
argumentHint: "[plan-name]",
|
||||||
},
|
},
|
||||||
|
"stop-continuation": {
|
||||||
|
description: "(builtin) Stop all continuation mechanisms (ralph loop, todo continuation, boulder) for this session",
|
||||||
|
template: `<command-instruction>
|
||||||
|
${STOP_CONTINUATION_TEMPLATE}
|
||||||
|
</command-instruction>`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export function loadBuiltinCommands(
|
export function loadBuiltinCommands(
|
||||||
|
|||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { describe, expect, test } from "bun:test"
|
||||||
|
import { STOP_CONTINUATION_TEMPLATE } from "./stop-continuation"
|
||||||
|
|
||||||
|
describe("stop-continuation template", () => {
|
||||||
|
test("should export a non-empty template string", () => {
|
||||||
|
// #given - the stop-continuation template
|
||||||
|
|
||||||
|
// #when - we access the template
|
||||||
|
|
||||||
|
// #then - it should be a non-empty string
|
||||||
|
expect(typeof STOP_CONTINUATION_TEMPLATE).toBe("string")
|
||||||
|
expect(STOP_CONTINUATION_TEMPLATE.length).toBeGreaterThan(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should describe the stop-continuation behavior", () => {
|
||||||
|
// #given - the stop-continuation template
|
||||||
|
|
||||||
|
// #when - we check the content
|
||||||
|
|
||||||
|
// #then - it should mention key behaviors
|
||||||
|
expect(STOP_CONTINUATION_TEMPLATE).toContain("todo-continuation-enforcer")
|
||||||
|
expect(STOP_CONTINUATION_TEMPLATE).toContain("Ralph Loop")
|
||||||
|
expect(STOP_CONTINUATION_TEMPLATE).toContain("boulder state")
|
||||||
|
})
|
||||||
|
})
|
||||||
13
src/features/builtin-commands/templates/stop-continuation.ts
Normal file
13
src/features/builtin-commands/templates/stop-continuation.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
export const STOP_CONTINUATION_TEMPLATE = `Stop all continuation mechanisms for the current session.
|
||||||
|
|
||||||
|
This command will:
|
||||||
|
1. Stop the todo-continuation-enforcer from automatically continuing incomplete tasks
|
||||||
|
2. Cancel any active Ralph Loop
|
||||||
|
3. Clear the boulder state for the current project
|
||||||
|
|
||||||
|
After running this command:
|
||||||
|
- The session will not auto-continue when idle
|
||||||
|
- You can manually continue work when ready
|
||||||
|
- The stop state is per-session and clears when the session ends
|
||||||
|
|
||||||
|
Use this when you need to pause automated continuation and take manual control.`
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import type { CommandDefinition } from "../claude-code-command-loader"
|
import type { CommandDefinition } from "../claude-code-command-loader"
|
||||||
|
|
||||||
export type BuiltinCommandName = "init-deep" | "ralph-loop" | "cancel-ralph" | "ulw-loop" | "refactor" | "start-work"
|
export type BuiltinCommandName = "init-deep" | "ralph-loop" | "cancel-ralph" | "ulw-loop" | "refactor" | "start-work" | "stop-continuation"
|
||||||
|
|
||||||
export interface BuiltinCommandConfig {
|
export interface BuiltinCommandConfig {
|
||||||
disabled_commands?: BuiltinCommandName[]
|
disabled_commands?: BuiltinCommandName[]
|
||||||
|
|||||||
@ -34,3 +34,4 @@ export { createAtlasHook } from "./atlas";
|
|||||||
export { createDelegateTaskRetryHook } from "./delegate-task-retry";
|
export { createDelegateTaskRetryHook } from "./delegate-task-retry";
|
||||||
export { createQuestionLabelTruncatorHook } from "./question-label-truncator";
|
export { createQuestionLabelTruncatorHook } from "./question-label-truncator";
|
||||||
export { createSubagentQuestionBlockerHook } from "./subagent-question-blocker";
|
export { createSubagentQuestionBlockerHook } from "./subagent-question-blocker";
|
||||||
|
export { createStopContinuationGuardHook, type StopContinuationGuard } from "./stop-continuation-guard";
|
||||||
|
|||||||
106
src/hooks/stop-continuation-guard/index.test.ts
Normal file
106
src/hooks/stop-continuation-guard/index.test.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import { describe, expect, test } from "bun:test"
|
||||||
|
import { createStopContinuationGuardHook } from "./index"
|
||||||
|
|
||||||
|
describe("stop-continuation-guard", () => {
|
||||||
|
function createMockPluginInput() {
|
||||||
|
return {
|
||||||
|
client: {
|
||||||
|
tui: {
|
||||||
|
showToast: async () => ({}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
directory: "/tmp/test",
|
||||||
|
} as never
|
||||||
|
}
|
||||||
|
|
||||||
|
test("should mark session as stopped", () => {
|
||||||
|
// #given - a guard hook with no stopped sessions
|
||||||
|
const guard = createStopContinuationGuardHook(createMockPluginInput())
|
||||||
|
const sessionID = "test-session-1"
|
||||||
|
|
||||||
|
// #when - we stop continuation for the session
|
||||||
|
guard.stop(sessionID)
|
||||||
|
|
||||||
|
// #then - session should be marked as stopped
|
||||||
|
expect(guard.isStopped(sessionID)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should return false for non-stopped sessions", () => {
|
||||||
|
// #given - a guard hook with no stopped sessions
|
||||||
|
const guard = createStopContinuationGuardHook(createMockPluginInput())
|
||||||
|
|
||||||
|
// #when - we check a session that was never stopped
|
||||||
|
|
||||||
|
// #then - it should return false
|
||||||
|
expect(guard.isStopped("non-existent-session")).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should clear stopped state for a session", () => {
|
||||||
|
// #given - a session that was stopped
|
||||||
|
const guard = createStopContinuationGuardHook(createMockPluginInput())
|
||||||
|
const sessionID = "test-session-2"
|
||||||
|
guard.stop(sessionID)
|
||||||
|
|
||||||
|
// #when - we clear the session
|
||||||
|
guard.clear(sessionID)
|
||||||
|
|
||||||
|
// #then - session should no longer be stopped
|
||||||
|
expect(guard.isStopped(sessionID)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should handle multiple sessions independently", () => {
|
||||||
|
// #given - multiple sessions with different stop states
|
||||||
|
const guard = createStopContinuationGuardHook(createMockPluginInput())
|
||||||
|
const session1 = "session-1"
|
||||||
|
const session2 = "session-2"
|
||||||
|
const session3 = "session-3"
|
||||||
|
|
||||||
|
// #when - we stop some sessions but not others
|
||||||
|
guard.stop(session1)
|
||||||
|
guard.stop(session2)
|
||||||
|
|
||||||
|
// #then - each session has its own state
|
||||||
|
expect(guard.isStopped(session1)).toBe(true)
|
||||||
|
expect(guard.isStopped(session2)).toBe(true)
|
||||||
|
expect(guard.isStopped(session3)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should clear session on session.deleted event", async () => {
|
||||||
|
// #given - a session that was stopped
|
||||||
|
const guard = createStopContinuationGuardHook(createMockPluginInput())
|
||||||
|
const sessionID = "test-session-3"
|
||||||
|
guard.stop(sessionID)
|
||||||
|
|
||||||
|
// #when - session is deleted
|
||||||
|
await guard.event({
|
||||||
|
event: {
|
||||||
|
type: "session.deleted",
|
||||||
|
properties: { info: { id: sessionID } },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// #then - session should no longer be stopped (cleaned up)
|
||||||
|
expect(guard.isStopped(sessionID)).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should not affect other sessions on session.deleted", async () => {
|
||||||
|
// #given - multiple stopped sessions
|
||||||
|
const guard = createStopContinuationGuardHook(createMockPluginInput())
|
||||||
|
const session1 = "session-keep"
|
||||||
|
const session2 = "session-delete"
|
||||||
|
guard.stop(session1)
|
||||||
|
guard.stop(session2)
|
||||||
|
|
||||||
|
// #when - one session is deleted
|
||||||
|
await guard.event({
|
||||||
|
event: {
|
||||||
|
type: "session.deleted",
|
||||||
|
properties: { info: { id: session2 } },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// #then - other session should remain stopped
|
||||||
|
expect(guard.isStopped(session1)).toBe(true)
|
||||||
|
expect(guard.isStopped(session2)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
54
src/hooks/stop-continuation-guard/index.ts
Normal file
54
src/hooks/stop-continuation-guard/index.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import type { PluginInput } from "@opencode-ai/plugin"
|
||||||
|
import { log } from "../../shared/logger"
|
||||||
|
|
||||||
|
const HOOK_NAME = "stop-continuation-guard"
|
||||||
|
|
||||||
|
export interface StopContinuationGuard {
|
||||||
|
event: (input: { event: { type: string; properties?: unknown } }) => Promise<void>
|
||||||
|
stop: (sessionID: string) => void
|
||||||
|
isStopped: (sessionID: string) => boolean
|
||||||
|
clear: (sessionID: string) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createStopContinuationGuardHook(
|
||||||
|
_ctx: PluginInput
|
||||||
|
): StopContinuationGuard {
|
||||||
|
const stoppedSessions = new Set<string>()
|
||||||
|
|
||||||
|
const stop = (sessionID: string): void => {
|
||||||
|
stoppedSessions.add(sessionID)
|
||||||
|
log(`[${HOOK_NAME}] Continuation stopped for session`, { sessionID })
|
||||||
|
}
|
||||||
|
|
||||||
|
const isStopped = (sessionID: string): boolean => {
|
||||||
|
return stoppedSessions.has(sessionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clear = (sessionID: string): void => {
|
||||||
|
stoppedSessions.delete(sessionID)
|
||||||
|
log(`[${HOOK_NAME}] Continuation guard cleared for session`, { sessionID })
|
||||||
|
}
|
||||||
|
|
||||||
|
const event = async ({
|
||||||
|
event,
|
||||||
|
}: {
|
||||||
|
event: { type: string; properties?: unknown }
|
||||||
|
}): Promise<void> => {
|
||||||
|
const props = event.properties as Record<string, unknown> | undefined
|
||||||
|
|
||||||
|
if (event.type === "session.deleted") {
|
||||||
|
const sessionInfo = props?.info as { id?: string } | undefined
|
||||||
|
if (sessionInfo?.id) {
|
||||||
|
clear(sessionInfo.id)
|
||||||
|
log(`[${HOOK_NAME}] Session deleted: cleaned up`, { sessionID: sessionInfo.id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
event,
|
||||||
|
stop,
|
||||||
|
isStopped,
|
||||||
|
clear,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1178,4 +1178,68 @@ describe("todo-continuation-enforcer", () => {
|
|||||||
// #then - continuation injected (no agents to skip)
|
// #then - continuation injected (no agents to skip)
|
||||||
expect(promptCalls.length).toBe(1)
|
expect(promptCalls.length).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("should not inject when isContinuationStopped returns true", async () => {
|
||||||
|
// #given - session with continuation stopped
|
||||||
|
const sessionID = "main-stopped"
|
||||||
|
setMainSession(sessionID)
|
||||||
|
|
||||||
|
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {
|
||||||
|
isContinuationStopped: (id) => id === sessionID,
|
||||||
|
})
|
||||||
|
|
||||||
|
// #when - session goes idle
|
||||||
|
await hook.handler({
|
||||||
|
event: { type: "session.idle", properties: { sessionID } },
|
||||||
|
})
|
||||||
|
|
||||||
|
await fakeTimers.advanceBy(3000)
|
||||||
|
|
||||||
|
// #then - no continuation injected (stopped flag is true)
|
||||||
|
expect(promptCalls).toHaveLength(0)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should inject when isContinuationStopped returns false", async () => {
|
||||||
|
// #given - session with continuation not stopped
|
||||||
|
const sessionID = "main-not-stopped"
|
||||||
|
setMainSession(sessionID)
|
||||||
|
|
||||||
|
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {
|
||||||
|
isContinuationStopped: () => false,
|
||||||
|
})
|
||||||
|
|
||||||
|
// #when - session goes idle
|
||||||
|
await hook.handler({
|
||||||
|
event: { type: "session.idle", properties: { sessionID } },
|
||||||
|
})
|
||||||
|
|
||||||
|
await fakeTimers.advanceBy(3000)
|
||||||
|
|
||||||
|
// #then - continuation injected (stopped flag is false)
|
||||||
|
expect(promptCalls.length).toBe(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("should cancel all countdowns via cancelAllCountdowns", async () => {
|
||||||
|
// #given - multiple sessions with running countdowns
|
||||||
|
const session1 = "main-cancel-all-1"
|
||||||
|
const session2 = "main-cancel-all-2"
|
||||||
|
setMainSession(session1)
|
||||||
|
|
||||||
|
const hook = createTodoContinuationEnforcer(createMockPluginInput(), {})
|
||||||
|
|
||||||
|
// #when - first session goes idle
|
||||||
|
await hook.handler({
|
||||||
|
event: { type: "session.idle", properties: { sessionID: session1 } },
|
||||||
|
})
|
||||||
|
await fakeTimers.advanceBy(500)
|
||||||
|
|
||||||
|
// #when - cancel all countdowns
|
||||||
|
hook.cancelAllCountdowns()
|
||||||
|
|
||||||
|
// #when - advance past countdown time
|
||||||
|
await fakeTimers.advanceBy(3000)
|
||||||
|
|
||||||
|
// #then - no continuation injected (all countdowns cancelled)
|
||||||
|
expect(promptCalls).toHaveLength(0)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -18,12 +18,14 @@ const DEFAULT_SKIP_AGENTS = ["prometheus", "compaction"]
|
|||||||
export interface TodoContinuationEnforcerOptions {
|
export interface TodoContinuationEnforcerOptions {
|
||||||
backgroundManager?: BackgroundManager
|
backgroundManager?: BackgroundManager
|
||||||
skipAgents?: string[]
|
skipAgents?: string[]
|
||||||
|
isContinuationStopped?: (sessionID: string) => boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TodoContinuationEnforcer {
|
export interface TodoContinuationEnforcer {
|
||||||
handler: (input: { event: { type: string; properties?: unknown } }) => Promise<void>
|
handler: (input: { event: { type: string; properties?: unknown } }) => Promise<void>
|
||||||
markRecovering: (sessionID: string) => void
|
markRecovering: (sessionID: string) => void
|
||||||
markRecoveryComplete: (sessionID: string) => void
|
markRecoveryComplete: (sessionID: string) => void
|
||||||
|
cancelAllCountdowns: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Todo {
|
interface Todo {
|
||||||
@ -95,7 +97,7 @@ export function createTodoContinuationEnforcer(
|
|||||||
ctx: PluginInput,
|
ctx: PluginInput,
|
||||||
options: TodoContinuationEnforcerOptions = {}
|
options: TodoContinuationEnforcerOptions = {}
|
||||||
): TodoContinuationEnforcer {
|
): TodoContinuationEnforcer {
|
||||||
const { backgroundManager, skipAgents = DEFAULT_SKIP_AGENTS } = options
|
const { backgroundManager, skipAgents = DEFAULT_SKIP_AGENTS, isContinuationStopped } = options
|
||||||
const sessions = new Map<string, SessionState>()
|
const sessions = new Map<string, SessionState>()
|
||||||
|
|
||||||
function getState(sessionID: string): SessionState {
|
function getState(sessionID: string): SessionState {
|
||||||
@ -420,6 +422,11 @@ export function createTodoContinuationEnforcer(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isContinuationStopped?.(sessionID)) {
|
||||||
|
log(`[${HOOK_NAME}] Skipped: continuation stopped for session`, { sessionID })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
startCountdown(sessionID, incompleteCount, todos.length, resolvedInfo)
|
startCountdown(sessionID, incompleteCount, todos.length, resolvedInfo)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -485,9 +492,17 @@ export function createTodoContinuationEnforcer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cancelAllCountdowns = (): void => {
|
||||||
|
for (const sessionID of sessions.keys()) {
|
||||||
|
cancelCountdown(sessionID)
|
||||||
|
}
|
||||||
|
log(`[${HOOK_NAME}] All countdowns cancelled`)
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handler,
|
handler,
|
||||||
markRecovering,
|
markRecovering,
|
||||||
markRecoveryComplete,
|
markRecoveryComplete,
|
||||||
|
cancelAllCountdowns,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
38
src/index.ts
38
src/index.ts
@ -35,6 +35,7 @@ import {
|
|||||||
createSisyphusJuniorNotepadHook,
|
createSisyphusJuniorNotepadHook,
|
||||||
createQuestionLabelTruncatorHook,
|
createQuestionLabelTruncatorHook,
|
||||||
createSubagentQuestionBlockerHook,
|
createSubagentQuestionBlockerHook,
|
||||||
|
createStopContinuationGuardHook,
|
||||||
} from "./hooks";
|
} from "./hooks";
|
||||||
import {
|
import {
|
||||||
contextCollector,
|
contextCollector,
|
||||||
@ -77,6 +78,7 @@ import { BackgroundManager } from "./features/background-agent";
|
|||||||
import { SkillMcpManager } from "./features/skill-mcp-manager";
|
import { SkillMcpManager } from "./features/skill-mcp-manager";
|
||||||
import { initTaskToastManager } from "./features/task-toast-manager";
|
import { initTaskToastManager } from "./features/task-toast-manager";
|
||||||
import { TmuxSessionManager } from "./features/tmux-subagent";
|
import { TmuxSessionManager } from "./features/tmux-subagent";
|
||||||
|
import { clearBoulderState } from "./features/boulder-state";
|
||||||
import { type HookName } from "./config";
|
import { type HookName } from "./config";
|
||||||
import { log, detectExternalNotificationPlugin, getNotificationConflictWarning, resetMessageCursor, includesCaseInsensitive, hasConnectedProvidersCache, getOpenCodeVersion, isOpenCodeVersionAtLeast, OPENCODE_NATIVE_AGENTS_INJECTION_VERSION } from "./shared";
|
import { log, detectExternalNotificationPlugin, getNotificationConflictWarning, resetMessageCursor, includesCaseInsensitive, hasConnectedProvidersCache, getOpenCodeVersion, isOpenCodeVersionAtLeast, OPENCODE_NATIVE_AGENTS_INJECTION_VERSION } from "./shared";
|
||||||
import { loadPluginConfig } from "./plugin-config";
|
import { loadPluginConfig } from "./plugin-config";
|
||||||
@ -277,8 +279,15 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
|
|
||||||
initTaskToastManager(ctx.client);
|
initTaskToastManager(ctx.client);
|
||||||
|
|
||||||
|
const stopContinuationGuard = isHookEnabled("stop-continuation-guard")
|
||||||
|
? createStopContinuationGuardHook(ctx)
|
||||||
|
: null;
|
||||||
|
|
||||||
const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer")
|
const todoContinuationEnforcer = isHookEnabled("todo-continuation-enforcer")
|
||||||
? createTodoContinuationEnforcer(ctx, { backgroundManager })
|
? createTodoContinuationEnforcer(ctx, {
|
||||||
|
backgroundManager,
|
||||||
|
isContinuationStopped: stopContinuationGuard?.isStopped,
|
||||||
|
})
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (sessionRecovery && todoContinuationEnforcer) {
|
if (sessionRecovery && todoContinuationEnforcer) {
|
||||||
@ -521,6 +530,7 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
await categorySkillReminder?.event(input);
|
await categorySkillReminder?.event(input);
|
||||||
await interactiveBashSession?.event(input);
|
await interactiveBashSession?.event(input);
|
||||||
await ralphLoop?.event(input);
|
await ralphLoop?.event(input);
|
||||||
|
await stopContinuationGuard?.event(input);
|
||||||
await atlasHook?.handler(input);
|
await atlasHook?.handler(input);
|
||||||
|
|
||||||
const { event } = input;
|
const { event } = input;
|
||||||
@ -664,14 +674,28 @@ const OhMyOpenCodePlugin: Plugin = async (ctx) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
ralphLoop.startLoop(sessionID, prompt, {
|
ralphLoop.startLoop(sessionID, prompt, {
|
||||||
ultrawork: true,
|
ultrawork: true,
|
||||||
maxIterations: maxIterMatch
|
maxIterations: maxIterMatch
|
||||||
? parseInt(maxIterMatch[1], 10)
|
? parseInt(maxIterMatch[1], 10)
|
||||||
: undefined,
|
: undefined,
|
||||||
completionPromise: promiseMatch?.[1],
|
completionPromise: promiseMatch?.[1],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (input.tool === "slashcommand") {
|
||||||
|
const args = output.args as { command?: string } | undefined;
|
||||||
|
const command = args?.command?.replace(/^\//, "").toLowerCase();
|
||||||
|
const sessionID = input.sessionID || getMainSessionID();
|
||||||
|
|
||||||
|
if (command === "stop-continuation" && sessionID) {
|
||||||
|
stopContinuationGuard?.stop(sessionID);
|
||||||
|
todoContinuationEnforcer?.cancelAllCountdowns();
|
||||||
|
ralphLoop?.cancelLoop(sessionID);
|
||||||
|
clearBoulderState(ctx.directory);
|
||||||
|
log("[stop-continuation] All continuation mechanisms stopped", { sessionID });
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"tool.execute.after": async (input, output) => {
|
"tool.execute.after": async (input, output) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user