oh-my-opencode 4.15.1 → 4.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/codex-qa/SKILL.md +9 -7
- package/.agents/skills/codex-qa/references/logging-debug.md +6 -4
- package/.agents/skills/opencode-qa/SKILL.md +9 -9
- package/.agents/skills/opencode-qa/references/tui-tmux.md +10 -8
- package/dist/cli/codex-ulw-loop.d.ts +8 -0
- package/dist/cli/doctor/checks/tui-plugin-config.d.ts +4 -4
- package/dist/cli/get-local-version/types.d.ts +1 -1
- package/dist/cli/index.js +792 -744
- package/dist/cli/install-senpi/index.d.ts +1 -0
- package/dist/cli/run/poll-for-completion.d.ts +4 -0
- package/dist/cli/run/session-resolver.d.ts +1 -0
- package/dist/cli/senpi-platform-flag.d.ts +4 -0
- package/dist/cli/types.d.ts +2 -1
- package/dist/cli-node/index.js +792 -744
- package/dist/features/background-agent/manager.d.ts +16 -0
- package/dist/features/builtin-commands/templates/start-work.d.ts +1 -1
- package/dist/features/monitor/process.d.ts +17 -1
- package/dist/hooks/anthropic-context-window-limit-recovery/aggressive-truncation-strategy.d.ts +1 -0
- package/dist/index.js +2206 -1971
- package/dist/skills/debugging/references/methodology/06-fix.md +6 -0
- package/dist/skills/frontend/ATTRIBUTION.md +10 -1
- package/dist/skills/frontend/SKILL.md +12 -7
- package/dist/skills/frontend/references/design/README.md +8 -4
- package/dist/skills/frontend/references/design/_INDEX.md +2 -2
- package/dist/skills/frontend/references/design/design-system-architecture.md +24 -2
- package/dist/skills/frontend/references/design/lazyweb.md +77 -0
- package/dist/skills/frontend/references/design/stitch-design-example.md +121 -0
- package/dist/skills/frontend/references/designpowers/README.md +2 -2
- package/dist/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
- package/dist/skills/programming/SKILL.md +10 -1
- package/dist/skills/programming/references/logging.md +91 -0
- package/dist/skills/start-work/SKILL.md +2 -2
- package/dist/skills/ulw-plan/SKILL.md +5 -5
- package/dist/skills/ulw-plan/references/full-workflow.md +7 -7
- package/dist/skills/ulw-plan/references/intent-unclear.md +3 -3
- package/dist/skills/ulw-research/ATTRIBUTION.md +2 -2
- package/dist/skills/ulw-research/SKILL.md +28 -7
- package/dist/skills/visual-qa/SKILL.md +13 -17
- package/dist/tools/skill/description-formatter.d.ts +1 -0
- package/dist/tools/skill/description-formatter.test-support.d.ts +9 -0
- package/dist/tools/skill/zauc-mocks-skill-tools/test-support.d.ts +25 -0
- package/dist/tui.js +121 -419
- package/docs/reference/web-terminal-visual-qa.md +39 -45
- package/package.json +48 -25
- package/packages/git-bash-mcp/dist/cli.js +2 -2
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/AGENTS.md +55 -0
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +19 -16
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +51 -0
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +936 -248
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +233 -135
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/cache-gc.ts +29 -0
- package/packages/omo-codex/plugin/components/codegraph/src/cli.ts +13 -0
- package/packages/omo-codex/plugin/components/codegraph/src/hook-sweep.ts +25 -0
- package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +3 -1
- package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +30 -1
- package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +16 -2
- package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +80 -0
- package/packages/omo-codex/plugin/components/codegraph/test/cache-gc.test.ts +34 -0
- package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +107 -0
- package/packages/omo-codex/plugin/components/codegraph/test/hook-registration.test.ts +26 -0
- package/packages/omo-codex/plugin/components/codegraph/test/hook-sweep.test.ts +29 -0
- package/packages/omo-codex/plugin/components/codegraph/test/hook.test.ts +8 -21
- package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +108 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-built-wrapper.test.ts +76 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +17 -111
- package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +45 -67
- package/packages/omo-codex/plugin/components/codegraph/test/sweep-cli.test.ts +56 -0
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/AGENTS.md +31 -0
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/AGENTS.md +45 -0
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus.md +37 -102
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/directive.md +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/AGENTS.md +46 -0
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +6 -0
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +6 -0
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/AGENTS.md +5 -2
- package/packages/omo-codex/plugin/components/ultrawork/CHANGELOG.md +2 -0
- package/packages/omo-codex/plugin/components/ultrawork/README.md +3 -3
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +18 -10
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +51 -2
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +2 -1
- package/packages/omo-codex/plugin/components/ultrawork/scripts/sync-directive.mjs +14 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +388 -0
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +16 -2
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +4 -4
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-unclear.md +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/src/codex-hook.ts +5 -3
- package/packages/omo-codex/plugin/components/ultrawork/src/skill-pointer.ts +56 -0
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +26 -5
- package/packages/omo-codex/plugin/components/ultrawork/test/directive-source.test.ts +12 -0
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +1 -0
- package/packages/omo-codex/plugin/components/ultrawork/test/skill-pointer.test.ts +82 -0
- package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +5 -0
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +18 -10
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +15 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +145 -32
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-hook.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-hook.js +5 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +55 -9
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-snapshot.d.ts +15 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-snapshot.js +33 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-types.d.ts +10 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +15 -11
- package/packages/omo-codex/plugin/components/ulw-loop/dist/ultrawork-directive.d.ts +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/ultrawork-directive.js +5 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/ultrawork-skill-pointer.d.ts +7 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/ultrawork-skill-pointer.js +46 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +7 -7
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +17 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-hook.ts +5 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +59 -11
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-snapshot.ts +38 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-types.ts +11 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +15 -7
- package/packages/omo-codex/plugin/components/ulw-loop/src/ultrawork-directive.ts +9 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/ultrawork-skill-pointer.ts +55 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-create-goals.test.ts +16 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/plan-io.test.ts +260 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/steering-snapshot.test.ts +124 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/steering.test.ts +101 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/ultrawork-directive.test.ts +49 -0
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +13 -13
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/build-components.mjs +63 -17
- package/packages/omo-codex/plugin/scripts/hook-status-message.mjs +10 -6
- package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +10 -3
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +1 -0
- package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +18 -0
- package/packages/omo-codex/plugin/skills/debugging/references/methodology/06-fix.md +6 -0
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +10 -1
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +12 -7
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +8 -4
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/references/design/design-system-architecture.md +24 -2
- package/packages/omo-codex/plugin/skills/frontend/references/design/lazyweb.md +77 -0
- package/packages/omo-codex/plugin/skills/frontend/references/design/stitch-design-example.md +121 -0
- package/packages/omo-codex/plugin/skills/frontend/references/designpowers/README.md +2 -2
- package/packages/omo-codex/plugin/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
- package/packages/omo-codex/plugin/skills/programming/SKILL.md +10 -1
- package/packages/omo-codex/plugin/skills/programming/references/logging.md +91 -0
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +388 -0
- package/packages/omo-codex/plugin/skills/ultrawork/agents/openai.yaml +2 -0
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +7 -7
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +16 -2
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +4 -4
- package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-unclear.md +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/ATTRIBUTION.md +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +28 -7
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +13 -17
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +4 -4
- package/packages/omo-codex/plugin/test/bootstrap-hooks.test.mjs +1 -1
- package/packages/omo-codex/plugin/test/hook-status-message.test.mjs +22 -9
- package/packages/omo-codex/plugin/test/install-time-build-runtime.test.mjs +1 -1
- package/packages/omo-codex/plugin/test/sync-hook-status-messages.test.mjs +6 -6
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +2 -0
- package/packages/omo-codex/plugin/test/ultrawork-skill-pointer.test.mjs +56 -0
- package/packages/omo-codex/plugin/test/ulw-plan-skill-contract.test.mjs +52 -0
- package/packages/omo-codex/plugin/test/ulw-research-epistemic-contract.test.mjs +98 -0
- package/packages/omo-codex/plugin/test/ulw-research-skill-contract.test.mjs +6 -6
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +310 -3979
- package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +2 -2
- package/packages/shared-skills/skills/debugging/references/methodology/06-fix.md +6 -0
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +10 -1
- package/packages/shared-skills/skills/frontend/SKILL.md +12 -7
- package/packages/shared-skills/skills/frontend/references/design/README.md +8 -4
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +2 -2
- package/packages/shared-skills/skills/frontend/references/design/design-system-architecture.md +24 -2
- package/packages/shared-skills/skills/frontend/references/design/lazyweb.md +77 -0
- package/packages/shared-skills/skills/frontend/references/design/stitch-design-example.md +121 -0
- package/packages/shared-skills/skills/frontend/references/designpowers/README.md +2 -2
- package/packages/shared-skills/skills/frontend/references/designpowers/lane-b-execution.md +1 -1
- package/packages/shared-skills/skills/programming/SKILL.md +10 -1
- package/packages/shared-skills/skills/programming/references/logging.md +91 -0
- package/packages/shared-skills/skills/start-work/SKILL.md +2 -2
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +5 -5
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +7 -7
- package/packages/shared-skills/skills/ulw-plan/references/intent-unclear.md +3 -3
- package/packages/shared-skills/skills/ulw-research/ATTRIBUTION.md +2 -2
- package/packages/shared-skills/skills/ulw-research/SKILL.md +28 -7
- package/packages/shared-skills/skills/visual-qa/SKILL.md +13 -17
- package/script/qa/strip-ansi.mjs +10 -0
- package/script/qa/web-terminal-visual-qa.mjs +112 -195
- package/script/qa/xterm-live-terminal.mjs +180 -0
- package/script/qa/web-terminal-renderer.mjs +0 -218
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
|
|
4
|
+
import { ULTRAWORK_DIRECTIVE } from "./directive.js";
|
|
5
|
+
|
|
6
|
+
export const ULTRAWORK_SKILL_POINTER_TEMPLATE = `<ultrawork-mode>
|
|
7
|
+
ULTRAWORK MODE IS ACTIVE FOR THIS TASK.
|
|
8
|
+
|
|
9
|
+
MANDATORY BOOTSTRAP: do all three steps, in order, before anything else.
|
|
10
|
+
|
|
11
|
+
1. First user-visible line this turn MUST be exactly:
|
|
12
|
+
\`ULTRAWORK MODE ENABLED!\`
|
|
13
|
+
|
|
14
|
+
2. Call \`create_goal\` NOW with \`objective\` set to the user's request.
|
|
15
|
+
Send \`objective\` only: no \`status\`, no budget fields. If the
|
|
16
|
+
\`create_goal\` tool is unavailable, open your reply with a binding
|
|
17
|
+
\`# Goal\` block instead. Never skip this step.
|
|
18
|
+
|
|
19
|
+
3. Read the FULL ultrawork directive NOW, before any other tool call,
|
|
20
|
+
plan, or edit. It is the \`ultrawork\` skill, stored at:
|
|
21
|
+
|
|
22
|
+
{{ULTRAWORK_SKILL_PATH}}
|
|
23
|
+
|
|
24
|
+
Read the whole file. If a read result comes back truncated, keep
|
|
25
|
+
reading the remaining line ranges until you have seen every line.
|
|
26
|
+
Every rule in that file is binding for this entire task: no
|
|
27
|
+
compromise, no summarizing from memory, no skipping. If the file does
|
|
28
|
+
not exist, tell the user the omo ultrawork skill is missing and
|
|
29
|
+
continue with steps 1 and 2 plus evidence-bound execution.
|
|
30
|
+
|
|
31
|
+
Do not start the requested work until all three steps are complete.
|
|
32
|
+
</ultrawork-mode>
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
const ULTRAWORK_SKILL_PATH_PLACEHOLDER = "{{ULTRAWORK_SKILL_PATH}}";
|
|
36
|
+
const ULTRAWORK_SKILL_FILE_URL = new URL("../../../skills/ultrawork/SKILL.md", import.meta.url);
|
|
37
|
+
|
|
38
|
+
export interface UltraworkAdditionalContextOptions {
|
|
39
|
+
readonly skillFilePath?: string | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveUltraworkSkillFilePath(): string {
|
|
43
|
+
return fileURLToPath(ULTRAWORK_SKILL_FILE_URL);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function buildUltraworkSkillPointer(skillFilePath: string): string {
|
|
47
|
+
return ULTRAWORK_SKILL_POINTER_TEMPLATE.replace(ULTRAWORK_SKILL_PATH_PLACEHOLDER, skillFilePath);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function buildUltraworkAdditionalContext(options: UltraworkAdditionalContextOptions = {}): string {
|
|
51
|
+
const skillFilePath = options.skillFilePath === undefined ? resolveUltraworkSkillFilePath() : options.skillFilePath;
|
|
52
|
+
if (skillFilePath !== null && existsSync(skillFilePath)) {
|
|
53
|
+
return buildUltraworkSkillPointer(skillFilePath);
|
|
54
|
+
}
|
|
55
|
+
return ULTRAWORK_DIRECTIVE;
|
|
56
|
+
}
|
|
@@ -183,14 +183,14 @@ describe("codex ultrawork hook", () => {
|
|
|
183
183
|
};
|
|
184
184
|
|
|
185
185
|
// when
|
|
186
|
-
const output = runUserPromptSubmitHook(payload);
|
|
186
|
+
const output = runUserPromptSubmitHook(payload, { skillFilePath: null });
|
|
187
187
|
const parsed = parseHookOutput(output);
|
|
188
188
|
|
|
189
189
|
// then
|
|
190
190
|
expect(parsed.hookSpecificOutput.additionalContext).toMatch(/# Manual-QA channels/);
|
|
191
191
|
expect(parsed.hookSpecificOutput.additionalContext).toMatch(/TESTS ALONE NEVER PROVE DONE/);
|
|
192
192
|
expect(parsed.hookSpecificOutput.additionalContext).toMatch(/1\. HTTP call/);
|
|
193
|
-
expect(parsed.hookSpecificOutput.additionalContext).toMatch(/2\.
|
|
193
|
+
expect(parsed.hookSpecificOutput.additionalContext).toMatch(/2\. Terminal \/ TUI/);
|
|
194
194
|
expect(parsed.hookSpecificOutput.additionalContext).toMatch(/3\. Browser use/);
|
|
195
195
|
expect(parsed.hookSpecificOutput.additionalContext).toMatch(/4\. Computer use/);
|
|
196
196
|
expect(parsed.hookSpecificOutput.additionalContext).toMatch(/CLEANUP \(PAIRED/);
|
|
@@ -209,7 +209,7 @@ describe("codex ultrawork hook", () => {
|
|
|
209
209
|
};
|
|
210
210
|
|
|
211
211
|
// when
|
|
212
|
-
const output = runUserPromptSubmitHook(payload);
|
|
212
|
+
const output = runUserPromptSubmitHook(payload, { skillFilePath: null });
|
|
213
213
|
const parsed = parseHookOutput(output);
|
|
214
214
|
|
|
215
215
|
// then
|
|
@@ -230,7 +230,7 @@ describe("codex ultrawork hook", () => {
|
|
|
230
230
|
};
|
|
231
231
|
|
|
232
232
|
// when
|
|
233
|
-
const output = runUserPromptSubmitHook(payload);
|
|
233
|
+
const output = runUserPromptSubmitHook(payload, { skillFilePath: null });
|
|
234
234
|
const parsed = parseHookOutput(output);
|
|
235
235
|
|
|
236
236
|
// then
|
|
@@ -244,6 +244,27 @@ describe("codex ultrawork hook", () => {
|
|
|
244
244
|
expect(directive).toMatch(/WORKING:/);
|
|
245
245
|
});
|
|
246
246
|
|
|
247
|
+
it("#given directive #when inspected #then blocks dependent work until spawned planners finish", () => {
|
|
248
|
+
// given
|
|
249
|
+
const payload = {
|
|
250
|
+
hook_event_name: "UserPromptSubmit",
|
|
251
|
+
prompt: "ulw",
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// when
|
|
255
|
+
const output = runUserPromptSubmitHook(payload, { skillFilePath: null });
|
|
256
|
+
const parsed = parseHookOutput(output);
|
|
257
|
+
|
|
258
|
+
// then
|
|
259
|
+
const directive = parsed.hookSpecificOutput.additionalContext;
|
|
260
|
+
expect(directive).toMatch(/Subagent-dependent transition barrier/);
|
|
261
|
+
expect(directive).toMatch(/Spawn every independent child for the current wave first/);
|
|
262
|
+
expect(directive).toMatch(/After the wave\s+is launched[\s\S]{0,240}wait_agent[\s\S]{0,240}terminal status/);
|
|
263
|
+
expect(directive).not.toMatch(/Immediately after any `multi_agent_v1\.spawn_agent`/);
|
|
264
|
+
expect(directive).toMatch(/Do not start dependent implementation/);
|
|
265
|
+
expect(directive).toMatch(/Do not mark an `update_plan` step `completed`/);
|
|
266
|
+
});
|
|
267
|
+
|
|
247
268
|
it("#given directive #when inspected #then keeps impact-proportional sizing invariants", () => {
|
|
248
269
|
// given
|
|
249
270
|
const payload = {
|
|
@@ -252,7 +273,7 @@ describe("codex ultrawork hook", () => {
|
|
|
252
273
|
};
|
|
253
274
|
|
|
254
275
|
// when
|
|
255
|
-
const output = runUserPromptSubmitHook(payload);
|
|
276
|
+
const output = runUserPromptSubmitHook(payload, { skillFilePath: null });
|
|
256
277
|
const parsed = parseHookOutput(output);
|
|
257
278
|
|
|
258
279
|
// then
|
|
@@ -13,4 +13,16 @@ describe("codex ultrawork directive source", () => {
|
|
|
13
13
|
// then
|
|
14
14
|
expect(codexPrompt).toBe(directive);
|
|
15
15
|
});
|
|
16
|
+
|
|
17
|
+
it("#given bundled ultrawork skill #when compared to the directive #then it wraps the same bytes with skill frontmatter", () => {
|
|
18
|
+
// given
|
|
19
|
+
const directive = readFileSync("directive.md", "utf8");
|
|
20
|
+
|
|
21
|
+
// when
|
|
22
|
+
const skill = readFileSync("skills/ultrawork/SKILL.md", "utf8");
|
|
23
|
+
|
|
24
|
+
// then
|
|
25
|
+
expect(skill.startsWith("---\nname: ultrawork\n")).toBe(true);
|
|
26
|
+
expect(skill.endsWith(directive)).toBe(true);
|
|
27
|
+
});
|
|
16
28
|
});
|
|
@@ -31,6 +31,7 @@ describe("codex ultrawork package metadata", () => {
|
|
|
31
31
|
expect(scripts["test"]).toBe("vitest --run");
|
|
32
32
|
expect(packageFiles).toContain("dist");
|
|
33
33
|
expect(packageFiles).toContain("directive.md");
|
|
34
|
+
expect(packageFiles).toContain("skills");
|
|
34
35
|
expect(packageFiles).not.toContain("hooks/ultrawork-detector.py");
|
|
35
36
|
expect(cliSource.startsWith("#!/usr/bin/env node")).toBe(true);
|
|
36
37
|
expect(hookCommands).toContain(`node "${pluginRoot}/dist/cli.js" hook user-prompt-submit`);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
6
|
+
|
|
7
|
+
import { runUserPromptSubmitHook } from "../src/codex-hook.js";
|
|
8
|
+
import { ULTRAWORK_DIRECTIVE } from "../src/directive.js";
|
|
9
|
+
import { buildUltraworkAdditionalContext, buildUltraworkSkillPointer } from "../src/skill-pointer.js";
|
|
10
|
+
import { parseHookOutput } from "./codex-hook-test-helpers.js";
|
|
11
|
+
|
|
12
|
+
const POINTER_MAX_BYTES = 4096;
|
|
13
|
+
const tempDirectories: string[] = [];
|
|
14
|
+
|
|
15
|
+
function makeTempDirectory(): string {
|
|
16
|
+
const directory = mkdtempSync(join(tmpdir(), "ultrawork-skill-"));
|
|
17
|
+
tempDirectories.push(directory);
|
|
18
|
+
return directory;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function writeSkillFile(): string {
|
|
22
|
+
const skillFilePath = join(makeTempDirectory(), "SKILL.md");
|
|
23
|
+
writeFileSync(skillFilePath, "---\nname: ultrawork\n---\n\n<ultrawork-mode>\ndirective body\n</ultrawork-mode>\n");
|
|
24
|
+
return skillFilePath;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
for (const directory of tempDirectories.splice(0)) {
|
|
29
|
+
rmSync(directory, { recursive: true, force: true });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("ultrawork skill pointer", () => {
|
|
34
|
+
it("#given an existing skill file #when context is built #then emits the compact pointer with the absolute path", () => {
|
|
35
|
+
// given
|
|
36
|
+
const skillFilePath = writeSkillFile();
|
|
37
|
+
|
|
38
|
+
// when
|
|
39
|
+
const context = buildUltraworkAdditionalContext({ skillFilePath });
|
|
40
|
+
|
|
41
|
+
// then
|
|
42
|
+
expect(context).toBe(buildUltraworkSkillPointer(skillFilePath));
|
|
43
|
+
expect(context.startsWith("<ultrawork-mode>")).toBe(true);
|
|
44
|
+
expect(context).toContain(skillFilePath);
|
|
45
|
+
expect(context).toContain("First user-visible line this turn MUST be exactly:");
|
|
46
|
+
expect(context).toContain("create_goal");
|
|
47
|
+
expect(context).not.toContain("Tier triage");
|
|
48
|
+
expect(Buffer.byteLength(context, "utf8")).toBeLessThan(POINTER_MAX_BYTES);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("#given a missing skill file #when context is built #then falls back to the full directive", () => {
|
|
52
|
+
// given
|
|
53
|
+
const missingSkillFilePath = join(makeTempDirectory(), "SKILL.md");
|
|
54
|
+
|
|
55
|
+
// when
|
|
56
|
+
const context = buildUltraworkAdditionalContext({ skillFilePath: missingSkillFilePath });
|
|
57
|
+
|
|
58
|
+
// then
|
|
59
|
+
expect(context).toBe(ULTRAWORK_DIRECTIVE);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("#given a null skill path #when context is built #then falls back to the full directive", () => {
|
|
63
|
+
expect(buildUltraworkAdditionalContext({ skillFilePath: null })).toBe(ULTRAWORK_DIRECTIVE);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("#given the hook runs with a skill file #when prompt is ulw #then hook JSON carries the pointer", () => {
|
|
67
|
+
// given
|
|
68
|
+
const skillFilePath = writeSkillFile();
|
|
69
|
+
|
|
70
|
+
// when
|
|
71
|
+
const output = runUserPromptSubmitHook(
|
|
72
|
+
{ hook_event_name: "UserPromptSubmit", prompt: "ulw this change" },
|
|
73
|
+
{ skillFilePath },
|
|
74
|
+
);
|
|
75
|
+
const parsed = parseHookOutput(output);
|
|
76
|
+
|
|
77
|
+
// then
|
|
78
|
+
expect(parsed.hookSpecificOutput.hookEventName).toBe("UserPromptSubmit");
|
|
79
|
+
expect(parsed.hookSpecificOutput.additionalContext).toMatch(/^<ultrawork-mode>/);
|
|
80
|
+
expect(parsed.hookSpecificOutput.additionalContext).toContain(skillFilePath);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## [0.1.0] - unreleased
|
|
4
4
|
|
|
5
|
+
- **Memory:** steering ledger entries no longer embed the full plan four times (`before`/`after` at both the audit and entry level). Accepted steers now record a compact `UlwLoopSteeringPlanSnapshot` (plan counters + only the goals the mutation touched), shrinking a measured real-world entry from 189KB to 7.8KB (~24x) and ending quadratic `ledger.jsonl` growth over long runs.
|
|
6
|
+
- **Memory:** steering dedup (`--idempotency-key` / `promptSignature`) streams the ledger line-by-line with a substring pre-filter instead of `JSON.parse`-ing every entry into memory; dedup returns strip legacy full-plan `before`/`after` payloads from re-surfaced audits. `readSteeringLedgerEntries` streams too.
|
|
7
|
+
- **Memory:** `withUlwLoopMutationLock` no longer retains the mutation result (full plan/audit) per `(repo, scope)` in its module-level lock map; settled gates self-evict, so long-lived embedders stop accumulating entries.
|
|
8
|
+
- **Fix:** `omo ulw-loop steer --idempotency-key` was parsed but never forwarded into the proposal, so CLI steers never deduped. It is now wired through.
|
|
9
|
+
- Standalone ultrawork injection (`--with-ultrawork`) now emits the same compact bootstrap pointer as the ultrawork component (opener mandate, `create_goal` with `objective` only, read the bundled `ultrawork` skill at a runtime-resolved absolute path), falling back to the full bundled directive when the plugin skills tree is absent. Keeps the injected payload below Codex App's hook-output truncation budget (code-yeongyu/oh-my-openagent#5828).
|
|
5
10
|
- Initial scaffold of codex-ulw-loop plugin.
|
|
6
11
|
- Per-Criterion Cycle: `EXECUTE` is now **EXECUTE-AS-SCENARIO** — the agent must run the Manual-QA channel scenario the criterion named (HTTP call / tmux / browser use / computer use; see new `## Manual-QA channels` section). Inserted a new **CLEAN (PAIRED, NEVER SKIP)** step that tears down every QA-spawned process / `tmux` session / browser context / container / port / temp dir before recording evidence; the cleanup receipt is embedded in the `--evidence` string. Missing receipt → record BLOCKED, not PASS. Added Constraint #13 and a Stop Rule for leftover state.
|
|
7
12
|
- New top-level **`## Manual-QA channels`** section explicitly enumerates the four channels (HTTP call, tmux, Browser use, Computer use) with concrete commands and required artifacts. Goal section now declares **TESTS ALONE NEVER PROVE DONE**: a green test suite is supporting evidence, never completion proof. Criterion-refinement step 2 requires each criterion to name its channel up front.
|
|
@@ -49,9 +49,10 @@ exercises the surface; capture the artifact.
|
|
|
49
49
|
1. HTTP call — hit the live endpoint with `curl -i` (or a
|
|
50
50
|
Playwright APIRequestContext); capture status line + headers +
|
|
51
51
|
body.
|
|
52
|
-
2.
|
|
53
|
-
|
|
54
|
-
is
|
|
52
|
+
2. Terminal / TUI - drive a real pty and prove it through the
|
|
53
|
+
xterm.js web terminal (see the TUI visual QA note below). tmux
|
|
54
|
+
`send-keys` is fine for a boot smoke; NEVER `tmux capture-pane`
|
|
55
|
+
for color / layout / CJK evidence, which degrades truecolor.
|
|
55
56
|
3. Browser use — in Codex, use `browser:control-in-app-browser`
|
|
56
57
|
first when available and no authenticated/persistent user browser
|
|
57
58
|
profile is required. Otherwise use Chrome to drive the REAL page;
|
|
@@ -79,13 +80,13 @@ channel scenario when the behavior is user-facing. `--dry-run`,
|
|
|
79
80
|
printing the command, "should respond", and "looks correct" never
|
|
80
81
|
count.
|
|
81
82
|
|
|
82
|
-
For TUI visual QA,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
For TUI visual QA, render the terminal through the real xterm.js web
|
|
84
|
+
terminal and screenshot it - never a `tmux capture-pane` dump, which
|
|
85
|
+
degrades color and wide-glyph width. In this repo:
|
|
86
|
+
`node script/qa/web-terminal-visual-qa.mjs --title "<surface>" --command "<cmd>" --input "{Enter}" --evidence-dir <dir>`
|
|
87
|
+
(live pty + xterm.js in Chrome; `--from-file <capture>` replays a raw
|
|
88
|
+
stream). Outside this repo, capture equivalent browser-rendered terminal
|
|
89
|
+
evidence: screenshot + plain transcript + cleanup receipt.
|
|
89
90
|
|
|
90
91
|
# Bootstrap (DO ALL FOUR BEFORE ANY OTHER WORK — NO SKIPPING)
|
|
91
92
|
|
|
@@ -287,6 +288,13 @@ evidence for that step. Do not start dependent implementation until the
|
|
|
287
288
|
audit, research, or review result is integrated or explicitly recorded
|
|
288
289
|
as inconclusive. Do not generate a plan before spawned research lanes
|
|
289
290
|
that feed the plan have returned or been closed as inconclusive.
|
|
291
|
+
Spawn every independent child for the current wave first. After the wave
|
|
292
|
+
is launched, run `multi_agent_v1.wait_agent` for each spawned child until
|
|
293
|
+
each reaches terminal status (`completed`, `failed`, `blocked`, or
|
|
294
|
+
explicitly recorded inconclusive) before any dependent `update_plan`
|
|
295
|
+
transition, `create_goal` continuation, implementation tool call, plan
|
|
296
|
+
drafting, approval-gate work, PR handoff, or final response. A timeout is
|
|
297
|
+
not terminal status.
|
|
290
298
|
Do not write the final answer, PR handoff, or completion summary while
|
|
291
299
|
active child agents remain open. Use short `multi_agent_v1.wait_agent` cycles.
|
|
292
300
|
After two silent waits send `TASK STILL ACTIVE: return <deliverable> or
|
|
@@ -24,8 +24,8 @@ export async function ulwLoopCommand(argv) {
|
|
|
24
24
|
const rest = argv.slice(1);
|
|
25
25
|
const repoRoot = process.cwd();
|
|
26
26
|
const json = hasFlag(rest, "--json");
|
|
27
|
-
const scope = commandScope(rest);
|
|
28
27
|
try {
|
|
28
|
+
const scope = commandScope(rest);
|
|
29
29
|
if (!isUlwLoopSubcommand(command)) {
|
|
30
30
|
if (json) {
|
|
31
31
|
printJsonError(new UlwLoopError(`Unknown ulw-loop subcommand: ${command}.`, "ULW_LOOP_SUBCOMMAND_UNKNOWN", {
|
|
@@ -79,7 +79,20 @@ export async function ulwLoopCommand(argv) {
|
|
|
79
79
|
function unhandledSubcommand(command) {
|
|
80
80
|
throw new UlwLoopError(`Unhandled ulw-loop subcommand: ${String(command)}.`, "ULW_LOOP_SUBCOMMAND_UNHANDLED");
|
|
81
81
|
}
|
|
82
|
+
const SESSION_ID_FLAG = "--session-id";
|
|
83
|
+
function sessionIdFlagPresent(argv) {
|
|
84
|
+
return hasFlag(argv, SESSION_ID_FLAG) || argv.some((arg) => arg.startsWith(`${SESSION_ID_FLAG}=`));
|
|
85
|
+
}
|
|
82
86
|
function commandScope(argv) {
|
|
83
|
-
|
|
87
|
+
if (sessionIdFlagPresent(argv)) {
|
|
88
|
+
const sessionId = readValue(argv, SESSION_ID_FLAG)?.trim();
|
|
89
|
+
if (!sessionId) {
|
|
90
|
+
throw new UlwLoopError(`${SESSION_ID_FLAG} requires a non-empty value.`, "ULW_LOOP_SESSION_ID_REQUIRED", {
|
|
91
|
+
details: { flag: SESSION_ID_FLAG },
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return { sessionId };
|
|
95
|
+
}
|
|
96
|
+
const sessionId = resolveUlwLoopSessionIdFromEnv();
|
|
84
97
|
return sessionId === null ? undefined : { sessionId };
|
|
85
98
|
}
|
|
@@ -83,7 +83,8 @@ function neverKind(kind) { return fail(`Unsupported steering kind: ${String(kind
|
|
|
83
83
|
export async function parseSteeringProposal(argv) {
|
|
84
84
|
const kind = parseSteeringKind(argv);
|
|
85
85
|
const source = parseSteeringSource(argv);
|
|
86
|
-
const
|
|
86
|
+
const idempotencyKey = text(readValue(argv, "--idempotency-key"), "--idempotency-key");
|
|
87
|
+
const base = { kind, source, evidence: required(argv, "--evidence"), rationale: required(argv, "--rationale"), ...(idempotencyKey === undefined ? {} : { idempotencyKey }) };
|
|
87
88
|
switch (kind) {
|
|
88
89
|
case "add_subgoal": return normalizeSteeringProposal({ ...base, title: required(argv, "--title"), objective: required(argv, "--objective") });
|
|
89
90
|
case "split_subgoal": {
|
|
@@ -521,7 +521,9 @@ function formatCodexGoalReconciliation(reconciliation) {
|
|
|
521
521
|
}
|
|
522
522
|
|
|
523
523
|
// components/ulw-loop/src/plan-io.ts
|
|
524
|
+
import { createReadStream } from "node:fs";
|
|
524
525
|
import { appendFile, mkdir, readFile as readFile4, rename, writeFile } from "node:fs/promises";
|
|
526
|
+
import { createInterface } from "node:readline";
|
|
525
527
|
var LEGACY_OBJECTIVE_PREFIX = `Complete all ulw-loop stories in ${ULW_LOOP_DIR}/${ULW_LOOP_GOALS}: `;
|
|
526
528
|
var LEGACY_OBJECTIVE = `Complete all ulw-loop stories listed in ${ULW_LOOP_DIR}/${ULW_LOOP_GOALS}. Use ${ULW_LOOP_DIR}/${ULW_LOOP_LEDGER} as the durable audit trail.`;
|
|
527
529
|
var locks = new Map;
|
|
@@ -540,11 +542,18 @@ async function withUlwLoopMutationLock(repoRoot, scopeOrFn, maybeFn) {
|
|
|
540
542
|
if (fn === undefined)
|
|
541
543
|
throw new UlwLoopError("Missing ulw-loop mutation body.", "ULW_LOOP_LOCK_BODY_MISSING");
|
|
542
544
|
const lockKey = `${repoRoot}\x00${ulwLoopRelativeDir(scope)}`;
|
|
543
|
-
const prior = locks.get(lockKey) ?? Promise.resolve();
|
|
545
|
+
const prior = locks.get(lockKey) ?? Promise.resolve(undefined);
|
|
544
546
|
const run = prior.then(fn, fn);
|
|
545
|
-
|
|
547
|
+
const gate = run.then(() => {
|
|
546
548
|
return;
|
|
547
|
-
})
|
|
549
|
+
}, () => {
|
|
550
|
+
return;
|
|
551
|
+
});
|
|
552
|
+
locks.set(lockKey, gate);
|
|
553
|
+
gate.then(() => {
|
|
554
|
+
if (locks.get(lockKey) === gate)
|
|
555
|
+
locks.delete(lockKey);
|
|
556
|
+
});
|
|
548
557
|
return run;
|
|
549
558
|
}
|
|
550
559
|
async function readUlwLoopPlan(repoRoot, scope) {
|
|
@@ -591,22 +600,36 @@ async function appendLedger(repoRoot, entry, scope) {
|
|
|
591
600
|
await appendFile(ulwLoopLedgerPath(repoRoot, scope), `${JSON.stringify(entry)}
|
|
592
601
|
`, "utf8");
|
|
593
602
|
}
|
|
594
|
-
async function
|
|
595
|
-
|
|
603
|
+
async function* ledgerLines(repoRoot, scope) {
|
|
604
|
+
const stream = createReadStream(ulwLoopLedgerPath(repoRoot, scope), { encoding: "utf8" });
|
|
605
|
+
const lines = createInterface({ input: stream, crlfDelay: Number.POSITIVE_INFINITY });
|
|
596
606
|
try {
|
|
597
|
-
|
|
607
|
+
for await (const line of lines) {
|
|
608
|
+
if (line.trim().length > 0)
|
|
609
|
+
yield line;
|
|
610
|
+
}
|
|
598
611
|
} catch (error) {
|
|
599
|
-
if (hasCode(error, "ENOENT"))
|
|
600
|
-
|
|
601
|
-
|
|
612
|
+
if (!hasCode(error, "ENOENT"))
|
|
613
|
+
throw error;
|
|
614
|
+
} finally {
|
|
615
|
+
lines.close();
|
|
616
|
+
stream.destroy();
|
|
602
617
|
}
|
|
603
|
-
|
|
604
|
-
|
|
618
|
+
}
|
|
619
|
+
async function findAcceptedSteeringLedgerEntry(repoRoot, key, scope) {
|
|
620
|
+
const probe = JSON.stringify(key);
|
|
621
|
+
for await (const line of ledgerLines(repoRoot, scope)) {
|
|
622
|
+
if (!line.includes(probe))
|
|
623
|
+
continue;
|
|
605
624
|
const entry = JSON.parse(line);
|
|
606
|
-
if (isSteeringKind(entry.kind))
|
|
607
|
-
|
|
625
|
+
if (!isSteeringKind(entry.kind))
|
|
626
|
+
continue;
|
|
627
|
+
if (entry.steering?.invariant.accepted !== true)
|
|
628
|
+
continue;
|
|
629
|
+
if (entry.idempotencyKey === key || entry.steering.idempotencyKey === key || entry.steering.promptSignature === key)
|
|
630
|
+
return entry;
|
|
608
631
|
}
|
|
609
|
-
return
|
|
632
|
+
return;
|
|
610
633
|
}
|
|
611
634
|
|
|
612
635
|
// components/ulw-loop/src/evidence.ts
|
|
@@ -1254,7 +1277,8 @@ function neverKind(kind) {
|
|
|
1254
1277
|
async function parseSteeringProposal(argv) {
|
|
1255
1278
|
const kind = parseSteeringKind(argv);
|
|
1256
1279
|
const source = parseSteeringSource(argv);
|
|
1257
|
-
const
|
|
1280
|
+
const idempotencyKey = text(readValue(argv, "--idempotency-key"), "--idempotency-key");
|
|
1281
|
+
const base = { kind, source, evidence: required2(argv, "--evidence"), rationale: required2(argv, "--rationale"), ...idempotencyKey === undefined ? {} : { idempotencyKey } };
|
|
1258
1282
|
switch (kind) {
|
|
1259
1283
|
case "add_subgoal":
|
|
1260
1284
|
return normalizeSteeringProposal({ ...base, title: required2(argv, "--title"), objective: required2(argv, "--objective") });
|
|
@@ -1725,6 +1749,30 @@ async function recordFinalReviewBlockers(repoRoot, args, scope) {
|
|
|
1725
1749
|
});
|
|
1726
1750
|
}
|
|
1727
1751
|
|
|
1752
|
+
// components/ulw-loop/src/steering-snapshot.ts
|
|
1753
|
+
function buildSteeringPlanSnapshot(plan, changedGoalIds) {
|
|
1754
|
+
const snapshot = {
|
|
1755
|
+
updatedAt: plan.updatedAt,
|
|
1756
|
+
goalCount: plan.goals.length,
|
|
1757
|
+
goalIds: plan.goals.map((goal) => goal.id),
|
|
1758
|
+
goals: plan.goals.filter((goal) => changedGoalIds.has(goal.id))
|
|
1759
|
+
};
|
|
1760
|
+
return plan.activeGoalId === undefined ? snapshot : { ...snapshot, activeGoalId: plan.activeGoalId };
|
|
1761
|
+
}
|
|
1762
|
+
function changedGoalIdsBetween(before, after) {
|
|
1763
|
+
const beforeById = new Map(before.goals.map((goal) => [goal.id, goal]));
|
|
1764
|
+
const changed = new Set;
|
|
1765
|
+
for (const goal of after.goals) {
|
|
1766
|
+
const prior = beforeById.get(goal.id);
|
|
1767
|
+
if (prior === undefined || JSON.stringify(prior) !== JSON.stringify(goal))
|
|
1768
|
+
changed.add(goal.id);
|
|
1769
|
+
beforeById.delete(goal.id);
|
|
1770
|
+
}
|
|
1771
|
+
for (const id of beforeById.keys())
|
|
1772
|
+
changed.add(id);
|
|
1773
|
+
return changed;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1728
1776
|
// components/ulw-loop/src/steering.ts
|
|
1729
1777
|
var SOURCES2 = ["user_prompt_submit", "finding", "cli"];
|
|
1730
1778
|
var PROTECTED = new Set(["aggregateCompletion", "codexObjective", "codexObjectiveAliases", "originalConstraints", "qualityGate", "status", "completedAt", "completionStatus"]);
|
|
@@ -1982,15 +2030,20 @@ async function steerUlwLoop(repoRoot, proposal, scope) {
|
|
|
1982
2030
|
return withUlwLoopMutationLock(repoRoot, scope, async () => {
|
|
1983
2031
|
const plan = await readUlwLoopPlan(repoRoot, scope);
|
|
1984
2032
|
const key = proposal.idempotencyKey ?? proposal.promptSignature;
|
|
1985
|
-
const prior = key === undefined ? undefined :
|
|
1986
|
-
if (prior?.steering !== undefined)
|
|
1987
|
-
|
|
2033
|
+
const prior = key === undefined ? undefined : await findAcceptedSteeringLedgerEntry(repoRoot, key, scope);
|
|
2034
|
+
if (prior?.steering !== undefined) {
|
|
2035
|
+
const { before: _before, after: _after, ...compactPrior } = prior.steering;
|
|
2036
|
+
return { plan, accepted: true, audit: { ...compactPrior, deduped: true }, rejectedReasons: [], deduped: true };
|
|
2037
|
+
}
|
|
1988
2038
|
const audit = validateUlwLoopSteeringProposal(plan, proposal);
|
|
1989
2039
|
const accepted = audit.invariant.accepted;
|
|
1990
2040
|
const next = accepted ? applySteeringMutation(plan, proposal, audit) : plan;
|
|
1991
|
-
const finalAudit = { ...audit
|
|
1992
|
-
if (accepted)
|
|
1993
|
-
|
|
2041
|
+
const finalAudit = { ...audit };
|
|
2042
|
+
if (accepted) {
|
|
2043
|
+
const changed = changedGoalIdsBetween(plan, next);
|
|
2044
|
+
finalAudit.before = buildSteeringPlanSnapshot(plan, changed);
|
|
2045
|
+
finalAudit.after = buildSteeringPlanSnapshot(next, changed);
|
|
2046
|
+
}
|
|
1994
2047
|
if (accepted)
|
|
1995
2048
|
await writePlan(repoRoot, next, scope);
|
|
1996
2049
|
await appendLedger(repoRoot, ledgerEntry(proposal, finalAudit, proposal.now?.toISOString() ?? iso()), scope);
|
|
@@ -2006,10 +2059,6 @@ function ledgerEntry(proposal, audit, at) {
|
|
|
2006
2059
|
entry.criterionId = proposal.criterionId;
|
|
2007
2060
|
if (proposal.idempotencyKey !== undefined)
|
|
2008
2061
|
entry.idempotencyKey = proposal.idempotencyKey;
|
|
2009
|
-
if (audit.before !== undefined)
|
|
2010
|
-
entry.before = audit.before;
|
|
2011
|
-
if (audit.after !== undefined)
|
|
2012
|
-
entry.after = audit.after;
|
|
2013
2062
|
return entry;
|
|
2014
2063
|
}
|
|
2015
2064
|
|
|
@@ -2205,8 +2254,8 @@ async function ulwLoopCommand(argv) {
|
|
|
2205
2254
|
const rest = argv.slice(1);
|
|
2206
2255
|
const repoRoot = process.cwd();
|
|
2207
2256
|
const json = hasFlag(rest, "--json");
|
|
2208
|
-
const scope = commandScope(rest);
|
|
2209
2257
|
try {
|
|
2258
|
+
const scope = commandScope(rest);
|
|
2210
2259
|
if (!isUlwLoopSubcommand(command)) {
|
|
2211
2260
|
if (json) {
|
|
2212
2261
|
printJsonError(new UlwLoopError(`Unknown ulw-loop subcommand: ${command}.`, "ULW_LOOP_SUBCOMMAND_UNKNOWN", {
|
|
@@ -2264,14 +2313,76 @@ async function ulwLoopCommand(argv) {
|
|
|
2264
2313
|
function unhandledSubcommand(command) {
|
|
2265
2314
|
throw new UlwLoopError(`Unhandled ulw-loop subcommand: ${String(command)}.`, "ULW_LOOP_SUBCOMMAND_UNHANDLED");
|
|
2266
2315
|
}
|
|
2316
|
+
var SESSION_ID_FLAG = "--session-id";
|
|
2317
|
+
function sessionIdFlagPresent(argv) {
|
|
2318
|
+
return hasFlag(argv, SESSION_ID_FLAG) || argv.some((arg) => arg.startsWith(`${SESSION_ID_FLAG}=`));
|
|
2319
|
+
}
|
|
2267
2320
|
function commandScope(argv) {
|
|
2268
|
-
|
|
2321
|
+
if (sessionIdFlagPresent(argv)) {
|
|
2322
|
+
const sessionId2 = readValue(argv, SESSION_ID_FLAG)?.trim();
|
|
2323
|
+
if (!sessionId2) {
|
|
2324
|
+
throw new UlwLoopError(`${SESSION_ID_FLAG} requires a non-empty value.`, "ULW_LOOP_SESSION_ID_REQUIRED", {
|
|
2325
|
+
details: { flag: SESSION_ID_FLAG }
|
|
2326
|
+
});
|
|
2327
|
+
}
|
|
2328
|
+
return { sessionId: sessionId2 };
|
|
2329
|
+
}
|
|
2330
|
+
const sessionId = resolveUlwLoopSessionIdFromEnv();
|
|
2269
2331
|
return sessionId === null ? undefined : { sessionId };
|
|
2270
2332
|
}
|
|
2271
2333
|
|
|
2272
2334
|
// components/ulw-loop/src/ultrawork-directive.ts
|
|
2273
|
-
import { readFileSync } from "node:fs";
|
|
2335
|
+
import { readFileSync as readFileSync2 } from "node:fs";
|
|
2336
|
+
|
|
2337
|
+
// components/ulw-loop/src/ultrawork-skill-pointer.ts
|
|
2338
|
+
import { existsSync as existsSync5, readFileSync } from "node:fs";
|
|
2339
|
+
import { fileURLToPath } from "node:url";
|
|
2340
|
+
var ULTRAWORK_SKILL_POINTER_TEMPLATE = `<ultrawork-mode>
|
|
2341
|
+
ULTRAWORK MODE IS ACTIVE FOR THIS TASK.
|
|
2342
|
+
|
|
2343
|
+
MANDATORY BOOTSTRAP: do all three steps, in order, before anything else.
|
|
2344
|
+
|
|
2345
|
+
1. First user-visible line this turn MUST be exactly:
|
|
2346
|
+
\`ULTRAWORK MODE ENABLED!\`
|
|
2347
|
+
|
|
2348
|
+
2. Call \`create_goal\` NOW with \`objective\` set to the user's request.
|
|
2349
|
+
Send \`objective\` only: no \`status\`, no budget fields. If the
|
|
2350
|
+
\`create_goal\` tool is unavailable, open your reply with a binding
|
|
2351
|
+
\`# Goal\` block instead. Never skip this step.
|
|
2352
|
+
|
|
2353
|
+
3. Read the FULL ultrawork directive NOW, before any other tool call,
|
|
2354
|
+
plan, or edit. It is the \`ultrawork\` skill, stored at:
|
|
2355
|
+
|
|
2356
|
+
{{ULTRAWORK_SKILL_PATH}}
|
|
2357
|
+
|
|
2358
|
+
Read the whole file. If a read result comes back truncated, keep
|
|
2359
|
+
reading the remaining line ranges until you have seen every line.
|
|
2360
|
+
Every rule in that file is binding for this entire task: no
|
|
2361
|
+
compromise, no summarizing from memory, no skipping. If the file does
|
|
2362
|
+
not exist, tell the user the omo ultrawork skill is missing and
|
|
2363
|
+
continue with steps 1 and 2 plus evidence-bound execution.
|
|
2364
|
+
|
|
2365
|
+
Do not start the requested work until all three steps are complete.
|
|
2366
|
+
</ultrawork-mode>
|
|
2367
|
+
`;
|
|
2368
|
+
var ULTRAWORK_SKILL_PATH_PLACEHOLDER = "{{ULTRAWORK_SKILL_PATH}}";
|
|
2369
|
+
var ULTRAWORK_SKILL_FILE_URL = new URL("../../../skills/ultrawork/SKILL.md", import.meta.url);
|
|
2274
2370
|
var ULTRAWORK_DIRECTIVE = readFileSync(new URL("../directive.md", import.meta.url), "utf8");
|
|
2371
|
+
function resolveUltraworkSkillFilePath() {
|
|
2372
|
+
return fileURLToPath(ULTRAWORK_SKILL_FILE_URL);
|
|
2373
|
+
}
|
|
2374
|
+
function buildUltraworkSkillPointer(skillFilePath) {
|
|
2375
|
+
return ULTRAWORK_SKILL_POINTER_TEMPLATE.replace(ULTRAWORK_SKILL_PATH_PLACEHOLDER, skillFilePath);
|
|
2376
|
+
}
|
|
2377
|
+
function buildUltraworkAdditionalContext(options = {}) {
|
|
2378
|
+
const skillFilePath = options.skillFilePath === undefined ? resolveUltraworkSkillFilePath() : options.skillFilePath;
|
|
2379
|
+
if (skillFilePath !== null && existsSync5(skillFilePath)) {
|
|
2380
|
+
return buildUltraworkSkillPointer(skillFilePath);
|
|
2381
|
+
}
|
|
2382
|
+
return ULTRAWORK_DIRECTIVE;
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
// components/ulw-loop/src/ultrawork-directive.ts
|
|
2275
2386
|
var ULTRAWORK_CURRENT_PROMPT_PATTERN = /(?:ultrawork|ulw)/i;
|
|
2276
2387
|
var ULTRAWORK_DIRECTIVE_MARKER = "<ultrawork-mode>";
|
|
2277
2388
|
var TRANSCRIPT_SEARCH_BYTES = 512000;
|
|
@@ -2284,14 +2395,14 @@ var CONTEXT_PRESSURE_MARKERS = [
|
|
|
2284
2395
|
"your input exceeds the context window",
|
|
2285
2396
|
"long threads and multiple compactions"
|
|
2286
2397
|
];
|
|
2287
|
-
function buildUltraworkDirectiveOutput(input) {
|
|
2398
|
+
function buildUltraworkDirectiveOutput(input, options = {}) {
|
|
2288
2399
|
if (isContextPressureRecoveryPrompt(input.prompt))
|
|
2289
2400
|
return "";
|
|
2290
2401
|
if (hasUltraworkDirectiveAlreadyInTranscript(input.transcript_path))
|
|
2291
2402
|
return "";
|
|
2292
2403
|
if (isContextPressureTranscript(input.transcript_path))
|
|
2293
2404
|
return "";
|
|
2294
|
-
return isUltraworkPrompt(input.prompt) ? formatAdditionalContextOutput(
|
|
2405
|
+
return isUltraworkPrompt(input.prompt) ? formatAdditionalContextOutput(buildUltraworkAdditionalContext(options)) : "";
|
|
2295
2406
|
}
|
|
2296
2407
|
function hasUltraworkDirectiveAlreadyInTranscript(transcriptPath) {
|
|
2297
2408
|
if (transcriptPath === undefined || transcriptPath === null)
|
|
@@ -2319,7 +2430,7 @@ function hasUltraworkDirectiveAlreadyInTranscript(transcriptPath) {
|
|
|
2319
2430
|
return false;
|
|
2320
2431
|
}
|
|
2321
2432
|
function readTranscriptTail(transcriptPath) {
|
|
2322
|
-
const rawTranscript =
|
|
2433
|
+
const rawTranscript = readFileSync2(transcriptPath);
|
|
2323
2434
|
return rawTranscript.subarray(Math.max(0, rawTranscript.byteLength - TRANSCRIPT_SEARCH_BYTES)).toString("utf8");
|
|
2324
2435
|
}
|
|
2325
2436
|
function isUltraworkPrompt(prompt) {
|
|
@@ -2409,7 +2520,9 @@ async function applyUserPromptUlwLoopSteering(payload, options = {}) {
|
|
|
2409
2520
|
if (proposal === null) {
|
|
2410
2521
|
if (hasSteeringDirectiveMarker(payload.prompt))
|
|
2411
2522
|
return "";
|
|
2412
|
-
|
|
2523
|
+
if (!options.includeUltraworkDirective)
|
|
2524
|
+
return "";
|
|
2525
|
+
return options.ultraworkSkillFilePath === undefined ? buildUltraworkDirectiveOutput(payload) : buildUltraworkDirectiveOutput(payload, { skillFilePath: options.ultraworkSkillFilePath });
|
|
2413
2526
|
}
|
|
2414
2527
|
const result = await steerUlwLoop(payload.cwd, proposal, payloadScope(payload));
|
|
2415
2528
|
if (!result.accepted)
|
|
@@ -10,6 +10,7 @@ export interface UserPromptSubmitPayload {
|
|
|
10
10
|
}
|
|
11
11
|
export interface UserPromptSubmitHookOptions {
|
|
12
12
|
readonly includeUltraworkDirective?: boolean;
|
|
13
|
+
readonly ultraworkSkillFilePath?: string | null;
|
|
13
14
|
}
|
|
14
15
|
export interface PreToolUsePayload {
|
|
15
16
|
readonly cwd: string;
|
|
@@ -36,7 +36,11 @@ export async function applyUserPromptUlwLoopSteering(payload, options = {}) {
|
|
|
36
36
|
if (proposal === null) {
|
|
37
37
|
if (hasSteeringDirectiveMarker(payload.prompt))
|
|
38
38
|
return "";
|
|
39
|
-
|
|
39
|
+
if (!options.includeUltraworkDirective)
|
|
40
|
+
return "";
|
|
41
|
+
return options.ultraworkSkillFilePath === undefined
|
|
42
|
+
? buildUltraworkDirectiveOutput(payload)
|
|
43
|
+
: buildUltraworkDirectiveOutput(payload, { skillFilePath: options.ultraworkSkillFilePath });
|
|
40
44
|
}
|
|
41
45
|
const result = await steerUlwLoop(payload.cwd, proposal, payloadScope(payload));
|
|
42
46
|
if (!result.accepted)
|