oh-my-opencode 5.0.0-beta.30 → 5.0.0-beta.32
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/dist/cli/index.js +178 -37
- package/dist/cli-node/index.js +178 -37
- package/dist/config/schema/background-task.d.ts +1 -0
- package/dist/config/schema/oh-my-opencode-config.d.ts +1 -0
- package/dist/features/background-agent/constants.d.ts +0 -7
- package/dist/features/background-agent/manager.d.ts +2 -1
- package/dist/features/background-agent/parent-wake-dispatched-tracker.d.ts +0 -1
- package/dist/features/background-agent/parent-wake-notifier-types.d.ts +1 -1
- package/dist/features/background-agent/parent-wake-prompt-dispatch.d.ts +1 -1
- package/dist/features/background-agent/parent-wake-session-inspector.d.ts +1 -1
- package/dist/features/background-agent/subagent-spawn-limits.d.ts +8 -0
- package/dist/features/monitor/manager-internals.d.ts +1 -1
- package/dist/features/monitor/output-injector-types.d.ts +1 -1
- package/dist/features/task-toast-manager/manager.d.ts +0 -24
- package/dist/features/team-mode/tools/messaging-live-delivery-recipient.d.ts +1 -0
- package/dist/features/team-mode/tools/messaging-runtime.d.ts +1 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/message-storage-directory.d.ts +0 -3
- package/dist/hooks/anthropic-context-window-limit-recovery/state.d.ts +1 -0
- package/dist/hooks/anthropic-context-window-limit-recovery/storage/empty-messages.d.ts +0 -1
- package/dist/hooks/anthropic-context-window-limit-recovery/tool-result-storage.d.ts +11 -0
- package/dist/hooks/claude-code-hooks/session-hook-state.d.ts +1 -0
- package/dist/hooks/claude-code-hooks/stop.d.ts +2 -0
- package/dist/hooks/claude-code-hooks/todo.d.ts +0 -1
- package/dist/hooks/claude-code-hooks/transcript.d.ts +2 -0
- package/dist/hooks/fsync-skip-warning/index.d.ts +4 -0
- package/dist/hooks/hashline-edit-diff-enhancer/hook.d.ts +1 -0
- package/dist/hooks/hashline-edit-diff-enhancer/pending-captures.d.ts +13 -0
- package/dist/hooks/keyword-detector/hook.d.ts +7 -0
- package/dist/hooks/keyword-detector/ultrawork/source-detector.d.ts +0 -1
- package/dist/hooks/model-fallback/hook.d.ts +1 -1
- package/dist/index.js +681 -273
- package/dist/oh-my-opencode.schema.json +5 -0
- package/dist/plugin-dispose.d.ts +3 -0
- package/dist/shared/agent-tool-restrictions.d.ts +0 -1
- package/dist/skills/ultimate-browsing/references/insane-search/README.md +9 -2
- package/dist/tools/delegate-task/sync-session-cleanup.d.ts +3 -0
- package/dist/tools/delegate-task/sync-session-poller.d.ts +1 -0
- package/dist/tools/delegate-task/types.d.ts +1 -0
- package/dist/tools/session-manager/directory-filter.d.ts +2 -0
- package/dist/tui.js +148 -9
- package/package.json +16 -15
- package/packages/git-bash-mcp/dist/cli.js +1 -0
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +47 -19
- package/packages/lsp-core/src/lsp/client-diagnostics-pull-timeout.test.ts +64 -0
- package/packages/lsp-core/src/lsp/client-wrapper.test.ts +97 -0
- package/packages/lsp-core/src/lsp/client-wrapper.ts +21 -6
- package/packages/lsp-core/src/lsp/client.ts +6 -3
- package/packages/lsp-core/src/lsp/constants.ts +5 -0
- package/packages/lsp-core/src/lsp/controlled-clock-test-support.ts +61 -0
- package/packages/lsp-core/src/lsp/errors.ts +15 -0
- package/packages/lsp-core/src/lsp/fixtures/workspace-edit-server.mjs +10 -3
- package/packages/lsp-core/src/lsp/manager-lifecycle.test.ts +262 -0
- package/packages/lsp-core/src/lsp/manager.ts +106 -13
- package/packages/lsp-core/src/lsp/timer-provider.ts +13 -0
- package/packages/lsp-core/src/lsp/transport-request-timeout.test.ts +73 -0
- package/packages/lsp-core/src/lsp/transport.ts +7 -2
- package/packages/lsp-core/src/lsp/workspace-document-state.test.ts +84 -1
- package/packages/lsp-core/src/lsp/workspace-document-state.ts +30 -11
- package/packages/lsp-core/src/lsp/workspace-markers.ts +18 -1
- package/packages/lsp-core/src/mcp.ts +3 -0
- package/packages/lsp-core/src/tools/parameters.test.ts +51 -0
- package/packages/lsp-daemon/dist/cli.js +155 -34
- package/packages/lsp-daemon/dist/client.js +155 -34
- package/packages/lsp-daemon/dist/index.js +155 -34
- package/packages/lsp-daemon/dist/proxy.js +3 -0
- package/packages/lsp-tools-mcp/dist/cli.js +155 -34
- package/packages/lsp-tools-mcp/dist/lsp/manager.js +134 -28
- package/packages/lsp-tools-mcp/dist/mcp.js +155 -34
- package/packages/lsp-tools-mcp/dist/tools.js +154 -34
- package/packages/lsp-tools-mcp/package.json +1 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- 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/dist/cli.js +17 -2
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +17 -2
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +12 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable-idle.test.ts +47 -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/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/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/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +152 -34
- 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/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- 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/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 -4
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +489 -4
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -2
- package/packages/omo-codex/plugin/components/ultrawork/scripts/sync-directive.mjs +30 -14
- package/packages/omo-codex/plugin/components/ultrawork/src/directive-content.ts +4 -0
- package/packages/omo-codex/plugin/components/ultrawork/src/directive.ts +5 -2
- package/packages/omo-codex/plugin/components/ultrawork/test/directive-source.test.ts +14 -8
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +12 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/AGENTS.md +5 -2
- package/packages/omo-codex/plugin/components/ulw-loop/README.md +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.js +9 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.d.ts +14 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-reconciliation.js +10 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-template.d.ts +8 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-template.js +113 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +9 -5
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +856 -347
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.d.ts +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +27 -6
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.d.ts +13 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-snapshot.js +14 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +17 -9
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +17 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.d.ts +13 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-missing-recovery.js +19 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.d.ts +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-aggregate.js +53 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-artifacts.d.ts +9 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-artifacts.js +85 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-fields.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.d.ts +4 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +41 -102
- package/packages/omo-codex/plugin/components/ulw-loop/dist/review-blockers.js +4 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +185 -14
- package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.d.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/status-next-actions.js +33 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.d.ts +18 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/surface.js +65 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +17 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-continuation.ts +7 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-reconciliation.ts +25 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-template.ts +127 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +10 -12
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +34 -5
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-snapshot.ts +23 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +19 -9
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +18 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-missing-recovery.ts +27 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-aggregate.ts +57 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-artifacts.ts +103 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-fields.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +54 -123
- package/packages/omo-codex/plugin/components/ulw-loop/src/review-blockers.ts +4 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +187 -12
- package/packages/omo-codex/plugin/components/ulw-loop/src/status-next-actions.ts +36 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/surface.ts +95 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-final.test.ts +107 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-template.test.ts +92 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-status-next-actions.test.ts +183 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +35 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/quality-gate-builder.ts +7 -3
- package/packages/omo-codex/plugin/components/ulw-loop/test/guided-recovery.test.ts +105 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-aggregate.test.ts +101 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-roles.test.ts +46 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-senpi-surface.test.ts +131 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +5 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard.test.ts +318 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/surface.test.ts +74 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/ultrawork-directive.test.ts +9 -3
- 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-guarding-ulw-loop-spawns.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-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-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/sync-skills.mjs +31 -2
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/insane-search/README.md +9 -2
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +17 -0
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +11 -1
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +13 -0
- package/packages/omo-codex/plugin/test/ultrawork-skill-pointer.test.mjs +5 -2
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
- package/packages/shared-skills/skills/ultimate-browsing/references/insane-search/README.md +9 -2
- package/dist/features/builtin-skills/index.d.ts +0 -1
- package/dist/features/builtin-skills/skill-file-loader.d.ts +0 -1
- package/dist/features/builtin-skills/skills/agent-browser-skill.d.ts +0 -1
- package/dist/features/builtin-skills/skills/agent-browser-template.d.ts +0 -1
- package/dist/features/builtin-skills/skills/debugging.d.ts +0 -1
- package/dist/features/builtin-skills/skills/dev-browser.d.ts +0 -1
- package/dist/features/builtin-skills/skills/frontend.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/commit-atomic-planning.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/commit-context-analysis.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/commit-execution-verification.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/commit-workflow.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/history-search-workflow.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/overview.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/quick-reference.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-sections/rebase-workflow.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master-skill-metadata.d.ts +0 -1
- package/dist/features/builtin-skills/skills/git-master.d.ts +0 -1
- package/dist/features/builtin-skills/skills/index.d.ts +0 -1
- package/dist/features/builtin-skills/skills/init-deep.d.ts +0 -1
- package/dist/features/builtin-skills/skills/playwright-cli.d.ts +0 -1
- package/dist/features/builtin-skills/skills/playwright-mcp-skill.d.ts +0 -1
- package/dist/features/builtin-skills/skills/playwright.d.ts +0 -1
- package/dist/features/builtin-skills/skills/remove-ai-slops.d.ts +0 -1
- package/dist/features/builtin-skills/skills/review-work.d.ts +0 -1
- package/dist/features/builtin-skills/skills/security-research.d.ts +0 -1
- package/dist/features/builtin-skills/skills/security-review.d.ts +0 -1
- package/dist/features/builtin-skills/skills/team-mode.d.ts +0 -1
- package/dist/features/builtin-skills/skills/visual-qa.d.ts +0 -1
- package/dist/features/builtin-skills/skills.d.ts +0 -1
- package/dist/features/builtin-skills/types.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/agent-loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/command-loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/discovery-core.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/discovery-paths.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/discovery.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/hook-loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/index.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/install-path-resolver.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/installed-plugin-database.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/loaded-plugin.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/mcp-server-loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/plugin-key.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/plugin-manifest.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/plugin-path-resolver.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/plugin-settings.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/scope-filter.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/skill-loader.d.ts +0 -1
- package/dist/features/claude-code-plugin-loader/types.d.ts +0 -1
- package/dist/hooks/ralph-loop/completion-promise-detector-test-input.d.ts +0 -11
- package/dist/hooks/ralph-loop/loop-session-recovery.d.ts +0 -7
- package/dist/shared/prompt-async-gate/message-inspection-error.d.ts +0 -1
- package/dist/shared/prompt-async-gate/pending-tool-turn.d.ts +0 -1
- package/dist/shared/prompt-async-gate/prompt-message-state.d.ts +0 -1
- package/dist/shared/prompt-async-gate/queue.d.ts +0 -1
- package/dist/shared/prompt-async-gate/recent-dispatches.d.ts +0 -1
- package/dist/shared/prompt-async-gate/reservations.d.ts +0 -1
- package/dist/shared/prompt-async-gate/semantic-dedupe.d.ts +0 -1
- package/dist/shared/prompt-async-gate/session-idle-dispatch.d.ts +0 -1
- package/dist/shared/prompt-async-gate/timing.d.ts +0 -1
- package/dist/shared/prompt-async-gate/types.d.ts +0 -1
- package/dist/tools/call-omo-agent/message-storage-directory.d.ts +0 -1
- package/dist/tools/delegate-task/model-string-parser.d.ts +0 -1
- package/packages/lsp-tools-mcp/dist/lsp/cleanup-errors.d.ts +0 -1
- package/packages/lsp-tools-mcp/dist/lsp/constants.d.ts +0 -1
- package/packages/lsp-tools-mcp/dist/lsp/language-mappings.d.ts +0 -1
- package/packages/lsp-tools-mcp/dist/lsp/process-signal-cleanup.d.ts +0 -1
- package/packages/lsp-tools-mcp/dist/missing-dependency-result.d.ts +0 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +0 -483
- package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +0 -490
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { writeFile } from "node:fs/promises";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
3
3
|
|
|
4
4
|
import { checkpointUlwLoop } from "../src/checkpoint.js";
|
|
5
5
|
import { ulwLoopBriefPath } from "../src/paths.js";
|
|
@@ -7,6 +7,112 @@ import { startNextUlwLoop } from "../src/plan-crud.js";
|
|
|
7
7
|
import { criterion, expectCode, goal, passGoal, plan, repoWith, snapshot } from "./fixtures/checkpoint-builders.js";
|
|
8
8
|
import { MISSING_ARTIFACT_PATH, qualityGateJson } from "./fixtures/quality-gate-builder.js";
|
|
9
9
|
|
|
10
|
+
function requiredSection(gate: Record<string, Record<string, unknown>>, key: string): Record<string, unknown> {
|
|
11
|
+
const section = gate[key];
|
|
12
|
+
if (section === undefined) throw new Error(`missing gate section: ${key}`);
|
|
13
|
+
return section;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe("checkpointUlwLoop final story surface resolution", () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
delete process.env["OMO_AGENT_TOOLKIT_SURFACE"];
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
delete process.env["OMO_AGENT_TOOLKIT_SURFACE"];
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("#given the omo-senpi surface #when the gate omits codeReview #then the final story completes", async () => {
|
|
26
|
+
process.env["OMO_AGENT_TOOLKIT_SURFACE"] = "omo-senpi";
|
|
27
|
+
const repo = await repoWith(
|
|
28
|
+
plan([passGoal("G001", { status: "complete" }), passGoal("G002")], { activeGoalId: "G002" }),
|
|
29
|
+
);
|
|
30
|
+
const gate = JSON.parse(await qualityGateJson(repo, undefined, "G001-finished", "omo-senpi")) as Record<
|
|
31
|
+
string,
|
|
32
|
+
Record<string, unknown>
|
|
33
|
+
>;
|
|
34
|
+
delete gate["codeReview"];
|
|
35
|
+
requiredSection(gate, "manualQa")["by"] = "main-session";
|
|
36
|
+
requiredSection(gate, "gateReview")["by"] = "category:deep";
|
|
37
|
+
|
|
38
|
+
const result = await checkpointUlwLoop(repo, {
|
|
39
|
+
goalId: "G002",
|
|
40
|
+
status: "complete",
|
|
41
|
+
evidence: "final work complete and validation passed",
|
|
42
|
+
codexGoalJson: snapshot("complete"),
|
|
43
|
+
qualityGateJson: JSON.stringify(gate),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(result.aggregateCompletion?.status).toBe("complete");
|
|
47
|
+
expect(result.plan.aggregateCompletion?.status).toBe("complete");
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("#given the same codeReview-free gate #when the surface is lazycodex #then the final story is rejected", async () => {
|
|
51
|
+
const repo = await repoWith(
|
|
52
|
+
plan([passGoal("G001", { status: "complete" }), passGoal("G002")], { activeGoalId: "G002" }),
|
|
53
|
+
);
|
|
54
|
+
const gate = JSON.parse(await qualityGateJson(repo, undefined, "G001-finished", "omo-senpi")) as Record<
|
|
55
|
+
string,
|
|
56
|
+
Record<string, unknown>
|
|
57
|
+
>;
|
|
58
|
+
delete gate["codeReview"];
|
|
59
|
+
requiredSection(gate, "manualQa")["by"] = "main-session";
|
|
60
|
+
requiredSection(gate, "gateReview")["by"] = "category:deep";
|
|
61
|
+
|
|
62
|
+
await expectCode(
|
|
63
|
+
() =>
|
|
64
|
+
checkpointUlwLoop(repo, {
|
|
65
|
+
goalId: "G002",
|
|
66
|
+
status: "complete",
|
|
67
|
+
evidence: "final work complete and validation passed",
|
|
68
|
+
codexGoalJson: snapshot("complete"),
|
|
69
|
+
qualityGateJson: JSON.stringify(gate),
|
|
70
|
+
}),
|
|
71
|
+
"ULW_LOOP_QUALITY_GATE_INVALID",
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("#given the omo-senpi surface #when the gate includes codeReview #then the final story is rejected", async () => {
|
|
76
|
+
process.env["OMO_AGENT_TOOLKIT_SURFACE"] = "omo-senpi";
|
|
77
|
+
const repo = await repoWith(
|
|
78
|
+
plan([passGoal("G001", { status: "complete" }), passGoal("G002")], { activeGoalId: "G002" }),
|
|
79
|
+
);
|
|
80
|
+
const gateJson = await qualityGateJson(repo, undefined, "G001-finished", "omo-senpi");
|
|
81
|
+
|
|
82
|
+
await expectCode(
|
|
83
|
+
() =>
|
|
84
|
+
checkpointUlwLoop(repo, {
|
|
85
|
+
goalId: "G002",
|
|
86
|
+
status: "complete",
|
|
87
|
+
evidence: "final work complete and validation passed",
|
|
88
|
+
codexGoalJson: snapshot("complete"),
|
|
89
|
+
qualityGateJson: gateJson,
|
|
90
|
+
}),
|
|
91
|
+
"ULW_LOOP_QUALITY_GATE_INVALID",
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("#given the omo-senpi surface #when the gate names lazycodex reviewers #then the final story is rejected", async () => {
|
|
96
|
+
process.env["OMO_AGENT_TOOLKIT_SURFACE"] = "omo-senpi";
|
|
97
|
+
const repo = await repoWith(
|
|
98
|
+
plan([passGoal("G001", { status: "complete" }), passGoal("G002")], { activeGoalId: "G002" }),
|
|
99
|
+
);
|
|
100
|
+
const gateJson = await qualityGateJson(repo);
|
|
101
|
+
|
|
102
|
+
await expectCode(
|
|
103
|
+
() =>
|
|
104
|
+
checkpointUlwLoop(repo, {
|
|
105
|
+
goalId: "G002",
|
|
106
|
+
status: "complete",
|
|
107
|
+
evidence: "final work complete and validation passed",
|
|
108
|
+
codexGoalJson: snapshot("complete"),
|
|
109
|
+
qualityGateJson: gateJson,
|
|
110
|
+
}),
|
|
111
|
+
"ULW_LOOP_QUALITY_GATE_INVALID",
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
10
116
|
describe("checkpointUlwLoop final story", () => {
|
|
11
117
|
it("requires quality-gate-json for the final goal complete", async () => {
|
|
12
118
|
const repo = await repoWith(
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { ulwLoopCommand } from "../src/cli-commands.js";
|
|
7
|
+
import { writePlan } from "../src/plan-io.js";
|
|
8
|
+
import { goal, plan } from "./fixtures/checkpoint-builders.js";
|
|
9
|
+
|
|
10
|
+
let repo: string;
|
|
11
|
+
let output: string[];
|
|
12
|
+
|
|
13
|
+
beforeEach(async () => {
|
|
14
|
+
repo = await mkdtemp(join(tmpdir(), "ulw-template-"));
|
|
15
|
+
output = [];
|
|
16
|
+
vi.spyOn(process, "cwd").mockReturnValue(repo);
|
|
17
|
+
vi.spyOn(process.stdout, "write").mockImplementation((chunk: string | Uint8Array): boolean => {
|
|
18
|
+
output.push(chunk.toString());
|
|
19
|
+
return true;
|
|
20
|
+
});
|
|
21
|
+
delete process.env["OMO_AGENT_TOOLKIT_SURFACE"];
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(async () => {
|
|
25
|
+
vi.restoreAllMocks();
|
|
26
|
+
await rm(repo, { recursive: true, force: true });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
async function run(args: string[]): Promise<Record<string, unknown>> {
|
|
30
|
+
const code = await ulwLoopCommand(["checkpoint", ...args]);
|
|
31
|
+
expect(code).toBe(0);
|
|
32
|
+
return JSON.parse(output.join(""));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
describe("checkpoint --print-template", () => {
|
|
36
|
+
it("#given an active senpi v2 plan #when printed with only goal-id #then emits the senpi gate skeleton", async () => {
|
|
37
|
+
await writePlan(
|
|
38
|
+
repo,
|
|
39
|
+
plan([goal({ id: "G001", attempt: 2 })], {
|
|
40
|
+
evidenceLayoutVersion: 2,
|
|
41
|
+
activeGoalId: "G001",
|
|
42
|
+
codexObjective: "Exact objective from goals.json",
|
|
43
|
+
}),
|
|
44
|
+
);
|
|
45
|
+
process.env["OMO_AGENT_TOOLKIT_SURFACE"] = "omo-senpi";
|
|
46
|
+
const result = await run(["--print-template", "--goal-id", "G001", "--json"]);
|
|
47
|
+
const gate = result["qualityGateTemplate"] as Record<string, unknown>;
|
|
48
|
+
expect(Object.keys(gate)).toEqual(["manualQa", "gateReview", "iteration", "criteriaCoverage"]);
|
|
49
|
+
expect((gate["manualQa"] as Record<string, unknown>)["by"]).toBe("main-session");
|
|
50
|
+
expect((gate["gateReview"] as Record<string, unknown>)["by"]).toBe("category:deep");
|
|
51
|
+
expect(result["codexGoalTemplate"]).toEqual({
|
|
52
|
+
goal: { objective: "Exact objective from goals.json", status: "complete" },
|
|
53
|
+
});
|
|
54
|
+
expect(result["attemptDir"]).toBe(".omo/evidence/ulw/session/G001/a2");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("#given an active v2 plan #when printed without any goal id #then targets the active goal", async () => {
|
|
58
|
+
await writePlan(
|
|
59
|
+
repo,
|
|
60
|
+
plan([goal({ id: "G001", attempt: 1 })], {
|
|
61
|
+
evidenceLayoutVersion: 2,
|
|
62
|
+
activeGoalId: "G001",
|
|
63
|
+
codexObjective: "Exact objective from goals.json",
|
|
64
|
+
}),
|
|
65
|
+
);
|
|
66
|
+
process.env["OMO_AGENT_TOOLKIT_SURFACE"] = "omo-senpi";
|
|
67
|
+
const result = await run(["--print-template", "--json"]);
|
|
68
|
+
const gate = result["qualityGateTemplate"] as Record<string, unknown>;
|
|
69
|
+
expect(Object.keys(gate)).toEqual(["manualQa", "gateReview", "iteration", "criteriaCoverage"]);
|
|
70
|
+
expect(result["attemptDir"]).toBe(".omo/evidence/ulw/session/G001/a1");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("#given two goals #when printed with an explicit non-active goal id #then targets that goal's attempt dir", async () => {
|
|
74
|
+
await writePlan(
|
|
75
|
+
repo,
|
|
76
|
+
plan([goal({ id: "G001", attempt: 1 }), goal({ id: "G002", attempt: 3 })], {
|
|
77
|
+
evidenceLayoutVersion: 2,
|
|
78
|
+
activeGoalId: "G001",
|
|
79
|
+
codexObjective: "Exact objective from goals.json",
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
const result = await run(["--print-template", "--goal-id", "G002", "--json"]);
|
|
83
|
+
expect(result["attemptDir"]).toBe(".omo/evidence/ulw/session/G002/a3");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("#given a v1 plan #when printed without status #then exits successfully and gives v1 guidance", async () => {
|
|
87
|
+
await writePlan(repo, plan([goal({ id: "G001" })]));
|
|
88
|
+
const result = await run(["--print-template", "--goal-id", "G001", "--json"]);
|
|
89
|
+
expect(result["guidance"]).toContain("evidence-layout v1");
|
|
90
|
+
expect(result).not.toHaveProperty("attemptDir");
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { mkdir, mkdtemp, rm } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { ulwLoopCommand } from "../src/cli-commands.js";
|
|
7
|
+
import { ulwLoopDir } from "../src/paths.js";
|
|
8
|
+
import { writePlan } from "../src/plan-io.js";
|
|
9
|
+
import type { UlwLoopItem, UlwLoopPlan, UlwLoopSuccessCriterion } from "../src/types.js";
|
|
10
|
+
|
|
11
|
+
const NOW = "2026-05-23T00:00:00.000Z";
|
|
12
|
+
const SESSION_ENV_KEYS = ["OMO_ULW_LOOP_SESSION_ID", "CODEX_SESSION_ID", "CODEX_THREAD_ID", "PI_SESSION_ID"] as const;
|
|
13
|
+
|
|
14
|
+
let testDir: string;
|
|
15
|
+
let out: string[];
|
|
16
|
+
let err: string[];
|
|
17
|
+
let savedEnv: Record<string, string | undefined>;
|
|
18
|
+
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
testDir = await mkdtemp(join(tmpdir(), "ug-status-next-"));
|
|
21
|
+
out = [];
|
|
22
|
+
err = [];
|
|
23
|
+
savedEnv = {};
|
|
24
|
+
for (const key of SESSION_ENV_KEYS) {
|
|
25
|
+
savedEnv[key] = process.env[key];
|
|
26
|
+
delete process.env[key];
|
|
27
|
+
}
|
|
28
|
+
vi.spyOn(process, "cwd").mockReturnValue(testDir);
|
|
29
|
+
vi.spyOn(process.stdout, "write").mockImplementation((chunk: string | Uint8Array): boolean => {
|
|
30
|
+
out.push(chunk.toString());
|
|
31
|
+
return true;
|
|
32
|
+
});
|
|
33
|
+
vi.spyOn(process.stderr, "write").mockImplementation((chunk: string | Uint8Array): boolean => {
|
|
34
|
+
err.push(chunk.toString());
|
|
35
|
+
return true;
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
afterEach(async () => {
|
|
40
|
+
vi.restoreAllMocks();
|
|
41
|
+
for (const key of SESSION_ENV_KEYS) {
|
|
42
|
+
const saved = savedEnv[key];
|
|
43
|
+
if (saved === undefined) delete process.env[key];
|
|
44
|
+
else process.env[key] = saved;
|
|
45
|
+
}
|
|
46
|
+
await rm(testDir, { recursive: true, force: true });
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
function stdoutJson(): Record<string, unknown> {
|
|
50
|
+
return JSON.parse(out.join(""));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function nextActions(): readonly string[] {
|
|
54
|
+
const value = stdoutJson()["nextActions"];
|
|
55
|
+
if (!Array.isArray(value)) throw new Error(`expected nextActions array, got ${JSON.stringify(value)}`);
|
|
56
|
+
return value.map((entry) => String(entry));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function criterion(id: string, status: UlwLoopSuccessCriterion["status"]): UlwLoopSuccessCriterion {
|
|
60
|
+
return {
|
|
61
|
+
id,
|
|
62
|
+
scenario: `${id} scenario`,
|
|
63
|
+
userModel: "happy",
|
|
64
|
+
expectedEvidence: `${id} proof`,
|
|
65
|
+
capturedEvidence: status === "pass" ? `${id} passed` : null,
|
|
66
|
+
status,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function goal(overrides: Partial<UlwLoopItem> = {}): UlwLoopItem {
|
|
71
|
+
return {
|
|
72
|
+
id: "G001",
|
|
73
|
+
title: "Build auth",
|
|
74
|
+
objective: "Implement JWT auth endpoint",
|
|
75
|
+
status: "in_progress",
|
|
76
|
+
successCriteria: [criterion("C001", "pass")],
|
|
77
|
+
attempt: 1,
|
|
78
|
+
createdAt: NOW,
|
|
79
|
+
updatedAt: NOW,
|
|
80
|
+
...overrides,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function plan(goals: UlwLoopItem[], overrides: Partial<UlwLoopPlan> = {}): UlwLoopPlan {
|
|
85
|
+
const result: UlwLoopPlan = {
|
|
86
|
+
version: 1,
|
|
87
|
+
evidenceLayoutVersion: 2,
|
|
88
|
+
createdAt: NOW,
|
|
89
|
+
updatedAt: NOW,
|
|
90
|
+
briefPath: ".omo/ulw-loop/brief.md",
|
|
91
|
+
goalsPath: ".omo/ulw-loop/goals.json",
|
|
92
|
+
ledgerPath: ".omo/ulw-loop/ledger.jsonl",
|
|
93
|
+
goals,
|
|
94
|
+
};
|
|
95
|
+
Object.assign(result, overrides);
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function legacyLayoutPlan(goals: UlwLoopItem[], overrides: Partial<UlwLoopPlan> = {}): UlwLoopPlan {
|
|
100
|
+
const result = plan(goals, overrides);
|
|
101
|
+
delete result.evidenceLayoutVersion;
|
|
102
|
+
return result;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function seed(seedPlan: UlwLoopPlan): Promise<void> {
|
|
106
|
+
await mkdir(ulwLoopDir(testDir), { recursive: true });
|
|
107
|
+
await writePlan(testDir, seedPlan);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
describe("#given a plan with no goals", () => {
|
|
111
|
+
it("#when status --json #then nextActions points at create-goals", async () => {
|
|
112
|
+
await seed(plan([]));
|
|
113
|
+
|
|
114
|
+
expect(await ulwLoopCommand(["status", "--json"])).toBe(0);
|
|
115
|
+
expect(nextActions().join("\n")).toContain('omo-agent-toolkit ulw-loop create-goals --brief "<brief>" --json');
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe("#given an active goal with unresolved criteria", () => {
|
|
120
|
+
it("#when status --json #then nextActions names record-evidence with the remaining criterion ids", async () => {
|
|
121
|
+
await seed(
|
|
122
|
+
plan(
|
|
123
|
+
[
|
|
124
|
+
goal({
|
|
125
|
+
successCriteria: [criterion("C001", "pass"), criterion("C002", "pending"), criterion("C003", "fail")],
|
|
126
|
+
}),
|
|
127
|
+
],
|
|
128
|
+
{
|
|
129
|
+
activeGoalId: "G001",
|
|
130
|
+
},
|
|
131
|
+
),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
expect(await ulwLoopCommand(["status", "--json"])).toBe(0);
|
|
135
|
+
const actions = nextActions().join("\n");
|
|
136
|
+
expect(actions).toContain("record-evidence");
|
|
137
|
+
expect(actions).toContain("C002");
|
|
138
|
+
expect(actions).toContain("C003");
|
|
139
|
+
expect(actions).not.toContain("C001");
|
|
140
|
+
expect(stdoutJson()).toHaveProperty("currentAttemptDir");
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
describe("#given the final story with every criterion passing", () => {
|
|
145
|
+
it("#when status --json #then nextActions tells the agent to complete the goal then print the template", async () => {
|
|
146
|
+
await seed(plan([goal({ successCriteria: [criterion("C001", "pass")] })], { activeGoalId: "G001" }));
|
|
147
|
+
|
|
148
|
+
expect(await ulwLoopCommand(["status", "--json"])).toBe(0);
|
|
149
|
+
const actions = nextActions().join("\n");
|
|
150
|
+
expect(actions).toContain("update_goal complete, then checkpoint --print-template");
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
describe("#given a review_blocked goal", () => {
|
|
155
|
+
it("#when status --json #then nextActions points at record-review-blockers", async () => {
|
|
156
|
+
await seed(plan([goal({ status: "review_blocked", successCriteria: [criterion("C001", "pass")] })]));
|
|
157
|
+
|
|
158
|
+
expect(await ulwLoopCommand(["status", "--json"])).toBe(0);
|
|
159
|
+
expect(nextActions().join("\n")).toContain("record-review-blockers");
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe("#given an evidence-layout v1 plan", () => {
|
|
164
|
+
it("#when status --json #then currentAttemptDir stays absent and nextActions explains the v1 layout", async () => {
|
|
165
|
+
await seed(legacyLayoutPlan([goal()], { activeGoalId: "G001" }));
|
|
166
|
+
|
|
167
|
+
expect(await ulwLoopCommand(["status", "--json"])).toBe(0);
|
|
168
|
+
expect(stdoutJson()).not.toHaveProperty("currentAttemptDir");
|
|
169
|
+
expect(nextActions().join("\n")).toContain("plan is evidence-layout v1; artifacts go under .omo/evidence/");
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("#given a valueless --session-id in a subprocess context", () => {
|
|
174
|
+
it("#when any subcommand runs #then the error explains the missing session env and the flag usage", async () => {
|
|
175
|
+
expect(await ulwLoopCommand(["status", "--session-id", "--json"])).toBe(1);
|
|
176
|
+
|
|
177
|
+
const payload = stdoutJson();
|
|
178
|
+
expect(payload).toMatchObject({ ok: false, error: { code: "ULW_LOOP_SESSION_ID_REQUIRED" } });
|
|
179
|
+
const message = String((payload["error"] as Record<string, unknown>)["message"]);
|
|
180
|
+
expect(message).toContain("--session-id <id>");
|
|
181
|
+
expect(message).toContain("subprocess");
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -6,6 +6,7 @@ import type { UlwLoopItem, UlwLoopPlan, UlwLoopSuccessCriterion } from "../src/t
|
|
|
6
6
|
|
|
7
7
|
const NOW = "2026-05-23T00:00:00.000Z";
|
|
8
8
|
const FINAL_REVIEW_ROLES = ["lazycodex-code-reviewer", "lazycodex-qa-executor", "lazycodex-gate-reviewer"] as const;
|
|
9
|
+
const SENPI_REVIEW_ROLES = ["omo-senpi-code-reviewer", "omo-senpi-qa-executor", "omo-senpi-gate-reviewer"] as const;
|
|
9
10
|
const QUALITY_GATE_SECTIONS = ["codeReview", "manualQa", "gateReview", "iteration", "criteriaCoverage"] as const;
|
|
10
11
|
|
|
11
12
|
function makeCriterion(overrides: Partial<UlwLoopSuccessCriterion> = {}): UlwLoopSuccessCriterion {
|
|
@@ -94,6 +95,40 @@ describe("buildCodexGoalInstruction aggregate mode", () => {
|
|
|
94
95
|
expect(text).toContain("checkpoint");
|
|
95
96
|
});
|
|
96
97
|
|
|
98
|
+
it("#given a final story on the omo-senpi surface #when rendering instructions #then teaches the single-gate category chain", () => {
|
|
99
|
+
const { text } = buildCodexGoalInstruction({
|
|
100
|
+
plan: makePlan({ codexGoalMode: "aggregate" }),
|
|
101
|
+
goal: makeGoal(),
|
|
102
|
+
isFinal: true,
|
|
103
|
+
surface: "omo-senpi",
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
expectTextToContainAll(text, [
|
|
107
|
+
"main-session",
|
|
108
|
+
'category: "deep"',
|
|
109
|
+
"unspecified-high",
|
|
110
|
+
"unspecified-low",
|
|
111
|
+
"gateReview.by",
|
|
112
|
+
"category:<name>",
|
|
113
|
+
"--print-template",
|
|
114
|
+
]);
|
|
115
|
+
for (const role of [...FINAL_REVIEW_ROLES, ...SENPI_REVIEW_ROLES]) expect(text).not.toContain(role);
|
|
116
|
+
expect(text).toMatch(/model_unavailable/);
|
|
117
|
+
expect(text).not.toContain("attempted_chain");
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("#given a final story with the explicit lazycodex surface #when rendering instructions #then keeps the lazycodex reviewers", () => {
|
|
121
|
+
const { text } = buildCodexGoalInstruction({
|
|
122
|
+
plan: makePlan({ codexGoalMode: "aggregate" }),
|
|
123
|
+
goal: makeGoal(),
|
|
124
|
+
isFinal: true,
|
|
125
|
+
surface: "lazycodex",
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expectTextToContainAll(text, FINAL_REVIEW_ROLES);
|
|
129
|
+
for (const role of SENPI_REVIEW_ROLES) expect(text).not.toContain(role);
|
|
130
|
+
});
|
|
131
|
+
|
|
97
132
|
it("#given a scoped plan #when rendering final commands #then includes the session id option", () => {
|
|
98
133
|
const { text } = buildCodexGoalInstruction({
|
|
99
134
|
plan: makePlan({
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
|
+
import { reviewerRolesFor, type UlwLoopToolkitSurface } from "../../src/surface.js";
|
|
5
|
+
|
|
4
6
|
// v2 evidence layout: artifacts must live inside the goal's current attempt dir
|
|
5
7
|
export function qaDirFor(goalId: string, attempt = 1, sessionId = "session"): string {
|
|
6
8
|
return `.omo/evidence/ulw/${sessionId}/${goalId}/a${attempt}`;
|
|
@@ -25,13 +27,15 @@ export async function qualityGateJson(
|
|
|
25
27
|
repoRoot: string,
|
|
26
28
|
cliArtifactPath?: string,
|
|
27
29
|
goalId = "G001-finished",
|
|
30
|
+
surface: UlwLoopToolkitSurface = "lazycodex",
|
|
28
31
|
): Promise<string> {
|
|
32
|
+
const roles = reviewerRolesFor(surface);
|
|
29
33
|
const qaDir = qaDirFor(goalId);
|
|
30
34
|
await writeQualityGateArtifacts(repoRoot, qaDir);
|
|
31
35
|
const cliPath = cliArtifactPath ?? `${qaDir}/cli-pass.txt`;
|
|
32
36
|
return JSON.stringify({
|
|
33
37
|
codeReview: {
|
|
34
|
-
by:
|
|
38
|
+
by: roles.codeReview,
|
|
35
39
|
recommendation: "APPROVE",
|
|
36
40
|
codeQualityStatus: "CLEAR",
|
|
37
41
|
reportPath: `${qaDir}/code-review.md`,
|
|
@@ -39,7 +43,7 @@ export async function qualityGateJson(
|
|
|
39
43
|
blockers: [],
|
|
40
44
|
},
|
|
41
45
|
manualQa: {
|
|
42
|
-
by:
|
|
46
|
+
by: roles.manualQa,
|
|
43
47
|
status: "passed",
|
|
44
48
|
evidence: "Ran CLI checkpoint validation with artifact-backed evidence.",
|
|
45
49
|
surfaceEvidence: [
|
|
@@ -78,7 +82,7 @@ export async function qualityGateJson(
|
|
|
78
82
|
],
|
|
79
83
|
},
|
|
80
84
|
gateReview: {
|
|
81
|
-
by:
|
|
85
|
+
by: roles.gateReview,
|
|
82
86
|
recommendation: "APPROVE",
|
|
83
87
|
reportPath: `${qaDir}/gate-review.md`,
|
|
84
88
|
evidence: "Verified all criteria and artifact evidence.",
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { mkdir, mkdtemp } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { checkpointUlwLoop } from "../src/checkpoint.js";
|
|
7
|
+
import { ULW_LOOP_AGGREGATE_CODEX_OBJECTIVE } from "../src/goal-status.js";
|
|
8
|
+
import { ulwLoopDir } from "../src/paths.js";
|
|
9
|
+
import { readUlwLoopPlan } from "../src/plan-io.js";
|
|
10
|
+
import { recordFinalReviewBlockers } from "../src/review-blockers.js";
|
|
11
|
+
import { UlwLoopError } from "../src/types.js";
|
|
12
|
+
import { goal, passGoal, plan, repoWith, snapshot } from "./fixtures/checkpoint-builders.js";
|
|
13
|
+
|
|
14
|
+
async function captureError(action: () => Promise<unknown>): Promise<UlwLoopError> {
|
|
15
|
+
try {
|
|
16
|
+
await action();
|
|
17
|
+
} catch (error) {
|
|
18
|
+
expect(error).toBeInstanceOf(UlwLoopError);
|
|
19
|
+
if (error instanceof UlwLoopError) return error;
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
throw new Error("Expected UlwLoopError");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe("#given a codex goal snapshot whose objective differs from the plan", () => {
|
|
26
|
+
it("#when checkpoint reconciles it #then the mismatch carries the verbatim expected and received objectives", async () => {
|
|
27
|
+
const repo = await repoWith(plan([passGoal("G001"), goal({ id: "G002", status: "pending" })]));
|
|
28
|
+
|
|
29
|
+
const error = await captureError(() =>
|
|
30
|
+
checkpointUlwLoop(repo, {
|
|
31
|
+
goalId: "G001",
|
|
32
|
+
status: "complete",
|
|
33
|
+
evidence: "work complete and validation passed",
|
|
34
|
+
codexGoalJson: snapshot("active", "wrong objective"),
|
|
35
|
+
}),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
expect(error.code).toBe("ulw_loop_codex_snapshot_mismatch");
|
|
39
|
+
expect(error.details).toMatchObject({
|
|
40
|
+
expectedObjective: ULW_LOOP_AGGREGATE_CODEX_OBJECTIVE,
|
|
41
|
+
receivedObjective: "wrong objective",
|
|
42
|
+
});
|
|
43
|
+
expect(error.message).toContain(
|
|
44
|
+
"objective must equal the plan's codexObjective exactly — copy the expected value below",
|
|
45
|
+
);
|
|
46
|
+
expect(error.message).toContain(ULW_LOOP_AGGREGATE_CODEX_OBJECTIVE);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("#when record-review-blockers reconciles it #then the same guided payload is emitted", async () => {
|
|
50
|
+
const repo = await repoWith(
|
|
51
|
+
plan([passGoal("G001", { status: "complete" }), goal({ id: "G002", status: "in_progress" })]),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const error = await captureError(() =>
|
|
55
|
+
recordFinalReviewBlockers(repo, {
|
|
56
|
+
goalId: "G002",
|
|
57
|
+
title: "Resolve final review blockers",
|
|
58
|
+
objective: "Address the BLOCK findings",
|
|
59
|
+
evidence: "review verdict: REQUEST_CHANGES",
|
|
60
|
+
codexGoalJson: JSON.stringify({ goal: { objective: "stale objective", status: "active" } }),
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
expect(error.code).toBe("ulw_loop_codex_snapshot_mismatch");
|
|
65
|
+
expect(error.details).toMatchObject({
|
|
66
|
+
expectedObjective: ULW_LOOP_AGGREGATE_CODEX_OBJECTIVE,
|
|
67
|
+
receivedObjective: "stale objective",
|
|
68
|
+
});
|
|
69
|
+
expect(error.message).toContain(
|
|
70
|
+
"objective must equal the plan's codexObjective exactly — copy the expected value below",
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("#given no ulw-loop plan on disk", () => {
|
|
76
|
+
it("#when the plan is read #then the error names the exact create-goals bootstrap command", async () => {
|
|
77
|
+
const repo = await mkdtemp(join(tmpdir(), "ug-guided-plan-"));
|
|
78
|
+
|
|
79
|
+
const error = await captureError(() => readUlwLoopPlan(repo));
|
|
80
|
+
|
|
81
|
+
expect(error.code).toBe("ULW_LOOP_PLAN_MISSING");
|
|
82
|
+
expect(error.message).toContain('omo-agent-toolkit ulw-loop create-goals --brief "<brief>" --json');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("#when sibling session dirs exist #then the error lists the existing session ids", async () => {
|
|
86
|
+
const repo = await mkdtemp(join(tmpdir(), "ug-guided-plan-"));
|
|
87
|
+
await mkdir(join(ulwLoopDir(repo), "session-alpha"), { recursive: true });
|
|
88
|
+
await mkdir(join(ulwLoopDir(repo), "session-beta"), { recursive: true });
|
|
89
|
+
|
|
90
|
+
const error = await captureError(() => readUlwLoopPlan(repo, { sessionId: "session-gamma" }));
|
|
91
|
+
|
|
92
|
+
expect(error.code).toBe("ULW_LOOP_PLAN_MISSING");
|
|
93
|
+
expect(error.message).toContain("session-alpha");
|
|
94
|
+
expect(error.message).toContain("session-beta");
|
|
95
|
+
expect(error.details).toMatchObject({ existingSessionIds: ["session-alpha", "session-beta"] });
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("#when no session dirs exist #then the error lists no session ids", async () => {
|
|
99
|
+
const repo = await mkdtemp(join(tmpdir(), "ug-guided-plan-"));
|
|
100
|
+
|
|
101
|
+
const error = await captureError(() => readUlwLoopPlan(repo, { sessionId: "session-gamma" }));
|
|
102
|
+
|
|
103
|
+
expect(error.details?.["existingSessionIds"]).toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
});
|