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
|
@@ -703,6 +703,55 @@ export function detectWriteFailure(output) {
|
|
|
703
703
|
return errorPatterns.some(pattern => pattern.test(cleaned));
|
|
704
704
|
}
|
|
705
705
|
|
|
706
|
+
// Detect Copilot CLI's deterministic write/edit success markers so docs or
|
|
707
|
+
// serialized tool output containing diagnostic prose do not override success.
|
|
708
|
+
export function isCopilotCliWriteSuccess(output) {
|
|
709
|
+
if (!output) return false;
|
|
710
|
+
|
|
711
|
+
const cleaned = stripClaudeTempCwdErrors(output);
|
|
712
|
+
const successPatterns = [
|
|
713
|
+
/(^|\n)The file .+ has been updated successfully\.?(\n|$)/i,
|
|
714
|
+
/(^|\n)File (?:created|written|updated) successfully(?:\s+at:\s*.+)?\.?(\n|$)/i,
|
|
715
|
+
/(^|\n).*file state is current in your context\b.*(\n|$)/i,
|
|
716
|
+
];
|
|
717
|
+
|
|
718
|
+
return successPatterns.some(pattern => pattern.test(cleaned));
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function extractTextFromKnownToolResponseField(value, depth = 0) {
|
|
722
|
+
if (typeof value === 'string') return [value];
|
|
723
|
+
if (!value || depth > 4) return [];
|
|
724
|
+
|
|
725
|
+
if (Array.isArray(value)) {
|
|
726
|
+
return value.flatMap(item => extractTextFromKnownToolResponseField(item, depth + 1));
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (typeof value !== 'object') return [];
|
|
730
|
+
|
|
731
|
+
const textFields = ['text', 'message', 'result', 'output', 'stdout'];
|
|
732
|
+
const texts = [];
|
|
733
|
+
for (const field of textFields) {
|
|
734
|
+
if (typeof value[field] === 'string') {
|
|
735
|
+
texts.push(value[field]);
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
if (
|
|
740
|
+
'content' in value &&
|
|
741
|
+
value.content &&
|
|
742
|
+
(Array.isArray(value.content) || typeof value.content === 'object')
|
|
743
|
+
) {
|
|
744
|
+
texts.push(...extractTextFromKnownToolResponseField(value.content, depth + 1));
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
return texts;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function hasStructuredWriteSuccess(rawResponse) {
|
|
751
|
+
if (!rawResponse || typeof rawResponse === 'string') return false;
|
|
752
|
+
return extractTextFromKnownToolResponseField(rawResponse).some(isCopilotCliWriteSuccess);
|
|
753
|
+
}
|
|
754
|
+
|
|
706
755
|
// Get agent completion summary from tracking state
|
|
707
756
|
function getAgentCompletionSummary(directory, quietLevel = QUIET_LEVEL) {
|
|
708
757
|
const trackingFile = join(directory, '.omcp', 'state', 'subagent-tracking.json');
|
|
@@ -731,7 +780,7 @@ function getAgentCompletionSummary(directory, quietLevel = QUIET_LEVEL) {
|
|
|
731
780
|
|
|
732
781
|
// Generate contextual message
|
|
733
782
|
function generateMessage(toolName, toolOutput, sessionId, toolCount, directory, options = {}) {
|
|
734
|
-
const { wasTruncated = false, rawLength = 0 } = options;
|
|
783
|
+
const { wasTruncated = false, rawLength = 0, structuredWriteSuccess = false } = options;
|
|
735
784
|
let message = '';
|
|
736
785
|
|
|
737
786
|
switch (toolName) {
|
|
@@ -782,7 +831,7 @@ function generateMessage(toolName, toolOutput, sessionId, toolCount, directory,
|
|
|
782
831
|
}
|
|
783
832
|
|
|
784
833
|
case 'Edit':
|
|
785
|
-
if (detectWriteFailure(toolOutput)) {
|
|
834
|
+
if (!structuredWriteSuccess && !isCopilotCliWriteSuccess(toolOutput) && detectWriteFailure(toolOutput)) {
|
|
786
835
|
message = 'Edit operation failed. Verify file exists and content matches exactly.';
|
|
787
836
|
} else if (QUIET_LEVEL === 0) {
|
|
788
837
|
message = 'Code modified. Verify changes work as expected before marking complete.';
|
|
@@ -790,7 +839,7 @@ function generateMessage(toolName, toolOutput, sessionId, toolCount, directory,
|
|
|
790
839
|
break;
|
|
791
840
|
|
|
792
841
|
case 'Write':
|
|
793
|
-
if (detectWriteFailure(toolOutput)) {
|
|
842
|
+
if (!structuredWriteSuccess && !isCopilotCliWriteSuccess(toolOutput) && detectWriteFailure(toolOutput)) {
|
|
794
843
|
message = 'Write operation failed. Check file permissions and directory existence.';
|
|
795
844
|
} else if (QUIET_LEVEL === 0) {
|
|
796
845
|
message = 'File written. Test the changes to ensure they work correctly.';
|
|
@@ -847,6 +896,8 @@ async function main() {
|
|
|
847
896
|
|
|
848
897
|
const toolName = data.tool_name || data.toolName || '';
|
|
849
898
|
const rawResponse = data.tool_response || data.toolOutput || '';
|
|
899
|
+
const structuredWriteSuccess =
|
|
900
|
+
(toolName === 'Write' || toolName === 'Edit') && hasStructuredWriteSuccess(rawResponse);
|
|
850
901
|
const toolOutput = typeof rawResponse === 'string' ? rawResponse : JSON.stringify(rawResponse);
|
|
851
902
|
const { clipped: clippedToolOutput, wasTruncated } = clipToolOutputForAnalysis(toolName, toolOutput);
|
|
852
903
|
const sessionId = data.session_id || data.sessionId || 'unknown';
|
|
@@ -892,6 +943,7 @@ async function main() {
|
|
|
892
943
|
generateMessage(toolName, clippedToolOutput, sessionId, toolCount, directory, {
|
|
893
944
|
wasTruncated,
|
|
894
945
|
rawLength: toolOutput.length,
|
|
946
|
+
structuredWriteSuccess,
|
|
895
947
|
}),
|
|
896
948
|
maybeBuildPreemptiveCompactionMessage(toolName, data, directory),
|
|
897
949
|
);
|
|
@@ -82,10 +82,10 @@ function readAgentDefinitionModel(subagentType) {
|
|
|
82
82
|
// Reject path traversal: agent names are simple identifiers; no path separators allowed.
|
|
83
83
|
if (!/^[a-zA-Z0-9_-]+$/.test(agentType)) return null;
|
|
84
84
|
// Build a prioritised list of agents/ directories to search.
|
|
85
|
-
//
|
|
85
|
+
// PLUGIN_ROOT is tried first when set; the script-relative path is always the
|
|
86
86
|
// final fallback. Checking per-file (not just per-directory) means a partially-populated
|
|
87
87
|
// plugin install doesn't hide agents that exist in the script-relative tree.
|
|
88
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
88
|
+
const pluginRoot = process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT;
|
|
89
89
|
const scriptAgentsDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'agents');
|
|
90
90
|
const candidateDirs = [
|
|
91
91
|
...(pluginRoot ? [join(pluginRoot, 'agents')] : []),
|
|
@@ -755,12 +755,8 @@ async function main() {
|
|
|
755
755
|
? `Set ANTHROPIC_DEFAULT_${derivedTier}_MODEL=<valid-bedrock-id> in settings.json env, or set OMC_SUBAGENT_MODEL as a global override.`
|
|
756
756
|
: `Remove the \`model\` parameter, or set ANTHROPIC_DEFAULT_SONNET_MODEL=<valid-bedrock-id> in settings.json env.`;
|
|
757
757
|
console.log(JSON.stringify({
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
hookEventName: 'PreToolUse',
|
|
761
|
-
permissionDecision: 'deny',
|
|
762
|
-
permissionDecisionReason: `[MODEL ROUTING] This environment uses a non-standard provider (Bedrock/Vertex/proxy). ${guidance} The model "${toolModel}" is not valid for this provider.`
|
|
763
|
-
}
|
|
758
|
+
permissionDecision: 'deny',
|
|
759
|
+
permissionDecisionReason: `[MODEL ROUTING] This environment uses a non-standard provider (Bedrock/Vertex/proxy). ${guidance} The model "${toolModel}" is not valid for this provider.`
|
|
764
760
|
}));
|
|
765
761
|
return;
|
|
766
762
|
}
|
|
@@ -777,12 +773,8 @@ async function main() {
|
|
|
777
773
|
? `Pass model="${tierAlias}" explicitly on this ${toolName} call — tier aliases resolve cleanly on Bedrock.`
|
|
778
774
|
: `Pass model="${tierAlias}" explicitly on this ${toolName} call, and set ANTHROPIC_DEFAULT_${tierAlias.toUpperCase()}_MODEL=<valid-bedrock-id> in settings.json env.`;
|
|
779
775
|
console.log(JSON.stringify({
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
hookEventName: 'PreToolUse',
|
|
783
|
-
permissionDecision: 'deny',
|
|
784
|
-
permissionDecisionReason: `[MODEL ROUTING] Your session model "${sessionModel}" has a context-window suffix ([1m]) that sub-agents cannot inherit — the runtime strips it to a bare Anthropic model ID which is invalid on Bedrock. ${suggestion}`
|
|
785
|
-
}
|
|
776
|
+
permissionDecision: 'deny',
|
|
777
|
+
permissionDecisionReason: `[MODEL ROUTING] Your session model "${sessionModel}" has a context-window suffix ([1m]) that sub-agents cannot inherit — the runtime strips it to a bare Anthropic model ID which is invalid on Bedrock. ${suggestion}`
|
|
786
778
|
}));
|
|
787
779
|
return;
|
|
788
780
|
}
|
|
@@ -804,12 +796,8 @@ async function main() {
|
|
|
804
796
|
const guidance = `Add model="${defTierAlias}" to this ${toolName} call — tier aliases resolve to configured provider models (${resolvedModel}).`;
|
|
805
797
|
const agentType = (toolInput.subagent_type).replace(/^oh-my-copilot:/, '');
|
|
806
798
|
console.log(JSON.stringify({
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
hookEventName: 'PreToolUse',
|
|
810
|
-
permissionDecision: 'deny',
|
|
811
|
-
permissionDecisionReason: `[MODEL ROUTING] Agent type "${agentType}" has model "${agentDefModel}" in its definition, which is not valid for this Bedrock/Vertex/proxy environment. ${guidance}`
|
|
812
|
-
}
|
|
799
|
+
permissionDecision: 'deny',
|
|
800
|
+
permissionDecisionReason: `[MODEL ROUTING] Agent type "${agentType}" has model "${agentDefModel}" in its definition, which is not valid for this Bedrock/Vertex/proxy environment. ${guidance}`
|
|
813
801
|
}));
|
|
814
802
|
return;
|
|
815
803
|
}
|
|
@@ -823,7 +811,7 @@ async function main() {
|
|
|
823
811
|
// Fires in PreToolUse so users get notified BEFORE the tool blocks for input (#597)
|
|
824
812
|
if (toolName === 'AskUserQuestion') {
|
|
825
813
|
try {
|
|
826
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
814
|
+
const pluginRoot = process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT;
|
|
827
815
|
if (pluginRoot) {
|
|
828
816
|
const { notify } = await import(pathToFileURL(join(pluginRoot, 'dist', 'notifications', 'index.js')).href);
|
|
829
817
|
|
|
@@ -12,7 +12,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
12
12
|
const __dirname = dirname(__filename);
|
|
13
13
|
|
|
14
14
|
function getRuntimeBaseDir() {
|
|
15
|
-
return process.env.CLAUDE_PLUGIN_ROOT || join(__dirname, '..');
|
|
15
|
+
return process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT || join(__dirname, '..');
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
// Import timeout-protected stdin reader (prevents hangs on Linux/Windows, see issue #240, #524)
|
package/scripts/run.cjs
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
* /usr/bin/sh is a PE32+ binary the OS refuses to execute natively.
|
|
11
11
|
* Fixes issues #909, #899, #892, #869.
|
|
12
12
|
*
|
|
13
|
-
* Usage (from hooks.json
|
|
14
|
-
* /abs/path/to/node "${
|
|
15
|
-
* "${
|
|
13
|
+
* Usage (from hooks.json):
|
|
14
|
+
* /abs/path/to/node "${PLUGIN_ROOT}/scripts/run.cjs" \
|
|
15
|
+
* "${PLUGIN_ROOT}/scripts/<hook>.mjs" [args...]
|
|
16
16
|
*
|
|
17
17
|
* During post-install setup, the leading `node` token is replaced with
|
|
18
18
|
* process.execPath so nvm/fnm users and Windows users all get the right binary.
|
|
@@ -24,16 +24,16 @@ const { join, basename, dirname } = require('path');
|
|
|
24
24
|
|
|
25
25
|
const target = process.argv[2];
|
|
26
26
|
if (!target) {
|
|
27
|
-
// Nothing to run — exit cleanly so
|
|
27
|
+
// Nothing to run — exit cleanly so hooks are never blocked.
|
|
28
28
|
process.exit(0);
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Resolve the hook script target path, handling stale
|
|
32
|
+
* Resolve the hook script target path, handling stale PLUGIN_ROOT.
|
|
33
33
|
*
|
|
34
34
|
* When a plugin update replaces an old version directory with a symlink (or
|
|
35
35
|
* deletes it entirely), sessions that still reference the old version via
|
|
36
|
-
*
|
|
36
|
+
* PLUGIN_ROOT will fail with MODULE_NOT_FOUND.
|
|
37
37
|
*
|
|
38
38
|
* Resolution strategy:
|
|
39
39
|
* 1. Use the target as-is if it exists.
|
|
@@ -57,10 +57,10 @@ function resolveTarget(targetPath) {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// Fallback: scan plugin cache for the same script in the latest version.
|
|
60
|
-
//
|
|
60
|
+
// PLUGIN_ROOT is e.g. ~/.copilot/plugins/cache/omc/oh-my-copilot/4.2.14
|
|
61
61
|
// We look one level up for sibling version directories.
|
|
62
62
|
try {
|
|
63
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
63
|
+
const pluginRoot = process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT;
|
|
64
64
|
if (!pluginRoot) return null;
|
|
65
65
|
|
|
66
66
|
const cacheBase = dirname(pluginRoot); // .../oh-my-copilot/
|
|
@@ -134,7 +134,7 @@ function resolveHookTimeoutMs(targetPath, extraArgs) {
|
|
|
134
134
|
const resolved = resolveTarget(target);
|
|
135
135
|
if (!resolved) {
|
|
136
136
|
// Target not found anywhere — exit cleanly so hooks are never blocked.
|
|
137
|
-
// This is the graceful fallback for stale
|
|
137
|
+
// This is the graceful fallback for stale PLUGIN_ROOT paths.
|
|
138
138
|
process.exit(0);
|
|
139
139
|
}
|
|
140
140
|
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* OMC Safe Command Approver (PreToolUse)
|
|
5
|
+
*
|
|
6
|
+
* Auto-approves known-safe Bash commands (git status, npm test, dotnet build, etc.)
|
|
7
|
+
* so they don't trigger an interactive permission prompt. Works on both Copilot CLI
|
|
8
|
+
* and Claude Code — migrated from the Claude-Code-only permissionRequest handler.
|
|
9
|
+
*
|
|
10
|
+
* For non-Bash tools or unrecognised commands, outputs nothing (passes through to
|
|
11
|
+
* the runtime's native permission flow).
|
|
12
|
+
*
|
|
13
|
+
* Hook output (preToolUse — Copilot CLI compliant):
|
|
14
|
+
* - Allow ({ permissionDecision: "allow" }) for safe commands
|
|
15
|
+
* - No output for everything else (defer to runtime)
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { readStdin } from './lib/stdin.mjs';
|
|
19
|
+
|
|
20
|
+
// --- Safe command patterns (mirrored from src/hooks/permission-handler/index.ts) ---
|
|
21
|
+
|
|
22
|
+
const SAFE_PATTERNS = [
|
|
23
|
+
/^git (status|diff|log|branch|show|fetch)/,
|
|
24
|
+
/^npm (test|run (test|lint|build|check|typecheck))/,
|
|
25
|
+
/^pnpm (test|run (test|lint|build|check|typecheck))/,
|
|
26
|
+
/^yarn (test|run (test|lint|build|check|typecheck))/,
|
|
27
|
+
/^tsc( |$)/,
|
|
28
|
+
/^gh (issue|pr) (view|list|status)\b/,
|
|
29
|
+
/^eslint /,
|
|
30
|
+
/^prettier /,
|
|
31
|
+
/^cargo (test|check|clippy|build)/,
|
|
32
|
+
/^pytest/,
|
|
33
|
+
/^python -m pytest/,
|
|
34
|
+
/^ls( |$)/,
|
|
35
|
+
/^grep /,
|
|
36
|
+
/^find /,
|
|
37
|
+
/^wc /,
|
|
38
|
+
/^pwd$/,
|
|
39
|
+
/^which /,
|
|
40
|
+
/^echo /,
|
|
41
|
+
/^env$/,
|
|
42
|
+
/^node --version$/,
|
|
43
|
+
/^dotnet (--version|--list-sdks|--list-runtimes|--info)$/,
|
|
44
|
+
/^dotnet (build|test|run|restore|clean)( |$)/,
|
|
45
|
+
/^gh (pr|issue|repo|api|run|workflow) (view|list|status|diff|checks)/,
|
|
46
|
+
/^gh auth status/,
|
|
47
|
+
/^az (account show|group list|resource list|ad signed-in-user show)/,
|
|
48
|
+
/^az devops (project list|configure)/,
|
|
49
|
+
/^az pipelines (list|show|runs list)/,
|
|
50
|
+
/^az repos (list|show|pr list)/,
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
// Shell metacharacters that enable command chaining / injection
|
|
54
|
+
const DANGEROUS_SHELL_CHARS = /[;&|`$()<>\n\r\t\0\\{}\[\]*?~!#]/;
|
|
55
|
+
|
|
56
|
+
// Heredoc operator detection
|
|
57
|
+
const HEREDOC_PATTERN = /<<[-~]?\s*['"]?\w+['"]?/;
|
|
58
|
+
|
|
59
|
+
const SAFE_HEREDOC_PATTERNS = [
|
|
60
|
+
/^git commit\b/,
|
|
61
|
+
/^git tag\b/,
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const SAFE_RIPGREP_FLAGS = new Set([
|
|
65
|
+
'-n', '--line-number', '-S', '--smart-case', '-F', '--fixed-strings',
|
|
66
|
+
'-i', '--ignore-case', '--no-heading',
|
|
67
|
+
]);
|
|
68
|
+
|
|
69
|
+
function isSafeCommand(command) {
|
|
70
|
+
const trimmed = command.trim();
|
|
71
|
+
if (DANGEROUS_SHELL_CHARS.test(trimmed)) return false;
|
|
72
|
+
return SAFE_PATTERNS.some(p => p.test(trimmed));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function isHeredocWithSafeBase(command) {
|
|
76
|
+
const trimmed = command.trim();
|
|
77
|
+
if (!trimmed.includes('\n')) return false;
|
|
78
|
+
if (!HEREDOC_PATTERN.test(trimmed)) return false;
|
|
79
|
+
const firstLine = trimmed.split('\n')[0].trim();
|
|
80
|
+
return SAFE_HEREDOC_PATTERNS.some(p => p.test(firstLine));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function tokenizeShellCommand(command) {
|
|
84
|
+
const tokens = [];
|
|
85
|
+
let current = '';
|
|
86
|
+
let quote = null;
|
|
87
|
+
for (const char of command.trim()) {
|
|
88
|
+
if (quote) { if (char === quote) quote = null; else current += char; continue; }
|
|
89
|
+
if (char === '"' || char === "'") { quote = char; continue; }
|
|
90
|
+
if (/\s/.test(char)) { if (current) { tokens.push(current); current = ''; } continue; }
|
|
91
|
+
current += char;
|
|
92
|
+
}
|
|
93
|
+
if (quote) return null;
|
|
94
|
+
if (current) tokens.push(current);
|
|
95
|
+
return tokens;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isRipgrepReadOnly(tokens) {
|
|
99
|
+
if (tokens[0] !== 'rg') return false;
|
|
100
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
101
|
+
const t = tokens[i];
|
|
102
|
+
if (t.startsWith('-')) {
|
|
103
|
+
if (SAFE_RIPGREP_FLAGS.has(t)) continue;
|
|
104
|
+
if (t.startsWith('--type=') || t.startsWith('--glob=') || t.startsWith('-g')) continue;
|
|
105
|
+
if (t === '--type' || t === '--glob' || t === '-g' || t === '-t') { i++; continue; }
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isSafeRepoInspectionCommand(command) {
|
|
113
|
+
const tokens = tokenizeShellCommand(command);
|
|
114
|
+
if (!tokens || tokens.length === 0) return false;
|
|
115
|
+
if (isRipgrepReadOnly(tokens)) return true;
|
|
116
|
+
if (/^cat( |$)/.test(command) || /^head( |$)/.test(command) || /^tail( |$)/.test(command)) return true;
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function isSafeTargetedLocalTestCommand(command) {
|
|
121
|
+
const tokens = tokenizeShellCommand(command);
|
|
122
|
+
if (!tokens || tokens.length < 2) return false;
|
|
123
|
+
const bin = tokens[0];
|
|
124
|
+
if (bin === 'npx' && tokens[1] === 'vitest' && tokens.includes('run') && tokens.some(t => t.endsWith('.test.ts') || t.endsWith('.test.js'))) return true;
|
|
125
|
+
if (bin === 'pytest' && tokens.some(t => t.endsWith('.py') || t.includes('::') || t.startsWith('tests/'))) return true;
|
|
126
|
+
if (bin === 'go' && tokens[1] === 'test' && tokens.some(t => t.startsWith('./') || t.includes('...'))) return true;
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function main() {
|
|
131
|
+
try {
|
|
132
|
+
const input = await readStdin();
|
|
133
|
+
const data = JSON.parse(input);
|
|
134
|
+
|
|
135
|
+
const toolName = data.tool_name || data.toolName || '';
|
|
136
|
+
|
|
137
|
+
// Only process Bash tool calls
|
|
138
|
+
if (toolName !== 'Bash') return;
|
|
139
|
+
|
|
140
|
+
const command = data.tool_input?.command ?? data.toolInput?.command;
|
|
141
|
+
if (!command || typeof command !== 'string') return;
|
|
142
|
+
|
|
143
|
+
const trimmed = command.trim();
|
|
144
|
+
if (!trimmed) return;
|
|
145
|
+
|
|
146
|
+
if (isSafeCommand(trimmed) || isHeredocWithSafeBase(trimmed)
|
|
147
|
+
|| isSafeRepoInspectionCommand(trimmed) || isSafeTargetedLocalTestCommand(trimmed)) {
|
|
148
|
+
console.log(JSON.stringify({ permissionDecision: 'allow' }));
|
|
149
|
+
}
|
|
150
|
+
// No output for unrecognised commands — defer to runtime's native permission flow
|
|
151
|
+
} catch {
|
|
152
|
+
// On error, produce no output — never block on hook failure
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
main();
|
|
@@ -48,8 +48,32 @@ function readJsonFile(path) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
|
|
52
|
+
const SESSION_ID_ALLOWLIST = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/;
|
|
53
|
+
const WORKFLOW_SLOT_TOMBSTONE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
54
|
+
|
|
55
|
+
function isWorkflowSlotTombstonedForMode(omcRoot, mode, sessionId) {
|
|
56
|
+
const safeSessionId = typeof sessionId === 'string' && SESSION_ID_ALLOWLIST.test(sessionId) ? sessionId : '';
|
|
57
|
+
const ledgerPath = safeSessionId
|
|
58
|
+
? join(omcRoot, 'state', 'sessions', safeSessionId, 'skill-active-state.json')
|
|
59
|
+
: join(omcRoot, 'state', 'skill-active-state.json');
|
|
60
|
+
const ledger = readJsonFile(ledgerPath);
|
|
61
|
+
const slot = ledger?.active_skills?.[mode];
|
|
62
|
+
if (!slot || typeof slot !== 'object') return false;
|
|
63
|
+
if (typeof slot.completed_at !== 'string' || !slot.completed_at) return false;
|
|
64
|
+
const completedAt = new Date(slot.completed_at).getTime();
|
|
65
|
+
if (!Number.isFinite(completedAt)) return true;
|
|
66
|
+
return Date.now() - completedAt < WORKFLOW_SLOT_TOMBSTONE_TTL_MS;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function shouldRestoreModeState(omcRoot, mode, state, sessionId) {
|
|
70
|
+
if (!state?.active) return false;
|
|
71
|
+
if (isWorkflowSlotTombstonedForMode(omcRoot, mode, sessionId)) return false;
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
51
75
|
function getRuntimeBaseDir() {
|
|
52
|
-
return process.env.CLAUDE_PLUGIN_ROOT || join(__dirname, '..');
|
|
76
|
+
return process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT || join(__dirname, '..');
|
|
53
77
|
}
|
|
54
78
|
|
|
55
79
|
async function loadProjectMemoryModules() {
|
|
@@ -143,16 +167,71 @@ function semverCompare(a, b) {
|
|
|
143
167
|
return 0;
|
|
144
168
|
}
|
|
145
169
|
|
|
170
|
+
const SESSION_START_CONTEXT_BUDGET = 6000;
|
|
171
|
+
const SESSION_START_OMISSION_NOTICE = '[Additional SessionStart context omitted to preserve the 6000-character aggregate budget.]';
|
|
172
|
+
|
|
173
|
+
function compactBudgetedText(text, maxChars) {
|
|
174
|
+
const notice = '\n...[truncated to preserve SessionStart context budget]';
|
|
175
|
+
if (!text || text.length <= maxChars) return text || '';
|
|
176
|
+
if (maxChars <= notice.length) return notice.slice(0, Math.max(0, maxChars));
|
|
177
|
+
return `${text.slice(0, maxChars - notice.length).trimEnd()}${notice}`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function buildSessionStartAdditionalContext(messages) {
|
|
181
|
+
if (!Array.isArray(messages) || messages.length === 0) return '';
|
|
182
|
+
|
|
183
|
+
const sections = messages.map((message, index) => ({ index, message }));
|
|
184
|
+
const priorityOrder = [
|
|
185
|
+
/\[MODEL ROUTING OVERRIDE/,
|
|
186
|
+
/\[AUTOPILOT MODE RESTORED\]/,
|
|
187
|
+
/\[ULTRAWORK MODE RESTORED\]/,
|
|
188
|
+
/\[RALPH LOOP RESTORED\]/,
|
|
189
|
+
/\[PROJECT MEMORY\]/,
|
|
190
|
+
/\[NOTEPAD - Priority Context\]/,
|
|
191
|
+
/\[PENDING TASKS DETECTED\]/,
|
|
192
|
+
];
|
|
193
|
+
const prioritized = [];
|
|
194
|
+
const remaining = [];
|
|
195
|
+
for (const section of sections) {
|
|
196
|
+
const score = priorityOrder.findIndex((pattern) => pattern.test(section.message));
|
|
197
|
+
if (score !== -1) prioritized.push({ ...section, score });
|
|
198
|
+
else remaining.push({ ...section, score: priorityOrder.length + section.index });
|
|
199
|
+
}
|
|
200
|
+
const ordered = [...prioritized.sort((a, b) => a.score - b.score || a.index - b.index), ...remaining]
|
|
201
|
+
.map((entry) => entry.message);
|
|
202
|
+
|
|
203
|
+
let used = 0;
|
|
204
|
+
const selected = [];
|
|
205
|
+
for (const message of ordered) {
|
|
206
|
+
const separator = selected.length > 0 ? 1 : 0;
|
|
207
|
+
if (used + separator + message.length > SESSION_START_CONTEXT_BUDGET) {
|
|
208
|
+
const remainingBudget = SESSION_START_CONTEXT_BUDGET - used - separator;
|
|
209
|
+
if (remainingBudget > 0) {
|
|
210
|
+
selected.push(
|
|
211
|
+
remainingBudget > 120
|
|
212
|
+
? compactBudgetedText(message, remainingBudget)
|
|
213
|
+
: compactBudgetedText(SESSION_START_OMISSION_NOTICE, remainingBudget),
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
selected.push(message);
|
|
219
|
+
used += separator + message.length;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return selected.join('\n');
|
|
223
|
+
}
|
|
224
|
+
|
|
146
225
|
// Extract OMC version from CLAUDE.md content
|
|
147
226
|
function extractOmcVersion(content) {
|
|
148
227
|
const match = content.match(/<!-- OMC:VERSION:(\d+\.\d+\.\d+[^\s]*?) -->/);
|
|
149
228
|
return match ? match[1] : null;
|
|
150
229
|
}
|
|
151
230
|
|
|
152
|
-
// Get plugin version from
|
|
231
|
+
// Get plugin version from PLUGIN_ROOT
|
|
153
232
|
function getPluginVersion() {
|
|
154
233
|
try {
|
|
155
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
234
|
+
const pluginRoot = process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT;
|
|
156
235
|
if (!pluginRoot) return null;
|
|
157
236
|
const pkg = readJsonFile(join(pluginRoot, 'package.json'));
|
|
158
237
|
return pkg?.version || null;
|
|
@@ -401,7 +480,7 @@ async function main() {
|
|
|
401
480
|
} catch {}
|
|
402
481
|
|
|
403
482
|
// Warn if silentAutoUpdate is enabled but running in plugin mode (#1773)
|
|
404
|
-
if (process.env.CLAUDE_PLUGIN_ROOT) {
|
|
483
|
+
if (process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT) {
|
|
405
484
|
try {
|
|
406
485
|
const omcConfigPath = join(configDir, '.omc-config.json');
|
|
407
486
|
const omcConfig = readJsonFile(omcConfigPath);
|
|
@@ -434,7 +513,7 @@ async function main() {
|
|
|
434
513
|
ultraworkState = readJsonFile(join(omcRoot, 'state', 'ultrawork-state.json'));
|
|
435
514
|
}
|
|
436
515
|
|
|
437
|
-
if (ultraworkState
|
|
516
|
+
if (shouldRestoreModeState(omcRoot, 'ultrawork', ultraworkState, sessionId)) {
|
|
438
517
|
messages.push(`<session-restore>
|
|
439
518
|
|
|
440
519
|
[ULTRAWORK MODE RESTORED]
|
|
@@ -467,7 +546,7 @@ Treat this as prior-session context only. Prioritize the user's newest request,
|
|
|
467
546
|
ralphState = readJsonFile(join(omcRoot, 'ralph-state.json'));
|
|
468
547
|
}
|
|
469
548
|
}
|
|
470
|
-
if (ralphState
|
|
549
|
+
if (shouldRestoreModeState(omcRoot, 'ralph', ralphState, sessionId)) {
|
|
471
550
|
messages.push(`<session-restore>
|
|
472
551
|
|
|
473
552
|
[RALPH LOOP RESTORED]
|
|
@@ -627,12 +706,13 @@ ${cleanContent}
|
|
|
627
706
|
}
|
|
628
707
|
}
|
|
629
708
|
|
|
630
|
-
// Guard against
|
|
709
|
+
// Guard against PLUGIN_ROOT pointing to a stale/deleted version.
|
|
631
710
|
// When an old version directory is removed during upgrade but a running
|
|
632
|
-
// session still has the old
|
|
711
|
+
// session still has the old PLUGIN_ROOT in its environment, the
|
|
633
712
|
// directory won't exist. Create a symlink so subsequent hook invocations
|
|
634
713
|
// via run.cjs resolve correctly.
|
|
635
|
-
const pluginRootEnv = process.env.CLAUDE_PLUGIN_ROOT
|
|
714
|
+
const pluginRootEnv = (process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT || '')
|
|
715
|
+
.replace(/[\/\\]+$/, ''); // strip trailing separators
|
|
636
716
|
if (pluginRootEnv && !existsSync(pluginRootEnv)) {
|
|
637
717
|
const pluginRootVersion = basename(pluginRootEnv);
|
|
638
718
|
if (/^\d+\.\d+\.\d+/.test(pluginRootVersion) && versions.length > 0) {
|
|
@@ -667,7 +747,7 @@ ${cleanContent}
|
|
|
667
747
|
|
|
668
748
|
// Send session-start notification (non-blocking, fire-and-forget)
|
|
669
749
|
try {
|
|
670
|
-
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
750
|
+
const pluginRoot = process.env.PLUGIN_ROOT || process.env.CLAUDE_PLUGIN_ROOT;
|
|
671
751
|
if (pluginRoot) {
|
|
672
752
|
const { notify } = await import(pathToFileURL(join(pluginRoot, 'dist', 'notifications', 'index.js')).href);
|
|
673
753
|
// Fire and forget - don't await, don't block session start
|
|
@@ -697,7 +777,7 @@ ${cleanContent}
|
|
|
697
777
|
continue: true,
|
|
698
778
|
hookSpecificOutput: {
|
|
699
779
|
hookEventName: 'SessionStart',
|
|
700
|
-
additionalContext: messages
|
|
780
|
+
additionalContext: buildSessionStartAdditionalContext(messages)
|
|
701
781
|
}
|
|
702
782
|
}));
|
|
703
783
|
} else {
|
|
@@ -115,13 +115,14 @@ ensure_local_omc_git_exclude() {
|
|
|
115
115
|
|
|
116
116
|
cat >> "$exclude_path" <<'EOF'
|
|
117
117
|
# BEGIN OMC local artifacts
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
!.
|
|
118
|
+
!.omcp/
|
|
119
|
+
.omcp/*
|
|
120
|
+
!.omcp/skills/
|
|
121
|
+
!.omcp/skills/**
|
|
121
122
|
# END OMC local artifacts
|
|
122
123
|
EOF
|
|
123
124
|
|
|
124
|
-
echo "Configured git exclude for local .
|
|
125
|
+
echo "Configured git exclude for local .omcp artifacts (preserving .omcp/skills/)"
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
# Determine target path
|