oh-my-opencode 4.18.2 → 4.19.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/command/omomomo.md +1 -1
- package/.agents/skills/omomomo/SKILL.md +1 -1
- package/.opencode/command/omomomo.md +1 -1
- package/README.ja.md +6 -6
- package/README.ko.md +6 -6
- package/README.md +8 -8
- package/README.ru.md +6 -6
- package/README.zh-cn.md +6 -6
- package/dist/agents/atlas/agent.d.ts +7 -6
- package/dist/agents/gpt-prompt-identity.d.ts +3 -0
- package/dist/agents/sisyphus/gpt-5-5.d.ts +1 -1
- package/dist/agents/sisyphus/gpt-task-system-guide.d.ts +1 -0
- package/dist/agents/sisyphus/index.d.ts +5 -0
- package/dist/agents/sisyphus/kimi-k3.d.ts +19 -0
- package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
- package/dist/agents/sisyphus-junior/agent.d.ts +8 -4
- package/dist/agents/sisyphus-junior/gpt-5-5.d.ts +2 -2
- package/dist/agents/sisyphus-junior/index.d.ts +2 -0
- package/dist/agents/sisyphus-junior/kimi-k3.d.ts +12 -0
- package/dist/agents/types.d.ts +2 -2
- package/dist/cli/index.js +781 -215
- package/dist/cli-node/index.js +781 -215
- package/dist/config/index.d.ts +1 -1
- package/dist/config/schema/agent-overrides.d.ts +48 -48
- package/dist/config/schema/browser-automation.d.ts +1 -0
- package/dist/config/schema/categories.d.ts +6 -6
- package/dist/config/schema/codegraph.d.ts +2 -0
- package/dist/config/schema/commands.d.ts +1 -3
- package/dist/config/schema/default-mode.d.ts +1 -1
- package/dist/config/schema/fallback-models.d.ts +5 -5
- package/dist/config/schema/goal.d.ts +7 -0
- package/dist/config/schema/hooks.d.ts +1 -1
- package/dist/config/schema/oh-my-opencode-config.d.ts +56 -58
- package/dist/config/schema/ralph-loop.d.ts +1 -1
- package/dist/config/schema.d.ts +1 -1
- package/dist/create-hooks.d.ts +1 -1
- package/dist/features/builtin-commands/templates/goal.d.ts +1 -0
- package/dist/features/builtin-commands/templates/stop-continuation.d.ts +1 -1
- package/dist/features/builtin-commands/types.d.ts +1 -1
- package/dist/hooks/auto-slash-command/executor.d.ts +1 -0
- package/dist/hooks/codegraph-bootstrap/hook.d.ts +3 -2
- package/dist/hooks/goal/command-arguments.d.ts +13 -0
- package/dist/hooks/goal/controller.d.ts +26 -0
- package/dist/hooks/goal/index.d.ts +23 -0
- package/dist/hooks/goal/prompt.d.ts +3 -0
- package/dist/hooks/goal/store.d.ts +8 -0
- package/dist/hooks/goal/tools.d.ts +10 -0
- package/dist/hooks/goal/types.d.ts +93 -0
- package/dist/hooks/goal/validation.d.ts +4 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/keyword-detector/hook.d.ts +1 -2
- package/dist/hooks/ralph-loop/types.d.ts +1 -1
- package/dist/hooks/sisyphus-junior-notepad/constants.d.ts +1 -1
- package/dist/hooks/start-work/notepad-scaffold.d.ts +10 -0
- package/dist/index.js +5398 -5109
- package/dist/oh-my-opencode.schema.json +34 -19
- package/dist/plugin/chat-message/loop-commands.d.ts +1 -1
- package/dist/plugin/chat-message/types.d.ts +23 -10
- package/dist/plugin/hooks/create-core-hooks.d.ts +1 -1
- package/dist/plugin/hooks/create-session-hooks.d.ts +3 -2
- package/dist/plugin/hooks/create-transform-hooks.d.ts +0 -2
- package/dist/plugin/stop-continuation.d.ts +2 -2
- package/dist/shared/omo-process-sweep.d.ts +18 -0
- package/dist/shared/system-directive.d.ts +0 -1
- package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
- package/dist/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
- package/dist/skills/ulw-plan/SKILL.md +31 -8
- package/dist/skills/ulw-plan/references/full-workflow.md +117 -6
- package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/dist/skills/ulw-research/SKILL.md +3 -3
- package/dist/testing/create-plugin-module.d.ts +1 -0
- package/dist/tools/delegate-task/types.d.ts +0 -1
- package/dist/tui.js +138 -80
- package/package.json +14 -14
- package/packages/git-bash-mcp/dist/cli.js +54 -8
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +60 -0
- package/packages/lsp-core/src/lsp/client.ts +14 -1
- package/packages/lsp-core/src/lsp/fixtures/diagnostics-freshness-contract-probe.ts +2 -2
- package/packages/lsp-core/src/mcp.ts +9 -0
- package/packages/lsp-daemon/dist/cli.js +319 -51
- package/packages/lsp-daemon/dist/client.js +136 -48
- package/packages/lsp-daemon/dist/daemon-client.d.ts +0 -10
- package/packages/lsp-daemon/dist/daemon-client.js +4 -34
- package/packages/lsp-daemon/dist/daemon-failure-result.d.ts +3 -0
- package/packages/lsp-daemon/dist/daemon-failure-result.js +38 -0
- package/packages/lsp-daemon/dist/daemon-request-error.d.ts +14 -0
- package/packages/lsp-daemon/dist/daemon-request-error.js +26 -0
- package/packages/lsp-daemon/dist/daemon-server.js +5 -1
- package/packages/lsp-daemon/dist/index.js +137 -48
- package/packages/lsp-daemon/dist/proxy.d.ts +2 -0
- package/packages/lsp-daemon/dist/proxy.js +1 -0
- package/packages/lsp-daemon/dist/version-reap.d.ts +26 -0
- package/packages/lsp-daemon/dist/version-reap.js +182 -0
- package/packages/lsp-tools-mcp/dist/cli.js +61 -10
- package/packages/lsp-tools-mcp/dist/lsp/manager.js +6 -1
- package/packages/lsp-tools-mcp/dist/mcp.js +61 -10
- package/packages/lsp-tools-mcp/dist/tools.js +6 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +416 -25
- 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/bootstrap/src/setup.ts +22 -2
- package/packages/omo-codex/plugin/components/codegraph/AGENTS.md +7 -6
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +638 -267
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +178 -89
- package/packages/omo-codex/plugin/components/codegraph/package.json +2 -2
- package/packages/omo-codex/plugin/components/codegraph/src/hook-types.ts +3 -1
- package/packages/omo-codex/plugin/components/codegraph/src/hook.ts +3 -1
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +98 -32
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-unavailable.ts +5 -0
- package/packages/omo-codex/plugin/components/codegraph/src/serve.ts +15 -6
- package/packages/omo-codex/plugin/components/codegraph/src/session-start-worker.ts +6 -2
- package/packages/omo-codex/plugin/components/codegraph/src/sweep-cli.ts +1 -0
- package/packages/omo-codex/plugin/components/codegraph/test/hook-exclusion.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/hook-store-upgrade.test.ts +148 -0
- package/packages/omo-codex/plugin/components/codegraph/test/mcp-bridge-fixtures.ts +31 -2
- package/packages/omo-codex/plugin/components/codegraph/test/package-runtime.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge-lifecycle.test.ts +65 -1
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +3 -7
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-facade.test.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/test/serve-provision.test.ts +57 -3
- package/packages/omo-codex/plugin/components/codegraph/test/serve-unavailable.test.ts +41 -0
- package/packages/omo-codex/plugin/components/codegraph/test/serve.test.ts +109 -8
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-node-support.test.ts +2 -2
- package/packages/omo-codex/plugin/components/codegraph/test/session-start-worker-flow.test.ts +43 -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/AGENTS.md +1 -0
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +80 -40
- 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/dist/cli.js +11 -0
- 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/rules/src/post-compact-budget.ts +11 -0
- package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +60 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md +8 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js +147 -60
- 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/start-work-continuation/src/boulder-reader.ts +3 -71
- package/packages/omo-codex/plugin/components/start-work-continuation/src/plan-checklist.ts +171 -0
- package/packages/omo-codex/plugin/components/start-work-continuation/test/boulder-reader.test.ts +198 -23
- package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +4 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +6 -5
- package/packages/omo-codex/plugin/components/start-work-continuation/test/fixtures/plan-scaffold.md +15 -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/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 +10 -10
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +3 -3
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/dist/cli.js +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +31 -8
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/components/ultrawork/src/codex-hook.ts +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook-trigger-policy.test.ts +42 -0
- package/packages/omo-codex/plugin/components/ulw-loop/README.md +6 -4
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.d.ts +21 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint-continuation.js +64 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +15 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-arg-parser.js +1 -1
- 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 +3 -3
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.d.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-steering.js +85 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +0 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -33
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +998 -593
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.d.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/constants.js +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/goal-status.js +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +4 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-io.js +10 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.d.ts +15 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-batch.js +84 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.d.ts +5 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering-mutations.js +81 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/steering.js +7 -59
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +12 -4
- package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.d.ts +9 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/validation-batch.js +122 -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 +8 -6
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint-continuation.ts +84 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +13 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-arg-parser.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +3 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-steering.ts +49 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +13 -45
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/constants.ts +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +7 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/goal-status.ts +4 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +4 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-io.ts +20 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-batch.ts +125 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering-mutations.ts +59 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/steering.ts +6 -58
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +12 -4
- package/packages/omo-codex/plugin/components/ulw-loop/src/validation-batch.ts +122 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-continuation.test.ts +76 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-checkpoint-continuation.test.ts +111 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-steering-batch.test.ts +67 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-validation-batch.test.ts +57 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/steering-batch.test.ts +152 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +29 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch-checkpoint.test.ts +147 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/validation-batch.test.ts +73 -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-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 +2 -2
- 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 +2 -2
- 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 +2 -2
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-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-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 +41 -41
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +32 -28
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +17 -45
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/toml-section-editor.mjs +455 -0
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +3 -18
- package/packages/omo-codex/plugin/shared/test/config-loader.test.ts +43 -0
- package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +8 -6
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +31 -8
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +11 -11
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +26 -0
- package/packages/omo-codex/plugin/test/bootstrap-setup.test.mjs +41 -0
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +2 -2
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +6 -6
- package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +3 -3
- package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +62 -0
- package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +271 -14
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +3 -18
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +0 -5
- package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +293 -0
- package/packages/omo-codex/scripts/install-config.test.mjs +10 -10
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +401 -22
- package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +39 -2
- package/packages/omo-codex/scripts/install-marketplace-cache.test.mjs +4 -1
- package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.test.ts +87 -0
- package/packages/shared-skills/skills/programming/scripts/typescript/check-no-excuse-rules.ts +35 -5
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +31 -8
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +117 -6
- package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +46 -10
- package/packages/shared-skills/skills/ulw-research/SKILL.md +3 -3
- package/dist/features/builtin-commands/templates/ralph-loop.d.ts +0 -3
- package/dist/skills/ultraresearch/SKILL.md +0 -10
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +0 -273
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -12
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -107
- package/packages/shared-skills/skills/lcx-doctor/SKILL.md +0 -128
- package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +0 -268
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +0 -11
- package/packages/shared-skills/skills/ultraresearch/SKILL.md +0 -10
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-contribute-bug-fix/scripts/create-pr-body.mjs +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-doctor/agents/openai.yaml +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/SKILL.md +0 -0
- /package/{dist → packages/omo-codex/plugin/components/lcx}/skills/lcx-report-bug/agents/openai.yaml +0 -0
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
export function findTomlSection(config, headerLine) {
|
|
2
|
+
const targetHeaderPath = parseTomlTableHeader(headerLine);
|
|
3
|
+
const lines = config.match(/[^\n]*\n?|$/g) ?? [];
|
|
4
|
+
let offset = 0;
|
|
5
|
+
let start = -1;
|
|
6
|
+
let multilineQuote = null;
|
|
7
|
+
for (const line of lines) {
|
|
8
|
+
if (line.length === 0) break;
|
|
9
|
+
const multilineScan = scanTomlMultilineLine(line, multilineQuote);
|
|
10
|
+
multilineQuote = multilineScan.nextQuote;
|
|
11
|
+
if (multilineScan.wasInside) {
|
|
12
|
+
offset += line.length;
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
if (start === -1) {
|
|
16
|
+
if (tomlTableHeaderMatches(line, targetHeaderPath)) start = offset;
|
|
17
|
+
} else if (isTomlTableHeaderLine(line)) {
|
|
18
|
+
return { start, end: offset, text: config.slice(start, offset) };
|
|
19
|
+
}
|
|
20
|
+
offset += line.length;
|
|
21
|
+
}
|
|
22
|
+
if (start === -1) return null;
|
|
23
|
+
return { start, end: config.length, text: config.slice(start) };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function hasTomlSetting(config, keyPath) {
|
|
27
|
+
const targetPath = parseTomlDottedKey(keyPath);
|
|
28
|
+
if (!targetPath) return false;
|
|
29
|
+
|
|
30
|
+
let tablePath = [];
|
|
31
|
+
let multilineQuote = null;
|
|
32
|
+
for (const line of config.split("\n")) {
|
|
33
|
+
const multilineScan = scanTomlMultilineLine(line, multilineQuote);
|
|
34
|
+
multilineQuote = multilineScan.nextQuote;
|
|
35
|
+
if (multilineScan.wasInside) continue;
|
|
36
|
+
const normalizedLine = stripUnquotedInlineComment(line).trim();
|
|
37
|
+
if (normalizedLine.length === 0) continue;
|
|
38
|
+
|
|
39
|
+
const headerPath = parseTomlTableHeader(normalizedLine);
|
|
40
|
+
if (headerPath) {
|
|
41
|
+
tablePath = headerPath;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (isTomlTableHeaderLine(normalizedLine)) {
|
|
45
|
+
tablePath = null;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (!tablePath) continue;
|
|
49
|
+
|
|
50
|
+
const assignmentIndex = findUnquotedAssignment(normalizedLine);
|
|
51
|
+
if (assignmentIndex < 0) continue;
|
|
52
|
+
const settingPath = parseTomlDottedKey(normalizedLine.slice(0, assignmentIndex).trim());
|
|
53
|
+
if (!settingPath) continue;
|
|
54
|
+
const fullPath = [...tablePath, ...settingPath];
|
|
55
|
+
if (fullPath.length !== targetPath.length) continue;
|
|
56
|
+
if (fullPath.every((part, index) => part === targetPath[index])) return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function replaceOrInsertRootTomlSetting(config, keyPath, value) {
|
|
62
|
+
const targetPath = parseTomlDottedKey(keyPath);
|
|
63
|
+
if (!targetPath) return config;
|
|
64
|
+
const match = findRootTomlSetting(config, targetPath);
|
|
65
|
+
if (match) {
|
|
66
|
+
const commentIndex = findUnquotedComment(match.lineBody, match.assignmentIndex + 1);
|
|
67
|
+
const comment = commentIndex === -1 ? "" : ` ${match.lineBody.slice(commentIndex).trimStart()}`;
|
|
68
|
+
const replacement = `${match.lineBody.slice(0, match.assignmentIndex + 1)} ${value}${comment}${match.newline}`;
|
|
69
|
+
return config.slice(0, match.offset) + replacement + config.slice(match.offset + match.line.length);
|
|
70
|
+
}
|
|
71
|
+
const firstTable = findFirstTomlTableStart(config);
|
|
72
|
+
const root = config.slice(0, firstTable).trimEnd();
|
|
73
|
+
const suffix = config.slice(firstTable);
|
|
74
|
+
const replacement = `${root}${root.length > 0 ? "\n" : ""}${keyPath} = ${value}\n`;
|
|
75
|
+
if (suffix.length === 0) return replacement;
|
|
76
|
+
return `${replacement.trimEnd()}\n\n${suffix.trimStart()}`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function replaceOrInsertTomlSectionSetting(config, section, keyPath, value) {
|
|
80
|
+
const targetPath = parseTomlDottedKey(keyPath);
|
|
81
|
+
if (!targetPath) return config;
|
|
82
|
+
const lines = section.text.match(/[^\n]*\n?|$/g) ?? [];
|
|
83
|
+
let offset = 0;
|
|
84
|
+
let multilineQuote = null;
|
|
85
|
+
for (const line of lines) {
|
|
86
|
+
if (line.length === 0) break;
|
|
87
|
+
const multilineScan = scanTomlMultilineLine(line, multilineQuote);
|
|
88
|
+
multilineQuote = multilineScan.nextQuote;
|
|
89
|
+
if (multilineScan.wasInside) {
|
|
90
|
+
offset += line.length;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const assignmentIndex = findUnquotedAssignment(line);
|
|
94
|
+
if (assignmentIndex >= 0) {
|
|
95
|
+
const settingPath = parseTomlDottedKey(line.slice(0, assignmentIndex).trim());
|
|
96
|
+
if (settingPath && tomlPathMatches(settingPath, targetPath)) {
|
|
97
|
+
const replacement = replaceTomlAssignmentValue(line, assignmentIndex, value);
|
|
98
|
+
const patched = section.text.slice(0, offset) + replacement + section.text.slice(offset + line.length);
|
|
99
|
+
return config.slice(0, section.start) + patched + config.slice(section.end);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
offset += line.length;
|
|
103
|
+
}
|
|
104
|
+
const headerEnd = section.text.indexOf("\n");
|
|
105
|
+
const insertAt = headerEnd === -1 ? section.text.length : headerEnd + 1;
|
|
106
|
+
const patched = `${section.text.slice(0, insertAt)}${headerEnd === -1 ? "\n" : ""}${keyPath} = ${value}\n${section.text.slice(insertAt)}`;
|
|
107
|
+
return config.slice(0, section.start) + patched + config.slice(section.end);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function removeTomlSectionSetting(config, section, keyPath, expectedValue) {
|
|
111
|
+
const targetPath = parseTomlDottedKey(keyPath);
|
|
112
|
+
if (!targetPath) return config;
|
|
113
|
+
const lines = section.text.match(/[^\n]*\n?|$/g) ?? [];
|
|
114
|
+
let offset = 0;
|
|
115
|
+
let multilineQuote = null;
|
|
116
|
+
for (const line of lines) {
|
|
117
|
+
if (line.length === 0) break;
|
|
118
|
+
const multilineScan = scanTomlMultilineLine(line, multilineQuote);
|
|
119
|
+
multilineQuote = multilineScan.nextQuote;
|
|
120
|
+
if (multilineScan.wasInside) {
|
|
121
|
+
offset += line.length;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const assignmentIndex = findUnquotedAssignment(line);
|
|
125
|
+
if (assignmentIndex >= 0) {
|
|
126
|
+
const settingPath = parseTomlDottedKey(line.slice(0, assignmentIndex).trim());
|
|
127
|
+
if (settingPath && tomlPathMatches(settingPath, targetPath)) {
|
|
128
|
+
const lineBody = line.endsWith("\n") ? line.slice(0, -1) : line;
|
|
129
|
+
const commentIndex = findUnquotedComment(lineBody, assignmentIndex + 1);
|
|
130
|
+
const valueEnd = commentIndex === -1 ? lineBody.length : commentIndex;
|
|
131
|
+
if (lineBody.slice(assignmentIndex + 1, valueEnd).trim() !== expectedValue) return config;
|
|
132
|
+
const patched = section.text.slice(0, offset) + section.text.slice(offset + line.length);
|
|
133
|
+
return config.slice(0, section.start) + patched + config.slice(section.end);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
offset += line.length;
|
|
137
|
+
}
|
|
138
|
+
return config;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function removeRootTomlSetting(config, keyPath, expectedValue) {
|
|
142
|
+
const targetPath = parseTomlDottedKey(keyPath);
|
|
143
|
+
if (!targetPath) return config;
|
|
144
|
+
const match = findRootTomlSetting(config, targetPath);
|
|
145
|
+
if (!match) return config;
|
|
146
|
+
const commentIndex = findUnquotedComment(match.lineBody, match.assignmentIndex + 1);
|
|
147
|
+
const valueEnd = commentIndex === -1 ? match.lineBody.length : commentIndex;
|
|
148
|
+
if (match.lineBody.slice(match.assignmentIndex + 1, valueEnd).trim() !== expectedValue) return config;
|
|
149
|
+
return config.slice(0, match.offset) + config.slice(match.offset + match.line.length);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function tomlTableHeaderMatches(line, targetHeaderPath) {
|
|
153
|
+
if (!targetHeaderPath) return false;
|
|
154
|
+
const candidateHeaderPath = parseTomlTableHeader(line);
|
|
155
|
+
if (!candidateHeaderPath || candidateHeaderPath.length !== targetHeaderPath.length) return false;
|
|
156
|
+
return candidateHeaderPath.every((part, index) => part === targetHeaderPath[index]);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function isTomlTableHeaderLine(line) {
|
|
160
|
+
const normalizedLine = stripUnquotedInlineComment(line).trim();
|
|
161
|
+
return normalizedLine.startsWith("[") && normalizedLine.endsWith("]");
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function scanTomlMultilineLine(line, currentQuote) {
|
|
165
|
+
if (currentQuote) {
|
|
166
|
+
return {
|
|
167
|
+
wasInside: true,
|
|
168
|
+
nextQuote: findTomlMultilineDelimiter(line, currentQuote, 0) === -1 ? currentQuote : null,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let quote = null;
|
|
173
|
+
let index = 0;
|
|
174
|
+
while (index < line.length) {
|
|
175
|
+
const char = line[index];
|
|
176
|
+
if (quote === '"') {
|
|
177
|
+
if (char === "\\") {
|
|
178
|
+
index += 2;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (char === '"') quote = null;
|
|
182
|
+
index += 1;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (quote === "'") {
|
|
186
|
+
if (char === "'") quote = null;
|
|
187
|
+
index += 1;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (char === "#") break;
|
|
191
|
+
const delimiter = line.startsWith('"""', index) ? '"""' : line.startsWith("'''", index) ? "'''" : null;
|
|
192
|
+
if (delimiter) {
|
|
193
|
+
const closingIndex = findTomlMultilineDelimiter(line, delimiter, index + delimiter.length);
|
|
194
|
+
return { wasInside: false, nextQuote: closingIndex === -1 ? delimiter : null };
|
|
195
|
+
}
|
|
196
|
+
if (char === '"' || char === "'") quote = char;
|
|
197
|
+
index += 1;
|
|
198
|
+
}
|
|
199
|
+
return { wasInside: false, nextQuote: null };
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function findTomlMultilineDelimiter(line, delimiter, startIndex) {
|
|
203
|
+
let index = line.indexOf(delimiter, startIndex);
|
|
204
|
+
while (index !== -1) {
|
|
205
|
+
if (delimiter === "'''" || countPrecedingBackslashes(line, index) % 2 === 0) return index;
|
|
206
|
+
index = line.indexOf(delimiter, index + 1);
|
|
207
|
+
}
|
|
208
|
+
return -1;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function countPrecedingBackslashes(line, index) {
|
|
212
|
+
let count = 0;
|
|
213
|
+
let cursor = index - 1;
|
|
214
|
+
while (cursor >= 0 && line[cursor] === "\\") {
|
|
215
|
+
count += 1;
|
|
216
|
+
cursor -= 1;
|
|
217
|
+
}
|
|
218
|
+
return count;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function findRootTomlSetting(config, targetPath) {
|
|
222
|
+
const lines = config.match(/[^\n]*\n?|$/g) ?? [];
|
|
223
|
+
let offset = 0;
|
|
224
|
+
let multilineQuote = null;
|
|
225
|
+
for (const line of lines) {
|
|
226
|
+
if (line.length === 0) break;
|
|
227
|
+
const multilineScan = scanTomlMultilineLine(line, multilineQuote);
|
|
228
|
+
multilineQuote = multilineScan.nextQuote;
|
|
229
|
+
if (multilineScan.wasInside) {
|
|
230
|
+
offset += line.length;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
if (isTomlTableHeaderLine(line)) break;
|
|
234
|
+
const assignmentIndex = findUnquotedAssignment(line);
|
|
235
|
+
if (assignmentIndex >= 0) {
|
|
236
|
+
const settingPath = parseTomlDottedKey(line.slice(0, assignmentIndex).trim());
|
|
237
|
+
if (settingPath && tomlPathMatches(settingPath, targetPath)) {
|
|
238
|
+
const newline = line.endsWith("\n") ? "\n" : "";
|
|
239
|
+
return { line, lineBody: newline ? line.slice(0, -1) : line, newline, offset, assignmentIndex };
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
offset += line.length;
|
|
243
|
+
}
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function findFirstTomlTableStart(config) {
|
|
248
|
+
const lines = config.match(/[^\n]*\n?|$/g) ?? [];
|
|
249
|
+
let offset = 0;
|
|
250
|
+
let multilineQuote = null;
|
|
251
|
+
for (const line of lines) {
|
|
252
|
+
if (line.length === 0) break;
|
|
253
|
+
const multilineScan = scanTomlMultilineLine(line, multilineQuote);
|
|
254
|
+
multilineQuote = multilineScan.nextQuote;
|
|
255
|
+
if (!multilineScan.wasInside && isTomlTableHeaderLine(line)) return offset;
|
|
256
|
+
offset += line.length;
|
|
257
|
+
}
|
|
258
|
+
return config.length;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function findUnquotedComment(line, startIndex) {
|
|
262
|
+
let quote = null;
|
|
263
|
+
let index = startIndex;
|
|
264
|
+
while (index < line.length) {
|
|
265
|
+
const char = line[index];
|
|
266
|
+
if (quote === '"') {
|
|
267
|
+
if (char === "\\") {
|
|
268
|
+
index += 2;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if (char === '"') quote = null;
|
|
272
|
+
index += 1;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (quote === "'") {
|
|
276
|
+
if (char === "'") quote = null;
|
|
277
|
+
index += 1;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (char === '"' || char === "'") quote = char;
|
|
281
|
+
else if (char === "#") return index;
|
|
282
|
+
index += 1;
|
|
283
|
+
}
|
|
284
|
+
return -1;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function tomlPathMatches(candidate, target) {
|
|
288
|
+
return candidate.length === target.length && candidate.every((part, index) => part === target[index]);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function replaceTomlAssignmentValue(line, assignmentIndex, value) {
|
|
292
|
+
const newline = line.endsWith("\n") ? "\n" : "";
|
|
293
|
+
const lineBody = newline ? line.slice(0, -1) : line;
|
|
294
|
+
const commentIndex = findUnquotedComment(lineBody, assignmentIndex + 1);
|
|
295
|
+
const comment = commentIndex === -1 ? "" : ` ${lineBody.slice(commentIndex).trimStart()}`;
|
|
296
|
+
return `${lineBody.slice(0, assignmentIndex + 1)} ${value}${comment}${newline}`;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function parseTomlTableHeader(line) {
|
|
300
|
+
const normalizedLine = stripUnquotedInlineComment(line).trim();
|
|
301
|
+
if (!normalizedLine.startsWith("[") || !normalizedLine.endsWith("]") || normalizedLine.startsWith("[[")) {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
return parseTomlDottedKey(normalizedLine.slice(1, -1).trim());
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function stripUnquotedInlineComment(line) {
|
|
308
|
+
let quote = null;
|
|
309
|
+
let index = 0;
|
|
310
|
+
while (index < line.length) {
|
|
311
|
+
const char = line[index];
|
|
312
|
+
if (quote === '"') {
|
|
313
|
+
if (char === "\\") {
|
|
314
|
+
index += 2;
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (char === '"') quote = null;
|
|
318
|
+
index += 1;
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
321
|
+
if (quote === "'") {
|
|
322
|
+
if (char === "'") quote = null;
|
|
323
|
+
index += 1;
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (char === '"' || char === "'") {
|
|
327
|
+
quote = char;
|
|
328
|
+
index += 1;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (char === "#") return line.slice(0, index);
|
|
332
|
+
index += 1;
|
|
333
|
+
}
|
|
334
|
+
return line;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function findUnquotedAssignment(line) {
|
|
338
|
+
let quote = null;
|
|
339
|
+
let index = 0;
|
|
340
|
+
while (index < line.length) {
|
|
341
|
+
const char = line[index];
|
|
342
|
+
if (quote === '"') {
|
|
343
|
+
if (char === "\\") {
|
|
344
|
+
index += 2;
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
if (char === '"') quote = null;
|
|
348
|
+
index += 1;
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
351
|
+
if (quote === "'") {
|
|
352
|
+
if (char === "'") quote = null;
|
|
353
|
+
index += 1;
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (char === '"' || char === "'") {
|
|
357
|
+
quote = char;
|
|
358
|
+
index += 1;
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
if (char === "=") return index;
|
|
362
|
+
index += 1;
|
|
363
|
+
}
|
|
364
|
+
return -1;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function parseTomlDottedKey(input) {
|
|
368
|
+
const parts = [];
|
|
369
|
+
let index = 0;
|
|
370
|
+
while (index < input.length) {
|
|
371
|
+
index = skipWhitespace(input, index);
|
|
372
|
+
const parsedKey = parseTomlKeyPart(input, index);
|
|
373
|
+
if (!parsedKey) return null;
|
|
374
|
+
parts.push(parsedKey.value);
|
|
375
|
+
index = skipWhitespace(input, parsedKey.nextIndex);
|
|
376
|
+
if (index === input.length) return parts;
|
|
377
|
+
if (input[index] !== ".") return null;
|
|
378
|
+
index += 1;
|
|
379
|
+
}
|
|
380
|
+
return parts.length > 0 ? parts : null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function parseTomlKeyPart(input, startIndex) {
|
|
384
|
+
const quote = input[startIndex];
|
|
385
|
+
if (quote === "'") return parseLiteralTomlString(input, startIndex);
|
|
386
|
+
if (quote === '"') return parseBasicTomlString(input, startIndex);
|
|
387
|
+
return parseBareTomlKey(input, startIndex);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function parseLiteralTomlString(input, startIndex) {
|
|
391
|
+
let index = startIndex + 1;
|
|
392
|
+
let value = "";
|
|
393
|
+
while (index < input.length) {
|
|
394
|
+
const char = input[index];
|
|
395
|
+
if (char === "'") return { value, nextIndex: index + 1 };
|
|
396
|
+
value += char;
|
|
397
|
+
index += 1;
|
|
398
|
+
}
|
|
399
|
+
return null;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function parseBasicTomlString(input, startIndex) {
|
|
403
|
+
let index = startIndex + 1;
|
|
404
|
+
let value = "";
|
|
405
|
+
while (index < input.length) {
|
|
406
|
+
const char = input[index];
|
|
407
|
+
if (char === '"') return { value, nextIndex: index + 1 };
|
|
408
|
+
if (char !== "\\") {
|
|
409
|
+
value += char;
|
|
410
|
+
index += 1;
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
const escaped = parseBasicTomlEscape(input, index);
|
|
414
|
+
if (!escaped) return null;
|
|
415
|
+
value += escaped.value;
|
|
416
|
+
index = escaped.nextIndex;
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function parseBasicTomlEscape(input, backslashIndex) {
|
|
422
|
+
const escape = input[backslashIndex + 1];
|
|
423
|
+
if (escape === undefined) return null;
|
|
424
|
+
if (escape === "b") return { value: "\b", nextIndex: backslashIndex + 2 };
|
|
425
|
+
if (escape === "t") return { value: "\t", nextIndex: backslashIndex + 2 };
|
|
426
|
+
if (escape === "n") return { value: "\n", nextIndex: backslashIndex + 2 };
|
|
427
|
+
if (escape === "f") return { value: "\f", nextIndex: backslashIndex + 2 };
|
|
428
|
+
if (escape === "r") return { value: "\r", nextIndex: backslashIndex + 2 };
|
|
429
|
+
if (escape === '"') return { value: '"', nextIndex: backslashIndex + 2 };
|
|
430
|
+
if (escape === "\\") return { value: "\\", nextIndex: backslashIndex + 2 };
|
|
431
|
+
if (escape === "u") return parseUnicodeEscape(input, backslashIndex + 2, 4);
|
|
432
|
+
if (escape === "U") return parseUnicodeEscape(input, backslashIndex + 2, 8);
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function parseUnicodeEscape(input, digitsStart, digitCount) {
|
|
437
|
+
const digits = input.slice(digitsStart, digitsStart + digitCount);
|
|
438
|
+
if (digits.length !== digitCount || !/^[0-9A-Fa-f]+$/.test(digits)) return null;
|
|
439
|
+
const codePoint = Number.parseInt(digits, 16);
|
|
440
|
+
if (codePoint > 0x10ffff) return null;
|
|
441
|
+
return { value: String.fromCodePoint(codePoint), nextIndex: digitsStart + digitCount };
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function parseBareTomlKey(input, startIndex) {
|
|
445
|
+
let index = startIndex;
|
|
446
|
+
while (index < input.length && /[A-Za-z0-9_-]/.test(input[index])) index += 1;
|
|
447
|
+
if (index === startIndex) return null;
|
|
448
|
+
return { value: input.slice(startIndex, index), nextIndex: index };
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function skipWhitespace(input, startIndex) {
|
|
452
|
+
let index = startIndex;
|
|
453
|
+
while (index < input.length && /\s/.test(input[index])) index += 1;
|
|
454
|
+
return index;
|
|
455
|
+
}
|
|
@@ -14,6 +14,9 @@ const ignoredSkillSourceDirNames = new Set([".mypy_cache", ".omo", ".pytest_cach
|
|
|
14
14
|
const ignoredSkillSourceFileNames = new Set([".gitignore", ".npmignore", "pyrightconfig.json"]);
|
|
15
15
|
const skillSources = [
|
|
16
16
|
["comment-checker", "components/comment-checker/skills/comment-checker"],
|
|
17
|
+
["lcx-contribute-bug-fix", "components/lcx/skills/lcx-contribute-bug-fix"],
|
|
18
|
+
["lcx-doctor", "components/lcx/skills/lcx-doctor"],
|
|
19
|
+
["lcx-report-bug", "components/lcx/skills/lcx-report-bug"],
|
|
17
20
|
["lsp", "components/lsp/skills/lsp"],
|
|
18
21
|
["rules", "components/rules/skills/rules"],
|
|
19
22
|
["teammode", "components/teammode/skills/teammode"],
|
|
@@ -22,7 +25,6 @@ const skillSources = [
|
|
|
22
25
|
["ultrawork", "components/ultrawork/skills/ultrawork"],
|
|
23
26
|
];
|
|
24
27
|
const componentSkillNames = new Set(skillSources.map(([name]) => name));
|
|
25
|
-
const codexHiddenSharedSkillNames = new Set(["ultraresearch"]);
|
|
26
28
|
const skillDisplayPrefix = "(OmO) ";
|
|
27
29
|
|
|
28
30
|
function shouldCopySkillSource(source) {
|
|
@@ -182,22 +184,6 @@ prefixes when identity is needed.
|
|
|
182
184
|
`;
|
|
183
185
|
|
|
184
186
|
function applyCodexSkillOverlays(skillName, content) {
|
|
185
|
-
if (skillName === "ulw-research") {
|
|
186
|
-
return content
|
|
187
|
-
.replace(
|
|
188
|
-
", the legacy alias 'ultraresearch', any 'ulw' research wording,",
|
|
189
|
-
", any 'ulw' research wording,",
|
|
190
|
-
)
|
|
191
|
-
.replace(
|
|
192
|
-
'the legacy alias "ultraresearch" (also `/ultraresearch`, `$ultraresearch`), ',
|
|
193
|
-
"",
|
|
194
|
-
)
|
|
195
|
-
.replace(
|
|
196
|
-
"answer those normally, and mention that `ulw-research` is available (legacy alias: `ultraresearch`) when a question would clearly benefit from it.",
|
|
197
|
-
"answer those normally, and mention that `ulw-research` is available when a question would clearly benefit from it.",
|
|
198
|
-
)
|
|
199
|
-
.replace("# Ultraresearch Synthesis: <query>", "# ULW-Research Synthesis: <query>");
|
|
200
|
-
}
|
|
201
187
|
if (skillName === "start-work") {
|
|
202
188
|
return content
|
|
203
189
|
.replace(startWorkOriginalCompletion, startWorkCodexCompletion)
|
|
@@ -270,7 +256,6 @@ async function syncSkills() {
|
|
|
270
256
|
|
|
271
257
|
for (const skillName of sharedSkillNames) {
|
|
272
258
|
if (componentSkillNames.has(skillName)) continue;
|
|
273
|
-
if (codexHiddenSharedSkillNames.has(skillName)) continue;
|
|
274
259
|
await cp(join(sharedSkillsRoot, skillName), join(skillsRoot, skillName), {
|
|
275
260
|
filter: shouldCopySkillSource,
|
|
276
261
|
recursive: true,
|
|
@@ -85,6 +85,49 @@ describe("getCodexOmoConfig", () => {
|
|
|
85
85
|
expect(result.codegraph?.excluded_roots).toEqual(["/tmp/omo-research", "/private/tmp/omo-research"])
|
|
86
86
|
})
|
|
87
87
|
|
|
88
|
+
it("#given no codegraph.daemon key #when loading config #then daemon defaults to off", () => {
|
|
89
|
+
// given
|
|
90
|
+
const homeDir = createTemporaryDirectory("omo-codex-shared-daemon-default-home-")
|
|
91
|
+
const cwd = createTemporaryDirectory("omo-codex-shared-daemon-default-project-")
|
|
92
|
+
writeOmoConfig(homeDir, JSON.stringify({ codegraph: { enabled: true } }))
|
|
93
|
+
|
|
94
|
+
// when
|
|
95
|
+
const result = getCodexOmoConfig({ cwd, homeDir, env: {} })
|
|
96
|
+
|
|
97
|
+
// then
|
|
98
|
+
expect(result.codegraph?.daemon).toBeUndefined()
|
|
99
|
+
expect(result.codegraph?.daemon === true).toBe(false)
|
|
100
|
+
expect(result.warnings).toEqual([])
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it("#given codex SOT sets codegraph.daemon=true #when loading config #then daemon opt-in is returned", () => {
|
|
104
|
+
// given
|
|
105
|
+
const homeDir = createTemporaryDirectory("omo-codex-shared-daemon-on-home-")
|
|
106
|
+
const cwd = createTemporaryDirectory("omo-codex-shared-daemon-on-project-")
|
|
107
|
+
writeOmoConfig(homeDir, JSON.stringify({ "[codex]": { codegraph: { daemon: true } } }))
|
|
108
|
+
|
|
109
|
+
// when
|
|
110
|
+
const result = getCodexOmoConfig({ cwd, homeDir, env: {} })
|
|
111
|
+
|
|
112
|
+
// then
|
|
113
|
+
expect(result.codegraph?.daemon).toBe(true)
|
|
114
|
+
expect(result.warnings).toEqual([])
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it("#given codex SOT sets codegraph.daemon to a non-boolean #when loading config #then the value is rejected with a warning", () => {
|
|
118
|
+
// given
|
|
119
|
+
const homeDir = createTemporaryDirectory("omo-codex-shared-daemon-invalid-home-")
|
|
120
|
+
const cwd = createTemporaryDirectory("omo-codex-shared-daemon-invalid-project-")
|
|
121
|
+
writeOmoConfig(homeDir, JSON.stringify({ "[codex]": { codegraph: { daemon: "yes" } } }))
|
|
122
|
+
|
|
123
|
+
// when
|
|
124
|
+
const result = getCodexOmoConfig({ cwd, homeDir, env: {} })
|
|
125
|
+
|
|
126
|
+
// then
|
|
127
|
+
expect(result.codegraph?.daemon).toBeUndefined()
|
|
128
|
+
expect(result.warnings).toContain("config.[codex].codegraph.daemon must be a boolean")
|
|
129
|
+
})
|
|
130
|
+
|
|
88
131
|
it("#given legacy env override and SOT value #when loading config #then env wins over the SOT", () => {
|
|
89
132
|
// given
|
|
90
133
|
const homeDir = createTemporaryDirectory("omo-codex-shared-env-")
|
package/packages/omo-codex/plugin/skills/programming/scripts/typescript/check-no-excuse-rules.ts
CHANGED
|
@@ -19,6 +19,10 @@
|
|
|
19
19
|
* Usage:
|
|
20
20
|
* bun run scripts/check-no-excuse-rules.ts <file-or-dir>...
|
|
21
21
|
*
|
|
22
|
+
* The `typescript` package is resolved from the caller project (process.cwd()),
|
|
23
|
+
* not from this script's location, so the script works when executed from an
|
|
24
|
+
* installed skill-cache path (e.g. ~/.codex/...) against a project checkout.
|
|
25
|
+
*
|
|
22
26
|
* Exit codes:
|
|
23
27
|
* 0 - no violations
|
|
24
28
|
* 1 - violations found
|
|
@@ -26,9 +30,35 @@
|
|
|
26
30
|
*/
|
|
27
31
|
|
|
28
32
|
import fs from "node:fs"
|
|
33
|
+
import { createRequire } from "node:module"
|
|
29
34
|
import path from "node:path"
|
|
30
35
|
import process from "node:process"
|
|
31
|
-
import
|
|
36
|
+
import type * as tsTypes from "typescript"
|
|
37
|
+
|
|
38
|
+
type TsModule = typeof import("typescript")
|
|
39
|
+
|
|
40
|
+
function loadTypescriptFromCaller(): TsModule {
|
|
41
|
+
// A static `import ts from "typescript"` resolves from this script's location —
|
|
42
|
+
// an installed skill-cache path (e.g. ~/.codex/...) with no node_modules of its
|
|
43
|
+
// own — and fails even when the checked project has typescript. Resolve from the
|
|
44
|
+
// caller project (cwd) instead. When the caller has no local typescript, Bun
|
|
45
|
+
// falls back to a version-only stub from its global install cache instead of
|
|
46
|
+
// throwing, so the loaded module's API shape is verified as well.
|
|
47
|
+
const callerRequire = createRequire(path.join(process.cwd(), "no-excuse-anchor.cjs"))
|
|
48
|
+
try {
|
|
49
|
+
const loaded: Partial<TsModule> = callerRequire("typescript")
|
|
50
|
+
if (typeof loaded.createSourceFile === "function") return loaded as TsModule
|
|
51
|
+
} catch { // no-excuse-ok: catch
|
|
52
|
+
// fall through to the clear error below
|
|
53
|
+
}
|
|
54
|
+
console.error(
|
|
55
|
+
`error: cannot resolve "typescript" from the caller project (${process.cwd()}). ` +
|
|
56
|
+
"Install it in the project being checked (e.g. `bun add -d typescript`) and re-run.",
|
|
57
|
+
)
|
|
58
|
+
process.exit(2)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const ts: TsModule = loadTypescriptFromCaller()
|
|
32
62
|
|
|
33
63
|
type RuleId =
|
|
34
64
|
| "no-any-assertion"
|
|
@@ -95,7 +125,7 @@ function discoverFiles(inputs: string[]): string[] {
|
|
|
95
125
|
return files
|
|
96
126
|
}
|
|
97
127
|
|
|
98
|
-
function getLineText(sourceFile:
|
|
128
|
+
function getLineText(sourceFile: tsTypes.SourceFile, line: number): string {
|
|
99
129
|
const lineStarts = sourceFile.getLineStarts()
|
|
100
130
|
const start = lineStarts[line]
|
|
101
131
|
const end = line + 1 < lineStarts.length ? lineStarts[line + 1] : sourceFile.getEnd()
|
|
@@ -107,17 +137,17 @@ function analyzeFile(filePath: string): Violation[] {
|
|
|
107
137
|
const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true)
|
|
108
138
|
const violations: Violation[] = []
|
|
109
139
|
|
|
110
|
-
function pos(node:
|
|
140
|
+
function pos(node: tsTypes.Node): { line: number; column: number } {
|
|
111
141
|
const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile))
|
|
112
142
|
return { line: line + 1, column: character + 1 }
|
|
113
143
|
}
|
|
114
144
|
|
|
115
|
-
function lineHasOptOut(node:
|
|
145
|
+
function lineHasOptOut(node: tsTypes.Node): boolean {
|
|
116
146
|
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile))
|
|
117
147
|
return OPT_OUT_RE.test(getLineText(sourceFile, line))
|
|
118
148
|
}
|
|
119
149
|
|
|
120
|
-
function visit(node:
|
|
150
|
+
function visit(node: tsTypes.Node): void {
|
|
121
151
|
// ── as any / as unknown ──
|
|
122
152
|
if (ts.isAsExpression(node)) {
|
|
123
153
|
const typeText = node.type.getText(sourceFile)
|
|
@@ -113,9 +113,9 @@ If `ULW_LOOP_CLI` is empty, open the durable notepad first, record the missing C
|
|
|
113
113
|
|
|
114
114
|
Run one form:
|
|
115
115
|
```sh
|
|
116
|
-
omo ulw-loop create-goals --brief "<brief>" --json
|
|
117
|
-
omo ulw-loop create-goals --brief-file <path> --json
|
|
118
|
-
cat <brief> | omo ulw-loop create-goals --from-stdin --json
|
|
116
|
+
omo ulw-loop create-goals --brief "<brief>" [--validation-batch-json <json-or-path>] --json
|
|
117
|
+
omo ulw-loop create-goals --brief-file <path> [--validation-batch-json <json-or-path>] --json
|
|
118
|
+
cat <brief> | omo ulw-loop create-goals --from-stdin [--validation-batch-json <json-or-path>] --json
|
|
119
119
|
```
|
|
120
120
|
If the existing aggregate is already complete, do not steer or force the
|
|
121
121
|
completed default state for unrelated new work. Start a fresh run with
|
|
@@ -145,7 +145,7 @@ Read pending goals, criteria IDs, current ledger head, blockers, and aggregate C
|
|
|
145
145
|
Loop per goal. Cap at 5 cycles per goal. Cap identical same-criterion failures at 3.
|
|
146
146
|
|
|
147
147
|
### Acquire Next Goal
|
|
148
|
-
1. Run `omo ulw-loop complete-goals --json` and read the handoff, including criteria.
|
|
148
|
+
1. Run `omo ulw-loop complete-goals --json` and read the handoff, including criteria. After the first goal starts, a successful complete checkpoint normally prints the next goal instruction directly; use `complete-goals` as the manual fallback/resume path.
|
|
149
149
|
2. Call `get_goal` and inspect active Codex state.
|
|
150
150
|
3. Apply this table exactly:
|
|
151
151
|
|
|
@@ -177,7 +177,7 @@ Loop per goal. Cap at 5 cycles per goal. Cap identical same-criterion failures a
|
|
|
177
177
|
### Goal Completion
|
|
178
178
|
1. Non-final aggregate goal: confirm every `essential` criterion is `pass`; non-essential criteria may remain pending. Final aggregate goal: confirm every criterion across the whole plan is `pass`.
|
|
179
179
|
2. Call `get_goal` for a fresh snapshot.
|
|
180
|
-
3. Run `omo ulw-loop checkpoint --goal-id <id> --status complete --evidence "<criteria evidence summary>" --codex-goal-json <snapshot> --json
|
|
180
|
+
3. Run `omo ulw-loop checkpoint --goal-id <id> --status complete --evidence "<criteria evidence summary>" --codex-goal-json <snapshot> --json`; on success it auto-starts and prints the next eligible goal unless `--no-advance` is passed.
|
|
181
181
|
4. If blocked or failed, checkpoint with `--status blocked` or `--status failed` and include diagnosis evidence.
|
|
182
182
|
5. If this is the final goal, run the final quality gate first and pass `--quality-gate-json`.
|
|
183
183
|
|
|
@@ -219,7 +219,9 @@ Use steering only for structured evidence-backed mutation. Reject natural-langua
|
|
|
219
219
|
| annotate_ledger | Audit-only note | `--evidence`, `--rationale` |
|
|
220
220
|
| mark_blocked_superseded | Old story replaced by new evidence | `--goal-id`, `--replacements?`, `--evidence`, `--rationale` |
|
|
221
221
|
|
|
222
|
-
Command form: `omo ulw-loop steer --kind <kind> [<kind-specific-fields>] --evidence "<...>" --rationale "<...>" --json`.
|
|
222
|
+
Command form: `omo ulw-loop steer --kind <kind> [<kind-specific-fields>] --evidence "<...>" --rationale "<...>" --json`. For multiple evidence-backed plan-shape changes discovered together, pass `--proposals-json <json-or-path>` with an array of proposals; the batch applies atomically or rejects without partial plan mutation.
|
|
223
|
+
|
|
224
|
+
Validation batches are optional aggregate-mode review boundaries declared at create time with `--validation-batch-json`. A batch-final member requires all other members resolved, all member criteria pass, and a member-spanning quality gate; split/supersede steering keeps batch membership updated.
|
|
223
225
|
Structured prompt directives accepted: `OMO_ULW_LOOP_STEER: { ... }`, `omo.ulw-loop.steer: {...}`, `omo ulw-loop steer: {...}`.
|
|
224
226
|
|
|
225
227
|
## Constraints
|