oh-my-opencode 4.16.2 → 4.17.0

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.
Files changed (234) hide show
  1. package/.agents/command/omomomo.md +1 -1
  2. package/.agents/skills/hyperplan/SKILL.md +3 -3
  3. package/.agents/skills/omomomo/SKILL.md +1 -1
  4. package/.agents/skills/pre-publish-review/SKILL.md +3 -0
  5. package/.opencode/command/omomomo.md +1 -1
  6. package/.opencode/skills/hyperplan/SKILL.md +3 -3
  7. package/.opencode/skills/pre-publish-review/SKILL.md +3 -0
  8. package/README.ja.md +4 -4
  9. package/README.ko.md +4 -4
  10. package/README.md +4 -4
  11. package/README.ru.md +4 -4
  12. package/README.zh-cn.md +4 -4
  13. package/dist/agents/hephaestus/agent.d.ts +1 -1
  14. package/dist/agents/hephaestus/gpt-5-6.d.ts +2 -0
  15. package/dist/agents/momus-gpt-5-6.d.ts +1 -0
  16. package/dist/agents/types.d.ts +2 -0
  17. package/dist/cli/index.js +947 -567
  18. package/dist/cli-node/index.js +947 -567
  19. package/dist/features/background-agent/error-classifier.d.ts +5 -0
  20. package/dist/features/builtin-commands/templates/refactor-sections/intro-and-analysis.d.ts +1 -1
  21. package/dist/features/builtin-commands/templates/remove-ai-slops.d.ts +1 -1
  22. package/dist/features/team-mode/test-support/async-test-helpers.d.ts +2 -2
  23. package/dist/index.js +523 -81
  24. package/dist/plugin/chat-message/types.d.ts +4 -0
  25. package/dist/plugin/command-execute-before.d.ts +1 -0
  26. package/dist/plugin/stop-continuation.d.ts +17 -0
  27. package/dist/shared/live-server-route.d.ts +1 -1
  28. package/dist/skills/remove-ai-slops/SKILL.md +2 -2
  29. package/dist/skills/review-work/SKILL.md +1 -1
  30. package/dist/skills/start-work/SKILL.md +5 -2
  31. package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
  32. package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  33. package/dist/skills/ulw-research/SKILL.md +2 -2
  34. package/dist/skills/visual-qa/SKILL.md +1 -1
  35. package/dist/tui.js +195 -41
  36. package/package.json +15 -14
  37. package/packages/git-bash-mcp/package.json +27 -0
  38. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +3 -1
  39. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +465 -248
  40. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  41. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  42. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  43. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  44. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  45. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  46. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/AGENTS.md +2 -2
  48. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/cli.js +6 -2
  49. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/codex-hook.js +6 -2
  50. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +2 -2
  51. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  52. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/src/codex-hook.ts +6 -2
  53. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/cli.test.ts +1 -1
  54. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/codex-hook.test.ts +67 -2
  55. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  56. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +0 -13
  58. package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +93 -0
  59. package/packages/omo-codex/plugin/components/rules/dist/cli.js +43 -24
  60. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  61. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  62. package/packages/omo-codex/plugin/components/rules/src/codex-hook.ts +2 -1
  63. package/packages/omo-codex/plugin/components/rules/src/dynamic-target-fingerprints.ts +15 -6
  64. package/packages/omo-codex/plugin/components/rules/src/rules-engine-factory.ts +7 -2
  65. package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +2 -2
  66. package/packages/omo-codex/plugin/components/rules/test/codex-hook.test.ts +32 -0
  67. package/packages/omo-codex/plugin/components/rules/test/hephaestus-model-variant.test.ts +106 -0
  68. package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
  69. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  70. package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +1 -79
  71. package/packages/omo-codex/plugin/components/teammode/AGENTS.md +12 -9
  72. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  73. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  74. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +136 -76
  75. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +94 -23
  76. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +94 -14
  77. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-transport.mjs +55 -0
  78. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +72 -18
  79. package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +23 -0
  80. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  81. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  82. package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +23 -40
  83. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +1 -1
  84. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +3 -3
  85. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +7 -7
  86. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-qa-executor.toml +5 -5
  87. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +26 -0
  88. package/packages/omo-codex/plugin/components/ultrawork/agents/{lazycodex-executor.toml → lazycodex-worker-low.toml} +7 -5
  89. package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +26 -0
  90. package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +20 -48
  91. package/packages/omo-codex/plugin/components/ultrawork/agents/metis.toml +17 -29
  92. package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +20 -50
  93. package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +22 -43
  94. package/packages/omo-codex/plugin/components/ultrawork/directive.md +55 -28
  95. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  96. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  97. package/packages/omo-codex/plugin/components/ultrawork/skills/ultrawork/SKILL.md +55 -28
  98. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +1 -1
  99. package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  100. package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +22 -0
  101. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +0 -68
  102. package/packages/omo-codex/plugin/components/ulw-loop/AGENTS.md +1 -1
  103. package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +2 -0
  104. package/packages/omo-codex/plugin/components/ulw-loop/README.md +3 -1
  105. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +55 -28
  106. package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +6 -1
  107. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +1 -1
  108. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -2
  109. package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +405 -25
  110. package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +12 -1
  111. package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +4 -2
  112. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +7 -0
  113. package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +16 -1
  114. package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +1 -0
  115. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.d.ts +6 -0
  116. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +20 -0
  117. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.d.ts +1 -0
  118. package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +12 -9
  119. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.d.ts +3 -0
  120. package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +148 -0
  121. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.d.ts +2 -0
  122. package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +209 -0
  123. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +25 -2
  124. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  125. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +14 -15
  126. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +29 -27
  127. package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +6 -1
  128. package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +14 -3
  129. package/packages/omo-codex/plugin/components/ulw-loop/src/cli.ts +10 -0
  130. package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +12 -1
  131. package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +4 -2
  132. package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +27 -1
  133. package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +1 -0
  134. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +23 -0
  135. package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +16 -9
  136. package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +138 -0
  137. package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +208 -0
  138. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-create-goals.test.ts +12 -0
  139. package/packages/omo-codex/plugin/components/ulw-loop/test/cli-entrypoint.test.ts +4 -1
  140. package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +8 -35
  141. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/quality-gate-builder.ts +24 -13
  142. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +5 -2
  143. package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +43 -8
  144. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +55 -2
  145. package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard.test.ts +228 -0
  146. package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +193 -0
  147. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  148. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  149. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  150. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  151. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  152. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  153. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  154. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  155. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  156. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +18 -0
  157. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  158. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  159. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  160. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  161. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  162. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  163. package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
  164. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +17 -0
  165. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
  166. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +2 -2
  167. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  168. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  169. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  170. package/packages/omo-codex/plugin/model-catalog.json +16 -7
  171. package/packages/omo-codex/plugin/package-lock.json +13 -13
  172. package/packages/omo-codex/plugin/package.json +1 -1
  173. package/packages/omo-codex/plugin/scripts/auto-update.mjs +9 -2
  174. package/packages/omo-codex/plugin/scripts/entry-guard.mjs +26 -0
  175. package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +3 -1
  176. package/packages/omo-codex/plugin/scripts/migrate-codex-config/catalog.mjs +16 -7
  177. package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +39 -4
  178. package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +24 -4
  179. package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +3 -3
  180. package/packages/omo-codex/plugin/scripts/migrate-omo-sot.mjs +2 -2
  181. package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +2 -2
  182. package/packages/omo-codex/plugin/scripts/sync-skills.mjs +14 -6
  183. package/packages/omo-codex/plugin/scripts/sync-version.mjs +4 -2
  184. package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +3 -3
  185. package/packages/omo-codex/plugin/skills/refactor/SKILL.md +3 -3
  186. package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +5 -5
  187. package/packages/omo-codex/plugin/skills/review-work/SKILL.md +9 -3
  188. package/packages/omo-codex/plugin/skills/start-work/SKILL.md +6 -3
  189. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +136 -76
  190. package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +94 -23
  191. package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +94 -14
  192. package/packages/omo-codex/plugin/skills/teammode/scripts/team-transport.mjs +55 -0
  193. package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +72 -18
  194. package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +55 -28
  195. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +14 -15
  196. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +29 -27
  197. package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +1 -1
  198. package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  199. package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +4 -4
  200. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +4 -4
  201. package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +116 -10
  202. package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +23 -2
  203. package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +1 -1
  204. package/packages/omo-codex/plugin/test/aggregate-model-catalog.test.mjs +4 -4
  205. package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +4 -2
  206. package/packages/omo-codex/plugin/test/auto-update-restart-notice.test.mjs +33 -0
  207. package/packages/omo-codex/plugin/test/auto-update.test.mjs +4 -4
  208. package/packages/omo-codex/plugin/test/component-hook-contract-cases.mjs +2 -2
  209. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +4 -101
  210. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +160 -23
  211. package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +71 -0
  212. package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +32 -0
  213. package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +20 -2
  214. package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +1 -1
  215. package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -2
  216. package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +345 -0
  217. package/packages/omo-codex/plugin/test/ulw-plan-skill-contract.test.mjs +9 -40
  218. package/packages/omo-codex/plugin/test/ulw-research-skill-contract.test.mjs +4 -277
  219. package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +5 -5
  220. package/packages/omo-codex/scripts/install-config.test.mjs +22 -3
  221. package/packages/omo-codex/scripts/install-dist/install-local.mjs +508 -286
  222. package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +111 -1
  223. package/packages/shared-skills/skills/remove-ai-slops/SKILL.md +2 -2
  224. package/packages/shared-skills/skills/review-work/SKILL.md +1 -1
  225. package/packages/shared-skills/skills/start-work/SKILL.md +5 -2
  226. package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
  227. package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
  228. package/packages/shared-skills/skills/ulw-research/SKILL.md +2 -2
  229. package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
  230. package/dist/skills/visual-qa/scripts/skill-prompt-contract.test.ts +0 -296
  231. package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +0 -70
  232. package/packages/omo-codex/plugin/test/ulw-research-epistemic-contract.test.mjs +0 -98
  233. package/packages/shared-skills/skills/visual-qa/scripts/skill-prompt-contract.test.ts +0 -296
  234. /package/packages/omo-codex/plugin/components/rules/bundled-rules/{hephaestus.md → hephaestus/gpt-5.5.md} +0 -0
@@ -182,6 +182,7 @@ export async function runPostToolUseHook(
182
182
  completedPostCompactKind !== undefined
183
183
  ? withPostCompactBudget(dynamicConfig, { model: input.model, transcriptPath: input.transcript_path })
184
184
  : dynamicConfig,
185
+ input.model,
185
186
  );
186
187
  hydrateEngineState(engine, cachePath);
187
188
  debugTimer.lap("hydrate", {
@@ -189,7 +190,7 @@ export async function runPostToolUseHook(
189
190
  dynamicTargetFingerprints: engine.state.dynamicTargetFingerprints.size,
190
191
  staticDedup: engine.state.staticDedup.size,
191
192
  });
192
- const dynamicTargetFingerprints = fingerprintDynamicTargets(input.cwd, targetPaths, config);
193
+ const dynamicTargetFingerprints = fingerprintDynamicTargets(input.cwd, targetPaths, config, input.model);
193
194
  debugTimer.lap("fingerprint", { fingerprints: dynamicTargetFingerprints.length });
194
195
  const pendingTargetFingerprints = dynamicTargetFingerprints.filter(
195
196
  (target) => engine.state.dynamicTargetFingerprints.get(target.cacheKey) !== target.fingerprint,
@@ -1,12 +1,15 @@
1
1
  import { statSync } from "node:fs";
2
2
  import { resolve } from "node:path";
3
- import { isSameOrChildPath, toPosixPath, uniqueStrings } from "./path-utils.js";
4
- import { createRuleDiscoveryCache, findRuleCandidates } from "@oh-my-opencode/rules-engine/engine";
5
- import { hashContent } from "@oh-my-opencode/rules-engine/engine";
6
- import { sortCandidates } from "@oh-my-opencode/rules-engine/engine";
7
- import { findProjectRoot } from "@oh-my-opencode/rules-engine/engine";
8
- import { disabledSourcesFromConfig } from "@oh-my-opencode/rules-engine/engine";
9
3
  import type { PiRulesConfig, RuleCandidate } from "@oh-my-opencode/rules-engine/engine";
4
+ import {
5
+ createRuleDiscoveryCache,
6
+ disabledSourcesFromConfig,
7
+ findProjectRoot,
8
+ findRuleCandidates,
9
+ hashContent,
10
+ sortCandidates,
11
+ } from "@oh-my-opencode/rules-engine/engine";
12
+ import { isSameOrChildPath, toPosixPath, uniqueStrings } from "./path-utils.js";
10
13
 
11
14
  export interface DynamicTargetFingerprint {
12
15
  targetPath: string;
@@ -18,6 +21,7 @@ export function fingerprintDynamicTargets(
18
21
  cwd: string,
19
22
  targetPaths: ReadonlyArray<string>,
20
23
  config: PiRulesConfig,
24
+ model?: string,
21
25
  ): DynamicTargetFingerprint[] {
22
26
  const disabledSources = disabledSourcesFromConfig(config);
23
27
  const discoveryCache = createRuleDiscoveryCache();
@@ -34,6 +38,7 @@ export function fingerprintDynamicTargets(
34
38
  targetFile: string;
35
39
  disabledSources?: ReadonlySet<string>;
36
40
  cache: ReturnType<typeof createRuleDiscoveryCache>;
41
+ model?: string;
37
42
  } = {
38
43
  projectRoot,
39
44
  targetFile: targetPath,
@@ -42,6 +47,9 @@ export function fingerprintDynamicTargets(
42
47
  if (disabledSources !== undefined) {
43
48
  findOptions.disabledSources = disabledSources;
44
49
  }
50
+ if (model !== undefined) {
51
+ findOptions.model = model;
52
+ }
45
53
  const candidates = findRuleCandidates(findOptions);
46
54
  const candidateFingerprint = sortCandidates(candidates).map(fingerprintCandidate).join("\u0001");
47
55
  const cacheKey = dynamicTargetCacheKey(targetPath);
@@ -52,6 +60,7 @@ export function fingerprintDynamicTargets(
52
60
  [
53
61
  "v1",
54
62
  config.enabledSources === "auto" ? "auto" : config.enabledSources.join(","),
63
+ model ?? "",
55
64
  projectRoot ?? "",
56
65
  cacheKey,
57
66
  candidateFingerprint,
@@ -14,12 +14,17 @@ interface RulesEngineFactoryOptions {
14
14
 
15
15
  const componentRoot = dirname(dirname(fileURLToPath(import.meta.url)));
16
16
 
17
- export function createRulesEngine(options: RulesEngineFactoryOptions, config = configFromEnvironment(options.env)) {
17
+ export function createRulesEngine(
18
+ options: RulesEngineFactoryOptions,
19
+ config = configFromEnvironment(options.env),
20
+ model?: string,
21
+ ) {
18
22
  const platform = options.platform ?? process.platform;
19
23
  const pluginRoot = options.env?.["PLUGIN_ROOT"] ?? process.env["PLUGIN_ROOT"] ?? componentRoot;
20
24
 
21
25
  return createEngine(config, {
22
- findCandidates: (finderOptions) => findRuleCandidates({ ...finderOptions, platform, pluginRoot }),
26
+ findCandidates: (finderOptions) =>
27
+ findRuleCandidates({ ...finderOptions, platform, pluginRoot, ...(model === undefined ? {} : { model }) }),
23
28
  findProjectRoot,
24
29
  readFile: (path) => {
25
30
  try {
@@ -47,7 +47,7 @@ export function runStaticInjection(
47
47
  }
48
48
 
49
49
  const effectiveConfig = eventName === "UserPromptSubmit" ? withPromptBudget(config) : config;
50
- const engine = createRulesEngine(options, effectiveConfig);
50
+ const engine = createRulesEngine(options, effectiveConfig, model);
51
51
  hydrateEngineState(engine, cachePath);
52
52
  engine.state.cwd = cwd;
53
53
 
@@ -89,7 +89,7 @@ function runPostCompactRecovery(input: PostCompactRecoveryInput): string {
89
89
  model: input.model,
90
90
  transcriptPath: input.transcriptPath,
91
91
  });
92
- const engine = createRulesEngine(input.options, effectiveConfig);
92
+ const engine = createRulesEngine(input.options, effectiveConfig, input.model);
93
93
  hydrateEngineState(engine, input.cachePath);
94
94
  engine.state.cwd = input.cwd;
95
95
 
@@ -28,6 +28,7 @@ type SessionCache = {
28
28
  };
29
29
 
30
30
  const CLI_PATH = fileURLToPath(new URL("../dist/cli.js", import.meta.url));
31
+ const COMPONENT_ROOT = fileURLToPath(new URL("..", import.meta.url));
31
32
 
32
33
  function runHookCli(input: string, subcommand = "post-tool-use", env: NodeJS.ProcessEnv = {}): Promise<CliResult> {
33
34
  return new Promise((resolve, reject) => {
@@ -70,6 +71,12 @@ const RULES_ONLY_ENV = {
70
71
  CODEX_RULES_ENABLED_SOURCES: ".omo/rules",
71
72
  };
72
73
 
74
+ const DYNAMIC_BUNDLED_ONLY_ENV = {
75
+ CODEX_RULES_MODE: "dynamic",
76
+ CODEX_RULES_ENABLED_SOURCES: "plugin-bundled",
77
+ PLUGIN_ROOT: COMPONENT_ROOT,
78
+ };
79
+
73
80
  afterEach(() => {
74
81
  for (const directory of tempDirectories.splice(0)) {
75
82
  rmSync(directory, { recursive: true, force: true });
@@ -411,6 +418,31 @@ describe("codex rules hooks", () => {
411
418
  expect(readSessionCache(pluginData).dynamicTargetFingerprints).toEqual(cachedState.dynamicTargetFingerprints);
412
419
  });
413
420
 
421
+ it("#given bundled dynamic rules already injected for gpt-5.5 #when same target switches to gpt-5.6 #then emits the gpt-5.6 rule", async () => {
422
+ // given
423
+ const { root, pluginData } = makeTempProject();
424
+ const filePath = path.join(root, "src", "app.ts");
425
+ const input = postToolUseInput(root, filePath);
426
+ const firstOutput = await runPostToolUseHook(input, {
427
+ pluginDataRoot: pluginData,
428
+ env: DYNAMIC_BUNDLED_ONLY_ENV,
429
+ });
430
+
431
+ // when
432
+ const secondOutput = await runPostToolUseHook(
433
+ { ...input, model: "gpt-5.6-codex" },
434
+ { pluginDataRoot: pluginData, env: DYNAMIC_BUNDLED_ONLY_ENV },
435
+ );
436
+
437
+ // then
438
+ const firstContext = parseHookOutput(firstOutput).hookSpecificOutput?.additionalContext ?? "";
439
+ const secondContext = parseHookOutput(secondOutput).hookSpecificOutput?.additionalContext ?? "";
440
+ expect(firstContext).toContain("based on GPT-5.5");
441
+ expect(firstContext).not.toContain("based on GPT-5.6");
442
+ expect(secondContext).toContain("based on GPT-5.6");
443
+ expect(secondContext).not.toContain("based on GPT-5.5");
444
+ });
445
+
414
446
  it("#given default auto sources #when excluded AGENTS.md changes #then PostToolUse fingerprint stays stable", async () => {
415
447
  // given
416
448
  const { root, pluginData } = makeTempProject();
@@ -0,0 +1,106 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, describe, expect, it } from "vitest";
5
+
6
+ import { type CodexSessionStartInput, runSessionStartHook } from "../src/codex-hook.js";
7
+ import { findPluginBundledCandidates } from "@oh-my-opencode/rules-engine/engine";
8
+
9
+ const GPT_55_VARIANT_PATH = "bundled-rules/hephaestus/gpt-5.5.md";
10
+ const GPT_56_VARIANT_PATH = "bundled-rules/hephaestus/gpt-5.6.md";
11
+ const BUNDLED_ONLY_ENV = {
12
+ CODEX_RULES_ENABLED_SOURCES: "plugin-bundled",
13
+ };
14
+ const tempDirectories: string[] = [];
15
+ let originalPluginRoot: string | undefined;
16
+
17
+ afterEach(() => {
18
+ restoreEnv("PLUGIN_ROOT", originalPluginRoot);
19
+ for (const directory of tempDirectories.splice(0)) {
20
+ rmSync(directory, { recursive: true, force: true });
21
+ }
22
+ });
23
+
24
+ function makeProject(): { readonly root: string; readonly pluginData: string } {
25
+ originalPluginRoot = process.env["PLUGIN_ROOT"];
26
+ process.env["PLUGIN_ROOT"] = process.cwd();
27
+ const root = mkdtempSync(join(tmpdir(), "codex-rules-hephaestus-variant-project-"));
28
+ const pluginData = mkdtempSync(join(tmpdir(), "codex-rules-hephaestus-variant-data-"));
29
+ tempDirectories.push(root, pluginData);
30
+ writeFileSync(join(root, "package.json"), JSON.stringify({ name: "fixture" }));
31
+ return { root, pluginData };
32
+ }
33
+
34
+ function sessionStartInput(root: string, model: string): CodexSessionStartInput {
35
+ return {
36
+ session_id: "session-1",
37
+ transcript_path: null,
38
+ cwd: root,
39
+ hook_event_name: "SessionStart",
40
+ model,
41
+ permission_mode: "default",
42
+ source: "startup",
43
+ };
44
+ }
45
+
46
+ function restoreEnv(name: string, value: string | undefined): void {
47
+ if (value === undefined) {
48
+ delete process.env[name];
49
+ return;
50
+ }
51
+ process.env[name] = value;
52
+ }
53
+
54
+ describe("Hephaestus bundled rule model variants", () => {
55
+ it("#given packaged bundled rules #when discovering with a gpt-5.5 model #then only the gpt-5.5 variant is included", () => {
56
+ const candidates = findPluginBundledCandidates({ pluginRoot: process.cwd(), model: "gpt-5.5" });
57
+ const paths = candidates.map((candidate) => candidate.relativePath);
58
+
59
+ expect(paths).toContain(GPT_55_VARIANT_PATH);
60
+ expect(paths).not.toContain(GPT_56_VARIANT_PATH);
61
+ });
62
+
63
+ it("#given packaged bundled rules #when discovering with a gpt-5.6 family model #then only the gpt-5.6 variant is included", () => {
64
+ const candidates = findPluginBundledCandidates({ pluginRoot: process.cwd(), model: "gpt-5.6-codex" });
65
+ const paths = candidates.map((candidate) => candidate.relativePath);
66
+
67
+ expect(paths).toContain(GPT_56_VARIANT_PATH);
68
+ expect(paths).not.toContain(GPT_55_VARIANT_PATH);
69
+ });
70
+
71
+ it("#given packaged bundled rules #when discovering without a model #then the gpt-5.5 variant is the fallback", () => {
72
+ const candidates = findPluginBundledCandidates({ pluginRoot: process.cwd() });
73
+ const paths = candidates.map((candidate) => candidate.relativePath);
74
+
75
+ expect(paths).toContain(GPT_55_VARIANT_PATH);
76
+ expect(paths).not.toContain(GPT_56_VARIANT_PATH);
77
+ });
78
+
79
+ it("#given a gpt-5.5 session #when SessionStart runs #then the gpt-5.5 identity is injected in full", async () => {
80
+ const { root, pluginData } = makeProject();
81
+
82
+ const output = await runSessionStartHook(sessionStartInput(root, "gpt-5.5"), {
83
+ pluginDataRoot: pluginData,
84
+ env: BUNDLED_ONLY_ENV,
85
+ });
86
+
87
+ expect(output).toContain(`Instructions from: ${join(process.cwd(), GPT_55_VARIANT_PATH)}`);
88
+ expect(output).toContain("based on GPT-5.5");
89
+ expect(output).not.toContain("based on GPT-5.6");
90
+ expect(output).not.toContain("[Truncated. Full:");
91
+ });
92
+
93
+ it("#given a gpt-5.6 session #when SessionStart runs #then the gpt-5.6 identity is injected in full", async () => {
94
+ const { root, pluginData } = makeProject();
95
+
96
+ const output = await runSessionStartHook(sessionStartInput(root, "gpt-5.6-codex"), {
97
+ pluginDataRoot: pluginData,
98
+ env: BUNDLED_ONLY_ENV,
99
+ });
100
+
101
+ expect(output).toContain(`Instructions from: ${join(process.cwd(), GPT_56_VARIANT_PATH)}`);
102
+ expect(output).toContain("based on GPT-5.6");
103
+ expect(output).not.toContain("based on GPT-5.5");
104
+ expect(output).not.toContain("[Truncated. Full:");
105
+ });
106
+ });
@@ -7,7 +7,7 @@
7
7
  "type": "command",
8
8
  "command": "node \"${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js\" hook stop",
9
9
  "timeout": 10,
10
- "statusMessage": "(OmO 4.16.2) Checking Start-Work Continuation"
10
+ "statusMessage": "(OmO 4.17.0) Checking Start-Work Continuation"
11
11
  }
12
12
  ]
13
13
  }
@@ -19,7 +19,7 @@
19
19
  "type": "command",
20
20
  "command": "node \"${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js\" hook subagent-stop",
21
21
  "timeout": 10,
22
- "statusMessage": "(OmO 4.16.2) Checking Start-Work Continuation"
22
+ "statusMessage": "(OmO 4.17.0) Checking Start-Work Continuation"
23
23
  }
24
24
  ]
25
25
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@code-yeongyu/codex-start-work-continuation",
3
- "version": "4.16.2",
3
+ "version": "4.17.0",
4
4
  "description": "Codex Stop hook continuation injector for omo-codex start-work plans.",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.12.1",
@@ -1,4 +1,4 @@
1
- import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
1
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
2
2
  import { tmpdir } from "node:os";
3
3
  import { join } from "node:path";
4
4
  import { afterEach, describe, expect, it } from "vitest";
@@ -96,84 +96,6 @@ describe("start-work Stop hook", () => {
96
96
  expect(output).toBe("");
97
97
  });
98
98
 
99
- it("#given active codex work #when continuation directive is emitted #then subagent guidance is reliable", () => {
100
- // given
101
- const workspace = createWorkspace({
102
- boulderJson: createBoulderJson({ sessionIds: ["codex:sess_abc"], status: "active" }),
103
- planMarkdown: ["# Plan", "", "## TODOs", "- [ ] First"].join("\n"),
104
- });
105
- const fs = createMemoryFs();
106
-
107
- // when
108
- const output = runStopHook(createStopInput(workspace), fs);
109
-
110
- // then
111
- const parsed = parseBlockOutput(output);
112
- expect(parsed.reason).toMatch(/TASK:/);
113
- expect(parsed.reason).toMatch(/fork_context:\s*false/);
114
- expect(parsed.reason).toMatch(/wait_agent.*mailbox signals/);
115
- expect(parsed.reason).toMatch(/TASK STILL ACTIVE/);
116
- expect(parsed.reason).toMatch(/respawn.*smaller/);
117
- expect(parsed.reason).toMatch(/WORKING:/);
118
- });
119
-
120
- it("#given active codex work #when continuation directive is emitted #then QA weight is tier-scoped without echo bloat", () => {
121
- // given
122
- const workspace = createWorkspace({
123
- boulderJson: createBoulderJson({ sessionIds: ["codex:sess_abc"], status: "active" }),
124
- planMarkdown: ["# Plan", "", "## TODOs", "- [ ] First"].join("\n"),
125
- });
126
- const fs = createMemoryFs();
127
-
128
- // when
129
- const output = runStopHook(createStopInput(workspace), fs);
130
-
131
- // then
132
- const parsed = parseBlockOutput(output);
133
- expect(parsed.reason).toMatch(/LIGHT/);
134
- expect(parsed.reason).toMatch(/HEAVY/);
135
- expect(parsed.reason).toMatch(/When unsure[^.]{0,30}HEAVY/);
136
- expect(parsed.reason).toMatch(/mirrors its implementation/);
137
- expect((parsed.reason.match(/malformed input, prompt injection/g) ?? []).length).toBe(1);
138
- expect(parsed.reason.split(/\s+/).filter(Boolean).length).toBeLessThanOrEqual(1135);
139
- });
140
-
141
- it("#given active codex work #when continuation directive is emitted #then PR lifecycle stays worktree-bound", () => {
142
- // given
143
- const workspace = createWorkspace({
144
- boulderJson: createBoulderJson({
145
- sessionIds: ["codex:sess_abc"],
146
- status: "active",
147
- worktreePath: "/tmp/worktree",
148
- }),
149
- planMarkdown: ["# Plan", "", "## TODOs", "- [ ] First"].join("\n"),
150
- });
151
- const fs = createMemoryFs();
152
-
153
- // when
154
- const output = runStopHook(createStopInput(workspace), fs);
155
-
156
- // then
157
- const parsed = parseBlockOutput(output);
158
- expect(parsed.reason).toContain("PR or branch implementation/review/merge work requires a task-owned git worktree");
159
- expect(parsed.reason).toContain("Treat the main worktree as read-only context");
160
- expect(parsed.reason).toContain("create/update the PR, wait for CI/review/Cubic gates, merge by default");
161
- expect(parsed.reason).toContain("Do not create a PR, PR handoff, branch handoff, merge");
162
- });
163
-
164
- it("#given stop hook source #when inspected #then it remains Boulder-only without planning bootstrap logic", () => {
165
- // given
166
- const hook = readFileSync(new URL("../src/codex-hook.ts", import.meta.url), "utf8");
167
-
168
- // then
169
- expect(hook).toMatch(/readContinuationState/);
170
- expect(hook).toMatch(/START_WORK_CONTINUATION_DIRECTIVE/);
171
- expect(hook).toMatch(/decision:\s*"block"/);
172
- expect(hook).not.toMatch(
173
- /\bulw-plan\b|\bspawn_agent\b|\brequest_user_input\b|bootstrap|selectable plan|Phase 1|Create or update Boulder state/i,
174
- );
175
- });
176
-
177
99
  it("#given active work belongs to another harness #when hook runs #then returns empty output", () => {
178
100
  // given
179
101
  const workspace = createWorkspace({
@@ -7,18 +7,19 @@
7
7
  Codex team-mode component. Two deliverables in one dir:
8
8
 
9
9
  - **PostToolUse hook** (`@sisyphuslabs/codex-teammode`): fires after `create_thread` / `codex_app.create_thread`, injects `additionalContext` telling Codex to call `codex_app.set_thread_title` NOW with the real task/role. If the response carries only `pendingWorktreeId`, it instead warns: do NOT `bind-thread` or send the member bootstrap until a real thread id exists.
10
- - **`teammode` skill**: script-driven orchestration of a named team of Codex threads. Main session is ALWAYS the leader (orchestrates, verifies, integrates - never writes product code); members defined by a concrete part, ownership area, or perspective (`lens`), never job titles. Durable state under `.omo/teams/{session_id}/`: `team.json` + auto-generated `guide.md` member field manual + `artifacts/` exchange dir.
10
+ - **`teammode` skill**: script-driven orchestration of a named team of Codex workers on ONE immutable transport chosen at init: `multi_agent_v2` (preferred; members are native flat `spawn_agent` agents addressed by `task_name` / `/root/<task_name>` agent path, messaged with `send_message`/`followup_task`) or `codex_app` (fallback; members are app threads addressed by thread id). The leader inspects the active tool list and announces the selected transport to the user BEFORE `init`. Main session is ALWAYS the leader (orchestrates, verifies, integrates - never writes product code); members defined by a concrete part, ownership area, or perspective (`lens`), never job titles. Durable state under `.omo/teams/{session_id}/`: `team.json` + auto-generated `guide.md` member field manual + `artifacts/` exchange dir.
11
11
 
12
12
  ## KEY FILES
13
13
 
14
14
  - `src/codex-hook.ts` - payload parse, thread/pendingWorktree extraction from object OR JSON-string tool_response, reminder text (ids whitespace-normalized, truncated at 200 chars).
15
15
  - `src/cli.ts` - argv dispatch; only `hook post-tool-use`.
16
16
  - `hooks/hooks.json` - matcher `^(create_thread|codex_app\.create_thread)$`, runs `node ${PLUGIN_ROOT}/dist/cli.js hook post-tool-use`, timeout 10.
17
- - `skills/teammode/SKILL.md` - leader protocol: team-vs-subagent decision matrix, ulw-plan parallel-wave mapping, "Let members work - do not rush them" (4-reason message gate; quiet between heartbeats = working, not stalled), archive/delete/stop rules.
18
- - `skills/teammode/scripts/team.mjs` - controller CLI: init / add-member / bind-thread / member-prompt / set-status / worktree-add / worktree-remove / integrate / archive / delete / status / guide. Zero npm deps, node builtins only, runs under node or bun on all 3 OSes.
19
- - `skills/teammode/scripts/team-state.mjs` - state model + persistence: per-team `.team.lock` dir lock (`owner.json`; `OMO_TEAMMODE_LOCK_TIMEOUT_MS` / `OMO_TEAMMODE_LOCK_RETRY_MS`), atomic tmp+rename writes, symlink- and escape-guarded team dirs, schemaVersion 2, `MIN_MEMBERS = 2`, unique focus/name/threadTitle invariants.
17
+ - `skills/teammode/SKILL.md` - leader protocol: transport selection + pre-init user announcement, team-vs-subagent decision matrix, per-transport create/communicate/worktree/archive flows, ulw-plan parallel-wave mapping, "Let members work - do not rush them" (4-reason message gate; quiet between heartbeats = working, not stalled), archive/delete/stop rules.
18
+ - `skills/teammode/scripts/team.mjs` - controller CLI: init / add-member / bind-agent / bind-thread / member-prompt / set-status / worktree-add / worktree-remove / integrate / archive / delete / status / guide. Zero npm deps, node builtins only, runs under node or bun on all 3 OSes.
19
+ - `skills/teammode/scripts/team-transport.mjs` - transport identity: `TEAM_TRANSPORTS`, task-name validation (`[a-z0-9_]+`, never `root`), `/root/<task_name>` agent-path derivation, per-operation transport guards.
20
+ - `skills/teammode/scripts/team-state.mjs` - state model + persistence: per-team `.team.lock` dir lock (`owner.json`; `OMO_TEAMMODE_LOCK_TIMEOUT_MS` / `OMO_TEAMMODE_LOCK_RETRY_MS`), atomic tmp+rename writes, symlink- and escape-guarded team dirs, schemaVersion 3 (legacy 2 migrates in memory to `codex_app` and persists on the next mutation), `MIN_MEMBERS = 2`, unique focus/name invariants plus per-transport identity invariants (taskName/agentPath on V2, threadTitle on codex_app).
20
21
  - `skills/teammode/scripts/team-worktree.mjs` - git worktree provisioning + `merge --no-ff --no-edit` integration; conflict leaves the tree mid-merge for the leader to resolve and re-run.
21
- - `skills/teammode/scripts/team-guide.mjs` - pure string builders for `guide.md` + the short member bootstrap trigger (`codex://threads/<id>` deep links, `WORKING:` / `BLOCKED:` heartbeat rules, English-only member traffic).
22
+ - `skills/teammode/scripts/team-guide.mjs` - pure string builders for `guide.md` + the short member bootstrap trigger, branched by transport (V2: `/root` leader target + `members[].agentPath` peers + `send_message`/`followup_task`; codex_app: `codex_app.send_message_to_thread` + `codex://threads/<id>` deep links), `WORKING:` / `BLOCKED:` heartbeat rules, English-only member traffic.
22
23
  - `test/thread-title-hook.test.ts` - hook unit tests (vitest-style imports, run via `bun test`).
23
24
 
24
25
  ## WHERE TO LOOK
@@ -26,8 +27,9 @@ Codex team-mode component. Two deliverables in one dir:
26
27
  | Task | Location |
27
28
  |------|----------|
28
29
  | Hook reminder text | `src/codex-hook.ts` `threadTitleReminder` + `test/thread-title-hook.test.ts`; `plugin/test/teammode-thread-title.test.mjs` pins the exact strings |
29
- | Skill protocol text | `skills/teammode/SKILL.md`; contract tests `plugin/test/teammode-{communication,worktree,safety,archive-ambiguity,thread-links}.test.mjs` pin phrases |
30
- | team.json shape | `team-state.mjs` (single source of the shape; `validateTeam` rejects schemaVersion != 2) |
30
+ | Skill protocol text | `skills/teammode/SKILL.md`; contract tests `plugin/test/teammode-{transport,communication,worktree,safety,archive-ambiguity,thread-links}.test.mjs` pin phrases |
31
+ | team.json shape | `team-state.mjs` (single source of the shape; `validateTeam` migrates schemaVersion 2 to 3 and rejects anything else) |
32
+ | Transport rules | `team-transport.mjs` (`parseTeamTransport`, `parseTaskName`, `assertTransport`) + `plugin/test/teammode-transport.test.mjs` |
31
33
  | Branch naming, worktree paths, merge | `team-worktree.mjs` (`team/<sessionId>/<memberId>` branches, worktrees confined to `<team dir>/worktrees/<memberId>`) |
32
34
  | Hook wiring | `hooks/hooks.json` (component) + `plugin/hooks/post-tool-use-checking-thread-title-hygiene.json` (aggregate; adds `commandWindows` powershell node-dispatch) |
33
35
 
@@ -40,7 +42,8 @@ Codex team-mode component. Two deliverables in one dir:
40
42
  - All mutating subcommands serialize through `withTeamLock` and re-read committed state before writing. "team state is locked by ..." means the mutation did NOT happen; retry, or inspect `.team.lock/owner.json` after a crash.
41
43
  - Archive ambiguity: "Ambiguous Codex thread id" from `codex_app.set_thread_archived` is an app-thread archival blocker, not a team-state blocker. Record it via `archive --note`, never claim the thread was archived, never delete team state before evidence is copied or the user accepts the loss.
42
44
  - `integrate` lands member branches with merge commits only (`--no-ff`); never squash or rebase.
43
- - `bind-thread` refuses while the team has fewer than 2 members; a single-member team is a subagent, not a team. Member focus, name, and threadTitle must each be unique (case/whitespace-insensitive).
44
- - Every member is a real `codex_app.create_thread` thread bound via `bind-thread`; a spawned in-process subagent (`multi_agent_v1.spawn_agent` or flat `spawn_agent`) is never a substitute.
45
+ - `bind-agent`/`bind-thread` refuse while the team has fewer than 2 members; a single-member team is a subagent, not a team. Member focus and name must be unique (case/whitespace-insensitive); V2 adds unique taskName/agentPath, codex_app adds unique threadTitle.
46
+ - Binding is transport-strict and mutation-safe: `bind-thread` on a V2 team and `bind-agent` on a codex_app team fail before persisting, leaving `team.json` byte-identical; `bind-agent` also rejects any path other than the member's precomputed `/root/<task_name>`.
47
+ - On `multi_agent_v2` teams a member IS a durable flat `spawn_agent` agent (spawned with `fork_turns: "none"`, re-tasked via `followup_task`, no title/deep-link/archive primitive - `interrupt_agent` + durable team state is the archive story). On `codex_app` teams every member is a real `codex_app.create_thread` thread bound via `bind-thread`, and a spawned in-process subagent is never a substitute there. V2 has no spawn-time cwd: create worktrees BEFORE spawn so the bootstrap carries the path.
45
48
  - The hook must stay silent (empty stdout) on unrelated tools and malformed payloads; it never blocks a Codex turn.
46
49
  - `init` with an explicit `--session <leader thread id>` makes `leader.sessionId` messageable; without it members cannot report and the leader is stuck polling. Re-running `init` is a safe no-op.
@@ -8,7 +8,7 @@
8
8
  "type": "command",
9
9
  "command": "node \"${PLUGIN_ROOT}/dist/cli.js\" hook post-tool-use",
10
10
  "timeout": 10,
11
- "statusMessage": "(OmO 4.16.2) Checking Thread Title Hygiene"
11
+ "statusMessage": "(OmO 4.17.0) Checking Thread Title Hygiene"
12
12
  }
13
13
  ]
14
14
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sisyphuslabs/codex-teammode",
3
- "version": "4.16.2",
3
+ "version": "4.17.0",
4
4
  "description": "Codex team-mode hook component that keeps background thread titles descriptive after create_thread.",
5
5
  "type": "module",
6
6
  "private": true,