oh-my-copilot 4.13.0 → 4.13.2
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +2 -2
- package/agents/analyst.agent.md +2 -1
- package/agents/architect.agent.md +2 -1
- package/agents/code-reviewer.agent.md +2 -1
- package/agents/critic.agent.md +2 -1
- package/agents/debugger.agent.md +2 -1
- package/agents/designer.agent.md +2 -1
- package/agents/devils-advocate.agent.md +2 -1
- package/agents/document-specialist.agent.md +2 -1
- package/agents/executor.agent.md +2 -1
- package/agents/explore.agent.md +2 -1
- package/agents/git-master.agent.md +2 -1
- package/agents/planner.agent.md +2 -1
- package/agents/qa-tester.agent.md +2 -1
- package/agents/scientist.agent.md +2 -1
- package/agents/security-reviewer.agent.md +2 -1
- package/agents/test-engineer.agent.md +2 -1
- package/agents/verifier.agent.md +2 -1
- package/agents/writer.agent.md +2 -1
- package/bridge/cli.cjs +2823 -1491
- package/bridge/mcp-server.cjs +15 -2
- package/bridge/run-mcp-server.sh +1 -1
- package/bridge/runtime-cli.cjs +3589 -2448
- package/bridge/team-bridge.cjs +6 -4
- package/bridge/team-mcp.cjs +19607 -0
- package/bridge/team.js +3576 -2080
- package/dist/__tests__/auto-slash-aliases.test.js +5 -5
- package/dist/__tests__/auto-slash-aliases.test.js.map +1 -1
- package/dist/__tests__/bedrock-lm-suffix-hook.test.js +2 -2
- package/dist/__tests__/bedrock-lm-suffix-hook.test.js.map +1 -1
- package/dist/__tests__/cleanup-orphans-unref.test.d.ts +2 -0
- package/dist/__tests__/cleanup-orphans-unref.test.d.ts.map +1 -0
- package/dist/__tests__/cleanup-orphans-unref.test.js +44 -0
- package/dist/__tests__/cleanup-orphans-unref.test.js.map +1 -0
- package/dist/__tests__/context-safety.test.js +3 -3
- package/dist/__tests__/context-safety.test.js.map +1 -1
- package/dist/__tests__/hooks/learner/bridge.test.js +106 -0
- package/dist/__tests__/hooks/learner/bridge.test.js.map +1 -1
- package/dist/__tests__/hooks/learner/parser.test.js +30 -0
- package/dist/__tests__/hooks/learner/parser.test.js.map +1 -1
- package/dist/__tests__/hooks.test.js +47 -0
- package/dist/__tests__/hooks.test.js.map +1 -1
- package/dist/__tests__/hud/extra-usage.test.js +42 -0
- package/dist/__tests__/hud/extra-usage.test.js.map +1 -1
- package/dist/__tests__/hud/render-enterprise.test.d.ts +5 -0
- package/dist/__tests__/hud/render-enterprise.test.d.ts.map +1 -0
- package/dist/__tests__/hud/render-enterprise.test.js +232 -0
- package/dist/__tests__/hud/render-enterprise.test.js.map +1 -0
- package/dist/__tests__/hud/render-rate-limits-priority.test.js +159 -0
- package/dist/__tests__/hud/render-rate-limits-priority.test.js.map +1 -1
- package/dist/__tests__/hud/stdin-session-scope.test.d.ts +2 -0
- package/dist/__tests__/hud/stdin-session-scope.test.d.ts.map +1 -0
- package/dist/__tests__/hud/stdin-session-scope.test.js +169 -0
- package/dist/__tests__/hud/stdin-session-scope.test.js.map +1 -0
- package/dist/__tests__/hud/usage-api.test.js +111 -1
- package/dist/__tests__/hud/usage-api.test.js.map +1 -1
- package/dist/__tests__/issue-2652-runtime-wiring-and-output-contract.test.js +2 -0
- package/dist/__tests__/issue-2652-runtime-wiring-and-output-contract.test.js.map +1 -1
- package/dist/__tests__/keyword-detector-echo-guard.test.d.ts +2 -0
- package/dist/__tests__/keyword-detector-echo-guard.test.d.ts.map +1 -0
- package/dist/__tests__/keyword-detector-echo-guard.test.js +218 -0
- package/dist/__tests__/keyword-detector-echo-guard.test.js.map +1 -0
- package/dist/__tests__/keyword-detector-script.test.js +2 -1
- package/dist/__tests__/keyword-detector-script.test.js.map +1 -1
- package/dist/__tests__/omc-cli-rendering.test.js +13 -10
- package/dist/__tests__/omc-cli-rendering.test.js.map +1 -1
- package/dist/__tests__/pre-tool-enforcer.test.js +22 -38
- package/dist/__tests__/pre-tool-enforcer.test.js.map +1 -1
- package/dist/__tests__/preemptive-compaction-hook.test.js +63 -0
- package/dist/__tests__/preemptive-compaction-hook.test.js.map +1 -1
- package/dist/__tests__/safe-command-approver.test.d.ts +2 -0
- package/dist/__tests__/safe-command-approver.test.d.ts.map +1 -0
- package/dist/__tests__/safe-command-approver.test.js +103 -0
- package/dist/__tests__/safe-command-approver.test.js.map +1 -0
- package/dist/__tests__/skills.test.js +18 -0
- package/dist/__tests__/skills.test.js.map +1 -1
- package/dist/__tests__/state-root-resolution.test.js +64 -0
- package/dist/__tests__/state-root-resolution.test.js.map +1 -1
- package/dist/cli/__tests__/launch.test.js +24 -0
- package/dist/cli/__tests__/launch.test.js.map +1 -1
- package/dist/cli/__tests__/team-status-worktree.test.d.ts +2 -0
- package/dist/cli/__tests__/team-status-worktree.test.d.ts.map +1 -0
- package/dist/cli/__tests__/team-status-worktree.test.js +96 -0
- package/dist/cli/__tests__/team-status-worktree.test.js.map +1 -0
- package/dist/cli/__tests__/team.test.js +164 -0
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/__tests__/tmux-utils.test.js +18 -3
- package/dist/cli/__tests__/tmux-utils.test.js.map +1 -1
- package/dist/cli/autoresearch-setup-session.d.ts.map +1 -1
- package/dist/cli/autoresearch-setup-session.js +1 -0
- package/dist/cli/autoresearch-setup-session.js.map +1 -1
- package/dist/cli/commands/team.js +1 -1
- package/dist/cli/launch.d.ts.map +1 -1
- package/dist/cli/launch.js +10 -2
- package/dist/cli/launch.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +103 -4
- package/dist/cli/team.js.map +1 -1
- package/dist/cli/tmux-utils.d.ts.map +1 -1
- package/dist/cli/tmux-utils.js +5 -2
- package/dist/cli/tmux-utils.js.map +1 -1
- package/dist/cli/utils/formatting.d.ts +0 -10
- package/dist/cli/utils/formatting.d.ts.map +1 -1
- package/dist/cli/utils/formatting.js +0 -52
- package/dist/cli/utils/formatting.js.map +1 -1
- package/dist/config/loader.d.ts.map +1 -1
- package/dist/config/loader.js +34 -8
- package/dist/config/loader.js.map +1 -1
- package/dist/features/__tests__/magic-keywords.test.d.ts +2 -0
- package/dist/features/__tests__/magic-keywords.test.d.ts.map +1 -0
- package/dist/features/__tests__/magic-keywords.test.js +19 -0
- package/dist/features/__tests__/magic-keywords.test.js.map +1 -0
- package/dist/features/delegation-enforcer.d.ts +2 -0
- package/dist/features/delegation-enforcer.d.ts.map +1 -1
- package/dist/features/delegation-enforcer.js +10 -0
- package/dist/features/delegation-enforcer.js.map +1 -1
- package/dist/features/magic-keywords.d.ts +1 -1
- package/dist/features/magic-keywords.d.ts.map +1 -1
- package/dist/features/magic-keywords.js +5 -192
- package/dist/features/magic-keywords.js.map +1 -1
- package/dist/hooks/__tests__/bridge-routing.test.js +23 -0
- package/dist/hooks/__tests__/bridge-routing.test.js.map +1 -1
- package/dist/hooks/bridge-normalize.d.ts +5 -0
- package/dist/hooks/bridge-normalize.d.ts.map +1 -1
- package/dist/hooks/bridge-normalize.js +6 -0
- package/dist/hooks/bridge-normalize.js.map +1 -1
- package/dist/hooks/bridge.d.ts +4 -0
- package/dist/hooks/bridge.d.ts.map +1 -1
- package/dist/hooks/bridge.js +60 -3
- package/dist/hooks/bridge.js.map +1 -1
- package/dist/hooks/keyword-detector/index.js +1 -1
- package/dist/hooks/keyword-detector/index.js.map +1 -1
- package/dist/hooks/keyword-detector/ultrawork/__tests__/index.test.d.ts +2 -0
- package/dist/hooks/keyword-detector/ultrawork/__tests__/index.test.d.ts.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/__tests__/index.test.js +37 -0
- package/dist/hooks/keyword-detector/ultrawork/__tests__/index.test.js.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/default.d.ts +3 -0
- package/dist/hooks/keyword-detector/ultrawork/default.d.ts.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/default.js +67 -0
- package/dist/hooks/keyword-detector/ultrawork/default.js.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/gemini.d.ts +3 -0
- package/dist/hooks/keyword-detector/ultrawork/gemini.d.ts.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/gemini.js +47 -0
- package/dist/hooks/keyword-detector/ultrawork/gemini.js.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/gpt.d.ts +3 -0
- package/dist/hooks/keyword-detector/ultrawork/gpt.d.ts.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/gpt.js +52 -0
- package/dist/hooks/keyword-detector/ultrawork/gpt.js.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/index.d.ts +8 -0
- package/dist/hooks/keyword-detector/ultrawork/index.d.ts.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/index.js +24 -0
- package/dist/hooks/keyword-detector/ultrawork/index.js.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/planner.d.ts +3 -0
- package/dist/hooks/keyword-detector/ultrawork/planner.d.ts.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/planner.js +57 -0
- package/dist/hooks/keyword-detector/ultrawork/planner.js.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/source-detector.d.ts +6 -0
- package/dist/hooks/keyword-detector/ultrawork/source-detector.d.ts.map +1 -0
- package/dist/hooks/keyword-detector/ultrawork/source-detector.js +37 -0
- package/dist/hooks/keyword-detector/ultrawork/source-detector.js.map +1 -0
- package/dist/hooks/learner/bridge.d.ts +2 -0
- package/dist/hooks/learner/bridge.d.ts.map +1 -1
- package/dist/hooks/learner/bridge.js +15 -109
- package/dist/hooks/learner/bridge.js.map +1 -1
- package/dist/hooks/learner/index.d.ts.map +1 -1
- package/dist/hooks/learner/index.js +51 -14
- package/dist/hooks/learner/index.js.map +1 -1
- package/dist/hooks/learner/parser.d.ts +9 -0
- package/dist/hooks/learner/parser.d.ts.map +1 -1
- package/dist/hooks/learner/parser.js +18 -5
- package/dist/hooks/learner/parser.js.map +1 -1
- package/dist/hooks/learner/types.d.ts +6 -0
- package/dist/hooks/learner/types.d.ts.map +1 -1
- package/dist/hooks/persistent-mode/index.d.ts.map +1 -1
- package/dist/hooks/persistent-mode/index.js +7 -4
- package/dist/hooks/persistent-mode/index.js.map +1 -1
- package/dist/hooks/persistent-mode/session-isolation.test.js +8 -3
- package/dist/hooks/persistent-mode/session-isolation.test.js.map +1 -1
- package/dist/hooks/persistent-mode/stop-hook-blocking.test.js +142 -7
- package/dist/hooks/persistent-mode/stop-hook-blocking.test.js.map +1 -1
- package/dist/hooks/project-memory/__tests__/learner.test.js +7 -0
- package/dist/hooks/project-memory/__tests__/learner.test.js.map +1 -1
- package/dist/hooks/project-memory/learner.d.ts +1 -1
- package/dist/hooks/project-memory/learner.d.ts.map +1 -1
- package/dist/hooks/project-memory/learner.js +3 -0
- package/dist/hooks/project-memory/learner.js.map +1 -1
- package/dist/hooks/session-end/index.d.ts.map +1 -1
- package/dist/hooks/session-end/index.js +43 -0
- package/dist/hooks/session-end/index.js.map +1 -1
- package/dist/hooks/skill-bridge.cjs +116 -87
- package/dist/hud/__tests__/enterprise-cost.test.d.ts +5 -0
- package/dist/hud/__tests__/enterprise-cost.test.d.ts.map +1 -0
- package/dist/hud/__tests__/enterprise-cost.test.js +151 -0
- package/dist/hud/__tests__/enterprise-cost.test.js.map +1 -0
- package/dist/hud/__tests__/usage-api-enterprise.test.d.ts +5 -0
- package/dist/hud/__tests__/usage-api-enterprise.test.d.ts.map +1 -0
- package/dist/hud/__tests__/usage-api-enterprise.test.js +249 -0
- package/dist/hud/__tests__/usage-api-enterprise.test.js.map +1 -0
- package/dist/hud/elements/enterprise-cost.d.ts +16 -0
- package/dist/hud/elements/enterprise-cost.d.ts.map +1 -0
- package/dist/hud/elements/enterprise-cost.js +63 -0
- package/dist/hud/elements/enterprise-cost.js.map +1 -0
- package/dist/hud/elements/permission.d.ts.map +1 -1
- package/dist/hud/elements/permission.js +2 -5
- package/dist/hud/elements/permission.js.map +1 -1
- package/dist/hud/elements/session.d.ts.map +1 -1
- package/dist/hud/elements/session.js +5 -9
- package/dist/hud/elements/session.js.map +1 -1
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +22 -5
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +34 -2
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/stdin.d.ts.map +1 -1
- package/dist/hud/stdin.js +49 -5
- package/dist/hud/stdin.js.map +1 -1
- package/dist/hud/types.d.ts +16 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/hud/usage-api.d.ts +19 -1
- package/dist/hud/usage-api.d.ts.map +1 -1
- package/dist/hud/usage-api.js +101 -18
- package/dist/hud/usage-api.js.map +1 -1
- package/dist/installer/__tests__/hook-templates.test.js +23 -0
- package/dist/installer/__tests__/hook-templates.test.js.map +1 -1
- package/dist/installer/hooks.d.ts +4 -2
- package/dist/installer/hooks.d.ts.map +1 -1
- package/dist/installer/hooks.js +5 -94
- package/dist/installer/hooks.js.map +1 -1
- package/dist/installer/index.d.ts +1 -0
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +64 -24
- package/dist/installer/index.js.map +1 -1
- package/dist/installer/mcp-registry.d.ts.map +1 -1
- package/dist/installer/mcp-registry.js +3 -2
- package/dist/installer/mcp-registry.js.map +1 -1
- package/dist/mcp/__tests__/team-server-artifact-convergence.test.js +156 -4
- package/dist/mcp/__tests__/team-server-artifact-convergence.test.js.map +1 -1
- package/dist/mcp/omc-tools-server.d.ts +11 -17
- package/dist/mcp/omc-tools-server.d.ts.map +1 -1
- package/dist/mcp/omc-tools-server.js +22 -32
- package/dist/mcp/omc-tools-server.js.map +1 -1
- package/dist/mcp/team-job-convergence.d.ts +9 -2
- package/dist/mcp/team-job-convergence.d.ts.map +1 -1
- package/dist/mcp/team-job-convergence.js +35 -14
- package/dist/mcp/team-job-convergence.js.map +1 -1
- package/dist/mcp/team-server.d.ts.map +1 -1
- package/dist/mcp/team-server.js +93 -13
- package/dist/mcp/team-server.js.map +1 -1
- package/dist/ralphthon/prd.d.ts.map +1 -1
- package/dist/ralphthon/prd.js +13 -17
- package/dist/ralphthon/prd.js.map +1 -1
- package/dist/shared/types.d.ts +3 -1
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/team/__tests__/api-interop.cleanup.test.d.ts +2 -0
- package/dist/team/__tests__/api-interop.cleanup.test.d.ts.map +1 -0
- package/dist/team/__tests__/api-interop.cleanup.test.js +234 -0
- package/dist/team/__tests__/api-interop.cleanup.test.js.map +1 -0
- package/dist/team/__tests__/api-interop.dispatch.test.js +2 -1
- package/dist/team/__tests__/api-interop.dispatch.test.js.map +1 -1
- package/dist/team/__tests__/git-worktree.test.js +164 -3
- package/dist/team/__tests__/git-worktree.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +38 -22
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/resolved-routing-snapshot.test.js +29 -1
- package/dist/team/__tests__/resolved-routing-snapshot.test.js.map +1 -1
- package/dist/team/__tests__/runtime-cli.test.js +2 -2
- package/dist/team/__tests__/runtime-cli.test.js.map +1 -1
- package/dist/team/__tests__/runtime-prompt-mode.test.js +17 -17
- package/dist/team/__tests__/runtime-prompt-mode.test.js.map +1 -1
- package/dist/team/__tests__/runtime-v2-role-routing.test.js +7 -1
- package/dist/team/__tests__/runtime-v2-role-routing.test.js.map +1 -1
- package/dist/team/__tests__/runtime-v2.dispatch.test.js +145 -16
- package/dist/team/__tests__/runtime-v2.dispatch.test.js.map +1 -1
- package/dist/team/__tests__/runtime-v2.monitor.test.d.ts +2 -0
- package/dist/team/__tests__/runtime-v2.monitor.test.d.ts.map +1 -0
- package/dist/team/__tests__/runtime-v2.monitor.test.js +219 -0
- package/dist/team/__tests__/runtime-v2.monitor.test.js.map +1 -0
- package/dist/team/__tests__/runtime-v2.shutdown.test.d.ts +2 -0
- package/dist/team/__tests__/runtime-v2.shutdown.test.d.ts.map +1 -0
- package/dist/team/__tests__/runtime-v2.shutdown.test.js +292 -0
- package/dist/team/__tests__/runtime-v2.shutdown.test.js.map +1 -0
- package/dist/team/__tests__/scaling-launch-config.test.js +204 -4
- package/dist/team/__tests__/scaling-launch-config.test.js.map +1 -1
- package/dist/team/__tests__/stage-router.test.js +29 -1
- package/dist/team/__tests__/stage-router.test.js.map +1 -1
- package/dist/team/__tests__/team-summary-worktree.test.d.ts +2 -0
- package/dist/team/__tests__/team-summary-worktree.test.d.ts.map +1 -0
- package/dist/team/__tests__/team-summary-worktree.test.js +85 -0
- package/dist/team/__tests__/team-summary-worktree.test.js.map +1 -0
- package/dist/team/__tests__/tmux-session-liveness.test.d.ts +2 -0
- package/dist/team/__tests__/tmux-session-liveness.test.d.ts.map +1 -0
- package/dist/team/__tests__/tmux-session-liveness.test.js +37 -0
- package/dist/team/__tests__/tmux-session-liveness.test.js.map +1 -0
- package/dist/team/__tests__/worker-bootstrap.test.js +47 -1
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/__tests__/worktree-contract-fields.test.d.ts +2 -0
- package/dist/team/__tests__/worktree-contract-fields.test.d.ts.map +1 -0
- package/dist/team/__tests__/worktree-contract-fields.test.js +157 -0
- package/dist/team/__tests__/worktree-contract-fields.test.js.map +1 -0
- package/dist/team/api-interop.d.ts +2 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +121 -14
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/capabilities.d.ts.map +1 -1
- package/dist/team/capabilities.js +2 -0
- package/dist/team/capabilities.js.map +1 -1
- package/dist/team/cli-worker-contract.d.ts +7 -6
- package/dist/team/cli-worker-contract.d.ts.map +1 -1
- package/dist/team/cli-worker-contract.js +7 -6
- package/dist/team/cli-worker-contract.js.map +1 -1
- package/dist/team/events.d.ts +3 -0
- package/dist/team/events.d.ts.map +1 -1
- package/dist/team/events.js +2 -1
- package/dist/team/events.js.map +1 -1
- package/dist/team/git-worktree.d.ts +78 -10
- package/dist/team/git-worktree.d.ts.map +1 -1
- package/dist/team/git-worktree.js +453 -97
- package/dist/team/git-worktree.js.map +1 -1
- package/dist/team/governance.d.ts +3 -2
- package/dist/team/governance.d.ts.map +1 -1
- package/dist/team/governance.js.map +1 -1
- package/dist/team/model-contract.d.ts +18 -2
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +55 -27
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/monitor.d.ts.map +1 -1
- package/dist/team/monitor.js +47 -3
- package/dist/team/monitor.js.map +1 -1
- package/dist/team/runtime-v2.d.ts +9 -0
- package/dist/team/runtime-v2.d.ts.map +1 -1
- package/dist/team/runtime-v2.js +347 -120
- package/dist/team/runtime-v2.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +4 -1
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/scaling.d.ts.map +1 -1
- package/dist/team/scaling.js +110 -13
- package/dist/team/scaling.js.map +1 -1
- package/dist/team/state/tasks.d.ts.map +1 -1
- package/dist/team/state/tasks.js +1 -0
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/team-ops.d.ts.map +1 -1
- package/dist/team/team-ops.js +112 -16
- package/dist/team/team-ops.js.map +1 -1
- package/dist/team/tmux-session.d.ts +17 -29
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +116 -107
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/team/types.d.ts +31 -17
- package/dist/team/types.d.ts.map +1 -1
- package/dist/team/types.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +7 -0
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +27 -13
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/team/worker-canonicalization.d.ts.map +1 -1
- package/dist/team/worker-canonicalization.js +2 -0
- package/dist/team/worker-canonicalization.js.map +1 -1
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +1 -16
- package/dist/utils/frontmatter.js.map +1 -1
- package/dist/utils/omc-cli-rendering.d.ts.map +1 -1
- package/dist/utils/omc-cli-rendering.js +1 -12
- package/dist/utils/omc-cli-rendering.js.map +1 -1
- package/dist/utils/resolve-node.d.ts.map +1 -1
- package/dist/utils/resolve-node.js +22 -28
- package/dist/utils/resolve-node.js.map +1 -1
- package/docs/MIGRATION.md +1003 -0
- package/docs/REFERENCE.md +4 -0
- package/docs/TEAM-WORKTREE-MODE.md +86 -0
- package/docs/copilot-instructions.md +1 -1
- package/docs/reference/compatibility.md +1 -1
- package/hooks/hooks.json +34 -13
- package/package.json +1 -1
- package/scripts/cleanup-orphans.mjs +24 -3
- package/scripts/context-safety.mjs +10 -11
- package/scripts/keyword-detector.debug.mjs +2 -2
- package/scripts/keyword-detector.mjs +219 -57
- package/scripts/lib/state-root.cjs +1 -1
- package/scripts/lib/state-root.mjs +3 -3
- package/scripts/persistent-mode.cjs +30 -4
- package/scripts/persistent-mode.mjs +28 -2
- package/scripts/plugin-setup.mjs +8 -7
- package/scripts/post-tool-rules-injector.mjs +1 -1
- package/scripts/post-tool-verifier.mjs +55 -3
- package/scripts/pre-tool-enforcer.mjs +9 -21
- package/scripts/project-memory-session.mjs +1 -1
- package/scripts/run.cjs +9 -9
- package/scripts/safe-command-approver.mjs +156 -0
- package/scripts/session-start.mjs +91 -11
- package/scripts/setup-copilot-instructions.sh +5 -4
- package/scripts/skill-injector.mjs +60 -23
- package/scripts/test-pr25.sh +1 -1
- package/scripts/uninstall.sh +1 -1
- package/scripts/verify-deliverables.mjs +1 -1
- package/skills/deep-interview/SKILL.md +23 -10
- package/skills/learner/SKILL.md +1 -1
- package/skills/project-session-manager/lib/config.sh +1 -1
- package/skills/project-session-manager/lib/parse.sh +1 -1
- package/skills/project-session-manager/lib/providers/azure-devops.sh +1 -1
- package/skills/project-session-manager/lib/providers/bitbucket.sh +1 -1
- package/skills/project-session-manager/lib/providers/gitea.sh +1 -1
- package/skills/project-session-manager/lib/providers/github.sh +1 -1
- package/skills/project-session-manager/lib/providers/gitlab.sh +1 -1
- package/skills/project-session-manager/lib/providers/interface.sh +1 -1
- package/skills/project-session-manager/lib/providers/jira.sh +1 -1
- package/skills/project-session-manager/lib/session.sh +1 -1
- package/skills/project-session-manager/lib/tmux.sh +1 -1
- package/skills/project-session-manager/lib/worktree.sh +1 -1
- package/skills/project-session-manager/psm.sh +1 -1
- package/skills/project-session-manager/tests/test-psm-prompt-injection.sh +1 -1
- package/skills/skill/SKILL.md +1 -1
- package/templates/hooks/keyword-detector.mjs +183 -26
- package/templates/hooks/persistent-mode.mjs +28 -2
- package/templates/hooks/session-start.mjs +126 -11
|
@@ -28,23 +28,36 @@ import { join, dirname } from 'path';
|
|
|
28
28
|
import { homedir } from 'os';
|
|
29
29
|
import { fileURLToPath } from 'url';
|
|
30
30
|
import { getClaudeConfigDir } from './lib/config-dir.mjs';
|
|
31
|
+
import { atomicWriteFileSync } from './lib/atomic-write.mjs';
|
|
31
32
|
import { readStdin } from './lib/stdin.mjs';
|
|
32
33
|
|
|
33
|
-
// Resolve OMC package root:
|
|
34
|
-
const _omcRoot = process.env.CLAUDE_PLUGIN_ROOT ||
|
|
34
|
+
// Resolve OMC package root: PLUGIN_ROOT (plugin system) or derive from this script's location
|
|
35
|
+
const _omcRoot = process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT ||
|
|
35
36
|
join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
37
|
+
const SKILL_INVOCATION_USER_REQUEST_MAX = 1200;
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
function compactHookText(text, maxChars = SKILL_INVOCATION_USER_REQUEST_MAX) {
|
|
40
|
+
const notice = '\n...[truncated; original user prompt remains available in the conversation]';
|
|
41
|
+
if (!text || text.length <= maxChars) return text || '';
|
|
42
|
+
if (maxChars <= notice.length) return notice.slice(0, Math.max(0, maxChars));
|
|
43
|
+
return `${text.slice(0, maxChars - notice.length).trimEnd()}${notice}`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getSkillPathCandidates(skillName) {
|
|
47
|
+
const roots = [
|
|
48
|
+
process.env.PLUGIN_ROOT,
|
|
49
|
+
process.env.CLAUDE_PLUGIN_ROOT,
|
|
50
|
+
_omcRoot,
|
|
51
|
+
process.cwd(),
|
|
52
|
+
].filter(Boolean);
|
|
53
|
+
return [...new Set(roots.map(root => join(root, 'skills', skillName, 'SKILL.md')))];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function resolveSkillPath(skillName) {
|
|
57
|
+
for (const skillPath of getSkillPathCandidates(skillName)) {
|
|
58
|
+
if (existsSync(skillPath)) return skillPath;
|
|
46
59
|
}
|
|
47
|
-
return
|
|
60
|
+
return getSkillPathCandidates(skillName)[0] || `skills/${skillName}/SKILL.md`;
|
|
48
61
|
}
|
|
49
62
|
|
|
50
63
|
const ULTRATHINK_MESSAGE = `<think-mode>
|
|
@@ -204,7 +217,7 @@ const ROLE_BOUNDARY_PATTERN =
|
|
|
204
217
|
/^<\s*\/?\s*(system|human|assistant|user|tool_use|tool_result)\b[^>]*>/i;
|
|
205
218
|
const SKILL_TRANSCRIPT_LINE_PATTERN =
|
|
206
219
|
/^\s*Skill:\s+oh-my-(?:copilot|claudecode|codex):/i;
|
|
207
|
-
const USER_REQUEST_LINE_PATTERN = /^\s*User request
|
|
220
|
+
const USER_REQUEST_LINE_PATTERN = /^\s*User request(?:\s*\([^)]*\))?:\s*$/i;
|
|
208
221
|
const SHELL_TRANSCRIPT_LINE_PATTERN = /^\s*[$%❯]\s+/;
|
|
209
222
|
const GIT_DIFF_START_PATTERNS = [
|
|
210
223
|
/^diff\s+--git\s+a\//,
|
|
@@ -347,6 +360,119 @@ const QUESTION_FOLLOWUP_PATTERNS = [
|
|
|
347
360
|
/\b(?:how\s+many|how\s+much|why|what\s+happened|what\s+went\s+wrong|token\s+budget|cost|pricing)\b/i,
|
|
348
361
|
/(?:왜|얼마|몇\s*번|몇번|토큰|가격|비용|질문)/u,
|
|
349
362
|
];
|
|
363
|
+
|
|
364
|
+
// Patterns that identify system-generated echoes (hook outputs) which can be
|
|
365
|
+
// pasted back into the prompt verbatim. If a mode keyword only appears inside
|
|
366
|
+
// such an echo block we MUST NOT re-activate the mode: otherwise a user who
|
|
367
|
+
// copies a "[RALPH LOOP - ITERATION N] ..." block into a new session to debug
|
|
368
|
+
// it will unintentionally re-trigger ralph, and the pasted text ends up as
|
|
369
|
+
// the new state.prompt — producing a recursive self-reinforcing loop.
|
|
370
|
+
// Continuation lines that hook output typically emits DIRECTLY after a
|
|
371
|
+
// recognized block header. They must be stripped only in that context —
|
|
372
|
+
// never standalone — because a user might legitimately start a prompt with
|
|
373
|
+
// "Task: …" or similar (Codex automated review P1/P2 on #2795).
|
|
374
|
+
const ECHO_CONTINUATION = '(?:\\r?\\n[ \\t]*(?:Task:\\s|When FULLY complete \\(after Architect verification\\)|run\\s+\\/oh-my-(?:claudecode|codex|copilot):cancel).*)*';
|
|
375
|
+
|
|
376
|
+
// NOTE: each pattern is a SINGLE LOGICAL BLOCK: the block header line +
|
|
377
|
+
// zero-or-more continuation lines that hooks emit right after it. The whole
|
|
378
|
+
// match is stripped together. Both `i` (case-insensitive against the
|
|
379
|
+
// lower-cased cleanPrompt upstream) and `m` (so `^`/`$` match line
|
|
380
|
+
// boundaries) are required.
|
|
381
|
+
function buildEchoBlockRegex(headerBody) {
|
|
382
|
+
return new RegExp(`^[ \\t]*${headerBody}.*${ECHO_CONTINUATION}$`, 'gim');
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const SYSTEM_ECHO_BLOCK_PATTERNS = [
|
|
386
|
+
// persistent-mode.mjs block headers
|
|
387
|
+
buildEchoBlockRegex('\\[RALPH LOOP\\s*-\\s*ITERATION[^\\]\\n]*\\]'),
|
|
388
|
+
buildEchoBlockRegex('\\[RALPH LOOP\\s*-\\s*(?:HARD LIMIT|EXTENDED)\\]'),
|
|
389
|
+
buildEchoBlockRegex('\\[TEAM\\s*-\\s*Phase:[^\\]\\n]*\\]'),
|
|
390
|
+
buildEchoBlockRegex('\\[AUTOPILOT[^\\]\\n]*\\]'),
|
|
391
|
+
buildEchoBlockRegex('\\[ULTRAPILOT[^\\]\\n]*\\]'),
|
|
392
|
+
buildEchoBlockRegex('\\[ULTRAWORK[^\\]\\n]*\\]'),
|
|
393
|
+
buildEchoBlockRegex('\\[ULTRAQA[^\\]\\n]*\\]'),
|
|
394
|
+
buildEchoBlockRegex('\\[PIPELINE[^\\]\\n]*\\]'),
|
|
395
|
+
buildEchoBlockRegex('\\[SWARM[^\\]\\n]*\\]'),
|
|
396
|
+
buildEchoBlockRegex('\\[TOOL ERROR[^\\]\\n]*\\]'),
|
|
397
|
+
// keyword-detector.mjs block headers
|
|
398
|
+
buildEchoBlockRegex('\\[MAGIC KEYWORD:[^\\]\\n]*\\]'),
|
|
399
|
+
buildEchoBlockRegex('\\[MAGIC KEYWORDS DETECTED:[^\\]\\n]*\\]'),
|
|
400
|
+
// Stop-hook wrapping by the Claude Code harness
|
|
401
|
+
buildEchoBlockRegex('Stop hook (?:blocking error|feedback|stopped continuation)'),
|
|
402
|
+
buildEchoBlockRegex('PreToolUse:[^\\n]*hook additional context:'),
|
|
403
|
+
buildEchoBlockRegex('PostToolUse:[^\\n]*hook additional context:'),
|
|
404
|
+
];
|
|
405
|
+
|
|
406
|
+
// Signature lines indicating the text is predominantly a system echo. Even
|
|
407
|
+
// when the block patterns above fail to delimit cleanly (e.g. truncation),
|
|
408
|
+
// presence of these sentinels should make us treat the prompt as an echo.
|
|
409
|
+
// All patterns use `i` because hasActionableKeyword sees a lowercased prompt.
|
|
410
|
+
const SYSTEM_ECHO_SIGNATURES = [
|
|
411
|
+
/\bWhen FULLY complete \(after Architect verification\)\b/i,
|
|
412
|
+
/\brun\s+\/oh-my-(?:claudecode|codex|copilot):cancel\b/i,
|
|
413
|
+
/\[RALPH LOOP\s*-\s*ITERATION\b/i,
|
|
414
|
+
];
|
|
415
|
+
|
|
416
|
+
const MAX_STATE_PROMPT_LEN = 500;
|
|
417
|
+
|
|
418
|
+
function stripSystemEchoes(text) {
|
|
419
|
+
if (typeof text !== 'string' || text.length === 0) return '';
|
|
420
|
+
let cleaned = text;
|
|
421
|
+
for (const pattern of SYSTEM_ECHO_BLOCK_PATTERNS) {
|
|
422
|
+
cleaned = cleaned.replace(pattern, ' ');
|
|
423
|
+
}
|
|
424
|
+
return cleaned;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function looksLikeSystemEcho(text) {
|
|
428
|
+
if (typeof text !== 'string' || text.length === 0) return false;
|
|
429
|
+
if (SYSTEM_ECHO_SIGNATURES.some((pattern) => pattern.test(text))) return true;
|
|
430
|
+
// Also treat the presence of ANY echo block pattern as an echo, so that
|
|
431
|
+
// mode-specific paste blocks (AUTOPILOT, ULTRAWORK, TEAM, etc.) are
|
|
432
|
+
// recognized without needing a dedicated signature line.
|
|
433
|
+
for (const pattern of SYSTEM_ECHO_BLOCK_PATTERNS) {
|
|
434
|
+
const probe = new RegExp(pattern.source, pattern.flags.replace('g', ''));
|
|
435
|
+
if (probe.test(text)) return true;
|
|
436
|
+
}
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Sanitize a prompt before persisting it to a *-state.json file.
|
|
442
|
+
* Returning the raw prompt risks storing large system echoes or pasted
|
|
443
|
+
* hook output, which persistent-mode.mjs would then blast back into the
|
|
444
|
+
* next Stop-hook block reason on every iteration.
|
|
445
|
+
*
|
|
446
|
+
* Strategy:
|
|
447
|
+
* 1. Strip echoes first. If non-empty content remains AND that remainder no
|
|
448
|
+
* longer looks like an echo, keep it (preserves the real user request in
|
|
449
|
+
* an "echo + blank line + real request" paste).
|
|
450
|
+
* 2. Otherwise, if the raw prompt looks like a pure echo, substitute the
|
|
451
|
+
* placeholder sentinel.
|
|
452
|
+
* 3. Finally, hard-truncate to MAX_STATE_PROMPT_LEN chars.
|
|
453
|
+
*/
|
|
454
|
+
function sanitizePromptForState(prompt) {
|
|
455
|
+
if (typeof prompt !== 'string') return '';
|
|
456
|
+
const trimmed = prompt.trim();
|
|
457
|
+
if (!trimmed) return '';
|
|
458
|
+
|
|
459
|
+
const stripped = stripSystemEchoes(trimmed).trim();
|
|
460
|
+
if (stripped.length > 0 && !looksLikeSystemEcho(stripped)) {
|
|
461
|
+
return stripped.length > MAX_STATE_PROMPT_LEN
|
|
462
|
+
? `${stripped.slice(0, MAX_STATE_PROMPT_LEN - 3)}...`
|
|
463
|
+
: stripped;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
if (looksLikeSystemEcho(trimmed)) {
|
|
467
|
+
return '(prompt omitted: pasted system echo)';
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// Fallback (stripping left nothing but original isn't recognizably an echo)
|
|
471
|
+
const base = stripped.length > 0 ? stripped : trimmed;
|
|
472
|
+
return base.length > MAX_STATE_PROMPT_LEN
|
|
473
|
+
? `${base.slice(0, MAX_STATE_PROMPT_LEN - 3)}...`
|
|
474
|
+
: base;
|
|
475
|
+
}
|
|
350
476
|
const MODE_REFERENCE_PATTERN =
|
|
351
477
|
/\b(?:ralph|autopilot|auto[\s-]?pilot|ultrawork|ulw|ralplan|ultrathink|deepsearch|deep[\s-]?analyze|deepanalyze|deep[\s-]interview|ouroboros|ccg|claude-codex-gemini|deerflow)\b/gi;
|
|
352
478
|
|
|
@@ -461,15 +587,25 @@ function isInformationalKeywordContext(text, position, keywordLength, keywordTex
|
|
|
461
587
|
}
|
|
462
588
|
|
|
463
589
|
function hasActionableKeyword(text, pattern) {
|
|
590
|
+
// Strip system-generated echo blocks (persistent-mode/keyword-detector
|
|
591
|
+
// outputs, Stop-hook wrappers) BEFORE searching for keywords. Otherwise
|
|
592
|
+
// pasting a previous "[RALPH LOOP - ITERATION N] ..." block into a prompt
|
|
593
|
+
// would re-activate ralph mode and the echo itself would be saved as
|
|
594
|
+
// state.prompt, which persistent-mode.mjs then blasts back on every
|
|
595
|
+
// iteration — a self-reinforcing loop that is hard to cancel.
|
|
596
|
+
const searchText = looksLikeSystemEcho(text)
|
|
597
|
+
? stripSystemEchoes(text)
|
|
598
|
+
: text;
|
|
599
|
+
|
|
464
600
|
const flags = pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`;
|
|
465
601
|
const globalPattern = new RegExp(pattern.source, flags);
|
|
466
602
|
|
|
467
|
-
for (const match of
|
|
603
|
+
for (const match of searchText.matchAll(globalPattern)) {
|
|
468
604
|
if (match.index === undefined) {
|
|
469
605
|
continue;
|
|
470
606
|
}
|
|
471
607
|
|
|
472
|
-
if (isInformationalKeywordContext(
|
|
608
|
+
if (isInformationalKeywordContext(searchText, match.index, match[0].length, match[0])) {
|
|
473
609
|
continue;
|
|
474
610
|
}
|
|
475
611
|
|
|
@@ -496,19 +632,26 @@ function hasExplicitInvocationContext(text, position, keywordLength, keywordText
|
|
|
496
632
|
}
|
|
497
633
|
|
|
498
634
|
function hasActionableRalplanKeyword(text, pattern) {
|
|
635
|
+
// Same echo guard as hasActionableKeyword.
|
|
636
|
+
const searchText = looksLikeSystemEcho(text)
|
|
637
|
+
? stripSystemEchoes(text)
|
|
638
|
+
: text;
|
|
639
|
+
|
|
499
640
|
const flags = pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`;
|
|
500
641
|
const globalPattern = new RegExp(pattern.source, flags);
|
|
501
642
|
|
|
502
|
-
for (const match of
|
|
643
|
+
for (const match of searchText.matchAll(globalPattern)) {
|
|
503
644
|
if (match.index === undefined) {
|
|
504
645
|
continue;
|
|
505
646
|
}
|
|
506
647
|
|
|
507
|
-
|
|
648
|
+
// match.index is relative to searchText — use searchText for all
|
|
649
|
+
// downstream context lookups to keep indices aligned.
|
|
650
|
+
if (isInformationalKeywordContext(searchText, match.index, match[0].length, match[0])) {
|
|
508
651
|
continue;
|
|
509
652
|
}
|
|
510
653
|
|
|
511
|
-
if (!hasExplicitInvocationContext(
|
|
654
|
+
if (!hasExplicitInvocationContext(searchText, match.index, match[0].length, match[0])) {
|
|
512
655
|
continue;
|
|
513
656
|
}
|
|
514
657
|
|
|
@@ -520,6 +663,10 @@ function hasActionableRalplanKeyword(text, pattern) {
|
|
|
520
663
|
|
|
521
664
|
// Create state file for a mode
|
|
522
665
|
function activateState(directory, prompt, stateName, sessionId) {
|
|
666
|
+
const now = new Date().toISOString();
|
|
667
|
+
// Sanitize prompt BEFORE writing to state: prevents pasted system echoes
|
|
668
|
+
// and oversized blobs from being persisted and re-emitted by Stop hook.
|
|
669
|
+
const safePrompt = sanitizePromptForState(prompt);
|
|
523
670
|
let state;
|
|
524
671
|
|
|
525
672
|
if (stateName === 'ralph') {
|
|
@@ -528,45 +675,51 @@ function activateState(directory, prompt, stateName, sessionId) {
|
|
|
528
675
|
active: true,
|
|
529
676
|
iteration: 1,
|
|
530
677
|
max_iterations: 100,
|
|
531
|
-
started_at:
|
|
532
|
-
prompt:
|
|
678
|
+
started_at: now,
|
|
679
|
+
prompt: safePrompt,
|
|
533
680
|
session_id: sessionId || undefined,
|
|
534
681
|
project_path: directory,
|
|
535
682
|
linked_ultrawork: true,
|
|
536
683
|
awaiting_confirmation: true,
|
|
537
|
-
|
|
684
|
+
awaiting_confirmation_set_at: now,
|
|
685
|
+
last_checked_at: now
|
|
538
686
|
};
|
|
539
687
|
} else if (stateName === 'ralplan') {
|
|
540
688
|
// Ralplan needs active + session_id for stop-hook enforcement
|
|
541
689
|
state = {
|
|
542
690
|
active: true,
|
|
543
|
-
started_at:
|
|
691
|
+
started_at: now,
|
|
544
692
|
session_id: sessionId || undefined,
|
|
545
693
|
project_path: directory,
|
|
546
694
|
awaiting_confirmation: true,
|
|
547
|
-
|
|
695
|
+
awaiting_confirmation_set_at: now,
|
|
696
|
+
last_checked_at: now
|
|
548
697
|
};
|
|
549
698
|
} else {
|
|
550
699
|
// Generic state for ultrawork, autopilot, etc.
|
|
551
700
|
state = {
|
|
552
701
|
active: true,
|
|
553
|
-
started_at:
|
|
554
|
-
original_prompt:
|
|
702
|
+
started_at: now,
|
|
703
|
+
original_prompt: safePrompt,
|
|
555
704
|
session_id: sessionId || undefined,
|
|
556
705
|
project_path: directory,
|
|
557
706
|
reinforcement_count: 0,
|
|
558
707
|
awaiting_confirmation: true,
|
|
559
|
-
|
|
708
|
+
awaiting_confirmation_set_at: now,
|
|
709
|
+
last_checked_at: now
|
|
560
710
|
};
|
|
561
711
|
}
|
|
562
712
|
|
|
563
|
-
// Write to session-scoped path if sessionId available
|
|
713
|
+
// Write to session-scoped path if sessionId available. Use atomic writes
|
|
714
|
+
// so that concurrent hook processes cannot expose half-written JSON to
|
|
715
|
+
// persistent-mode.mjs's readJsonFile (which would otherwise return null
|
|
716
|
+
// and temporarily drop mode enforcement).
|
|
564
717
|
if (sessionId && /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/.test(sessionId)) {
|
|
565
718
|
const sessionDir = join(directory, '.omcp', 'state', 'sessions', sessionId);
|
|
566
719
|
if (!existsSync(sessionDir)) {
|
|
567
720
|
try { mkdirSync(sessionDir, { recursive: true }); } catch {}
|
|
568
721
|
}
|
|
569
|
-
try {
|
|
722
|
+
try { atomicWriteFileSync(join(sessionDir, `${stateName}-state.json`), JSON.stringify(state, null, 2)); } catch {}
|
|
570
723
|
return;
|
|
571
724
|
}
|
|
572
725
|
|
|
@@ -575,7 +728,7 @@ function activateState(directory, prompt, stateName, sessionId) {
|
|
|
575
728
|
if (!existsSync(localDir)) {
|
|
576
729
|
try { mkdirSync(localDir, { recursive: true }); } catch {}
|
|
577
730
|
}
|
|
578
|
-
try {
|
|
731
|
+
try { atomicWriteFileSync(join(localDir, `${stateName}-state.json`), JSON.stringify(state, null, 2)); } catch {}
|
|
579
732
|
}
|
|
580
733
|
|
|
581
734
|
function activateRalplanStartupState(directory, prompt, sessionId) {
|
|
@@ -584,7 +737,7 @@ function activateRalplanStartupState(directory, prompt, sessionId) {
|
|
|
584
737
|
active: true,
|
|
585
738
|
started_at: now,
|
|
586
739
|
current_phase: 'ralplan',
|
|
587
|
-
original_prompt: prompt,
|
|
740
|
+
original_prompt: sanitizePromptForState(prompt),
|
|
588
741
|
session_id: sessionId || undefined,
|
|
589
742
|
project_path: directory,
|
|
590
743
|
awaiting_confirmation: true,
|
|
@@ -597,7 +750,7 @@ function activateRalplanStartupState(directory, prompt, sessionId) {
|
|
|
597
750
|
if (!existsSync(sessionDir)) {
|
|
598
751
|
try { mkdirSync(sessionDir, { recursive: true }); } catch {}
|
|
599
752
|
}
|
|
600
|
-
try {
|
|
753
|
+
try { atomicWriteFileSync(join(sessionDir, 'ralplan-state.json'), JSON.stringify(state, null, 2)); } catch {}
|
|
601
754
|
return;
|
|
602
755
|
}
|
|
603
756
|
|
|
@@ -605,7 +758,7 @@ function activateRalplanStartupState(directory, prompt, sessionId) {
|
|
|
605
758
|
if (!existsSync(localDir)) {
|
|
606
759
|
try { mkdirSync(localDir, { recursive: true }); } catch {}
|
|
607
760
|
}
|
|
608
|
-
try {
|
|
761
|
+
try { atomicWriteFileSync(join(localDir, 'ralplan-state.json'), JSON.stringify(state, null, 2)); } catch {}
|
|
609
762
|
}
|
|
610
763
|
|
|
611
764
|
/**
|
|
@@ -688,26 +841,27 @@ function isTeamEnabled() {
|
|
|
688
841
|
}
|
|
689
842
|
|
|
690
843
|
/**
|
|
691
|
-
* Create a skill invocation
|
|
692
|
-
*
|
|
693
|
-
* Falls back to Skill tool invocation (requires plugin marketplace install).
|
|
844
|
+
* Create a compact skill invocation guide without inlining SKILL.md bodies.
|
|
845
|
+
* Full skill text remains available by path, avoiding UserPromptSubmit token blowups.
|
|
694
846
|
*/
|
|
695
847
|
function createSkillInvocation(skillName, originalPrompt, args = '') {
|
|
696
|
-
const argsSection = args ?
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
848
|
+
const argsSection = args ? `
|
|
849
|
+
Arguments: ${args}` : '';
|
|
850
|
+
const skillPath = resolveSkillPath(skillName);
|
|
851
|
+
const pathStatus = existsSync(skillPath)
|
|
852
|
+
? `Read fallback: open ${skillPath} and follow its SKILL.md instructions.`
|
|
853
|
+
: `Read fallback: locate skills/${skillName}/SKILL.md in the active oh-my-copilot plugin/install and follow it.`;
|
|
702
854
|
|
|
703
|
-
|
|
855
|
+
return `[MAGIC KEYWORD: ${skillName.toUpperCase()}]
|
|
704
856
|
|
|
705
|
-
Skill:
|
|
857
|
+
Skill routing detected: ${skillName}
|
|
858
|
+
Preferred invocation: /oh-my-copilot:${skillName}${args ? ` ${args}` : ''}
|
|
859
|
+
${pathStatus}${argsSection}
|
|
706
860
|
|
|
707
|
-
User request:
|
|
708
|
-
${originalPrompt}
|
|
861
|
+
User request (compact echo; original prompt remains authoritative):
|
|
862
|
+
${compactHookText(originalPrompt)}
|
|
709
863
|
|
|
710
|
-
IMPORTANT:
|
|
864
|
+
IMPORTANT: Start the ${skillName} workflow immediately. If the slash invocation is unavailable, read the SKILL.md at the fallback path instead of relying on this compact guide.`;
|
|
711
865
|
}
|
|
712
866
|
|
|
713
867
|
/**
|
|
@@ -720,23 +874,24 @@ function createMultiSkillInvocation(skills, originalPrompt) {
|
|
|
720
874
|
}
|
|
721
875
|
|
|
722
876
|
const skillBlocks = skills.map((s, i) => {
|
|
723
|
-
const
|
|
724
|
-
const
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
return `### Skill ${i + 1}: ${s.name.toUpperCase()}
|
|
877
|
+
const skillPath = resolveSkillPath(s.name);
|
|
878
|
+
const argsText = s.args ? ` ${s.args}` : '';
|
|
879
|
+
const pathStatus = existsSync(skillPath)
|
|
880
|
+
? `Read fallback: ${skillPath}`
|
|
881
|
+
: `Read fallback: locate skills/${s.name}/SKILL.md in the active oh-my-copilot plugin/install`;
|
|
882
|
+
return `### Skill ${i + 1}: ${s.name.toUpperCase()}
|
|
883
|
+
Preferred invocation: /oh-my-copilot:${s.name}${argsText}
|
|
884
|
+
${pathStatus}`;
|
|
729
885
|
}).join('\n\n');
|
|
730
886
|
|
|
731
|
-
const hasDirectContent = skills.some(s => loadSkillContent(s.name));
|
|
732
887
|
return `[MAGIC KEYWORDS DETECTED: ${skills.map(s => s.name.toUpperCase()).join(', ')}]
|
|
733
888
|
|
|
734
|
-
|
|
889
|
+
Execute ALL detected workflows in order using compact invocation guidance. Do not inline full SKILL.md files into the prompt.
|
|
735
890
|
|
|
736
891
|
${skillBlocks}
|
|
737
892
|
|
|
738
|
-
User request:
|
|
739
|
-
${originalPrompt}
|
|
893
|
+
User request (compact echo; original prompt remains authoritative):
|
|
894
|
+
${compactHookText(originalPrompt)}
|
|
740
895
|
|
|
741
896
|
IMPORTANT: Complete ALL skills listed above in order. Start with the first skill IMMEDIATELY.`;
|
|
742
897
|
}
|
|
@@ -844,7 +999,14 @@ async function main() {
|
|
|
844
999
|
return;
|
|
845
1000
|
}
|
|
846
1001
|
|
|
847
|
-
|
|
1002
|
+
// Strip pasted system-echo blocks BEFORE any mode-keyword dispatch runs.
|
|
1003
|
+
// This covers both the hasActionableKeyword() call sites AND alternative
|
|
1004
|
+
// matchers (e.g. isAntiSlopCleanupRequest) that bypass it. The helper
|
|
1005
|
+
// inside hasActionableKeyword also strips defensively, which stays as
|
|
1006
|
+
// belt-and-suspenders.
|
|
1007
|
+
const cleanPrompt = stripSystemEchoes(
|
|
1008
|
+
sanitizeForKeywordDetection(prompt).toLowerCase(),
|
|
1009
|
+
);
|
|
848
1010
|
|
|
849
1011
|
// Collect all matching keywords
|
|
850
1012
|
const matches = [];
|
|
@@ -19,7 +19,7 @@ const { createHash } = require('crypto');
|
|
|
19
19
|
* @returns {Promise<string>} Absolute path to the .omcp root
|
|
20
20
|
*/
|
|
21
21
|
async function resolveOmcStateRoot(directory) {
|
|
22
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
22
|
+
const pluginRoot = process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT;
|
|
23
23
|
if (pluginRoot) {
|
|
24
24
|
try {
|
|
25
25
|
const { pathToFileURL } = require('url');
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* hook scripts, respecting the OMC_STATE_DIR environment variable.
|
|
6
6
|
*
|
|
7
7
|
* Delegates to getOmcRoot() from dist/lib/worktree-paths.js (the canonical
|
|
8
|
-
* implementation) when
|
|
8
|
+
* implementation) when PLUGIN_ROOT is available. Falls back to inline
|
|
9
9
|
* logic when dist is not built — this should never happen in production, but
|
|
10
10
|
* provides a safe fallback during development or first-run scenarios.
|
|
11
11
|
*
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - Uses directory path as hash source (not git remote URL). Matches
|
|
14
14
|
* canonical behavior for local-only repos; may differ for remote-backed
|
|
15
15
|
* repos when dist is missing — acceptable since dist is always present
|
|
16
|
-
* in production (
|
|
16
|
+
* in production (PLUGIN_ROOT is always set).
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import { join, basename } from 'path';
|
|
@@ -27,7 +27,7 @@ import { pathToFileURL } from 'url';
|
|
|
27
27
|
* @returns {Promise<string>} Absolute path to the .omcp root
|
|
28
28
|
*/
|
|
29
29
|
export async function resolveOmcStateRoot(directory) {
|
|
30
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
30
|
+
const pluginRoot = process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT;
|
|
31
31
|
if (pluginRoot) {
|
|
32
32
|
try {
|
|
33
33
|
const { getOmcRoot } = await import(
|
|
@@ -244,7 +244,7 @@ async function sendStopNotification(modeName, stateData, sessionId, directory) {
|
|
|
244
244
|
if (stateData._stopNotified) return;
|
|
245
245
|
|
|
246
246
|
try {
|
|
247
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
247
|
+
const pluginRoot = process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT;
|
|
248
248
|
if (!pluginRoot) return;
|
|
249
249
|
|
|
250
250
|
const { pathToFileURL } = require('url');
|
|
@@ -572,6 +572,32 @@ function readStateFileWithSession(stateDir, filename, sessionId) {
|
|
|
572
572
|
return readStateFile(stateDir, filename);
|
|
573
573
|
}
|
|
574
574
|
|
|
575
|
+
const WORKFLOW_SLOT_TOMBSTONE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
576
|
+
|
|
577
|
+
function isWorkflowSlotTombstonedForMode(stateDir, mode, sessionId) {
|
|
578
|
+
const safeSessionId = sessionId && /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/.test(sessionId) ? sessionId : "";
|
|
579
|
+
const ledgerPath = safeSessionId
|
|
580
|
+
? join(stateDir, "sessions", safeSessionId, "skill-active-state.json")
|
|
581
|
+
: join(stateDir, "skill-active-state.json");
|
|
582
|
+
const ledger = readJsonFile(ledgerPath);
|
|
583
|
+
const slot = ledger?.active_skills?.[mode];
|
|
584
|
+
if (!slot || typeof slot !== "object") return false;
|
|
585
|
+
if (typeof slot.completed_at !== "string" || !slot.completed_at) return false;
|
|
586
|
+
const completedAt = new Date(slot.completed_at).getTime();
|
|
587
|
+
if (!Number.isFinite(completedAt)) return true;
|
|
588
|
+
return Date.now() - completedAt < WORKFLOW_SLOT_TOMBSTONE_TTL_MS;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function isAuthoritativeModeActive(stateDir, mode, loaded, sessionId) {
|
|
592
|
+
const state = loaded?.state;
|
|
593
|
+
if (!state?.active) return false;
|
|
594
|
+
if (isWorkflowSlotTombstonedForMode(stateDir, mode, sessionId)) return false;
|
|
595
|
+
const safeSessionId = sessionId && /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/.test(sessionId) ? sessionId : "";
|
|
596
|
+
if (safeSessionId && state.session_id && state.session_id !== safeSessionId) return false;
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
|
|
575
601
|
function getActiveSubagentCount(stateDir) {
|
|
576
602
|
try {
|
|
577
603
|
const tracking = readJsonFile(join(stateDir, "subagent-tracking.json"));
|
|
@@ -891,7 +917,7 @@ async function main() {
|
|
|
891
917
|
|
|
892
918
|
// Priority 1: Ralph Loop (explicit persistence mode)
|
|
893
919
|
// Skip if state is stale (older than 2 hours) - prevents blocking new sessions
|
|
894
|
-
if (ralph
|
|
920
|
+
if (isAuthoritativeModeActive(stateDir, "ralph", ralph, sessionId) && !isAwaitingConfirmation(ralph.state) && !isStaleState(ralph.state) && isSessionMatch(ralph.state, sessionId)) {
|
|
895
921
|
const iteration = ralph.state.iteration || 1;
|
|
896
922
|
const maxIter = ralph.state.max_iterations || 100;
|
|
897
923
|
|
|
@@ -1212,7 +1238,7 @@ async function main() {
|
|
|
1212
1238
|
// Session isolation: only block if state belongs to this session (issue #311)
|
|
1213
1239
|
// Project isolation: only block if state belongs to this project
|
|
1214
1240
|
if (
|
|
1215
|
-
ultrawork
|
|
1241
|
+
isAuthoritativeModeActive(stateDir, "ultrawork", ultrawork, sessionId) && !isAwaitingConfirmation(ultrawork.state) &&
|
|
1216
1242
|
!isStaleState(ultrawork.state) &&
|
|
1217
1243
|
isSessionMatch(ultrawork.state, sessionId) &&
|
|
1218
1244
|
isStateForCurrentProject(ultrawork.state, directory, ultrawork.isGlobal)
|
|
@@ -1327,7 +1353,7 @@ async function main() {
|
|
|
1327
1353
|
const idleRepoState = getIdleNotificationRepoState(directory);
|
|
1328
1354
|
if (sessionId && shouldSendIdleNotification(stateDir, idleRepoState)) {
|
|
1329
1355
|
try {
|
|
1330
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
1356
|
+
const pluginRoot = process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT;
|
|
1331
1357
|
if (pluginRoot) {
|
|
1332
1358
|
const { pathToFileURL } = require('url');
|
|
1333
1359
|
import(pathToFileURL(join(pluginRoot, 'dist', 'notifications', 'index.js')).href)
|
|
@@ -370,6 +370,32 @@ function readStateFileWithSession(stateDir, globalStateDir, filename, sessionId)
|
|
|
370
370
|
return readStateFile(stateDir, globalStateDir, filename);
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
+
const WORKFLOW_SLOT_TOMBSTONE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
374
|
+
|
|
375
|
+
function isWorkflowSlotTombstonedForMode(stateDir, mode, sessionId) {
|
|
376
|
+
const safeSessionId = sanitizeSessionId(sessionId);
|
|
377
|
+
const ledgerPath = safeSessionId
|
|
378
|
+
? join(stateDir, "sessions", safeSessionId, "skill-active-state.json")
|
|
379
|
+
: join(stateDir, "skill-active-state.json");
|
|
380
|
+
const ledger = readJsonFile(ledgerPath);
|
|
381
|
+
const slot = ledger?.active_skills?.[mode];
|
|
382
|
+
if (!slot || typeof slot !== "object") return false;
|
|
383
|
+
if (typeof slot.completed_at !== "string" || !slot.completed_at) return false;
|
|
384
|
+
const completedAt = new Date(slot.completed_at).getTime();
|
|
385
|
+
if (!Number.isFinite(completedAt)) return true;
|
|
386
|
+
return Date.now() - completedAt < WORKFLOW_SLOT_TOMBSTONE_TTL_MS;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function isAuthoritativeModeActive(stateDir, mode, loaded, sessionId) {
|
|
390
|
+
const state = loaded?.state;
|
|
391
|
+
if (!state?.active) return false;
|
|
392
|
+
if (isWorkflowSlotTombstonedForMode(stateDir, mode, sessionId)) return false;
|
|
393
|
+
const safeSessionId = sanitizeSessionId(sessionId);
|
|
394
|
+
if (safeSessionId && state.session_id && state.session_id !== safeSessionId) return false;
|
|
395
|
+
return true;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
|
|
373
399
|
function isSessionCancelInProgress(stateDir, sessionId) {
|
|
374
400
|
const isActiveSignal = (signalPath) => {
|
|
375
401
|
const signal = readJsonFile(signalPath);
|
|
@@ -762,7 +788,7 @@ async function main() {
|
|
|
762
788
|
// Priority 1: Ralph Loop (explicit persistence mode)
|
|
763
789
|
// Skip if state is stale (older than 2 hours) - prevents blocking new sessions
|
|
764
790
|
if (
|
|
765
|
-
ralph
|
|
791
|
+
isAuthoritativeModeActive(stateDir, "ralph", ralph, sessionId) && !isAwaitingConfirmation(ralph.state) &&
|
|
766
792
|
!isStaleState(ralph.state) &&
|
|
767
793
|
isStateForCurrentProject(ralph.state, directory, ralph.isGlobal)
|
|
768
794
|
) {
|
|
@@ -1102,7 +1128,7 @@ async function main() {
|
|
|
1102
1128
|
// If state has session_id, it must match. If no session_id (legacy), allow.
|
|
1103
1129
|
// Project isolation: only block if state belongs to this project
|
|
1104
1130
|
if (
|
|
1105
|
-
ultrawork
|
|
1131
|
+
isAuthoritativeModeActive(stateDir, "ultrawork", ultrawork, sessionId) && !isAwaitingConfirmation(ultrawork.state) &&
|
|
1106
1132
|
!isStaleState(ultrawork.state) &&
|
|
1107
1133
|
(hasValidSessionId
|
|
1108
1134
|
? ultrawork.state.session_id === sessionId
|
package/scripts/plugin-setup.mjs
CHANGED
|
@@ -271,9 +271,10 @@ try {
|
|
|
271
271
|
// substitutes the real process.execPath so Copilot CLI always invokes the same
|
|
272
272
|
// Node binary that ran this setup script.
|
|
273
273
|
//
|
|
274
|
-
//
|
|
275
|
-
// 1.
|
|
276
|
-
// 2.
|
|
274
|
+
// Three patterns are handled:
|
|
275
|
+
// 1. Current – node "${PLUGIN_ROOT}/scripts/run.cjs" ... (all platforms)
|
|
276
|
+
// 2. Legacy – node "${CLAUDE_PLUGIN_ROOT}/scripts/run.cjs" ... (migration)
|
|
277
|
+
// 3. Old format – sh "${CLAUDE_PLUGIN_ROOT}/scripts/find-node.sh" ... (Windows
|
|
277
278
|
// backward-compat: migrates old installs to the new run.cjs chain)
|
|
278
279
|
//
|
|
279
280
|
// Fixes issues #909, #899, #892, #869.
|
|
@@ -283,11 +284,11 @@ try {
|
|
|
283
284
|
const data = JSON.parse(readFileSync(hooksJsonPath, 'utf-8'));
|
|
284
285
|
let patched = false;
|
|
285
286
|
|
|
286
|
-
// Pattern 1 (
|
|
287
|
+
// Pattern 1+2 (current/legacy): node "${PLUGIN_ROOT}/scripts/run.cjs" <rest>
|
|
287
288
|
const runCjsPattern =
|
|
288
|
-
/^node ("\$\{CLAUDE_PLUGIN_ROOT\}\/scripts\/run\.cjs".*)$/;
|
|
289
|
+
/^node ("\$\{(?:PLUGIN_ROOT|CLAUDE_PLUGIN_ROOT)\}\/scripts\/run\.cjs".*)$/;
|
|
289
290
|
|
|
290
|
-
// Pattern
|
|
291
|
+
// Pattern 3 (old, Windows backward-compat): sh find-node.sh <target> [args]
|
|
291
292
|
const findNodePattern =
|
|
292
293
|
/^sh "\$\{CLAUDE_PLUGIN_ROOT\}\/scripts\/find-node\.sh" "(\$\{CLAUDE_PLUGIN_ROOT\}\/scripts\/[^"]+)"(.*)$/;
|
|
293
294
|
|
|
@@ -308,7 +309,7 @@ try {
|
|
|
308
309
|
if (process.platform === 'win32') {
|
|
309
310
|
const m2 = hook.command.match(findNodePattern);
|
|
310
311
|
if (m2) {
|
|
311
|
-
hook.command = `"${nodeBin}" "\${
|
|
312
|
+
hook.command = `"${nodeBin}" "\${PLUGIN_ROOT}/scripts/run.cjs" "${m2[1]}"${m2[2]}`;
|
|
312
313
|
patched = true;
|
|
313
314
|
}
|
|
314
315
|
}
|
|
@@ -23,7 +23,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
23
23
|
const __dirname = dirname(__filename);
|
|
24
24
|
|
|
25
25
|
function getRuntimeBaseDir() {
|
|
26
|
-
return process.env.CLAUDE_PLUGIN_ROOT || join(__dirname, '..');
|
|
26
|
+
return process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT || join(__dirname, '..');
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// Dynamic import — graceful no-op when dist/ is not built (first run / dev)
|