oh-my-copilot 4.13.21 → 4.13.45

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 (195) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +8 -0
  4. package/agents/code-reviewer.agent.md +24 -6
  5. package/agents/designer.agent.md +12 -0
  6. package/bridge/cli.cjs +1978 -1214
  7. package/bridge/mcp-server.cjs +678 -261
  8. package/bridge/runtime-cli.cjs +55 -8
  9. package/bridge/team.js +507 -38
  10. package/dist/__tests__/agent-registry.test.js +92 -1
  11. package/dist/__tests__/agent-registry.test.js.map +1 -1
  12. package/dist/__tests__/bedrock-model-routing.test.js +18 -17
  13. package/dist/__tests__/bedrock-model-routing.test.js.map +1 -1
  14. package/dist/__tests__/delegation-enforcer.test.js +46 -6
  15. package/dist/__tests__/delegation-enforcer.test.js.map +1 -1
  16. package/dist/__tests__/hooks/session-end-cleanup.test.d.ts +2 -0
  17. package/dist/__tests__/hooks/session-end-cleanup.test.d.ts.map +1 -0
  18. package/dist/__tests__/hooks/session-end-cleanup.test.js +93 -0
  19. package/dist/__tests__/hooks/session-end-cleanup.test.js.map +1 -0
  20. package/dist/__tests__/hud/stdin.test.d.ts +2 -0
  21. package/dist/__tests__/hud/stdin.test.d.ts.map +1 -0
  22. package/dist/__tests__/hud/stdin.test.js +556 -0
  23. package/dist/__tests__/hud/stdin.test.js.map +1 -0
  24. package/dist/__tests__/model-routing-override-three-site-sync.test.d.ts +2 -0
  25. package/dist/__tests__/model-routing-override-three-site-sync.test.d.ts.map +1 -0
  26. package/dist/__tests__/model-routing-override-three-site-sync.test.js +75 -0
  27. package/dist/__tests__/model-routing-override-three-site-sync.test.js.map +1 -0
  28. package/dist/__tests__/non-claude-provider-detection.test.js +37 -0
  29. package/dist/__tests__/non-claude-provider-detection.test.js.map +1 -1
  30. package/dist/__tests__/package-dir-resolution-regression.test.js +25 -0
  31. package/dist/__tests__/package-dir-resolution-regression.test.js.map +1 -1
  32. package/dist/__tests__/pre-tool-enforcer.test.js +51 -0
  33. package/dist/__tests__/pre-tool-enforcer.test.js.map +1 -1
  34. package/dist/__tests__/session-start-script-context.test.js +121 -0
  35. package/dist/__tests__/session-start-script-context.test.js.map +1 -1
  36. package/dist/__tests__/setup-claude-md-script.test.js +44 -1
  37. package/dist/__tests__/setup-claude-md-script.test.js.map +1 -1
  38. package/dist/agents/definitions.d.ts.map +1 -1
  39. package/dist/agents/definitions.js +2 -1
  40. package/dist/agents/definitions.js.map +1 -1
  41. package/dist/autoresearch/__tests__/decide-outcome.test.d.ts +2 -0
  42. package/dist/autoresearch/__tests__/decide-outcome.test.d.ts.map +1 -0
  43. package/dist/autoresearch/__tests__/decide-outcome.test.js +69 -0
  44. package/dist/autoresearch/__tests__/decide-outcome.test.js.map +1 -0
  45. package/dist/autoresearch/runtime.d.ts.map +1 -1
  46. package/dist/autoresearch/runtime.js +16 -2
  47. package/dist/autoresearch/runtime.js.map +1 -1
  48. package/dist/cli/__tests__/launch.test.js +23 -0
  49. package/dist/cli/__tests__/launch.test.js.map +1 -1
  50. package/dist/cli/__tests__/self-improve-paths.test.js +3 -2
  51. package/dist/cli/__tests__/self-improve-paths.test.js.map +1 -1
  52. package/dist/cli/__tests__/team.test.js +100 -0
  53. package/dist/cli/__tests__/team.test.js.map +1 -1
  54. package/dist/cli/commands/__tests__/team-role-shorthand.test.js +30 -0
  55. package/dist/cli/commands/__tests__/team-role-shorthand.test.js.map +1 -1
  56. package/dist/cli/commands/__tests__/team.test.js +48 -1
  57. package/dist/cli/commands/__tests__/team.test.js.map +1 -1
  58. package/dist/cli/commands/team.d.ts +2 -0
  59. package/dist/cli/commands/team.d.ts.map +1 -1
  60. package/dist/cli/commands/team.js +9 -2
  61. package/dist/cli/commands/team.js.map +1 -1
  62. package/dist/cli/launch.d.ts.map +1 -1
  63. package/dist/cli/launch.js +7 -0
  64. package/dist/cli/launch.js.map +1 -1
  65. package/dist/cli/team.d.ts.map +1 -1
  66. package/dist/cli/team.js +34 -3
  67. package/dist/cli/team.js.map +1 -1
  68. package/dist/config/__tests__/loader.test.js +35 -4
  69. package/dist/config/__tests__/loader.test.js.map +1 -1
  70. package/dist/config/__tests__/models.test.js +126 -3
  71. package/dist/config/__tests__/models.test.js.map +1 -1
  72. package/dist/config/models.d.ts +8 -0
  73. package/dist/config/models.d.ts.map +1 -1
  74. package/dist/config/models.js +119 -8
  75. package/dist/config/models.js.map +1 -1
  76. package/dist/features/delegation-enforcer.d.ts.map +1 -1
  77. package/dist/features/delegation-enforcer.js +10 -9
  78. package/dist/features/delegation-enforcer.js.map +1 -1
  79. package/dist/hooks/__tests__/bridge-routing.test.js +1 -0
  80. package/dist/hooks/__tests__/bridge-routing.test.js.map +1 -1
  81. package/dist/hooks/autopilot/enforcement.d.ts.map +1 -1
  82. package/dist/hooks/autopilot/enforcement.js +19 -0
  83. package/dist/hooks/autopilot/enforcement.js.map +1 -1
  84. package/dist/hooks/bridge.d.ts.map +1 -1
  85. package/dist/hooks/bridge.js +26 -4
  86. package/dist/hooks/bridge.js.map +1 -1
  87. package/dist/hooks/persistent-mode/stop-hook-blocking.test.js +37 -1
  88. package/dist/hooks/persistent-mode/stop-hook-blocking.test.js.map +1 -1
  89. package/dist/hooks/session-end/index.d.ts +9 -2
  90. package/dist/hooks/session-end/index.d.ts.map +1 -1
  91. package/dist/hooks/session-end/index.js +90 -35
  92. package/dist/hooks/session-end/index.js.map +1 -1
  93. package/dist/hud/stdin.d.ts +12 -1
  94. package/dist/hud/stdin.d.ts.map +1 -1
  95. package/dist/hud/stdin.js +123 -18
  96. package/dist/hud/stdin.js.map +1 -1
  97. package/dist/hud/types.d.ts +1 -0
  98. package/dist/hud/types.d.ts.map +1 -1
  99. package/dist/hud/types.js.map +1 -1
  100. package/dist/installer/__tests__/session-start-template.test.d.ts +2 -0
  101. package/dist/installer/__tests__/session-start-template.test.d.ts.map +1 -0
  102. package/dist/installer/__tests__/session-start-template.test.js +174 -0
  103. package/dist/installer/__tests__/session-start-template.test.js.map +1 -0
  104. package/dist/lib/__tests__/mode-state-io.test.js +75 -12
  105. package/dist/lib/__tests__/mode-state-io.test.js.map +1 -1
  106. package/dist/lib/mode-state-io.d.ts +11 -0
  107. package/dist/lib/mode-state-io.d.ts.map +1 -1
  108. package/dist/lib/mode-state-io.js +64 -0
  109. package/dist/lib/mode-state-io.js.map +1 -1
  110. package/dist/lib/worktree-paths.d.ts +12 -0
  111. package/dist/lib/worktree-paths.d.ts.map +1 -1
  112. package/dist/lib/worktree-paths.js +75 -0
  113. package/dist/lib/worktree-paths.js.map +1 -1
  114. package/dist/planning/__tests__/artifacts.test.d.ts +2 -0
  115. package/dist/planning/__tests__/artifacts.test.d.ts.map +1 -0
  116. package/dist/planning/__tests__/artifacts.test.js +486 -0
  117. package/dist/planning/__tests__/artifacts.test.js.map +1 -0
  118. package/dist/planning/artifact-names.d.ts +13 -0
  119. package/dist/planning/artifact-names.d.ts.map +1 -0
  120. package/dist/planning/artifact-names.js +108 -0
  121. package/dist/planning/artifact-names.js.map +1 -0
  122. package/dist/planning/artifacts.d.ts +47 -0
  123. package/dist/planning/artifacts.d.ts.map +1 -0
  124. package/dist/planning/artifacts.js +221 -0
  125. package/dist/planning/artifacts.js.map +1 -0
  126. package/dist/team/__tests__/api-interop.compatibility.test.js +194 -5
  127. package/dist/team/__tests__/api-interop.compatibility.test.js.map +1 -1
  128. package/dist/team/__tests__/model-contract.test.js +92 -2
  129. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  130. package/dist/team/__tests__/runtime-v2.dispatch.test.js +30 -0
  131. package/dist/team/__tests__/runtime-v2.dispatch.test.js.map +1 -1
  132. package/dist/team/__tests__/scaling.test.js +39 -1
  133. package/dist/team/__tests__/scaling.test.js.map +1 -1
  134. package/dist/team/__tests__/worker-bootstrap.test.js +6 -0
  135. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  136. package/dist/team/api-interop.d.ts.map +1 -1
  137. package/dist/team/api-interop.js +89 -2
  138. package/dist/team/api-interop.js.map +1 -1
  139. package/dist/team/delegation-evidence.d.ts +5 -0
  140. package/dist/team/delegation-evidence.d.ts.map +1 -0
  141. package/dist/team/delegation-evidence.js +29 -0
  142. package/dist/team/delegation-evidence.js.map +1 -0
  143. package/dist/team/model-contract.d.ts +8 -0
  144. package/dist/team/model-contract.d.ts.map +1 -1
  145. package/dist/team/model-contract.js +13 -0
  146. package/dist/team/model-contract.js.map +1 -1
  147. package/dist/team/runtime-v2.d.ts +2 -1
  148. package/dist/team/runtime-v2.d.ts.map +1 -1
  149. package/dist/team/runtime-v2.js +3 -1
  150. package/dist/team/runtime-v2.js.map +1 -1
  151. package/dist/team/state/tasks.d.ts +4 -1
  152. package/dist/team/state/tasks.d.ts.map +1 -1
  153. package/dist/team/state/tasks.js +38 -1
  154. package/dist/team/state/tasks.js.map +1 -1
  155. package/dist/team/team-ops.d.ts +4 -1
  156. package/dist/team/team-ops.d.ts.map +1 -1
  157. package/dist/team/team-ops.js +2 -2
  158. package/dist/team/team-ops.js.map +1 -1
  159. package/dist/team/types.d.ts +22 -1
  160. package/dist/team/types.d.ts.map +1 -1
  161. package/dist/team/types.js.map +1 -1
  162. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  163. package/dist/team/worker-bootstrap.js +6 -1
  164. package/dist/team/worker-bootstrap.js.map +1 -1
  165. package/dist/tools/__tests__/state-tools.test.js +322 -5
  166. package/dist/tools/__tests__/state-tools.test.js.map +1 -1
  167. package/dist/tools/__tests__/wiki-tools-working-directory.test.d.ts +2 -0
  168. package/dist/tools/__tests__/wiki-tools-working-directory.test.d.ts.map +1 -0
  169. package/dist/tools/__tests__/wiki-tools-working-directory.test.js +66 -0
  170. package/dist/tools/__tests__/wiki-tools-working-directory.test.js.map +1 -0
  171. package/dist/tools/state-tools.d.ts +3 -10
  172. package/dist/tools/state-tools.d.ts.map +1 -1
  173. package/dist/tools/state-tools.js +393 -91
  174. package/dist/tools/state-tools.js.map +1 -1
  175. package/dist/tools/wiki-tools.js +8 -8
  176. package/dist/tools/wiki-tools.js.map +1 -1
  177. package/docs/GETTING-STARTED.md +7 -0
  178. package/docs/copilot-instructions.md +1 -1
  179. package/package.json +1 -1
  180. package/scripts/lib/model-routing-override-message.mjs +21 -0
  181. package/scripts/persistent-mode.cjs +36 -0
  182. package/scripts/persistent-mode.mjs +36 -0
  183. package/scripts/post-tool-verifier.mjs +1 -0
  184. package/scripts/pre-tool-enforcer.mjs +60 -0
  185. package/scripts/session-start.mjs +63 -0
  186. package/scripts/setup-copilot-instructions.sh +12 -2
  187. package/skills/configure-notifications/SKILL.md +3 -3
  188. package/skills/mcp-setup/SKILL.md +60 -45
  189. package/skills/omc-setup/SKILL.md +3 -3
  190. package/skills/omc-setup/phases/01-install-claude-md.md +1 -1
  191. package/skills/omc-setup/phases/04-welcome.md +8 -8
  192. package/skills/project-session-manager/SKILL.md +2 -2
  193. package/skills/project-session-manager/psm.sh +1 -1
  194. package/templates/hooks/lib/model-routing-override-message.mjs +21 -0
  195. package/templates/hooks/session-start.mjs +62 -0
package/bridge/team.js CHANGED
@@ -2105,15 +2105,42 @@ var init_ssrf_guard = __esm({
2105
2105
  });
2106
2106
 
2107
2107
  // src/config/models.ts
2108
+ function readEnvValue(key) {
2109
+ const value = process.env[key]?.trim();
2110
+ return value || void 0;
2111
+ }
2108
2112
  function resolveTierModelFromEnv(tier) {
2109
2113
  for (const key of TIER_ENV_KEYS[tier]) {
2110
- const value = process.env[key]?.trim();
2114
+ const value = readEnvValue(key);
2115
+ if (value) {
2116
+ return value;
2117
+ }
2118
+ }
2119
+ return void 0;
2120
+ }
2121
+ function getDirectModelEnvValue() {
2122
+ for (const key of DIRECT_MODEL_ENV_KEYS) {
2123
+ const value = readEnvValue(key);
2111
2124
  if (value) {
2112
2125
  return value;
2113
2126
  }
2114
2127
  }
2115
2128
  return void 0;
2116
2129
  }
2130
+ function getProviderDetectionModelEnvValues() {
2131
+ const directModel = getDirectModelEnvValue();
2132
+ if (directModel) {
2133
+ return [directModel];
2134
+ }
2135
+ const values = /* @__PURE__ */ new Set();
2136
+ for (const tier of INHERIT_TIER_PRIORITY) {
2137
+ const value = resolveTierModelFromEnv(tier);
2138
+ if (value) {
2139
+ values.add(value);
2140
+ }
2141
+ }
2142
+ return [...values];
2143
+ }
2117
2144
  function getDefaultModelHigh() {
2118
2145
  return resolveTierModelFromEnv("HIGH") || BUILTIN_TIER_MODEL_DEFAULTS.HIGH;
2119
2146
  }
@@ -2146,17 +2173,19 @@ function isBedrock() {
2146
2173
  if (modelId && /^((us|eu|ap|global)\.anthropic\.|anthropic\.claude)/i.test(modelId)) {
2147
2174
  return true;
2148
2175
  }
2176
+ if (modelId && /^arn:aws(-[^:]+)?:bedrock:/i.test(modelId) && /:(inference-profile|application-inference-profile)\//i.test(modelId) && modelId.toLowerCase().includes("claude")) {
2177
+ return true;
2178
+ }
2149
2179
  return false;
2150
2180
  }
2151
2181
  function isVertexAI() {
2152
2182
  if (process.env.CLAUDE_CODE_USE_VERTEX === "1") {
2153
2183
  return true;
2154
2184
  }
2155
- const modelId = process.env.CLAUDE_MODEL || process.env.ANTHROPIC_MODEL || "";
2156
- if (modelId && modelId.toLowerCase().startsWith("vertex_ai/")) {
2157
- return true;
2158
- }
2159
- return false;
2185
+ return hasVertexModelId(getProviderDetectionModelEnvValues());
2186
+ }
2187
+ function hasVertexModelId(modelIds) {
2188
+ return modelIds.some((modelId) => modelId.toLowerCase().startsWith("vertex_ai/"));
2160
2189
  }
2161
2190
  function isProviderSpecificModelId(modelId) {
2162
2191
  return isBedrockModelId(modelId) || isVertexModelId(modelId);
@@ -2195,11 +2224,13 @@ function isNonCopilotProvider() {
2195
2224
  }
2196
2225
  return false;
2197
2226
  }
2198
- var TIER_ENV_KEYS, COPILOT_FAMILY_DEFAULTS, BUILTIN_TIER_MODEL_DEFAULTS, COPILOT_FAMILY_HIGH_VARIANTS, BUILTIN_EXTERNAL_MODEL_DEFAULTS;
2227
+ var DIRECT_MODEL_ENV_KEYS, INHERIT_TIER_PRIORITY, TIER_ENV_KEYS, COPILOT_FAMILY_DEFAULTS, BUILTIN_TIER_MODEL_DEFAULTS, COPILOT_FAMILY_HIGH_VARIANTS, BUILTIN_EXTERNAL_MODEL_DEFAULTS;
2199
2228
  var init_models = __esm({
2200
2229
  "src/config/models.ts"() {
2201
2230
  "use strict";
2202
2231
  init_ssrf_guard();
2232
+ DIRECT_MODEL_ENV_KEYS = ["CLAUDE_MODEL", "ANTHROPIC_MODEL"];
2233
+ INHERIT_TIER_PRIORITY = ["MEDIUM", "HIGH", "LOW"];
2203
2234
  TIER_ENV_KEYS = {
2204
2235
  LOW: [
2205
2236
  "OMC_MODEL_LOW",
@@ -3192,6 +3223,7 @@ var init_definitions = __esm({
3192
3223
  init_utils();
3193
3224
  init_loader();
3194
3225
  init_strict_mode_guidance();
3226
+ init_models();
3195
3227
  init_architect();
3196
3228
  init_designer();
3197
3229
  init_writer();
@@ -3275,6 +3307,9 @@ var init_definitions = __esm({
3275
3307
 
3276
3308
  // src/features/delegation-enforcer.ts
3277
3309
  function normalizeToCcAlias(model) {
3310
+ if (isProviderSpecificModelId(model)) {
3311
+ return model;
3312
+ }
3278
3313
  const family = resolveClaudeFamily(model);
3279
3314
  return family ? CC_FAMILY_TO_ALIAS[family] ?? model : model;
3280
3315
  }
@@ -3451,6 +3486,9 @@ function resolveCliBinaryPath(binary) {
3451
3486
  resolvedPathCache.set(binary, resolvedPath);
3452
3487
  return resolvedPath;
3453
3488
  }
3489
+ function shouldUseClaudeBareMode(env = process.env) {
3490
+ return typeof env.ANTHROPIC_API_KEY === "string" && env.ANTHROPIC_API_KEY.trim().length > 0;
3491
+ }
3454
3492
  function getContract(agentType) {
3455
3493
  const contract = CONTRACTS[agentType];
3456
3494
  if (!contract) {
@@ -3560,6 +3598,9 @@ var init_model_contract = __esm({
3560
3598
  installInstructions: "Install Claude CLI: https://claude.ai/download",
3561
3599
  buildLaunchArgs(model, extraFlags = []) {
3562
3600
  const args = ["--dangerously-skip-permissions"];
3601
+ if (shouldUseClaudeBareMode() && !extraFlags.includes("--bare")) {
3602
+ args.push("--bare");
3603
+ }
3563
3604
  if (model) {
3564
3605
  const resolved = isProviderSpecificModelId(model) ? model : normalizeToCcAlias(model);
3565
3606
  args.push("--model", resolved);
@@ -3749,7 +3790,7 @@ function generateWorkerOverlay(params) {
3749
3790
  const shutdownAckPath = buildTeamStateInstructionPath(teamName, instructionStateRoot, "workers", workerName, "shutdown-ack.json");
3750
3791
  const claimTaskCommand = formatOmcCliInvocation(`team api claim-task --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"worker\\":\\"${workerName}\\"}" --json`);
3751
3792
  const sendAckCommand = formatOmcCliInvocation(`team api send-message --input "{\\"team_name\\":\\"${teamName}\\",\\"from_worker\\":\\"${workerName}\\",\\"to_worker\\":\\"leader-fixed\\",\\"body\\":\\"ACK: ${workerName} initialized\\"}" --json`);
3752
- const completeTaskCommand = formatOmcCliInvocation(`team api transition-task-status --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"from\\":\\"in_progress\\",\\"to\\":\\"completed\\",\\"claim_token\\":\\"<claim_token>\\"}" --json`);
3793
+ const completeTaskCommand = formatOmcCliInvocation(`team api transition-task-status --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"from\\":\\"in_progress\\",\\"to\\":\\"completed\\",\\"claim_token\\":\\"<claim_token>\\",\\"result\\":\\"Summary: <what changed>\\\\nVerification: <tests/checks run>\\\\nSubagent skip reason: worker protocol forbids nested subagents; completed focused probe in-session\\"}" --json`);
3753
3794
  const failTaskCommand = formatOmcCliInvocation(`team api transition-task-status --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"from\\":\\"in_progress\\",\\"to\\":\\"failed\\",\\"claim_token\\":\\"<claim_token>\\"}" --json`);
3754
3795
  const readTaskCommand = formatOmcCliInvocation(`team api read-task --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\"}" --json`);
3755
3796
  const releaseClaimCommand = formatOmcCliInvocation(`team api release-task-claim --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"claim_token\\":\\"<claim_token>\\",\\"worker\\":\\"${workerName}\\"}" --json`);
@@ -3802,6 +3843,11 @@ Use the CLI API for all task lifecycle operations. Do NOT directly edit task fil
3802
3843
  - Complete task: \`${completeTaskCommand}\`
3803
3844
  - Fail task: \`${failTaskCommand}\`
3804
3845
  - Release claim (rollback): \`${releaseClaimCommand}\`
3846
+ - Delegation compliance evidence (required for broad delegated tasks):
3847
+ - The completion command MUST include a \`result\` string with summary and verification evidence.
3848
+ - Because worker protocol forbids nested sub-agents, use: \`Subagent skip reason: <why in-session execution was safer/sufficient>\`
3849
+ - Only if the leader explicitly grants an exception to spawn nested help, use: \`Subagent spawn evidence: <count, child task names/thread ids, and integrated findings>\`
3850
+ - Completion is rejected with \`missing_delegation_compliance_evidence\` when required evidence is absent.
3805
3851
 
3806
3852
  ## Canonical Team State Root
3807
3853
  - Resolve the team state root in this order: \`OMC_TEAM_STATE_ROOT\` env -> worker identity \`team_state_root\` -> config/manifest \`team_state_root\` -> ${params.cwd}/.omcp/state/team/${teamName}.
@@ -5934,8 +5980,8 @@ async function startMergeOrchestrator(config) {
5934
5980
  let persisted = { lastShas: {} };
5935
5981
  if (existsSync18(persistedPath)) {
5936
5982
  try {
5937
- const { readFileSync: readFileSync12 } = await import("node:fs");
5938
- persisted = JSON.parse(readFileSync12(persistedPath, "utf-8"));
5983
+ const { readFileSync: readFileSync13 } = await import("node:fs");
5984
+ persisted = JSON.parse(readFileSync13(persistedPath, "utf-8"));
5939
5985
  } catch {
5940
5986
  persisted = { lastShas: {} };
5941
5987
  }
@@ -6324,8 +6370,8 @@ async function recoverFromRestart(config) {
6324
6370
  let persistedShasLoaded = 0;
6325
6371
  if (existsSync18(persistedPath)) {
6326
6372
  try {
6327
- const { readFileSync: readFileSync12 } = await import("node:fs");
6328
- const persisted = JSON.parse(readFileSync12(persistedPath, "utf-8"));
6373
+ const { readFileSync: readFileSync13 } = await import("node:fs");
6374
+ const persisted = JSON.parse(readFileSync13(persistedPath, "utf-8"));
6329
6375
  persistedShasLoaded = Object.keys(persisted.lastShas ?? {}).length;
6330
6376
  } catch {
6331
6377
  persistedShasLoaded = 0;
@@ -6538,7 +6584,7 @@ function buildV2TaskInstruction(teamName, workerName, task, taskId, cliOutputCon
6538
6584
  {}
6539
6585
  );
6540
6586
  const completeTaskCommand = formatOmcCliInvocation(
6541
- `team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "completed", claim_token: "<claim_token>" })}' --json`
6587
+ `team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "completed", claim_token: "<claim_token>", result: "Summary: <what changed>\\nVerification: <tests/checks run>\\nSubagent skip reason: worker protocol forbids nested subagents; completed focused probe in-session" })}' --json`
6542
6588
  );
6543
6589
  const failTaskCommand = formatOmcCliInvocation(
6544
6590
  `team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "failed", claim_token: "<claim_token>" })}' --json`
@@ -6553,6 +6599,7 @@ function buildV2TaskInstruction(teamName, workerName, task, taskId, cliOutputCon
6553
6599
  `2. Do the work described below.`,
6554
6600
  `3. On completion (use claim_token from step 1):`,
6555
6601
  ` ${completeTaskCommand}`,
6602
+ ` The result field is required for completion evidence. For broad delegated tasks, include either "Subagent skip reason: <why no nested worker was needed/allowed>" or, only when explicitly allowed by the leader, "Subagent spawn evidence: <child task names/thread ids and integrated findings>".`,
6556
6603
  `4. On failure (use claim_token from step 1):`,
6557
6604
  ` ${failTaskCommand}`,
6558
6605
  `5. ACK/progress replies are not a stop signal. Keep executing your assigned or next feasible work until the task is actually complete or failed, then transition and exit.`,
@@ -6916,6 +6963,7 @@ async function startTeamV2(config) {
6916
6963
  status: "pending",
6917
6964
  owner: null,
6918
6965
  result: null,
6966
+ ...config.tasks[i].delegation ? { delegation: config.tasks[i].delegation } : {},
6919
6967
  created_at: (/* @__PURE__ */ new Date()).toISOString()
6920
6968
  }, null, 2), "utf-8");
6921
6969
  }
@@ -7292,10 +7340,10 @@ async function requeueDeadWorkerTasks(teamName, deadWorkerNames, cwd) {
7292
7340
  await writeFile8(sidecarPath, JSON.stringify(sidecar, null, 2), "utf-8");
7293
7341
  const taskPath2 = absPath(cwd, TeamPaths.taskFile(sanitized, task.id));
7294
7342
  try {
7295
- const { readFileSync: readFileSync12, writeFileSync: writeFileSync4 } = await import("fs");
7343
+ const { readFileSync: readFileSync13, writeFileSync: writeFileSync4 } = await import("fs");
7296
7344
  const { withFileLockSync: withFileLockSync2 } = await Promise.resolve().then(() => (init_file_lock(), file_lock_exports));
7297
7345
  withFileLockSync2(taskPath2 + ".lock", () => {
7298
- const raw = readFileSync12(taskPath2, "utf-8");
7346
+ const raw = readFileSync13(taskPath2, "utf-8");
7299
7347
  const taskData = JSON.parse(raw);
7300
7348
  if (taskData.status === "in_progress") {
7301
7349
  taskData.status = "pending";
@@ -7325,7 +7373,7 @@ async function processCliWorkerVerdicts(teamName, cwd) {
7325
7373
  "team.runtime-v2.processCliWorkerVerdicts appendTeamEvent failed"
7326
7374
  );
7327
7375
  const { rename: rename3 } = await import("fs/promises");
7328
- const { readFileSync: readFileSync12, writeFileSync: writeFileSync4, existsSync: fsExistsSync } = await import("fs");
7376
+ const { readFileSync: readFileSync13, writeFileSync: writeFileSync4, existsSync: fsExistsSync } = await import("fs");
7329
7377
  const { withFileLockSync: withFileLockSync2 } = await Promise.resolve().then(() => (init_file_lock(), file_lock_exports));
7330
7378
  for (const worker of config.workers) {
7331
7379
  const outputFile = worker.output_file;
@@ -7359,7 +7407,7 @@ async function processCliWorkerVerdicts(teamName, cwd) {
7359
7407
  const taskPath2 = absPath(cwd, TeamPaths.taskFile(sanitized, taskId));
7360
7408
  if (!fsExistsSync(taskPath2)) continue;
7361
7409
  try {
7362
- const taskRaw = readFileSync12(taskPath2, "utf-8");
7410
+ const taskRaw = readFileSync13(taskPath2, "utf-8");
7363
7411
  const taskData = JSON.parse(taskRaw);
7364
7412
  if (taskData.owner === worker.name && taskData.status === "in_progress") {
7365
7413
  targetTaskId = taskId;
@@ -7387,7 +7435,7 @@ async function processCliWorkerVerdicts(teamName, cwd) {
7387
7435
  let transitionOk = false;
7388
7436
  try {
7389
7437
  withFileLockSync2(targetTaskPath + ".lock", () => {
7390
- const raw = readFileSync12(targetTaskPath, "utf-8");
7438
+ const raw = readFileSync13(targetTaskPath, "utf-8");
7391
7439
  const taskData = JSON.parse(raw);
7392
7440
  if (taskData.status !== "in_progress" || taskData.owner !== worker.name) {
7393
7441
  return;
@@ -7936,10 +7984,10 @@ var init_runtime_v2 = __esm({
7936
7984
  // src/cli/team.ts
7937
7985
  import { randomUUID as randomUUID6 } from "crypto";
7938
7986
  import { spawn } from "child_process";
7939
- import { existsSync as existsSync21, mkdirSync as mkdirSync3, readFileSync as readFileSync11, writeFileSync as writeFileSync3 } from "fs";
7987
+ import { existsSync as existsSync22, mkdirSync as mkdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "fs";
7940
7988
  import { readFile as readFile11, readdir as readdir3, rm as rm5 } from "fs/promises";
7941
7989
  import { homedir as homedir3 } from "os";
7942
- import { dirname as dirname16, join as join24 } from "path";
7990
+ import { dirname as dirname16, join as join25 } from "path";
7943
7991
  import { fileURLToPath as fileURLToPath3 } from "url";
7944
7992
 
7945
7993
  // src/team/api-interop.ts
@@ -8010,7 +8058,31 @@ async function claimTask(taskId, workerName, expectedVersion, deps) {
8010
8058
  if (!lock.ok) return { ok: false, error: "claim_conflict" };
8011
8059
  return lock.value;
8012
8060
  }
8013
- async function transitionTaskStatus(taskId, from, to, claimToken, deps) {
8061
+ function extractDelegationComplianceEvidence(task, terminalData) {
8062
+ const plan = task.delegation;
8063
+ if (!plan || plan.mode === "none") return null;
8064
+ if (plan.mode === "optional" && plan.required_parallel_probe !== true) return null;
8065
+ const result = typeof terminalData?.result === "string" ? terminalData.result : "";
8066
+ const spawnMatch = result.match(/^\s*Subagent spawn evidence:\s*(.+)$/im);
8067
+ if (spawnMatch?.[1]?.trim()) {
8068
+ const detail = spawnMatch[1].trim();
8069
+ if (!/^none\b|^0\b/i.test(detail)) {
8070
+ return { status: "spawned", source: "terminal_result", detail, recorded_at: (/* @__PURE__ */ new Date()).toISOString() };
8071
+ }
8072
+ }
8073
+ if (plan.skip_allowed_reason_required === true) {
8074
+ const skipMatch = result.match(/^\s*Subagent skip reason:\s*(.+)$/im);
8075
+ if (skipMatch?.[1]?.trim()) {
8076
+ return { status: "skipped", source: "terminal_result", detail: skipMatch[1].trim(), recorded_at: (/* @__PURE__ */ new Date()).toISOString() };
8077
+ }
8078
+ }
8079
+ return null;
8080
+ }
8081
+ function requiresDelegationComplianceEvidence(task) {
8082
+ const plan = task.delegation;
8083
+ return !!plan && (plan.mode === "auto" || plan.mode === "required" || plan.required_parallel_probe === true);
8084
+ }
8085
+ async function transitionTaskStatus(taskId, from, to, claimToken, terminalData, deps) {
8014
8086
  if (!deps.canTransitionTaskStatus(from, to)) return { ok: false, error: "invalid_transition" };
8015
8087
  const lock = await deps.withTaskClaimLock(deps.teamName, taskId, deps.cwd, async () => {
8016
8088
  const current = await deps.readTask(deps.teamName, taskId, deps.cwd);
@@ -8023,10 +8095,19 @@ async function transitionTaskStatus(taskId, from, to, claimToken, deps) {
8023
8095
  return { ok: false, error: "claim_conflict" };
8024
8096
  }
8025
8097
  if (new Date(v.claim.leased_until) <= /* @__PURE__ */ new Date()) return { ok: false, error: "lease_expired" };
8098
+ const normalizedResult = typeof terminalData?.result === "string" ? terminalData.result : void 0;
8099
+ const normalizedError = typeof terminalData?.error === "string" ? terminalData.error : void 0;
8100
+ const delegationCompliance = to === "completed" ? extractDelegationComplianceEvidence(v, terminalData) : null;
8101
+ if (to === "completed" && requiresDelegationComplianceEvidence(v) && !delegationCompliance) {
8102
+ return { ok: false, error: "missing_delegation_compliance_evidence" };
8103
+ }
8026
8104
  const updated = {
8027
8105
  ...v,
8028
8106
  status: to,
8029
8107
  completed_at: to === "completed" ? (/* @__PURE__ */ new Date()).toISOString() : v.completed_at,
8108
+ result: to === "completed" ? normalizedResult : void 0,
8109
+ error: to === "failed" ? normalizedError : void 0,
8110
+ delegation_compliance: to === "completed" ? delegationCompliance ?? v.delegation_compliance : v.delegation_compliance,
8030
8111
  claim: void 0,
8031
8112
  version: v.version + 1
8032
8113
  };
@@ -8386,8 +8467,8 @@ async function teamClaimTask(teamName, taskId, workerName, expectedVersion, cwd)
8386
8467
  writeAtomic
8387
8468
  });
8388
8469
  }
8389
- async function teamTransitionTaskStatus(teamName, taskId, from, to, claimToken, cwd) {
8390
- return transitionTaskStatus(taskId, from, to, claimToken, {
8470
+ async function teamTransitionTaskStatus(teamName, taskId, from, to, claimToken, cwd, terminalData) {
8471
+ return transitionTaskStatus(taskId, from, to, claimToken, terminalData, {
8391
8472
  teamName,
8392
8473
  cwd,
8393
8474
  readTask: teamReadTask,
@@ -8880,7 +8961,7 @@ async function resumeTeam(teamName, cwd) {
8880
8961
  init_runtime_v2();
8881
8962
  init_git_worktree();
8882
8963
  init_swallowed_error();
8883
- var TEAM_UPDATE_TASK_MUTABLE_FIELDS = /* @__PURE__ */ new Set(["subject", "description", "blocked_by", "requires_code_change"]);
8964
+ var TEAM_UPDATE_TASK_MUTABLE_FIELDS = /* @__PURE__ */ new Set(["subject", "description", "blocked_by", "requires_code_change", "delegation"]);
8884
8965
  var TEAM_UPDATE_TASK_REQUEST_FIELDS = /* @__PURE__ */ new Set(["team_name", "task_id", "workingDirectory", ...TEAM_UPDATE_TASK_MUTABLE_FIELDS]);
8885
8966
  var TEAM_API_OPERATIONS = [
8886
8967
  "send-message",
@@ -8933,6 +9014,60 @@ function parseValidatedTaskIdArray(value, fieldName) {
8933
9014
  }
8934
9015
  return taskIds;
8935
9016
  }
9017
+ function parseTaskDelegationPlan(value) {
9018
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
9019
+ throw new Error("delegation must be an object");
9020
+ }
9021
+ const raw = value;
9022
+ const mode = raw.mode;
9023
+ if (mode !== "none" && mode !== "optional" && mode !== "auto" && mode !== "required") {
9024
+ throw new Error("delegation.mode must be one of: none, optional, auto, required");
9025
+ }
9026
+ const plan = { mode };
9027
+ if ("max_parallel_subtasks" in raw) {
9028
+ if (!isFiniteInteger(raw.max_parallel_subtasks) || raw.max_parallel_subtasks < 1) {
9029
+ throw new Error("delegation.max_parallel_subtasks must be a positive integer when provided");
9030
+ }
9031
+ plan.max_parallel_subtasks = raw.max_parallel_subtasks;
9032
+ }
9033
+ if ("required_parallel_probe" in raw) {
9034
+ if (typeof raw.required_parallel_probe !== "boolean") throw new Error("delegation.required_parallel_probe must be a boolean when provided");
9035
+ plan.required_parallel_probe = raw.required_parallel_probe;
9036
+ }
9037
+ if ("spawn_before_serial_search_threshold" in raw) {
9038
+ if (!isFiniteInteger(raw.spawn_before_serial_search_threshold) || raw.spawn_before_serial_search_threshold < 1) {
9039
+ throw new Error("delegation.spawn_before_serial_search_threshold must be a positive integer when provided");
9040
+ }
9041
+ plan.spawn_before_serial_search_threshold = raw.spawn_before_serial_search_threshold;
9042
+ }
9043
+ if ("child_model_policy" in raw) {
9044
+ const policy = raw.child_model_policy;
9045
+ if (policy !== "standard" && policy !== "fast" && policy !== "inherit" && policy !== "frontier") {
9046
+ throw new Error("delegation.child_model_policy must be one of: standard, fast, inherit, frontier");
9047
+ }
9048
+ plan.child_model_policy = policy;
9049
+ }
9050
+ if ("child_model" in raw) {
9051
+ if (typeof raw.child_model !== "string") throw new Error("delegation.child_model must be a string when provided");
9052
+ plan.child_model = raw.child_model;
9053
+ }
9054
+ if ("subtask_candidates" in raw) {
9055
+ if (!Array.isArray(raw.subtask_candidates) || !raw.subtask_candidates.every((item) => typeof item === "string")) {
9056
+ throw new Error("delegation.subtask_candidates must be an array of strings when provided");
9057
+ }
9058
+ plan.subtask_candidates = raw.subtask_candidates;
9059
+ }
9060
+ if ("child_report_format" in raw) {
9061
+ const format = raw.child_report_format;
9062
+ if (format !== "bullets" && format !== "json") throw new Error("delegation.child_report_format must be bullets or json when provided");
9063
+ plan.child_report_format = format;
9064
+ }
9065
+ if ("skip_allowed_reason_required" in raw) {
9066
+ if (typeof raw.skip_allowed_reason_required !== "boolean") throw new Error("delegation.skip_allowed_reason_required must be a boolean when provided");
9067
+ plan.skip_allowed_reason_required = raw.skip_allowed_reason_required;
9068
+ }
9069
+ return plan;
9070
+ }
8936
9071
  function teamStateExists(teamName, candidateCwd) {
8937
9072
  if (!TEAM_NAME_SAFE_PATTERN.test(teamName)) return false;
8938
9073
  const teamRoot = join23(candidateCwd, ".omcp", "state", "team", teamName);
@@ -9304,13 +9439,22 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
9304
9439
  const owner = args.owner;
9305
9440
  const blockedBy = args.blocked_by;
9306
9441
  const requiresCodeChange = args.requires_code_change;
9442
+ let delegation;
9443
+ if ("delegation" in args) {
9444
+ try {
9445
+ delegation = parseTaskDelegationPlan(args.delegation);
9446
+ } catch (error) {
9447
+ return { ok: false, operation, error: { code: "invalid_input", message: error.message } };
9448
+ }
9449
+ }
9307
9450
  const task = await teamCreateTask(teamName, {
9308
9451
  subject,
9309
9452
  description,
9310
9453
  status: "pending",
9311
9454
  owner: owner || void 0,
9312
9455
  blocked_by: blockedBy,
9313
- requires_code_change: requiresCodeChange
9456
+ requires_code_change: requiresCodeChange,
9457
+ ...delegation ? { delegation } : {}
9314
9458
  }, cwd);
9315
9459
  return { ok: true, operation, data: { task } };
9316
9460
  }
@@ -9372,6 +9516,13 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
9372
9516
  return { ok: false, operation, error: { code: "invalid_input", message: error.message } };
9373
9517
  }
9374
9518
  }
9519
+ if ("delegation" in args) {
9520
+ try {
9521
+ updates.delegation = parseTaskDelegationPlan(args.delegation);
9522
+ } catch (error) {
9523
+ return { ok: false, operation, error: { code: "invalid_input", message: error.message } };
9524
+ }
9525
+ }
9375
9526
  const task = await teamUpdateTask(teamName, taskId, updates, cwd);
9376
9527
  return task ? { ok: true, operation, data: { task } } : { ok: false, operation, error: { code: "task_not_found", message: "task_not_found" } };
9377
9528
  }
@@ -9395,6 +9546,8 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
9395
9546
  const from = String(args.from || "").trim();
9396
9547
  const to = String(args.to || "").trim();
9397
9548
  const claimToken = String(args.claim_token || "").trim();
9549
+ const transitionResult = args.result;
9550
+ const transitionError = args.error;
9398
9551
  if (!teamName || !taskId || !from || !to || !claimToken) {
9399
9552
  return { ok: false, operation, error: { code: "invalid_input", message: "team_name, task_id, from, to, claim_token are required" } };
9400
9553
  }
@@ -9402,7 +9555,24 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
9402
9555
  if (!allowed.has(from) || !allowed.has(to)) {
9403
9556
  return { ok: false, operation, error: { code: "invalid_input", message: "from and to must be valid task statuses" } };
9404
9557
  }
9405
- const result = await teamTransitionTaskStatus(teamName, taskId, from, to, claimToken, cwd);
9558
+ if (transitionResult !== void 0 && typeof transitionResult !== "string") {
9559
+ return { ok: false, operation, error: { code: "invalid_input", message: "result must be a string when provided" } };
9560
+ }
9561
+ if (transitionError !== void 0 && typeof transitionError !== "string") {
9562
+ return { ok: false, operation, error: { code: "invalid_input", message: "error must be a string when provided" } };
9563
+ }
9564
+ const result = await teamTransitionTaskStatus(
9565
+ teamName,
9566
+ taskId,
9567
+ from,
9568
+ to,
9569
+ claimToken,
9570
+ cwd,
9571
+ {
9572
+ result: typeof transitionResult === "string" ? transitionResult : void 0,
9573
+ error: typeof transitionError === "string" ? transitionError : void 0
9574
+ }
9575
+ );
9406
9576
  return { ok: true, operation, data: result };
9407
9577
  }
9408
9578
  case "release-task-claim": {
@@ -9625,6 +9795,278 @@ init_tmux_session();
9625
9795
  init_team_name();
9626
9796
  init_monitor();
9627
9797
  init_process_utils();
9798
+
9799
+ // src/planning/artifacts.ts
9800
+ import { readdirSync as readdirSync5, readFileSync as readFileSync11, existsSync as existsSync21 } from "fs";
9801
+ import { join as join24 } from "path";
9802
+
9803
+ // src/planning/artifact-names.ts
9804
+ import { basename as basename7 } from "path";
9805
+ var PLANNING_ARTIFACT_TIMESTAMP_PATTERN = /^\d{8}T\d{6}Z$/;
9806
+ function legacyTestSpecSlug(fileNameOrPath) {
9807
+ const match = basename7(fileNameOrPath).match(/^test-?spec-(?<slug>.+)\.md$/i);
9808
+ return match?.groups?.slug ?? null;
9809
+ }
9810
+ function requiredTimestampedTestSpecFileName(prdArtifact) {
9811
+ return prdArtifact.kind === "prd" && prdArtifact.timestamp ? `test-spec-${prdArtifact.timestamp}-${prdArtifact.slug}.md` : null;
9812
+ }
9813
+ function splitTimestampPrefix(rawSlug) {
9814
+ const separatorIndex = rawSlug.indexOf("-");
9815
+ if (separatorIndex === -1) {
9816
+ return { slug: rawSlug };
9817
+ }
9818
+ const prefix = rawSlug.slice(0, separatorIndex);
9819
+ if (!PLANNING_ARTIFACT_TIMESTAMP_PATTERN.test(prefix)) {
9820
+ return { slug: rawSlug };
9821
+ }
9822
+ return {
9823
+ timestamp: prefix,
9824
+ slug: rawSlug.slice(separatorIndex + 1)
9825
+ };
9826
+ }
9827
+ function parsePlanningArtifactFileName(fileNameOrPath) {
9828
+ const fileName = basename7(fileNameOrPath);
9829
+ const autoresearchDeepInterviewMatch = fileName.match(
9830
+ /^deep-interview-autoresearch-(?<slug>.+)\.md$/i
9831
+ );
9832
+ if (autoresearchDeepInterviewMatch?.groups?.slug) {
9833
+ const parsedSlug = splitTimestampPrefix(autoresearchDeepInterviewMatch.groups.slug);
9834
+ if (!parsedSlug.slug) return null;
9835
+ return {
9836
+ kind: "deep-interview-autoresearch",
9837
+ ...parsedSlug
9838
+ };
9839
+ }
9840
+ const deepInterviewMatch = fileName.match(/^deep-interview-(?<slug>.+)\.md$/i);
9841
+ if (deepInterviewMatch?.groups?.slug) {
9842
+ const parsedSlug = splitTimestampPrefix(deepInterviewMatch.groups.slug);
9843
+ if (!parsedSlug.slug) return null;
9844
+ return {
9845
+ kind: "deep-interview",
9846
+ ...parsedSlug
9847
+ };
9848
+ }
9849
+ const prdMatch = fileName.match(/^prd-(?<slug>.+)\.md$/i);
9850
+ if (prdMatch?.groups?.slug) {
9851
+ const parsedSlug = splitTimestampPrefix(prdMatch.groups.slug);
9852
+ if (!parsedSlug.slug) return null;
9853
+ return {
9854
+ kind: "prd",
9855
+ ...parsedSlug
9856
+ };
9857
+ }
9858
+ const testSpecMatch = fileName.match(/^test-?spec-(?<slug>.+)\.md$/i);
9859
+ if (testSpecMatch?.groups?.slug) {
9860
+ const parsedSlug = splitTimestampPrefix(testSpecMatch.groups.slug);
9861
+ if (!parsedSlug.slug) return null;
9862
+ return {
9863
+ kind: "test-spec",
9864
+ ...parsedSlug
9865
+ };
9866
+ }
9867
+ return null;
9868
+ }
9869
+ function comparePlanningArtifactPaths(left, right) {
9870
+ const leftParsed = parsePlanningArtifactFileName(left);
9871
+ const rightParsed = parsePlanningArtifactFileName(right);
9872
+ if (leftParsed?.timestamp && rightParsed?.timestamp && leftParsed.timestamp !== rightParsed.timestamp) {
9873
+ return leftParsed.timestamp.localeCompare(rightParsed.timestamp);
9874
+ }
9875
+ if (leftParsed?.timestamp && !rightParsed?.timestamp) {
9876
+ return 1;
9877
+ }
9878
+ if (!leftParsed?.timestamp && rightParsed?.timestamp) {
9879
+ return -1;
9880
+ }
9881
+ return left.localeCompare(right);
9882
+ }
9883
+ function selectMatchingTestSpecsForPrd(prdPath, testSpecPaths) {
9884
+ if (!prdPath) {
9885
+ return [];
9886
+ }
9887
+ const prdArtifact = parsePlanningArtifactFileName(prdPath);
9888
+ if (prdArtifact?.kind !== "prd") {
9889
+ return [];
9890
+ }
9891
+ const requiredTimestampedFileName = requiredTimestampedTestSpecFileName(prdArtifact);
9892
+ return (requiredTimestampedFileName ? testSpecPaths.filter((path4) => basename7(path4) === requiredTimestampedFileName) : testSpecPaths.filter((path4) => legacyTestSpecSlug(path4) === prdArtifact.slug)).sort(comparePlanningArtifactPaths);
9893
+ }
9894
+ function selectLatestPlanningArtifactPath(paths) {
9895
+ return [...paths].sort(comparePlanningArtifactPaths).at(-1) ?? null;
9896
+ }
9897
+
9898
+ // src/planning/artifacts.ts
9899
+ function readFileSafe(path4) {
9900
+ try {
9901
+ return readFileSync11(path4, "utf-8");
9902
+ } catch {
9903
+ return null;
9904
+ }
9905
+ }
9906
+ function escapeRegex(value) {
9907
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
9908
+ }
9909
+ function getSectionContent(markdown, heading) {
9910
+ const headingRe = new RegExp(
9911
+ `^##\\s+${escapeRegex(heading)}[ \\t]*$`,
9912
+ "im"
9913
+ );
9914
+ const headingMatch = headingRe.exec(markdown);
9915
+ if (!headingMatch || headingMatch.index === void 0) return null;
9916
+ const bodyStart = headingMatch.index + headingMatch[0].length;
9917
+ const rest = markdown.slice(bodyStart).replace(/^\r?\n/, "");
9918
+ const nextHeadingMatch = /\r?\n##\s+/.exec(rest);
9919
+ const body = (nextHeadingMatch ? rest.slice(0, nextHeadingMatch.index) : rest).trim();
9920
+ return body.length > 0 ? body : null;
9921
+ }
9922
+ function hasRequiredSections(markdown, headings) {
9923
+ return headings.every(
9924
+ (heading) => getSectionContent(markdown, heading) !== null
9925
+ );
9926
+ }
9927
+ function getPlansDirCandidates(cwd) {
9928
+ return [join24(cwd, ".omc", "plans"), join24(cwd, ".omx", "plans")];
9929
+ }
9930
+ function sortArtifactPathsDescending(paths) {
9931
+ return [...paths].sort((a, b) => comparePlanningArtifactPaths(b, a));
9932
+ }
9933
+ function hasCompletePlanningPair(prdPath, matchingTestSpecPaths) {
9934
+ if (matchingTestSpecPaths.length === 0) {
9935
+ return false;
9936
+ }
9937
+ const prd = readFileSafe(prdPath);
9938
+ const testSpec = readFileSafe(matchingTestSpecPaths[0]);
9939
+ if (!prd || !testSpec) {
9940
+ return false;
9941
+ }
9942
+ return hasRequiredSections(prd, [
9943
+ "Acceptance criteria",
9944
+ "Requirement coverage map"
9945
+ ]) && hasRequiredSections(testSpec, [
9946
+ "Unit coverage",
9947
+ "Verification mapping"
9948
+ ]);
9949
+ }
9950
+ function readPlanningArtifacts(cwd) {
9951
+ let entries;
9952
+ const prdPaths = [];
9953
+ const testSpecPaths = [];
9954
+ for (const plansDir of getPlansDirCandidates(cwd)) {
9955
+ if (!existsSync21(plansDir)) {
9956
+ continue;
9957
+ }
9958
+ try {
9959
+ entries = readdirSync5(plansDir);
9960
+ } catch {
9961
+ continue;
9962
+ }
9963
+ for (const entry of entries) {
9964
+ if (entry.startsWith("prd-") && entry.endsWith(".md")) {
9965
+ prdPaths.push(join24(plansDir, entry));
9966
+ } else if (entry.startsWith("test-spec-") && entry.endsWith(".md")) {
9967
+ testSpecPaths.push(join24(plansDir, entry));
9968
+ }
9969
+ }
9970
+ }
9971
+ return {
9972
+ prdPaths: sortArtifactPathsDescending(prdPaths),
9973
+ testSpecPaths: sortArtifactPathsDescending(testSpecPaths)
9974
+ };
9975
+ }
9976
+ function decodeQuotedValue(raw) {
9977
+ const normalized = raw.trim();
9978
+ if (!normalized) return null;
9979
+ try {
9980
+ return JSON.parse(normalized);
9981
+ } catch {
9982
+ if (normalized.startsWith('"') && normalized.endsWith('"') || normalized.startsWith("'") && normalized.endsWith("'")) {
9983
+ return normalized.slice(1, -1);
9984
+ }
9985
+ return null;
9986
+ }
9987
+ }
9988
+ function launchHintPattern(mode) {
9989
+ return mode === "team" ? /(?<command>(?:om[cx]\s+team|\$team)(?:\s+ralph)?(?:\s+(?<count>\d+)(?::(?<role>[a-z][a-z0-9-]*))?)?\s+(?<task>"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')(?<flags>(?:\s+--[\w-]+)*))/gi : /(?<command>(?:om[cx]\s+ralph|\$ralph)\s+(?<task>"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')(?<flags>(?:\s+--[\w-]+)*))/gi;
9990
+ }
9991
+ function collectLaunchHintMatches(content, mode) {
9992
+ return [...content.matchAll(launchHintPattern(mode))];
9993
+ }
9994
+ function selectLaunchHintMatch(matches, normalizedTask, normalizedCommand) {
9995
+ const decodedMatches = matches.flatMap((match) => {
9996
+ const command = match[0]?.trim();
9997
+ const task = match.groups?.task ? decodeQuotedValue(match.groups.task) : null;
9998
+ if (!command || task == null) return [];
9999
+ const flags = match.groups?.flags ?? "";
10000
+ const workerCount = match.groups?.count ? Number.parseInt(match.groups.count, 10) : void 0;
10001
+ return [{
10002
+ command,
10003
+ task,
10004
+ ...workerCount == null ? {} : { workerCount },
10005
+ agentType: match.groups?.role || void 0,
10006
+ linkedRalph: /\sralph(?:\s|$)/.test(command) || parseFlags(flags).linkedRalph
10007
+ }];
10008
+ });
10009
+ const matchesToConsider = normalizedCommand ? decodedMatches.filter((match) => match.command === normalizedCommand) : normalizedTask ? decodedMatches.filter((match) => match.task.trim() === normalizedTask) : decodedMatches;
10010
+ if (matchesToConsider.length === 0) return { status: "no-match" };
10011
+ if (matchesToConsider.length > 1) return { status: "ambiguous" };
10012
+ return { status: "unique", ...matchesToConsider[0] };
10013
+ }
10014
+ function parseFlags(flagStr) {
10015
+ return {
10016
+ linkedRalph: /--linked-ralph/.test(flagStr)
10017
+ };
10018
+ }
10019
+ function readApprovedExecutionLaunchHintOutcome(cwd, mode, options = {}) {
10020
+ const artifacts = readPlanningArtifacts(cwd);
10021
+ if (artifacts.prdPaths.length === 0) return { status: "absent" };
10022
+ const prdPath = options.prdPath ? artifacts.prdPaths.includes(options.prdPath) ? options.prdPath : null : selectLatestPlanningArtifactPath(artifacts.prdPaths);
10023
+ const matchingTestSpecs = selectMatchingTestSpecsForPrd(
10024
+ prdPath,
10025
+ artifacts.testSpecPaths
10026
+ );
10027
+ if (!prdPath) return { status: "absent" };
10028
+ if (artifacts.testSpecPaths.length > 0 && matchingTestSpecs.length === 0) {
10029
+ return { status: "absent" };
10030
+ }
10031
+ const content = readFileSafe(prdPath);
10032
+ if (!content) return { status: "absent" };
10033
+ const selected = selectLaunchHintMatch(
10034
+ collectLaunchHintMatches(content, mode),
10035
+ options.task?.trim(),
10036
+ options.command?.trim()
10037
+ );
10038
+ if (selected.status === "ambiguous") return { status: "ambiguous" };
10039
+ if (selected.status !== "unique") return { status: "absent" };
10040
+ if (options.requirePlanningComplete && !hasCompletePlanningPair(prdPath, matchingTestSpecs)) {
10041
+ return { status: "incomplete" };
10042
+ }
10043
+ if (mode === "team") {
10044
+ return {
10045
+ status: "resolved",
10046
+ hint: {
10047
+ mode: "team",
10048
+ command: selected.command,
10049
+ task: selected.task,
10050
+ workerCount: selected.workerCount,
10051
+ agentType: selected.agentType,
10052
+ linkedRalph: selected.linkedRalph,
10053
+ sourcePath: prdPath
10054
+ }
10055
+ };
10056
+ }
10057
+ return {
10058
+ status: "resolved",
10059
+ hint: {
10060
+ mode: "ralph",
10061
+ command: selected.command,
10062
+ task: selected.task,
10063
+ linkedRalph: selected.linkedRalph,
10064
+ sourcePath: prdPath
10065
+ }
10066
+ };
10067
+ }
10068
+
10069
+ // src/cli/team.ts
9628
10070
  var JOB_ID_PATTERN = /^omc-[a-z0-9]{1,16}$/;
9629
10071
  var VALID_CLI_AGENT_TYPES = /* @__PURE__ */ new Set(["claude", "copilot", "codex", "gemini", "cursor"]);
9630
10072
  var SUBCOMMANDS = /* @__PURE__ */ new Set(["start", "status", "wait", "cleanup", "resume", "shutdown", "api", "help", "--help", "-h"]);
@@ -9658,31 +10100,31 @@ function assertTeamSpawnAllowed(env = process.env) {
9658
10100
  );
9659
10101
  }
9660
10102
  function resolveJobsDir(env = process.env) {
9661
- return env.OMC_JOBS_DIR || join24(homedir3(), ".omcp", "team-jobs");
10103
+ return env.OMC_JOBS_DIR || join25(homedir3(), ".omcp", "team-jobs");
9662
10104
  }
9663
10105
  function resolveRuntimeCliPath(env = process.env) {
9664
10106
  if (env.OMC_RUNTIME_CLI_PATH) {
9665
10107
  return env.OMC_RUNTIME_CLI_PATH;
9666
10108
  }
9667
10109
  const moduleDir = dirname16(fileURLToPath3(import.meta.url));
9668
- return join24(moduleDir, "../../bridge/runtime-cli.cjs");
10110
+ return join25(moduleDir, "../../bridge/runtime-cli.cjs");
9669
10111
  }
9670
10112
  function ensureJobsDir(jobsDir) {
9671
- if (!existsSync21(jobsDir)) {
10113
+ if (!existsSync22(jobsDir)) {
9672
10114
  mkdirSync3(jobsDir, { recursive: true });
9673
10115
  }
9674
10116
  }
9675
10117
  function jobPath(jobsDir, jobId) {
9676
- return join24(jobsDir, `${jobId}.json`);
10118
+ return join25(jobsDir, `${jobId}.json`);
9677
10119
  }
9678
10120
  function resultArtifactPath(jobsDir, jobId) {
9679
- return join24(jobsDir, `${jobId}-result.json`);
10121
+ return join25(jobsDir, `${jobId}-result.json`);
9680
10122
  }
9681
10123
  function panesArtifactPath(jobsDir, jobId) {
9682
- return join24(jobsDir, `${jobId}-panes.json`);
10124
+ return join25(jobsDir, `${jobId}-panes.json`);
9683
10125
  }
9684
10126
  function teamStateRoot2(cwd, teamName) {
9685
- return join24(cwd, ".omcp", "state", "team", teamName);
10127
+ return join25(cwd, ".omcp", "state", "team", teamName);
9686
10128
  }
9687
10129
  function validateJobId(jobId) {
9688
10130
  if (!JOB_ID_PATTERN.test(jobId)) {
@@ -9722,7 +10164,7 @@ async function resolveCleanupPaneEvidence(job, jobsDir, jobId) {
9722
10164
  }
9723
10165
  function readJobFromDisk(jobId, jobsDir) {
9724
10166
  try {
9725
- const content = readFileSync11(jobPath(jobsDir, jobId), "utf-8");
10167
+ const content = readFileSync12(jobPath(jobsDir, jobId), "utf-8");
9726
10168
  return parseJsonSafe(content);
9727
10169
  } catch {
9728
10170
  return null;
@@ -9751,7 +10193,7 @@ function generateJobId(now = Date.now()) {
9751
10193
  }
9752
10194
  function convergeWithResultArtifact(jobId, job, jobsDir) {
9753
10195
  try {
9754
- const artifactRaw = readFileSync11(resultArtifactPath(jobsDir, jobId), "utf-8");
10196
+ const artifactRaw = readFileSync12(resultArtifactPath(jobsDir, jobId), "utf-8");
9755
10197
  const artifactParsed = parseJsonSafe(artifactRaw);
9756
10198
  if (artifactParsed?.status === "completed" || artifactParsed?.status === "failed") {
9757
10199
  return {
@@ -10495,9 +10937,9 @@ function parseLegacyStartAlias(args) {
10495
10937
  if (!spec) return null;
10496
10938
  const match = spec.match(/^(\d+):([a-zA-Z0-9_-]+)(?::([a-zA-Z0-9_-]+))?$/);
10497
10939
  if (!match) return null;
10498
- const workerCount = toInt(match[1], "worker-count");
10940
+ let workerCount = toInt(match[1], "worker-count");
10499
10941
  if (workerCount < 1) throw new Error("worker-count must be >= 1");
10500
- const agentType = normalizeAgentType(match[2]);
10942
+ let agentType = normalizeAgentType(match[2]);
10501
10943
  const role = match[3] || void 0;
10502
10944
  index += 1;
10503
10945
  let json = false;
@@ -10532,8 +10974,35 @@ function parseLegacyStartAlias(args) {
10532
10974
  }
10533
10975
  taskParts.push(token);
10534
10976
  }
10535
- const task = taskParts.join(" ").trim();
10977
+ let task = taskParts.join(" ").trim();
10536
10978
  if (!task) throw new Error("Legacy start alias requires a task string");
10979
+ const shortFollowup = ["team", "/team", "team please", "run team", "start team"].includes(task.toLowerCase());
10980
+ if (shortFollowup) {
10981
+ const approvedHintOutcome = readApprovedExecutionLaunchHintOutcome(cwd, "team", {
10982
+ requirePlanningComplete: true
10983
+ });
10984
+ if (approvedHintOutcome.status === "ambiguous") {
10985
+ throw new Error("approved_execution_hint_ambiguous:team");
10986
+ }
10987
+ if (approvedHintOutcome.status === "incomplete") {
10988
+ throw new Error("approved_execution_hint_incomplete:team");
10989
+ }
10990
+ if (approvedHintOutcome.status === "resolved") {
10991
+ task = approvedHintOutcome.hint.task;
10992
+ workerCount = approvedHintOutcome.hint.workerCount ?? workerCount;
10993
+ agentType = approvedHintOutcome.hint.agentType ? normalizeAgentType(approvedHintOutcome.hint.agentType) : agentType;
10994
+ ralph = approvedHintOutcome.hint.linkedRalph === true ? true : ralph;
10995
+ }
10996
+ } else {
10997
+ const command = `omc team ${ralph ? "ralph " : ""}${spec} ${JSON.stringify(task)}`;
10998
+ const approvedHintOutcome = readApprovedExecutionLaunchHintOutcome(cwd, "team", {
10999
+ task,
11000
+ command
11001
+ });
11002
+ if (approvedHintOutcome.status === "ambiguous") {
11003
+ throw new Error("approved_execution_hint_ambiguous:team");
11004
+ }
11005
+ }
10537
11006
  return {
10538
11007
  workerCount,
10539
11008
  agentType,