oh-my-copilot 4.13.21 → 4.13.54

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 (219) 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 +2231 -1222
  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__/auto-slash-aliases.test.js +6 -6
  13. package/dist/__tests__/auto-slash-aliases.test.js.map +1 -1
  14. package/dist/__tests__/auto-update.test.js +132 -2
  15. package/dist/__tests__/auto-update.test.js.map +1 -1
  16. package/dist/__tests__/bedrock-model-routing.test.js +18 -17
  17. package/dist/__tests__/bedrock-model-routing.test.js.map +1 -1
  18. package/dist/__tests__/delegation-enforcer.test.js +46 -6
  19. package/dist/__tests__/delegation-enforcer.test.js.map +1 -1
  20. package/dist/__tests__/hooks/session-end-cleanup.test.d.ts +2 -0
  21. package/dist/__tests__/hooks/session-end-cleanup.test.d.ts.map +1 -0
  22. package/dist/__tests__/hooks/session-end-cleanup.test.js +93 -0
  23. package/dist/__tests__/hooks/session-end-cleanup.test.js.map +1 -0
  24. package/dist/__tests__/hud/stdin.test.d.ts +2 -0
  25. package/dist/__tests__/hud/stdin.test.d.ts.map +1 -0
  26. package/dist/__tests__/hud/stdin.test.js +556 -0
  27. package/dist/__tests__/hud/stdin.test.js.map +1 -0
  28. package/dist/__tests__/model-routing-override-three-site-sync.test.d.ts +2 -0
  29. package/dist/__tests__/model-routing-override-three-site-sync.test.d.ts.map +1 -0
  30. package/dist/__tests__/model-routing-override-three-site-sync.test.js +75 -0
  31. package/dist/__tests__/model-routing-override-three-site-sync.test.js.map +1 -0
  32. package/dist/__tests__/non-claude-provider-detection.test.js +37 -0
  33. package/dist/__tests__/non-claude-provider-detection.test.js.map +1 -1
  34. package/dist/__tests__/package-dir-resolution-regression.test.js +25 -0
  35. package/dist/__tests__/package-dir-resolution-regression.test.js.map +1 -1
  36. package/dist/__tests__/pre-tool-enforcer.test.js +304 -0
  37. package/dist/__tests__/pre-tool-enforcer.test.js.map +1 -1
  38. package/dist/__tests__/session-start-script-context.test.js +121 -0
  39. package/dist/__tests__/session-start-script-context.test.js.map +1 -1
  40. package/dist/__tests__/setup-claude-md-script.test.js +44 -1
  41. package/dist/__tests__/setup-claude-md-script.test.js.map +1 -1
  42. package/dist/__tests__/skills.test.js +77 -7
  43. package/dist/__tests__/skills.test.js.map +1 -1
  44. package/dist/__tests__/wiki-hook-output-format.test.d.ts +2 -0
  45. package/dist/__tests__/wiki-hook-output-format.test.d.ts.map +1 -0
  46. package/dist/__tests__/wiki-hook-output-format.test.js +59 -0
  47. package/dist/__tests__/wiki-hook-output-format.test.js.map +1 -0
  48. package/dist/agents/definitions.d.ts.map +1 -1
  49. package/dist/agents/definitions.js +2 -1
  50. package/dist/agents/definitions.js.map +1 -1
  51. package/dist/autoresearch/__tests__/decide-outcome.test.d.ts +2 -0
  52. package/dist/autoresearch/__tests__/decide-outcome.test.d.ts.map +1 -0
  53. package/dist/autoresearch/__tests__/decide-outcome.test.js +69 -0
  54. package/dist/autoresearch/__tests__/decide-outcome.test.js.map +1 -0
  55. package/dist/autoresearch/runtime.d.ts.map +1 -1
  56. package/dist/autoresearch/runtime.js +16 -2
  57. package/dist/autoresearch/runtime.js.map +1 -1
  58. package/dist/cli/__tests__/launch.test.js +23 -0
  59. package/dist/cli/__tests__/launch.test.js.map +1 -1
  60. package/dist/cli/__tests__/self-improve-paths.test.js +3 -2
  61. package/dist/cli/__tests__/self-improve-paths.test.js.map +1 -1
  62. package/dist/cli/__tests__/team.test.js +100 -0
  63. package/dist/cli/__tests__/team.test.js.map +1 -1
  64. package/dist/cli/commands/__tests__/team-role-shorthand.test.js +30 -0
  65. package/dist/cli/commands/__tests__/team-role-shorthand.test.js.map +1 -1
  66. package/dist/cli/commands/__tests__/team.test.js +48 -1
  67. package/dist/cli/commands/__tests__/team.test.js.map +1 -1
  68. package/dist/cli/commands/team.d.ts +2 -0
  69. package/dist/cli/commands/team.d.ts.map +1 -1
  70. package/dist/cli/commands/team.js +9 -2
  71. package/dist/cli/commands/team.js.map +1 -1
  72. package/dist/cli/launch.d.ts.map +1 -1
  73. package/dist/cli/launch.js +7 -0
  74. package/dist/cli/launch.js.map +1 -1
  75. package/dist/cli/team.d.ts.map +1 -1
  76. package/dist/cli/team.js +34 -3
  77. package/dist/cli/team.js.map +1 -1
  78. package/dist/config/__tests__/loader.test.js +35 -4
  79. package/dist/config/__tests__/loader.test.js.map +1 -1
  80. package/dist/config/__tests__/models.test.js +126 -3
  81. package/dist/config/__tests__/models.test.js.map +1 -1
  82. package/dist/config/models.d.ts +8 -0
  83. package/dist/config/models.d.ts.map +1 -1
  84. package/dist/config/models.js +119 -8
  85. package/dist/config/models.js.map +1 -1
  86. package/dist/features/auto-update.d.ts.map +1 -1
  87. package/dist/features/auto-update.js +53 -5
  88. package/dist/features/auto-update.js.map +1 -1
  89. package/dist/features/delegation-enforcer.d.ts.map +1 -1
  90. package/dist/features/delegation-enforcer.js +10 -9
  91. package/dist/features/delegation-enforcer.js.map +1 -1
  92. package/dist/hooks/__tests__/bridge-routing.test.js +1 -0
  93. package/dist/hooks/__tests__/bridge-routing.test.js.map +1 -1
  94. package/dist/hooks/autopilot/enforcement.d.ts.map +1 -1
  95. package/dist/hooks/autopilot/enforcement.js +19 -0
  96. package/dist/hooks/autopilot/enforcement.js.map +1 -1
  97. package/dist/hooks/bridge.d.ts.map +1 -1
  98. package/dist/hooks/bridge.js +135 -4
  99. package/dist/hooks/bridge.js.map +1 -1
  100. package/dist/hooks/persistent-mode/index.d.ts +6 -0
  101. package/dist/hooks/persistent-mode/index.d.ts.map +1 -1
  102. package/dist/hooks/persistent-mode/index.js +96 -0
  103. package/dist/hooks/persistent-mode/index.js.map +1 -1
  104. package/dist/hooks/persistent-mode/stop-hook-blocking.test.js +152 -23
  105. package/dist/hooks/persistent-mode/stop-hook-blocking.test.js.map +1 -1
  106. package/dist/hooks/session-end/index.d.ts +9 -2
  107. package/dist/hooks/session-end/index.d.ts.map +1 -1
  108. package/dist/hooks/session-end/index.js +90 -35
  109. package/dist/hooks/session-end/index.js.map +1 -1
  110. package/dist/hud/stdin.d.ts +12 -1
  111. package/dist/hud/stdin.d.ts.map +1 -1
  112. package/dist/hud/stdin.js +123 -18
  113. package/dist/hud/stdin.js.map +1 -1
  114. package/dist/hud/types.d.ts +1 -0
  115. package/dist/hud/types.d.ts.map +1 -1
  116. package/dist/hud/types.js.map +1 -1
  117. package/dist/installer/__tests__/session-start-template.test.d.ts +2 -0
  118. package/dist/installer/__tests__/session-start-template.test.d.ts.map +1 -0
  119. package/dist/installer/__tests__/session-start-template.test.js +174 -0
  120. package/dist/installer/__tests__/session-start-template.test.js.map +1 -0
  121. package/dist/lib/__tests__/mode-state-io.test.js +75 -12
  122. package/dist/lib/__tests__/mode-state-io.test.js.map +1 -1
  123. package/dist/lib/mode-state-io.d.ts +11 -0
  124. package/dist/lib/mode-state-io.d.ts.map +1 -1
  125. package/dist/lib/mode-state-io.js +64 -0
  126. package/dist/lib/mode-state-io.js.map +1 -1
  127. package/dist/lib/worktree-paths.d.ts +12 -0
  128. package/dist/lib/worktree-paths.d.ts.map +1 -1
  129. package/dist/lib/worktree-paths.js +75 -0
  130. package/dist/lib/worktree-paths.js.map +1 -1
  131. package/dist/planning/__tests__/artifacts.test.d.ts +2 -0
  132. package/dist/planning/__tests__/artifacts.test.d.ts.map +1 -0
  133. package/dist/planning/__tests__/artifacts.test.js +486 -0
  134. package/dist/planning/__tests__/artifacts.test.js.map +1 -0
  135. package/dist/planning/artifact-names.d.ts +13 -0
  136. package/dist/planning/artifact-names.d.ts.map +1 -0
  137. package/dist/planning/artifact-names.js +108 -0
  138. package/dist/planning/artifact-names.js.map +1 -0
  139. package/dist/planning/artifacts.d.ts +47 -0
  140. package/dist/planning/artifacts.d.ts.map +1 -0
  141. package/dist/planning/artifacts.js +221 -0
  142. package/dist/planning/artifacts.js.map +1 -0
  143. package/dist/team/__tests__/api-interop.compatibility.test.js +194 -5
  144. package/dist/team/__tests__/api-interop.compatibility.test.js.map +1 -1
  145. package/dist/team/__tests__/model-contract.test.js +92 -2
  146. package/dist/team/__tests__/model-contract.test.js.map +1 -1
  147. package/dist/team/__tests__/runtime-v2.dispatch.test.js +30 -0
  148. package/dist/team/__tests__/runtime-v2.dispatch.test.js.map +1 -1
  149. package/dist/team/__tests__/scaling.test.js +39 -1
  150. package/dist/team/__tests__/scaling.test.js.map +1 -1
  151. package/dist/team/__tests__/worker-bootstrap.test.js +6 -0
  152. package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
  153. package/dist/team/api-interop.d.ts.map +1 -1
  154. package/dist/team/api-interop.js +89 -2
  155. package/dist/team/api-interop.js.map +1 -1
  156. package/dist/team/delegation-evidence.d.ts +5 -0
  157. package/dist/team/delegation-evidence.d.ts.map +1 -0
  158. package/dist/team/delegation-evidence.js +29 -0
  159. package/dist/team/delegation-evidence.js.map +1 -0
  160. package/dist/team/model-contract.d.ts +8 -0
  161. package/dist/team/model-contract.d.ts.map +1 -1
  162. package/dist/team/model-contract.js +13 -0
  163. package/dist/team/model-contract.js.map +1 -1
  164. package/dist/team/runtime-v2.d.ts +2 -1
  165. package/dist/team/runtime-v2.d.ts.map +1 -1
  166. package/dist/team/runtime-v2.js +3 -1
  167. package/dist/team/runtime-v2.js.map +1 -1
  168. package/dist/team/state/tasks.d.ts +4 -1
  169. package/dist/team/state/tasks.d.ts.map +1 -1
  170. package/dist/team/state/tasks.js +38 -1
  171. package/dist/team/state/tasks.js.map +1 -1
  172. package/dist/team/team-ops.d.ts +4 -1
  173. package/dist/team/team-ops.d.ts.map +1 -1
  174. package/dist/team/team-ops.js +2 -2
  175. package/dist/team/team-ops.js.map +1 -1
  176. package/dist/team/types.d.ts +22 -1
  177. package/dist/team/types.d.ts.map +1 -1
  178. package/dist/team/types.js.map +1 -1
  179. package/dist/team/worker-bootstrap.d.ts.map +1 -1
  180. package/dist/team/worker-bootstrap.js +6 -1
  181. package/dist/team/worker-bootstrap.js.map +1 -1
  182. package/dist/tools/__tests__/state-tools.test.js +322 -5
  183. package/dist/tools/__tests__/state-tools.test.js.map +1 -1
  184. package/dist/tools/__tests__/wiki-tools-working-directory.test.d.ts +2 -0
  185. package/dist/tools/__tests__/wiki-tools-working-directory.test.d.ts.map +1 -0
  186. package/dist/tools/__tests__/wiki-tools-working-directory.test.js +66 -0
  187. package/dist/tools/__tests__/wiki-tools-working-directory.test.js.map +1 -0
  188. package/dist/tools/state-tools.d.ts +3 -10
  189. package/dist/tools/state-tools.d.ts.map +1 -1
  190. package/dist/tools/state-tools.js +393 -91
  191. package/dist/tools/state-tools.js.map +1 -1
  192. package/dist/tools/wiki-tools.js +8 -8
  193. package/dist/tools/wiki-tools.js.map +1 -1
  194. package/docs/GETTING-STARTED.md +7 -0
  195. package/docs/REFERENCE.md +5 -5
  196. package/docs/copilot-instructions.md +1 -1
  197. package/package.json +1 -1
  198. package/scripts/lib/model-routing-override-message.mjs +21 -0
  199. package/scripts/persistent-mode.cjs +107 -0
  200. package/scripts/persistent-mode.mjs +102 -0
  201. package/scripts/post-tool-verifier.mjs +1 -0
  202. package/scripts/pre-tool-enforcer.mjs +163 -2
  203. package/scripts/session-start.mjs +63 -0
  204. package/scripts/setup-copilot-instructions.sh +12 -2
  205. package/scripts/wiki-pre-compact.mjs +1 -4
  206. package/skills/configure-notifications/SKILL.md +3 -3
  207. package/skills/deep-dive/SKILL.md +52 -4
  208. package/skills/deep-interview/SKILL.md +109 -15
  209. package/skills/mcp-setup/SKILL.md +60 -45
  210. package/skills/omc-setup/SKILL.md +3 -3
  211. package/skills/omc-setup/phases/01-install-claude-md.md +1 -1
  212. package/skills/omc-setup/phases/04-welcome.md +8 -8
  213. package/skills/plan/SKILL.md +1 -1
  214. package/skills/project-session-manager/SKILL.md +2 -2
  215. package/skills/project-session-manager/psm.sh +1 -1
  216. package/skills/ralplan/SKILL.md +2 -2
  217. package/templates/hooks/lib/model-routing-override-message.mjs +21 -0
  218. package/templates/hooks/persistent-mode.mjs +66 -0
  219. package/templates/hooks/session-start.mjs +62 -0
@@ -182,6 +182,7 @@ Do NOT skip this step. Do NOT move on without fixing the error.
182
182
  * from causing the stop hook to malfunction in new sessions.
183
183
  */
184
184
  const STALE_STATE_THRESHOLD_MS = 2 * 60 * 60 * 1000; // 2 hours
185
+ const PENDING_ASYNC_STATE_STALE_MS = 24 * 60 * 60 * 1000;
185
186
  const CANCEL_SIGNAL_TTL_MS = 30_000;
186
187
  const TEAM_TERMINAL_PHASES = new Set([
187
188
  "completed",
@@ -229,6 +230,66 @@ function isStaleState(state) {
229
230
  return age > STALE_STATE_THRESHOLD_MS;
230
231
  }
231
232
 
233
+
234
+ function parseTimestamp(value) {
235
+ if (typeof value !== "string" || value.length === 0) return null;
236
+ const parsed = new Date(value).getTime();
237
+ return Number.isFinite(parsed) ? parsed : null;
238
+ }
239
+
240
+ function isFreshTimestamp(value, ttlMs = PENDING_ASYNC_STATE_STALE_MS) {
241
+ const parsed = parseTimestamp(value);
242
+ return parsed !== null && Date.now() - parsed <= ttlMs;
243
+ }
244
+
245
+ function hasPendingBackgroundTask(stateDir, sessionId) {
246
+ const safeSessionId = sanitizeSessionId(sessionId);
247
+ const hudPath = safeSessionId
248
+ ? join(stateDir, "sessions", safeSessionId, "hud-state.json")
249
+ : join(stateDir, "hud-state.json");
250
+ const hudState = readJsonFile(hudPath);
251
+ return Boolean(hudState?.backgroundTasks?.some((task) => {
252
+ if (task?.status !== "running") return false;
253
+ return isFreshTimestamp(task.startedAt ?? task.startTime);
254
+ }));
255
+ }
256
+
257
+ function readPendingWakeupStates(stateDir, sessionId) {
258
+ const safeSessionId = sanitizeSessionId(sessionId);
259
+ const dirs = safeSessionId ? [join(stateDir, "sessions", safeSessionId), stateDir] : [stateDir];
260
+ const fileNames = ["scheduled-wakeup-state.json", "schedule-wakeup-state.json", "wakeup-state.json"];
261
+ const states = [];
262
+ for (const dir of dirs) {
263
+ for (const fileName of fileNames) {
264
+ const state = readJsonFile(join(dir, fileName));
265
+ if (state && typeof state === "object") states.push(state);
266
+ }
267
+ }
268
+ return states;
269
+ }
270
+
271
+ function hasPendingScheduledWakeup(stateDir, sessionId) {
272
+ const now = Date.now();
273
+ return readPendingWakeupStates(stateDir, sessionId).some((state) => {
274
+ const status = typeof state.status === "string" ? state.status.toLowerCase() : "";
275
+ if (["completed", "complete", "cancelled", "canceled", "failed", "expired"].includes(status)) {
276
+ return false;
277
+ }
278
+ const dueAt = parseTimestamp(
279
+ state.due_at ?? state.wakeup_at ?? state.scheduled_for ?? state.deadline_at ?? state.expires_at,
280
+ );
281
+ if (dueAt !== null) return dueAt > now;
282
+ if (state.active === true || state.pending === true) {
283
+ return isFreshTimestamp(state.created_at ?? state.updated_at ?? state.started_at);
284
+ }
285
+ return false;
286
+ });
287
+ }
288
+
289
+ function hasPendingOwnedAsyncWork(stateDir, sessionId) {
290
+ return hasPendingBackgroundTask(stateDir, sessionId) || hasPendingScheduledWakeup(stateDir, sessionId);
291
+ }
292
+
232
293
  function normalizeTeamPhase(state) {
233
294
  if (!state || typeof state !== "object") return null;
234
295
 
@@ -270,6 +331,36 @@ function isAwaitingConfirmation(state) {
270
331
  return Date.now() - setAtMs < AWAITING_CONFIRMATION_TTL_MS;
271
332
  }
272
333
 
334
+ function isOrphanedAutopilotRoutingEchoState(state) {
335
+ if (!state || typeof state !== "object") return false;
336
+
337
+ const phase = typeof state.phase === "string" ? state.phase.trim().toLowerCase() : "";
338
+ if (phase && phase !== "unspecified") return false;
339
+
340
+ const promptText = [
341
+ state.originalIdea,
342
+ state.original_idea,
343
+ state.prompt,
344
+ state.task_description,
345
+ ]
346
+ .filter((value) => typeof value === "string")
347
+ .join("\n")
348
+ .trim();
349
+
350
+ return /^\[MAGIC KEYWORDS?(?: DETECTED)?:\s*AUTOPILOT\s*\]\s*$/i.test(promptText);
351
+ }
352
+
353
+ function clearLoadedStateFile(loaded) {
354
+ const statePath = loaded?.path;
355
+ if (!statePath || !existsSync(statePath)) return;
356
+
357
+ try {
358
+ unlinkSync(statePath);
359
+ } catch {
360
+ // Best effort: failing to clean an orphan should not re-arm stop blocking.
361
+ }
362
+ }
363
+
273
364
  /**
274
365
  * Normalize a path for comparison.
275
366
  */
@@ -721,6 +812,11 @@ async function main() {
721
812
  return;
722
813
  }
723
814
 
815
+ if (hasPendingOwnedAsyncWork(stateDir, sessionId)) {
816
+ console.log(JSON.stringify({ continue: true, suppressOutput: true }));
817
+ return;
818
+ }
819
+
724
820
  // Read all mode states (session-scoped when sessionId provided)
725
821
  const ralph = readStateFileWithSession(
726
822
  stateDir,
@@ -866,6 +962,12 @@ async function main() {
866
962
  }
867
963
 
868
964
  // Priority 2: Autopilot (high-level orchestration)
965
+ if (isOrphanedAutopilotRoutingEchoState(autopilot.state)) {
966
+ clearLoadedStateFile(autopilot);
967
+ console.log(JSON.stringify({ continue: true, suppressOutput: true }));
968
+ return;
969
+ }
970
+
869
971
  if (
870
972
  autopilot.state?.active && !isAwaitingConfirmation(autopilot.state) &&
871
973
  !isStaleState(autopilot.state) &&
@@ -710,6 +710,7 @@ export function isCopilotCliWriteSuccess(output) {
710
710
 
711
711
  const cleaned = stripClaudeTempCwdErrors(output);
712
712
  const successPatterns = [
713
+ /(^|\n)The file has been updated successfully\.?(\n|$)/i,
713
714
  /(^|\n)The file .+ has been updated successfully\.?(\n|$)/i,
714
715
  /(^|\n)File (?:created|written|updated) successfully(?:\s+at:\s*.+)?\.?(\n|$)/i,
715
716
  /(^|\n).*file state is current in your context\b.*(\n|$)/i,
@@ -157,6 +157,152 @@ function readAgentDefinitionModel(subagentType) {
157
157
  }
158
158
  }
159
159
 
160
+
161
+ const SLOP_RISK_TOOL_NAMES = new Set([
162
+ 'Task',
163
+ 'TaskCreate',
164
+ 'TaskUpdate',
165
+ 'Agent',
166
+ 'Bash',
167
+ 'Edit',
168
+ 'MultiEdit',
169
+ 'Write',
170
+ 'NotebookEdit',
171
+ ]);
172
+ const SLOP_FALLBACK_LANGUAGE_PATTERN = /\b(?:fallback|fall\s+back|workaround|work\s+around)\b/i;
173
+ const SLOP_FALLBACK_ACTION_PATTERNS = [
174
+ /\b(?:add|build|create|implement|introduce|make|patch|use|using|write)\s+(?:an?\s+|the\s+)?(?:fallback|workaround)\b/i,
175
+ /\b(?:fallback|workaround)\s+(?:layer|path|handler|shim|patch|implementation|mechanism|mode)\b/i,
176
+ /\bworkaround\s+(?:it|this|that|the|a|an)\b/i,
177
+ /\b(?:fall\s+back|fallback)\s+(?:to|on|onto)\b/i,
178
+ /\bwork\s+around\s+(?:it|this|that|the|a|an)\b/i,
179
+ /\bwork\s+around\s+(?!(?:it|this|that|the|a|an)\b)(?:[a-z0-9][\w-]*\s+){0,5}[a-z0-9][\w-]*\b/i,
180
+ /(?:^|[\s"'`=:/\\])[\w.-]*(?:fallback|workaround)[\w.-]*\.(?:cjs|js|mjs|py|sh|ts|tsx)\b/i,
181
+ ];
182
+ const SLOP_BENIGN_TECHNICAL_PATTERNS = [
183
+ /\bfail[-\s]?soft\s+fallback(?:\s+(?:value|behavior|behaviour|result|semantics?))?\b/i,
184
+ /\bfallback\s+(?:value|variable|parameter|argument|option|setting|config(?:uration)?|default)\b/i,
185
+ /\bfallback\s+to\s+(?:the\s+)?default(?:\s+(?:config(?:uration)?|settings?|value|behavior|behaviour|option))?\b/i,
186
+ /\b(?:workaround|work\s+around)\s+for\s+(?:commit|change|issue|bug|regression|version|release|pr|pull\s+request|#[0-9]+|[a-f0-9]{7,40}\b)/i,
187
+ /\b(?:memory|sql|sqlite|mysql|postgres(?:ql)?|typescript|node|browser|runtime)\s+workaround\b/i,
188
+ ];
189
+ const SLOP_DOC_CONTEXT_PATTERN = /(?:^|[/\\])(?:docs?|documentation|guides?|instructions?|prompts?|\.om[ctx])(?:[/\\]|$)|\.(?:md|mdx|txt|rst)$/i;
190
+ const SLOP_SELF_REFERENCE_PATH_PATTERN = /(?:^|[/\\])(?:pre-tool-enforcer(?:\.mjs)?|pre-tool-enforcer\.test\.ts)(?:$|[/\\])/i;
191
+
192
+ function collectStringValues(value, output = [], depth = 0) {
193
+ if (depth > 5 || output.length > 100) return output;
194
+ if (typeof value === 'string') {
195
+ output.push(value);
196
+ return output;
197
+ }
198
+ if (Array.isArray(value)) {
199
+ for (const item of value) collectStringValues(item, output, depth + 1);
200
+ return output;
201
+ }
202
+ if (value && typeof value === 'object') {
203
+ for (const [key, child] of Object.entries(value)) {
204
+ // Skip hook/runtime metadata so warnings are driven by user-authored tool intent.
205
+ if (/^(cwd|directory|session_?id|transcript_?path|hook_event_name)$/i.test(key)) continue;
206
+ collectStringValues(child, output, depth + 1);
207
+ }
208
+ }
209
+ return output;
210
+ }
211
+
212
+ function collectLikelyPathValues(value, output = [], depth = 0) {
213
+ if (depth > 5 || output.length > 100 || !value || typeof value !== 'object') return output;
214
+ if (Array.isArray(value)) {
215
+ for (const item of value) collectLikelyPathValues(item, output, depth + 1);
216
+ return output;
217
+ }
218
+ for (const [key, child] of Object.entries(value)) {
219
+ if (typeof child === 'string' && /(?:^|_)(?:file_?path|path|filename|target|command)$/i.test(key)) {
220
+ output.push(child);
221
+ continue;
222
+ }
223
+ collectLikelyPathValues(child, output, depth + 1);
224
+ }
225
+ return output;
226
+ }
227
+
228
+ function stripSlopQuotedAndCodeContexts(text) {
229
+ return text
230
+ .replace(/```[\s\S]*?```/g, '\n')
231
+ .replace(/`[^`\r\n]*`/g, ' ')
232
+ .replace(/(["'])(?:\\.|(?!\1)[^\\\r\n])*\1/g, ' ');
233
+ }
234
+
235
+ function splitSlopInspectionSegments(text) {
236
+ return text
237
+ .split(/[\r\n!?;]+/)
238
+ .map(segment => segment.trim())
239
+ .filter(Boolean);
240
+ }
241
+
242
+ function removeBenignTechnicalSlopFallbackSpans(text) {
243
+ return SLOP_BENIGN_TECHNICAL_PATTERNS.reduce(
244
+ (result, pattern) => {
245
+ const flags = pattern.flags.includes('g') ? pattern.flags : `${pattern.flags}g`;
246
+ return result.replace(new RegExp(pattern.source, flags), ' ');
247
+ },
248
+ text,
249
+ );
250
+ }
251
+
252
+ function hasSlopFallbackActionShape(text) {
253
+ const strippedText = stripSlopQuotedAndCodeContexts(text);
254
+ return splitSlopInspectionSegments(strippedText).some(segment => (
255
+ SLOP_FALLBACK_ACTION_PATTERNS.some(pattern => (
256
+ pattern.test(removeBenignTechnicalSlopFallbackSpans(segment))
257
+ ))
258
+ ));
259
+ }
260
+
261
+ function isSelfReferentialSlopContext(toolInput) {
262
+ return collectLikelyPathValues(toolInput).some(value => SLOP_SELF_REFERENCE_PATH_PATTERN.test(value));
263
+ }
264
+
265
+ function isDocumentationSlopContext(toolInput) {
266
+ const pathLikeValues = collectLikelyPathValues(toolInput);
267
+ return pathLikeValues.some(value => SLOP_DOC_CONTEXT_PATTERN.test(value));
268
+ }
269
+
270
+ function shouldWarnForSlopFallbackLanguage(data, toolName, inspectedText) {
271
+ if (!SLOP_RISK_TOOL_NAMES.has(toolName)) return false;
272
+ if (!SLOP_FALLBACK_LANGUAGE_PATTERN.test(inspectedText)) return false;
273
+
274
+ const toolInput = data.toolInput || data.tool_input || {};
275
+ if (isSelfReferentialSlopContext(toolInput)) return false;
276
+ if (isDocumentationSlopContext(toolInput)) {
277
+ return false;
278
+ }
279
+
280
+ return hasSlopFallbackActionShape(inspectedText);
281
+ }
282
+
283
+ function generateSlopWarning(data, toolName) {
284
+ const toolInput = data.toolInput || data.tool_input || {};
285
+ const promptLikeFields = {
286
+ prompt: data.prompt,
287
+ userPrompt: data.userPrompt,
288
+ user_prompt: data.user_prompt,
289
+ message: data.message,
290
+ };
291
+ const inspectedText = collectStringValues(toolInput)
292
+ .concat(collectStringValues(promptLikeFields))
293
+ .join('\n');
294
+ if (!shouldWarnForSlopFallbackLanguage(data, toolName, inspectedText)) return '';
295
+
296
+ return '[SLOP WARNING] Detected fallback/workaround language in this tool input. ' +
297
+ 'Do not make potential slop: avoid ad-hoc fallback layers, workaround shims, or environment-specific patches unless explicitly justified. ' +
298
+ 'For architecture concerns, consult the architect for a concrete design first. ' +
299
+ 'If this seems environment-specific, ask the user to confirm constraints before proceeding.';
300
+ }
301
+
302
+ function combineHookMessages(...messages) {
303
+ return messages.filter(Boolean).join('\n\n');
304
+ }
305
+
160
306
  const SESSION_ID_PATTERN = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$/;
161
307
  const MODE_STATE_FILES = [
162
308
  'autopilot-state.json',
@@ -169,6 +315,14 @@ const MODE_STATE_FILES = [
169
315
  'omc-teams-state.json',
170
316
  ];
171
317
  const QUIET_LEVEL = getQuietLevel();
318
+ const BUILT_IN_TASK_LIST_TOOL_NAMES = new Set([
319
+ 'TaskCreate',
320
+ 'TaskUpdate',
321
+ 'TaskList',
322
+ 'TaskGet',
323
+ 'TaskOutput',
324
+ 'TaskStop',
325
+ ]);
172
326
 
173
327
  function getQuietLevel() {
174
328
  const parsed = Number.parseInt(process.env.OMC_QUIET || '0', 10);
@@ -882,7 +1036,7 @@ async function main() {
882
1036
 
883
1037
  const todoStatus = getTodoStatus(directory);
884
1038
 
885
- if (toolName === 'Task' || toolName === 'TaskCreate' || toolName === 'TaskUpdate') {
1039
+ if (toolName === 'Task' || toolName === 'Agent') {
886
1040
  const rawTranscriptPath = data.transcript_path || data.transcriptPath || '';
887
1041
  const transcriptPath = resolveTranscriptPath(rawTranscriptPath, directory);
888
1042
  const preflightBlock = evaluateAgentHeavyPreflight({
@@ -895,13 +1049,20 @@ async function main() {
895
1049
  }
896
1050
  }
897
1051
 
1052
+ const slopWarning = generateSlopWarning(data, toolName);
898
1053
  let message;
899
- if (toolName === 'Task' || toolName === 'TaskCreate' || toolName === 'TaskUpdate') {
1054
+ if (BUILT_IN_TASK_LIST_TOOL_NAMES.has(toolName)) {
1055
+ console.log(JSON.stringify({ continue: true, suppressOutput: true }));
1056
+ return;
1057
+ }
1058
+
1059
+ if (toolName === 'Task' || toolName === 'Agent') {
900
1060
  const toolInput = data.toolInput || data.tool_input || null;
901
1061
  message = generateAgentSpawnMessage(toolInput, stateDir, todoStatus, sessionId);
902
1062
  } else {
903
1063
  message = generateMessage(toolName, todoStatus, modeActive);
904
1064
  }
1065
+ message = combineHookMessages(slopWarning, message);
905
1066
 
906
1067
  if (!message) {
907
1068
  console.log(JSON.stringify({ continue: true, suppressOutput: true }));
@@ -335,6 +335,69 @@ const SESSION_END_MODE_STATE_FILES = [
335
335
  'skill-active-state.json',
336
336
  ];
337
337
 
338
+ import { MODEL_ROUTING_OVERRIDE_MESSAGE } from './lib/model-routing-override-message.mjs';
339
+ export { MODEL_ROUTING_OVERRIDE_MESSAGE };
340
+
341
+ function isTruthyProviderFlag(value) {
342
+ return value === '1' || value === 'true';
343
+ }
344
+
345
+ function getSessionModelId() {
346
+ return process.env.CLAUDE_MODEL || process.env.ANTHROPIC_MODEL || '';
347
+ }
348
+
349
+ function isBedrockSession() {
350
+ if (isTruthyProviderFlag(process.env.CLAUDE_CODE_USE_BEDROCK)) return true;
351
+ const modelId = getSessionModelId();
352
+ return Boolean(
353
+ modelId && (
354
+ /^((us|eu|ap|global)\.anthropic\.|anthropic\.claude)/i.test(modelId) ||
355
+ (
356
+ /^arn:aws(-[^:]+)?:bedrock:/i.test(modelId) &&
357
+ /:(inference-profile|application-inference-profile)\//i.test(modelId) &&
358
+ modelId.toLowerCase().includes('claude')
359
+ )
360
+ )
361
+ );
362
+ }
363
+
364
+ function isVertexSession() {
365
+ if (isTruthyProviderFlag(process.env.CLAUDE_CODE_USE_VERTEX)) return true;
366
+ const modelId = getSessionModelId();
367
+ return Boolean(modelId && modelId.toLowerCase().startsWith('vertex_ai/'));
368
+ }
369
+
370
+ function readRoutingForceInheritFromConfig(directory) {
371
+ const configPaths = [
372
+ join(configDir, '.omc-config.json'),
373
+ join(directory, '.omc', 'config.json'),
374
+ ];
375
+
376
+ for (const configPath of configPaths) {
377
+ const config = readJsonFile(configPath);
378
+ if (config?.routing?.forceInherit === true) return true;
379
+ }
380
+
381
+ return false;
382
+ }
383
+
384
+ function shouldEmitModelRoutingOverride(directory) {
385
+ if (process.env.OMC_ROUTING_FORCE_INHERIT === 'true') return true;
386
+ if (process.env.OMC_ROUTING_FORCE_INHERIT === 'false') return false;
387
+ if (readRoutingForceInheritFromConfig(directory)) return true;
388
+
389
+ if (isBedrockSession() || isVertexSession()) return true;
390
+
391
+ const modelId = getSessionModelId();
392
+ if (modelId && !modelId.toLowerCase().includes('claude')) return true;
393
+
394
+ const baseUrl = process.env.ANTHROPIC_BASE_URL || '';
395
+ if (baseUrl && !baseUrl.includes('anthropic.com')) return true;
396
+
397
+ return false;
398
+ }
399
+
400
+
338
401
  function compactBudgetedText(text, maxChars) {
339
402
  const notice = '\n...[truncated to preserve SessionStart context budget]';
340
403
  if (!text || text.length <= maxChars) return text || '';
@@ -105,7 +105,16 @@ ensure_local_omc_git_exclude() {
105
105
  local block_start="# BEGIN OMC local artifacts"
106
106
 
107
107
  if [ -f "$exclude_path" ] && grep -Fq "$block_start" "$exclude_path"; then
108
- echo "OMC git exclude already configured"
108
+ if grep -Fxq ".omx/" "$exclude_path"; then
109
+ echo "OMC git exclude already configured"
110
+ return 0
111
+ fi
112
+
113
+ if [ -s "$exclude_path" ]; then
114
+ printf '\n' >> "$exclude_path"
115
+ fi
116
+ printf '.omx/\n' >> "$exclude_path"
117
+ echo "Updated OMC git exclude for local OMX artifacts"
109
118
  return 0
110
119
  fi
111
120
 
@@ -119,10 +128,11 @@ ensure_local_omc_git_exclude() {
119
128
  .omcp/*
120
129
  !.omcp/skills/
121
130
  !.omcp/skills/**
131
+ .omx/
122
132
  # END OMC local artifacts
123
133
  EOF
124
134
 
125
- echo "Configured git exclude for local .omcp artifacts (preserving .omcp/skills/)"
135
+ echo "Configured git exclude for local OMC/OMX artifacts (preserving .omcp/skills/)"
126
136
  }
127
137
 
128
138
  # Determine target path
@@ -10,10 +10,7 @@ async function main() {
10
10
  if (result.additionalContext) {
11
11
  console.log(JSON.stringify({
12
12
  continue: true,
13
- hookSpecificOutput: {
14
- hookEventName: 'PreCompact',
15
- additionalContext: result.additionalContext,
16
- },
13
+ systemMessage: result.additionalContext,
17
14
  }));
18
15
  } else {
19
16
  console.log(JSON.stringify({ continue: true, suppressOutput: true }));
@@ -239,7 +239,7 @@ Telegram Notifications Configured!
239
239
  Format: Markdown
240
240
  Events: session-end, ask-user-question
241
241
 
242
- Config saved to: ~/.copilot/.omcp-config.json
242
+ Config saved to: ~/.copilot/.omc-config.json
243
243
 
244
244
  You can also set these via environment variables:
245
245
  OMC_TELEGRAM_BOT_TOKEN=123456789:ABCdefGHI...
@@ -481,7 +481,7 @@ Discord Notifications Configured!
481
481
  Events: session-end, ask-user-question
482
482
  Username: OMC
483
483
 
484
- Config saved to: ~/.copilot/.omcp-config.json
484
+ Config saved to: ~/.copilot/.omc-config.json
485
485
 
486
486
  You can also set these via environment variables:
487
487
  OMC_DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
@@ -728,7 +728,7 @@ Slack Notifications Configured!
728
728
  Events: session-end, ask-user-question
729
729
  Username: OMC
730
730
 
731
- Config saved to: ~/.copilot/.omcp-config.json
731
+ Config saved to: ~/.copilot/.omc-config.json
732
732
 
733
733
  You can also set these via environment variables:
734
734
  OMC_SLACK_WEBHOOK_URL=https://hooks.slack.com/services/...
@@ -7,8 +7,8 @@ triggers:
7
7
  - "deep-dive"
8
8
  - "trace and interview"
9
9
  - "investigate deeply"
10
- pipeline: [deep-dive, omc-plan, autopilot]
11
- next-skill: omc-plan
10
+ pipeline: [deep-dive, plan, autopilot]
11
+ next-skill: plan
12
12
  next-skill-args: --consensus --direct
13
13
  handoff: .omcp/specs/deep-dive-{slug}.md
14
14
  ---
@@ -138,6 +138,12 @@ Use **Claude built-in team mode** to run 3 parallel tracer lanes:
138
138
  - Rank evidence strength (from controlled reproductions → speculation)
139
139
  - Name the **critical unknown** for the lane
140
140
  - Recommend the best **discriminating probe**
141
+ - For **Lane 3: Misplacement / SoT Violation** findings, classify every candidate MOVE destination with `ownership_scope` before ranking recommendations:
142
+ - `personal-config`: user-level dotfiles, `[$CLAUDE_CONFIG_DIR|~/.claude]/`, personal repositories, or user-only agent rules
143
+ - `shared-config`: company/org repositories, team-maintained config, or multi-tenant shared rules
144
+ - `external`: third-party, vendor, or OSS upstream repositories outside the user's ownership
145
+ - `project-scoped`: per-project storage owned by the current project boundary
146
+ - For Lane 3, compare source and destination `ownership_scope`; any cross-boundary MOVE (for example `personal-config` → `shared-config`) MUST be flagged with an explicit warning and MUST NOT be surfaced as the default recommendation. Prefer COMPRESS, KEEP, or a same-scope MOVE as the default when available.
141
147
  4. **Run a rebuttal round** between the leading hypothesis and the strongest alternative
142
148
  5. **Detect convergence**: if two "different" hypotheses reduce to the same mechanism, merge them explicitly
143
149
  6. **Leader synthesis**: produce the ranked output below
@@ -176,6 +182,15 @@ Save to `.omcp/specs/deep-dive-trace-{slug}.md`:
176
182
  - **Lane 2 ({hypothesis_2})**: {critical_unknown_2}
177
183
  - **Lane 3 ({hypothesis_3})**: {critical_unknown_3}
178
184
 
185
+ ## Lane 3 Misplacement / SoT Ownership Scope
186
+ For each MOVE candidate discovered by Lane 3, include:
187
+
188
+ | Source | Candidate destination | ownership_scope | Boundary relationship | Default? | Warning |
189
+ |--------|-----------------------|-----------------|-----------------------|----------|---------|
190
+ | ... | ... | personal-config/shared-config/external/project-scoped | same-scope/cross-boundary | yes/no | ... |
191
+
192
+ Cross-boundary MOVE candidates MUST have `Default? = no` and an explicit warning explaining the source/destination ownership mismatch. They may be listed as flagged alternatives, but the ranked synthesis MUST NOT present them as the default recommendation.
193
+
179
194
  ## Rebuttal Round
180
195
  - Best rebuttal to leader: ...
181
196
  - Why leader held / failed: ...
@@ -274,7 +289,36 @@ When ambiguity ≤ the resolved threshold for this run, generate the spec in **s
274
289
 
275
290
  Read `spec_path` and `trace_path` from state (not conversation context) for resume resilience.
276
291
 
277
- Present execution options via `AskUserQuestion`:
292
+ ### Workflow Pre-Flight
293
+
294
+ Before presenting execution options, run a lightweight workflow pre-flight when active project guidance mentions an issue-driven, worktree-driven, branch-first, or blocking pre-execution workflow. Treat guidance text as policy data from the user's environment; do not invent a gate when no such guidance is present.
295
+
296
+ 1. **Detect whether the guidance gate applies** by scanning the active project instructions already in context (for example `AGENTS.md`, `CLAUDE.md`, project docs, or hook-injected guidance) for phrases such as `issue-driven`, `worktree-driven`, `worktree`, `create issue`, `branch`, `do not write code`, `blocking requirement`, or equivalent workflow rules.
297
+ 2. **Check repository position** with read-only commands:
298
+ - `git rev-parse --show-toplevel` to confirm the repository root for the pending execution.
299
+ - `git branch --show-current` to identify the current branch; flag protected/default branches such as `main`, `master`, or `dev`.
300
+ - `git worktree list --porcelain` to distinguish a linked task worktree from the primary checkout when possible; flag a primary checkout or missing linked worktree when the guidance requires task worktrees.
301
+ 3. **Check for a linked issue** when the guidance is issue-driven:
302
+ - First look for an explicit issue reference in `spec_path`, `trace_path`, the current branch name, and the original task text.
303
+ - If no local reference is found and `gh` is available, optionally run a narrow `gh issue list --limit 20 --json number,title,state` search for a matching open issue.
304
+ - If no issue can be linked, flag `missing linked issue`; do not block on `gh` being unavailable.
305
+ 4. **If any precondition is missing**, surface a setup redirect before the execution menu:
306
+
307
+ **Question:** "Spec ready (ambiguity: {score}%). Detected workflow pre-flight issue(s): {findings}. Project guidance appears to require issue/branch/worktree setup before code execution. Set that up first?"
308
+
309
+ **Options:**
310
+
311
+ - **Set up issue/branch/worktree first (Recommended)**
312
+ - Description: "Redirect to the project's setup workflow before any execution skill writes code."
313
+ - Action: Invoke the known project setup skill or workflow if one is named in guidance; otherwise invoke `Skill("oh-my-claudecode:project-session-manager")` with `spec_path` and the pre-flight findings as context. After setup completes, rerun this Phase 5 pre-flight before showing execution options.
314
+ - **Proceed to execution options anyway**
315
+ - Description: "Acknowledge the workflow warning and continue to the normal execution menu."
316
+ - Action: Continue to the execution options below, preserving the warning in handoff context.
317
+ - **Refine further**
318
+ - Description: "Return to Phase 4 interview loop instead of preparing execution."
319
+ - Action: Return to Phase 4 interview loop.
320
+
321
+ If the guidance gate does not apply, or the pre-flight passes, present execution options via `AskUserQuestion`:
278
322
 
279
323
  **Question:** "Your spec is ready (ambiguity: {score}%). How would you like to proceed?"
280
324
 
@@ -282,7 +326,7 @@ Present execution options via `AskUserQuestion`:
282
326
 
283
327
  1. **Ralplan → Autopilot (Recommended)**
284
328
  - Description: "3-stage pipeline: consensus-refine this spec with Planner/Architect/Critic, then execute with full autopilot. Maximum quality."
285
- - Action: Invoke `Skill("oh-my-copilot:omc-plan")` with `--consensus --direct` flags and the spec file path (`spec_path` from state) as context. The `--direct` flag skips the omc-plan skill's interview phase (the deep-dive interview already gathered requirements), while `--consensus` triggers the Planner/Architect/Critic loop. When consensus completes and produces a plan in `.omcp/plans/`, invoke `Skill("oh-my-copilot:autopilot")` with the consensus plan as Phase 0+1 output — autopilot skips both Expansion and Planning, starting directly at Phase 2 (Execution).
329
+ - Action: Invoke `Skill("oh-my-copilot:plan")` with `--consensus --direct` flags and the spec file path (`spec_path` from state) as context. The `--direct` flag skips the omc-plan skill's interview phase (the deep-dive interview already gathered requirements), while `--consensus` triggers the Planner/Architect/Critic loop. When consensus completes and produces a plan in `.omcp/plans/`, invoke `Skill("oh-my-copilot:autopilot")` with the consensus plan as Phase 0+1 output — autopilot skips both Expansion and Planning, starting directly at Phase 2 (Execution).
286
330
  - Pipeline: `deep-dive spec → omc-plan --consensus --direct → autopilot execution`
287
331
 
288
332
  2. **Execute with autopilot (skip ralplan)**
@@ -326,6 +370,7 @@ Output: spec.md Output: consensus-plan.md Output: working code
326
370
  - Use `state_write(mode="deep-interview")` with `state.source = "deep-dive"` for all state persistence
327
371
  - Use `state_read(mode="deep-interview")` for resume — check `state.source === "deep-dive"` to distinguish
328
372
  - Use `Write` tool to save trace result to `.omcp/specs/deep-dive-trace-{slug}.md` and final spec to `.omcp/specs/deep-dive-{slug}.md`; use `.omcp/state/` or `state_write` for ephemeral artifacts
373
+ - Run the Phase 5 workflow pre-flight before execution options when project guidance requires issue/branch/worktree setup
329
374
  - Use `Skill()` to bridge to execution modes (Phase 5) — never implement directly
330
375
  - Wrap all trace-derived text in `<trace-context>` delimiters when injecting into prompts
331
376
  </Tool_Usage>
@@ -421,12 +466,15 @@ Why bad: Duplicates deep-interview's behavioral contract. These values should be
421
466
  - [ ] Phase 2 confirms hypotheses via AskUserQuestion (1 round)
422
467
  - [ ] Phase 3 runs trace with 3 parallel lanes (team mode, sequential fallback)
423
468
  - [ ] Phase 3 saves trace result to `.omcp/specs/deep-dive-trace-{slug}.md` with per-lane critical unknowns
469
+ - [ ] Lane 3 MOVE candidates include `ownership_scope` and cross-boundary MOVE candidates are warned/flagged, not default recommendations
424
470
  - [ ] Phase 4 starts with 3-point injection (initial_idea, codebase_context, question_queue from per-lane unknowns)
425
471
  - [ ] Phase 4 references deep-interview SKILL.md Phases 2-4 (not duplicated inline)
426
472
  - [ ] Phase 4 handles low-confidence trace gracefully
427
473
  - [ ] Phase 4 wraps trace-derived text in `<trace-context>` delimiters (untrusted data guard)
428
474
  - [ ] Final spec saved to `.omcp/specs/deep-dive-{slug}.md` in standard deep-interview format
429
475
  - [ ] Final spec contains "Trace Findings" section
476
+ - [ ] Phase 5 workflow pre-flight detects issue/worktree/branch preconditions when project guidance requires them
477
+ - [ ] Phase 5 surfaces a setup redirect before execution options when the pre-flight finds missing preconditions
430
478
  - [ ] Phase 5 execution bridge passes spec_path explicitly to downstream skills
431
479
  - [ ] Phase 5 "Ralplan → Autopilot" option explicitly invokes autopilot after omc-plan consensus completes
432
480
  - [ ] State uses `mode="deep-interview"` with `state.source = "deep-dive"` discriminator