opencode-matrixx 2.6.9 → 2.6.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4540,7 +4540,7 @@ var require_picomatch2 = __commonJS(function(exports, module) {
4540
4540
  });
4541
4541
 
4542
4542
  // src/hooks/matrix-loop/constants.ts
4543
- var HOOK_NAME6 = "matrix-loop", DEFAULT_STATE_FILE = ".matrixx/matrix-loop.local.md", DEFAULT_MAX_ITERATIONS = 100, DEFAULT_COMPLETION_PROMISE = "DONE", DEFAULT_VERIFICATION_AGENT = "merovingian", DEFAULT_VERIFICATION_TIMEOUT_MS = 30000, DEFAULT_VERIFICATION_MAX_RETRIES = 2;
4543
+ var HOOK_NAME7 = "matrix-loop", DEFAULT_STATE_FILE = ".matrixx/matrix-loop.local.md", DEFAULT_MAX_ITERATIONS = 100, DEFAULT_COMPLETION_PROMISE = "DONE", DEFAULT_VERIFICATION_AGENT = "merovingian", DEFAULT_VERIFICATION_TIMEOUT_MS = 30000, DEFAULT_VERIFICATION_MAX_RETRIES = 2;
4544
4544
 
4545
4545
  // src/hooks/matrix-loop/storage.ts
4546
4546
  var exports_storage = {};
@@ -29559,7 +29559,7 @@ var init_ulw_research = __esm(() => {
29559
29559
  "glob",
29560
29560
  "look_at",
29561
29561
  "websearch_web_search_exa",
29562
- "white_rabbit_searchGitHub",
29562
+ "github_search",
29563
29563
  "context7_resolve-library-id",
29564
29564
  "context7_query-docs"
29565
29565
  ],
@@ -29670,7 +29670,7 @@ task(
29670
29670
  prompt="""
29671
29671
  Search open source repositories for implementations related to: <CURRENT_LEAD>
29672
29672
 
29673
- Use white_rabbit_searchGitHub to find real-world code patterns.
29673
+ Use github_search to find real-world code patterns.
29674
29674
  Focus on:
29675
29675
  - Production implementations in similar projects
29676
29676
  - Reference implementations or canonical examples
@@ -48167,7 +48167,7 @@ var TARGET_TOOLS = new Set([
48167
48167
  "context7_query-docs",
48168
48168
  "websearch_web_search_exa",
48169
48169
  "context7_get-library-docs",
48170
- "white_rabbit_searchgithub"
48170
+ "github_search"
48171
48171
  ]);
48172
48172
  var AGENT_TOOLS = new Set([
48173
48173
  "task"
@@ -55863,6 +55863,80 @@ function createPostReadInjectorHook(ctx, options) {
55863
55863
  function createDirectoryAgentsInjectorHook(ctx) {
55864
55864
  return createPostReadInjectorHook(ctx, { trackedTools: ["read"] });
55865
55865
  }
55866
+ // src/hooks/document-reader-guard/constants.ts
55867
+ var HOOK_NAME5 = "document-reader-guard";
55868
+ var BINARY_EXTENSIONS = [
55869
+ ".pdf",
55870
+ ".docx",
55871
+ ".doc",
55872
+ ".xlsx",
55873
+ ".xls",
55874
+ ".pptx",
55875
+ ".ppt",
55876
+ ".jpg",
55877
+ ".jpeg",
55878
+ ".png",
55879
+ ".gif",
55880
+ ".webp",
55881
+ ".mp3",
55882
+ ".wav"
55883
+ ];
55884
+ var BLOCKED_PATTERNS = [
55885
+ /\bpdftotext\b/,
55886
+ /\bpdfinfo\b/,
55887
+ /\bpandoc\b.*\.(pdf|docx?|xlsx?|pptx?)/,
55888
+ /\blibreoffice\b|\bsoffice\b/,
55889
+ /\bcat\s+[^|&;]*\.(pdf|docx?|xlsx?|pptx?|jpe?g|png|gif|webp|mp3|wav)/,
55890
+ /\bhead\s+[^|&;]*\.(pdf|docx?|xlsx?|pptx?|jpe?g|png|gif|webp|mp3|wav)/,
55891
+ /\btail\s+[^|&;]*\.(pdf|docx?|xlsx?|pptx?|jpe?g|png|gif|webp|mp3|wav)/,
55892
+ /\bless\s+[^|&;]*\.(pdf|docx?|xlsx?|pptx?|jpe?g|png|gif|webp|mp3|wav)/,
55893
+ /\bpython3?\b.*(fitz|pymupdf|pdfminer|pdfplumber|python-pptx|\bdocx\b|openpyxl|pillow|\bPIL\b)/,
55894
+ /\bpython3?\b[^|&;]*\.(pdf|docx?|xlsx?|pptx?|jpe?g|png|gif|webp|mp3|wav)/,
55895
+ /\bmarkitdown\b/
55896
+ ];
55897
+ var READ_BLOCK_MESSAGE = "[document-reader-guard] BLOCKED: Use document_reader__convert_to_markdown (document_reader MCP, Microsoft MarkItDown) for binary documents \u2014 generic Read cannot parse them. " + "Call convert_to_markdown with an absolute URI like file:///absolute/path/to/report.pdf, then summarize \u2014 do not dump raw output. " + "For context isolation delegate to the construct subagent so the main context stays clean.";
55898
+ var BASH_BLOCK_MESSAGE = "[document-reader-guard] BLOCKED: Do not read documents via bash CLIs (pdftotext, pdfinfo, pandoc, libreoffice, cat/head/tail, python fitz/pypdf/pdfminer/pdfplumber/docx/openpyxl, markitdown CLI). " + "Use document_reader__convert_to_markdown with file:///absolute/path instead \u2014 delegate to construct for summarization.";
55899
+ // src/hooks/document-reader-guard/hook.ts
55900
+ function getFilePath(args) {
55901
+ const raw = args?.filePath ?? args?.path ?? args?.file;
55902
+ return typeof raw === "string" ? raw : undefined;
55903
+ }
55904
+ function isBinaryDocument(filePath) {
55905
+ const normalized = filePath.toLowerCase().replace(/\\/g, "/").split("?")[0].split("#")[0];
55906
+ return BINARY_EXTENSIONS.some((ext) => normalized.endsWith(ext));
55907
+ }
55908
+ function createDocumentReaderGuardHook(ctx) {
55909
+ return {
55910
+ "tool.execute.before": async (input, output) => {
55911
+ const tool = input.tool?.toLowerCase();
55912
+ if (tool !== "read" && tool !== "bash")
55913
+ return;
55914
+ if (tool === "read") {
55915
+ const filePath = getFilePath(output.args);
55916
+ if (!filePath || !isBinaryDocument(filePath))
55917
+ return;
55918
+ log(`[${HOOK_NAME5}] BLOCKED generic Read on binary document \u2014 use document_reader MCP`, {
55919
+ sessionID: input.sessionID,
55920
+ tool: input.tool,
55921
+ filePath
55922
+ });
55923
+ throw new Error(READ_BLOCK_MESSAGE);
55924
+ }
55925
+ const args = output.args;
55926
+ const cmd = args?.command;
55927
+ if (!cmd)
55928
+ return;
55929
+ if (!BLOCKED_PATTERNS.some((rx) => rx.test(cmd)))
55930
+ return;
55931
+ ctx.directory;
55932
+ log(`[${HOOK_NAME5}] BLOCKED bash document read`, {
55933
+ sessionID: input.sessionID,
55934
+ command: cmd.slice(0, 120)
55935
+ });
55936
+ throw new Error(BASH_BLOCK_MESSAGE);
55937
+ }
55938
+ };
55939
+ }
55866
55940
  // src/hooks/edit-error-recovery/hook.ts
55867
55941
  var EDIT_ERROR_PATTERNS = [
55868
55942
  "oldString and newString must be different",
@@ -59571,7 +59645,7 @@ var JSON_ERROR_TOOL_EXCLUDE_LIST = [
59571
59645
  "grep",
59572
59646
  "webfetch",
59573
59647
  "look_at",
59574
- "white_rabbit_searchgithub",
59648
+ "github_search",
59575
59649
  "websearch_web_search_exa"
59576
59650
  ];
59577
59651
  var JSON_ERROR_PATTERNS = [
@@ -59616,18 +59690,18 @@ ${JSON_ERROR_REMINDER}`;
59616
59690
  }
59617
59691
  // src/features/ultrawork-state/manager.ts
59618
59692
  init_logger();
59619
- var HOOK_NAME5 = "ultrawork-state";
59693
+ var HOOK_NAME6 = "ultrawork-state";
59620
59694
  var sessionStates = new Map;
59621
59695
  function getUltraworkState(sessionID) {
59622
59696
  return sessionStates.get(sessionID);
59623
59697
  }
59624
59698
  function enableUltrawork(sessionID) {
59625
59699
  sessionStates.set(sessionID, "enabled");
59626
- log(`[${HOOK_NAME5}] Ultrawork enabled for session`, { sessionID });
59700
+ log(`[${HOOK_NAME6}] Ultrawork enabled for session`, { sessionID });
59627
59701
  }
59628
59702
  function disableUltrawork(sessionID) {
59629
59703
  sessionStates.set(sessionID, "disabled");
59630
- log(`[${HOOK_NAME5}] Ultrawork disabled for session`, { sessionID });
59704
+ log(`[${HOOK_NAME6}] Ultrawork disabled for session`, { sessionID });
59631
59705
  }
59632
59706
  // src/hooks/keyword-detector/hook.ts
59633
59707
  function createKeywordDetectorHook(ctx, _collector) {
@@ -59760,7 +59834,7 @@ function createLoopStateController(options) {
59760
59834
  };
59761
59835
  const success2 = writeState(directory, state3, stateDir);
59762
59836
  if (success2) {
59763
- log(`[${HOOK_NAME6}] Loop started`, {
59837
+ log(`[${HOOK_NAME7}] Loop started`, {
59764
59838
  sessionID,
59765
59839
  maxIterations: state3.max_iterations,
59766
59840
  completionPromise: state3.completion_promise
@@ -59775,7 +59849,7 @@ function createLoopStateController(options) {
59775
59849
  }
59776
59850
  const success2 = clearState(directory, stateDir);
59777
59851
  if (success2) {
59778
- log(`[${HOOK_NAME6}] Loop cancelled`, { sessionID, iteration: state3.iteration });
59852
+ log(`[${HOOK_NAME7}] Loop cancelled`, { sessionID, iteration: state3.iteration });
59779
59853
  }
59780
59854
  return success2;
59781
59855
  },
@@ -59852,7 +59926,7 @@ async function detectCompletionInSessionMessages(ctx, options) {
59852
59926
  return false;
59853
59927
  } catch (err) {
59854
59928
  setTimeout(() => {
59855
- log(`[${HOOK_NAME6}] Session messages check failed`, {
59929
+ log(`[${HOOK_NAME7}] Session messages check failed`, {
59856
59930
  sessionID: options.sessionID,
59857
59931
  error: String(err)
59858
59932
  });
@@ -59933,7 +60007,7 @@ async function verifyCompletion(ctx, options) {
59933
60007
  query: { directory: options.directory }
59934
60008
  });
59935
60009
  if (createResult.error || !createResult.data?.id) {
59936
- log(`[${HOOK_NAME6}] Verification session creation failed`);
60010
+ log(`[${HOOK_NAME7}] Verification session creation failed`);
59937
60011
  return { verified: true };
59938
60012
  }
59939
60013
  verifySessionID = createResult.data.id;
@@ -59963,16 +60037,16 @@ async function verifyCompletion(ctx, options) {
59963
60037
  return parseVerificationResponse(assistantText);
59964
60038
  }
59965
60039
  } catch {
59966
- log(`[${HOOK_NAME6}] Verification poll error`);
60040
+ log(`[${HOOK_NAME7}] Verification poll error`);
59967
60041
  }
59968
60042
  await new Promise((resolve4) => {
59969
60043
  setTimeout(resolve4, pollIntervalMs);
59970
60044
  });
59971
60045
  }
59972
- log(`[${HOOK_NAME6}] Verification timed out, assuming pass`);
60046
+ log(`[${HOOK_NAME7}] Verification timed out, assuming pass`);
59973
60047
  return { verified: true };
59974
60048
  } catch (err) {
59975
- log(`[${HOOK_NAME6}] Verification error: ${String(err)}`);
60049
+ log(`[${HOOK_NAME7}] Verification error: ${String(err)}`);
59976
60050
  return { verified: true };
59977
60051
  } finally {
59978
60052
  if (verifySessionID) {
@@ -60048,7 +60122,7 @@ async function injectContinuationPrompt(ctx, options) {
60048
60122
 
60049
60123
  // src/hooks/matrix-loop/matrix-loop-event-handler.ts
60050
60124
  async function handleVerificationFailure(ctx, options, state3, sessionID, reason, failedCount) {
60051
- log(`[${HOOK_NAME6}] Verification failed`, { sessionID, reason });
60125
+ log(`[${HOOK_NAME7}] Verification failed`, { sessionID, reason });
60052
60126
  const newState = options.loopState.incrementIteration();
60053
60127
  if (newState) {
60054
60128
  newState.verification_failed_count = failedCount + 1;
@@ -60066,7 +60140,7 @@ ${buildContinuationPrompt(newState ?? state3)}`,
60066
60140
  apiTimeoutMs: options.apiTimeoutMs
60067
60141
  });
60068
60142
  } catch (err) {
60069
- log(`[${HOOK_NAME6}] Failed to inject verification feedback`, { sessionID, error: String(err) });
60143
+ log(`[${HOOK_NAME7}] Failed to inject verification feedback`, { sessionID, error: String(err) });
60070
60144
  }
60071
60145
  }
60072
60146
  function createMatrixLoopEventHandler(ctx, options) {
@@ -60078,13 +60152,13 @@ function createMatrixLoopEventHandler(ctx, options) {
60078
60152
  if (!sessionID)
60079
60153
  return;
60080
60154
  if (inFlightSessions.has(sessionID)) {
60081
- log(`[${HOOK_NAME6}] Skipped: handler in flight`, { sessionID });
60155
+ log(`[${HOOK_NAME7}] Skipped: handler in flight`, { sessionID });
60082
60156
  return;
60083
60157
  }
60084
60158
  inFlightSessions.add(sessionID);
60085
60159
  try {
60086
60160
  if (options.sessionRecovery.isRecovering(sessionID)) {
60087
- log(`[${HOOK_NAME6}] Skipped: in recovery`, { sessionID });
60161
+ log(`[${HOOK_NAME7}] Skipped: in recovery`, { sessionID });
60088
60162
  return;
60089
60163
  }
60090
60164
  const state3 = options.loopState.getState();
@@ -60096,11 +60170,11 @@ function createMatrixLoopEventHandler(ctx, options) {
60096
60170
  const exists = await options.checkSessionExists(state3.session_id);
60097
60171
  if (!exists) {
60098
60172
  options.loopState.clear();
60099
- log(`[${HOOK_NAME6}] Cleared orphaned state from deleted session`, { orphanedSessionId: state3.session_id, currentSessionId: sessionID });
60173
+ log(`[${HOOK_NAME7}] Cleared orphaned state from deleted session`, { orphanedSessionId: state3.session_id, currentSessionId: sessionID });
60100
60174
  return;
60101
60175
  }
60102
60176
  } catch (err) {
60103
- log(`[${HOOK_NAME6}] Failed to check session existence`, { sessionId: state3.session_id, error: String(err) });
60177
+ log(`[${HOOK_NAME7}] Failed to check session existence`, { sessionId: state3.session_id, error: String(err) });
60104
60178
  }
60105
60179
  }
60106
60180
  return;
@@ -60119,7 +60193,7 @@ function createMatrixLoopEventHandler(ctx, options) {
60119
60193
  messages = Array.isArray(messagesResponse) ? messagesResponse : Array.isArray(responseData) ? responseData : [];
60120
60194
  } catch (err) {
60121
60195
  setTimeout(() => {
60122
- log(`[${HOOK_NAME6}] Session messages fetch failed`, { sessionID, error: String(err) });
60196
+ log(`[${HOOK_NAME7}] Session messages fetch failed`, { sessionID, error: String(err) });
60123
60197
  }, 0);
60124
60198
  messages = [];
60125
60199
  }
@@ -60132,7 +60206,7 @@ function createMatrixLoopEventHandler(ctx, options) {
60132
60206
  preFetchedMessages: messages
60133
60207
  });
60134
60208
  if (completionViaTranscript || completionViaApi) {
60135
- log(`[${HOOK_NAME6}] Completion detected!`, { sessionID, iteration: state3.iteration, promise: state3.completion_promise, detectedVia: completionViaTranscript ? "transcript_file" : "session_messages_api" });
60209
+ log(`[${HOOK_NAME7}] Completion detected!`, { sessionID, iteration: state3.iteration, promise: state3.completion_promise, detectedVia: completionViaTranscript ? "transcript_file" : "session_messages_api" });
60136
60210
  if (options.verification?.enabled === true) {
60137
60211
  const maxRetries = options.verification?.maxRetries ?? DEFAULT_VERIFICATION_MAX_RETRIES;
60138
60212
  const failedCount = state3.verification_failed_count ?? 0;
@@ -60167,7 +60241,7 @@ function createMatrixLoopEventHandler(ctx, options) {
60167
60241
  return;
60168
60242
  }
60169
60243
  if (state3.iteration >= state3.max_iterations) {
60170
- log(`[${HOOK_NAME6}] Max iterations reached`, { sessionID, iteration: state3.iteration, max: state3.max_iterations });
60244
+ log(`[${HOOK_NAME7}] Max iterations reached`, { sessionID, iteration: state3.iteration, max: state3.max_iterations });
60171
60245
  options.loopState.clear();
60172
60246
  await ctx.client.tui.showToast({ body: { title: "Matrix Loop Stopped", message: `Max iterations (${state3.max_iterations}) reached without completion`, variant: "warning", duration: 5000 } }).catch((err) => {
60173
60247
  log("[matrix-loop] Max iterations toast failed:", err);
@@ -60176,17 +60250,17 @@ function createMatrixLoopEventHandler(ctx, options) {
60176
60250
  }
60177
60251
  const newState = options.loopState.incrementIteration();
60178
60252
  if (!newState) {
60179
- log(`[${HOOK_NAME6}] Failed to increment iteration`, { sessionID });
60253
+ log(`[${HOOK_NAME7}] Failed to increment iteration`, { sessionID });
60180
60254
  return;
60181
60255
  }
60182
- log(`[${HOOK_NAME6}] Continuing loop`, { sessionID, iteration: newState.iteration, max: newState.max_iterations });
60256
+ log(`[${HOOK_NAME7}] Continuing loop`, { sessionID, iteration: newState.iteration, max: newState.max_iterations });
60183
60257
  await ctx.client.tui.showToast({ body: { title: "Matrix Loop", message: `Iteration ${newState.iteration}/${newState.max_iterations}`, variant: "info", duration: 2000 } }).catch((err) => {
60184
60258
  log("[matrix-loop] Iteration toast failed:", err);
60185
60259
  });
60186
60260
  try {
60187
60261
  await injectContinuationPrompt(ctx, { sessionID, prompt: buildContinuationPrompt(newState), directory: options.directory, apiTimeoutMs: options.apiTimeoutMs });
60188
60262
  } catch (err) {
60189
- log(`[${HOOK_NAME6}] Failed to inject continuation`, { sessionID, error: String(err) });
60263
+ log(`[${HOOK_NAME7}] Failed to inject continuation`, { sessionID, error: String(err) });
60190
60264
  }
60191
60265
  return;
60192
60266
  } finally {
@@ -60200,7 +60274,7 @@ function createMatrixLoopEventHandler(ctx, options) {
60200
60274
  const state3 = options.loopState.getState();
60201
60275
  if (state3?.session_id === sessionInfo.id) {
60202
60276
  options.loopState.clear();
60203
- log(`[${HOOK_NAME6}] Session deleted, loop cleared`, { sessionID: sessionInfo.id });
60277
+ log(`[${HOOK_NAME7}] Session deleted, loop cleared`, { sessionID: sessionInfo.id });
60204
60278
  }
60205
60279
  options.sessionRecovery.clear(sessionInfo.id);
60206
60280
  return;
@@ -60213,7 +60287,7 @@ function createMatrixLoopEventHandler(ctx, options) {
60213
60287
  const state3 = options.loopState.getState();
60214
60288
  if (state3?.session_id === sessionID) {
60215
60289
  options.loopState.clear();
60216
- log(`[${HOOK_NAME6}] User aborted, loop cleared`, { sessionID });
60290
+ log(`[${HOOK_NAME7}] User aborted, loop cleared`, { sessionID });
60217
60291
  }
60218
60292
  options.sessionRecovery.clear(sessionID);
60219
60293
  }
@@ -60260,7 +60334,7 @@ function createMatrixLoopHook(ctx, options) {
60260
60334
  // src/hooks/matrix-loop/index.ts
60261
60335
  init_storage();
60262
60336
  // src/hooks/mouse-notepad/constants.ts
60263
- var HOOK_NAME7 = "mouse-notepad";
60337
+ var HOOK_NAME8 = "mouse-notepad";
60264
60338
  var NOTEPAD_DIRECTIVE = `
60265
60339
  <Work_Context>
60266
60340
  ## Notepad Location (for recording learnings)
@@ -60307,14 +60381,14 @@ function createMouseNotepadHook(ctx) {
60307
60381
  return;
60308
60382
  }
60309
60383
  output.args.prompt = NOTEPAD_DIRECTIVE + prompt;
60310
- log(`[${HOOK_NAME7}] Injected notepad directive to task`, {
60384
+ log(`[${HOOK_NAME8}] Injected notepad directive to task`, {
60311
60385
  sessionID: input.sessionID
60312
60386
  });
60313
60387
  }
60314
60388
  };
60315
60389
  }
60316
60390
  // src/hooks/non-interactive-env/constants.ts
60317
- var HOOK_NAME8 = "non-interactive-env";
60391
+ var HOOK_NAME9 = "non-interactive-env";
60318
60392
  var NON_INTERACTIVE_ENV = {
60319
60393
  CI: "true",
60320
60394
  DEBIAN_FRONTEND: "noninteractive",
@@ -60405,7 +60479,7 @@ function createNonInteractiveEnvHook(_ctx) {
60405
60479
  }
60406
60480
  const envPrefix = buildEnvPrefix(NON_INTERACTIVE_ENV, "unix");
60407
60481
  output.args.command = `${envPrefix} ${command}`;
60408
- log(`[${HOOK_NAME8}] Prepended non-interactive env vars to git command`, {
60482
+ log(`[${HOOK_NAME9}] Prepended non-interactive env vars to git command`, {
60409
60483
  sessionID: input.sessionID,
60410
60484
  envPrefix
60411
60485
  });
@@ -60413,7 +60487,7 @@ function createNonInteractiveEnvHook(_ctx) {
60413
60487
  };
60414
60488
  }
60415
60489
  // src/hooks/oracle-md-only/constants.ts
60416
- var HOOK_NAME9 = "oracle-md-only";
60490
+ var HOOK_NAME10 = "oracle-md-only";
60417
60491
  var ORACLE_AGENT = "oracle";
60418
60492
  var ALLOWED_EXTENSIONS = [".md"];
60419
60493
  var BLOCKED_TOOLS = ["Write", "Edit", "write", "edit"];
@@ -60546,7 +60620,7 @@ function createOracleMdOnlyHook(ctx) {
60546
60620
  const prompt = output.args.prompt;
60547
60621
  if (prompt && !prompt.includes(SYSTEM_DIRECTIVE_PREFIX)) {
60548
60622
  output.args.prompt = PLANNING_CONSULT_WARNING + prompt;
60549
- log(`[${HOOK_NAME9}] Injected read-only planning warning to ${toolName}`, {
60623
+ log(`[${HOOK_NAME10}] Injected read-only planning warning to ${toolName}`, {
60550
60624
  sessionID: input.sessionID,
60551
60625
  tool: toolName,
60552
60626
  agent: agentName
@@ -60562,17 +60636,17 @@ function createOracleMdOnlyHook(ctx) {
60562
60636
  return;
60563
60637
  }
60564
60638
  if (!isAllowedFile(filePath, ctx.directory)) {
60565
- log(`[${HOOK_NAME9}] Blocked: Oracle can only write to .matrixx/*.md`, {
60639
+ log(`[${HOOK_NAME10}] Blocked: Oracle can only write to .matrixx/*.md`, {
60566
60640
  sessionID: input.sessionID,
60567
60641
  tool: toolName,
60568
60642
  filePath,
60569
60643
  agent: agentName
60570
60644
  });
60571
- throw new Error(`[${HOOK_NAME9}] ${getAgentDisplayName("oracle")} can only write/edit .md files inside .matrixx/ directory. ` + `Attempted to modify: ${filePath}. ` + `${getAgentDisplayName("oracle")} is a READ-ONLY planner. Use /start-work to execute the plan. ` + `APOLOGIZE TO THE USER, REMIND OF YOUR PLAN WRITING PROCESSES, TELL USER WHAT YOU WILL GOING TO DO AS THE PROCESS, WRITE THE PLAN`);
60645
+ throw new Error(`[${HOOK_NAME10}] ${getAgentDisplayName("oracle")} can only write/edit .md files inside .matrixx/ directory. ` + `Attempted to modify: ${filePath}. ` + `${getAgentDisplayName("oracle")} is a READ-ONLY planner. Use /start-work to execute the plan. ` + `APOLOGIZE TO THE USER, REMIND OF YOUR PLAN WRITING PROCESSES, TELL USER WHAT YOU WILL GOING TO DO AS THE PROCESS, WRITE THE PLAN`);
60572
60646
  }
60573
60647
  const normalizedPath = filePath.toLowerCase().replace(/\\/g, "/");
60574
60648
  if (normalizedPath.includes(".matrixx/plans/") || normalizedPath.includes(".matrixx\\plans\\")) {
60575
- log(`[${HOOK_NAME9}] Injecting workflow reminder for plan write`, {
60649
+ log(`[${HOOK_NAME10}] Injecting workflow reminder for plan write`, {
60576
60650
  sessionID: input.sessionID,
60577
60651
  tool: toolName,
60578
60652
  filePath,
@@ -60580,7 +60654,7 @@ function createOracleMdOnlyHook(ctx) {
60580
60654
  });
60581
60655
  output.message = (output.message || "") + ORACLE_WORKFLOW_REMINDER;
60582
60656
  }
60583
- log(`[${HOOK_NAME9}] Allowed: .matrixx/*.md write permitted`, {
60657
+ log(`[${HOOK_NAME10}] Allowed: .matrixx/*.md write permitted`, {
60584
60658
  sessionID: input.sessionID,
60585
60659
  tool: toolName,
60586
60660
  filePath,
@@ -60919,7 +60993,7 @@ var TaskDeleteInputSchema = exports_external.object({
60919
60993
  });
60920
60994
 
60921
60995
  // src/hooks/plan-persister/hook.ts
60922
- var HOOK_NAME10 = "plan-persister";
60996
+ var HOOK_NAME11 = "plan-persister";
60923
60997
  function createPlanPersister(ctx, options) {
60924
60998
  const { directory } = options;
60925
60999
  const capture = async (sessionID) => {
@@ -60954,7 +61028,7 @@ function createPlanPersister(ctx, options) {
60954
61028
  const response = await ctx.client.session.todo({ path: { id: sessionID } });
60955
61029
  todos = normalizeTodos(response);
60956
61030
  } catch (err) {
60957
- log(`[${HOOK_NAME10}] Failed to fetch todos`, { sessionID, error: String(err) });
61031
+ log(`[${HOOK_NAME11}] Failed to fetch todos`, { sessionID, error: String(err) });
60958
61032
  return;
60959
61033
  }
60960
61034
  }
@@ -60977,9 +61051,9 @@ function createPlanPersister(ctx, options) {
60977
61051
  ensurePlanDir(directory);
60978
61052
  const ok = atomicWrite(planPath, finalContent);
60979
61053
  if (ok) {
60980
- log(`[${HOOK_NAME10}] Plan file updated`, { planPath, completed, total });
61054
+ log(`[${HOOK_NAME11}] Plan file updated`, { planPath, completed, total });
60981
61055
  } else {
60982
- log(`[${HOOK_NAME10}] Failed to write plan file`, { planPath });
61056
+ log(`[${HOOK_NAME11}] Failed to write plan file`, { planPath });
60983
61057
  }
60984
61058
  };
60985
61059
  const event = async ({ event: evt }) => {
@@ -62341,7 +62415,7 @@ var RETRYABLE_ERROR_PATTERNS = [
62341
62415
  /(?:^|\s)503(?:\s|$)/,
62342
62416
  /(?:^|\s)529(?:\s|$)/
62343
62417
  ];
62344
- var HOOK_NAME11 = "runtime-fallback";
62418
+ var HOOK_NAME12 = "runtime-fallback";
62345
62419
 
62346
62420
  // src/hooks/runtime-fallback/fallback-models.ts
62347
62421
  init_logger();
@@ -62430,7 +62504,7 @@ function getRawFallbackModelsForSession(sessionID, agent, pluginConfig) {
62430
62504
  if (result)
62431
62505
  return result;
62432
62506
  }
62433
- log(`[${HOOK_NAME11}] No category/agent fallback models resolved for session`, { sessionID, agent });
62507
+ log(`[${HOOK_NAME12}] No category/agent fallback models resolved for session`, { sessionID, agent });
62434
62508
  return;
62435
62509
  }
62436
62510
 
@@ -62459,21 +62533,21 @@ function findNextAvailableFallback(state3, fallbackModels, cooldownSeconds) {
62459
62533
  if (!isModelInCooldown(candidate, state3, cooldownSeconds)) {
62460
62534
  return candidate;
62461
62535
  }
62462
- log(`[${HOOK_NAME11}] Skipping fallback model in cooldown`, { model: candidate, index: i });
62536
+ log(`[${HOOK_NAME12}] Skipping fallback model in cooldown`, { model: candidate, index: i });
62463
62537
  }
62464
62538
  return;
62465
62539
  }
62466
62540
  function prepareFallback(sessionID, state3, fallbackModels, config2) {
62467
62541
  if (state3.attemptCount >= config2.max_fallback_attempts) {
62468
- log(`[${HOOK_NAME11}] Max fallback attempts reached`, { sessionID, attempts: state3.attemptCount });
62542
+ log(`[${HOOK_NAME12}] Max fallback attempts reached`, { sessionID, attempts: state3.attemptCount });
62469
62543
  return { success: false, error: "Max fallback attempts reached", maxAttemptsReached: true };
62470
62544
  }
62471
62545
  const nextModel = findNextAvailableFallback(state3, fallbackModels, config2.cooldown_seconds);
62472
62546
  if (!nextModel) {
62473
- log(`[${HOOK_NAME11}] No available fallback models`, { sessionID });
62547
+ log(`[${HOOK_NAME12}] No available fallback models`, { sessionID });
62474
62548
  return { success: false, error: "No available fallback models (all in cooldown or exhausted)" };
62475
62549
  }
62476
- log(`[${HOOK_NAME11}] Preparing fallback`, {
62550
+ log(`[${HOOK_NAME12}] Preparing fallback`, {
62477
62551
  sessionID,
62478
62552
  from: state3.currentModel,
62479
62553
  to: nextModel,
@@ -62559,9 +62633,9 @@ function createAutoRetryHelpers(deps) {
62559
62633
  const abortSessionRequest = async (sessionID, source) => {
62560
62634
  try {
62561
62635
  await ctx.client.session.abort({ path: { id: sessionID } });
62562
- log(`[${HOOK_NAME11}] Aborted in-flight session request (${source})`, { sessionID });
62636
+ log(`[${HOOK_NAME12}] Aborted in-flight session request (${source})`, { sessionID });
62563
62637
  } catch (error51) {
62564
- log(`[${HOOK_NAME11}] Failed to abort in-flight session request (${source})`, {
62638
+ log(`[${HOOK_NAME12}] Failed to abort in-flight session request (${source})`, {
62565
62639
  sessionID,
62566
62640
  error: String(error51)
62567
62641
  });
@@ -62585,7 +62659,7 @@ function createAutoRetryHelpers(deps) {
62585
62659
  if (!state3)
62586
62660
  return;
62587
62661
  if (sessionRetryInFlight.has(sessionID)) {
62588
- log(`[${HOOK_NAME11}] Overriding in-flight retry due to session timeout`, { sessionID });
62662
+ log(`[${HOOK_NAME12}] Overriding in-flight retry due to session timeout`, { sessionID });
62589
62663
  }
62590
62664
  await abortSessionRequest(sessionID, "session.timeout");
62591
62665
  sessionRetryInFlight.delete(sessionID);
@@ -62595,7 +62669,7 @@ function createAutoRetryHelpers(deps) {
62595
62669
  const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
62596
62670
  if (fallbackModels.length === 0)
62597
62671
  return;
62598
- log(`[${HOOK_NAME11}] Session fallback timeout reached`, {
62672
+ log(`[${HOOK_NAME12}] Session fallback timeout reached`, {
62599
62673
  sessionID,
62600
62674
  timeoutSeconds: config2.timeout_seconds,
62601
62675
  currentModel: state3.currentModel
@@ -62609,7 +62683,7 @@ function createAutoRetryHelpers(deps) {
62609
62683
  };
62610
62684
  const autoRetryWithFallback = async (sessionID, newModel, resolvedAgent, source) => {
62611
62685
  if (sessionRetryInFlight.has(sessionID)) {
62612
- log(`[${HOOK_NAME11}] Retry already in flight, skipping (${source})`, { sessionID });
62686
+ log(`[${HOOK_NAME12}] Retry already in flight, skipping (${source})`, { sessionID });
62613
62687
  return;
62614
62688
  }
62615
62689
  const agentSettings = resolvedAgent ? pluginConfig?.agents?.[resolvedAgent] : undefined;
@@ -62618,7 +62692,7 @@ function createAutoRetryHelpers(deps) {
62618
62692
  reasoningEffort: agentSettings.reasoningEffort
62619
62693
  } : undefined);
62620
62694
  if (!retryModelPayload) {
62621
- log(`[${HOOK_NAME11}] Invalid model format (missing provider prefix): ${newModel}`);
62695
+ log(`[${HOOK_NAME12}] Invalid model format (missing provider prefix): ${newModel}`);
62622
62696
  const state3 = sessionStates2.get(sessionID);
62623
62697
  if (state3?.pendingFallbackModel) {
62624
62698
  state3.pendingFallbackModel = undefined;
@@ -62634,7 +62708,7 @@ function createAutoRetryHelpers(deps) {
62634
62708
  });
62635
62709
  const retryParts = getLastUserRetryParts(messagesResp);
62636
62710
  if (retryParts.length > 0) {
62637
- log(`[${HOOK_NAME11}] Auto-retrying with fallback model (${source})`, {
62711
+ log(`[${HOOK_NAME12}] Auto-retrying with fallback model (${source})`, {
62638
62712
  sessionID,
62639
62713
  model: newModel
62640
62714
  });
@@ -62653,10 +62727,10 @@ function createAutoRetryHelpers(deps) {
62653
62727
  });
62654
62728
  retryDispatched = true;
62655
62729
  } else {
62656
- log(`[${HOOK_NAME11}] No user message found for auto-retry (${source})`, { sessionID });
62730
+ log(`[${HOOK_NAME12}] No user message found for auto-retry (${source})`, { sessionID });
62657
62731
  }
62658
62732
  } catch (retryError) {
62659
- log(`[${HOOK_NAME11}] Auto-retry failed (${source})`, { sessionID, error: String(retryError) });
62733
+ log(`[${HOOK_NAME12}] Auto-retry failed (${source})`, { sessionID, error: String(retryError) });
62660
62734
  } finally {
62661
62735
  sessionRetryInFlight.delete(sessionID);
62662
62736
  if (!retryDispatched) {
@@ -62710,7 +62784,7 @@ function createAutoRetryHelpers(deps) {
62710
62784
  }
62711
62785
  }
62712
62786
  if (cleanedCount > 0) {
62713
- log(`[${HOOK_NAME11}] Cleaned up ${cleanedCount} stale session states`);
62787
+ log(`[${HOOK_NAME12}] Cleaned up ${cleanedCount} stale session states`);
62714
62788
  }
62715
62789
  };
62716
62790
  return {
@@ -62741,7 +62815,7 @@ function createChatMessageHandler(deps) {
62741
62815
  state3.pendingFallbackModel = undefined;
62742
62816
  return;
62743
62817
  }
62744
- log(`[${HOOK_NAME11}] Detected manual model change, resetting fallback state`, {
62818
+ log(`[${HOOK_NAME12}] Detected manual model change, resetting fallback state`, {
62745
62819
  sessionID,
62746
62820
  from: state3.currentModel,
62747
62821
  to: requestedModel
@@ -62753,7 +62827,7 @@ function createChatMessageHandler(deps) {
62753
62827
  if (state3.currentModel === state3.originalModel)
62754
62828
  return;
62755
62829
  const activeModel = state3.currentModel;
62756
- log(`[${HOOK_NAME11}] Applying fallback model override`, {
62830
+ log(`[${HOOK_NAME12}] Applying fallback model override`, {
62757
62831
  sessionID,
62758
62832
  from: input.model,
62759
62833
  to: activeModel
@@ -62940,7 +63014,7 @@ function resolveFallbackBootstrapModel(options) {
62940
63014
  const agentConfig = options.resolvedAgent && agentConfigs ? agentConfigs[options.resolvedAgent] : undefined;
62941
63015
  const agentModel = typeof agentConfig?.model === "string" ? agentConfig.model : undefined;
62942
63016
  if (agentModel) {
62943
- log(`[${HOOK_NAME11}] Derived model from agent config for ${options.source}`, {
63017
+ log(`[${HOOK_NAME12}] Derived model from agent config for ${options.source}`, {
62944
63018
  sessionID: options.sessionID,
62945
63019
  agent: options.resolvedAgent,
62946
63020
  model: agentModel
@@ -62951,7 +63025,7 @@ function resolveFallbackBootstrapModel(options) {
62951
63025
  if (agentCategory) {
62952
63026
  const agentCategoryModel = options.pluginConfig?.categories?.[agentCategory]?.model;
62953
63027
  if (typeof agentCategoryModel === "string" && agentCategoryModel.length > 0) {
62954
- log(`[${HOOK_NAME11}] Derived model from agent category config for ${options.source}`, {
63028
+ log(`[${HOOK_NAME12}] Derived model from agent category config for ${options.source}`, {
62955
63029
  sessionID: options.sessionID,
62956
63030
  agent: options.resolvedAgent,
62957
63031
  category: agentCategory,
@@ -62963,7 +63037,7 @@ function resolveFallbackBootstrapModel(options) {
62963
63037
  const sessionCategory = SessionCategoryRegistry.get(options.sessionID);
62964
63038
  const categoryModel = sessionCategory ? options.pluginConfig?.categories?.[sessionCategory]?.model : undefined;
62965
63039
  if (typeof categoryModel === "string" && categoryModel.length > 0) {
62966
- log(`[${HOOK_NAME11}] Derived model from session category config for ${options.source}`, {
63040
+ log(`[${HOOK_NAME12}] Derived model from session category config for ${options.source}`, {
62967
63041
  sessionID: options.sessionID,
62968
63042
  category: sessionCategory,
62969
63043
  model: categoryModel
@@ -62993,7 +63067,7 @@ async function dispatchFallbackRetry(deps, helpers, options) {
62993
63067
  await helpers.autoRetryWithFallback(options.sessionID, result.newModel, options.resolvedAgent, options.source);
62994
63068
  return;
62995
63069
  }
62996
- log(`[${HOOK_NAME11}] Fallback preparation failed`, {
63070
+ log(`[${HOOK_NAME12}] Fallback preparation failed`, {
62997
63071
  sessionID: options.sessionID,
62998
63072
  source: options.source,
62999
63073
  error: result.error
@@ -63046,14 +63120,14 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
63046
63120
  sessionStatusRetryKeys.set(sessionID, retryKey);
63047
63121
  if (sessionRetryInFlight.has(sessionID)) {
63048
63122
  if (timeoutEnabled) {
63049
- log(`[${HOOK_NAME11}] Overriding in-flight retry due to provider auto-retry signal`, {
63123
+ log(`[${HOOK_NAME12}] Overriding in-flight retry due to provider auto-retry signal`, {
63050
63124
  sessionID,
63051
63125
  model
63052
63126
  });
63053
63127
  await helpers.abortSessionRequest(sessionID, "session.status.retry-signal");
63054
63128
  sessionRetryInFlight.delete(sessionID);
63055
63129
  } else {
63056
- log(`[${HOOK_NAME11}] session.status retry skipped - retry already in flight`, { sessionID });
63130
+ log(`[${HOOK_NAME12}] session.status retry skipped - retry already in flight`, { sessionID });
63057
63131
  return;
63058
63132
  }
63059
63133
  }
@@ -63076,7 +63150,7 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
63076
63150
  });
63077
63151
  if (!initialModel) {
63078
63152
  sessionStatusRetryKeys.delete(sessionID);
63079
- log(`[${HOOK_NAME11}] session.status retry missing model info, cannot fallback`, { sessionID });
63153
+ log(`[${HOOK_NAME12}] session.status retry missing model info, cannot fallback`, { sessionID });
63080
63154
  return;
63081
63155
  }
63082
63156
  state3 = createFallbackState(initialModel);
@@ -63085,20 +63159,20 @@ function createSessionStatusHandler(deps, helpers, sessionStatusRetryKeys) {
63085
63159
  sessionLastAccess.set(sessionID, Date.now());
63086
63160
  if (state3.pendingFallbackModel) {
63087
63161
  if (timeoutEnabled) {
63088
- log(`[${HOOK_NAME11}] Clearing pending fallback due to provider auto-retry signal`, {
63162
+ log(`[${HOOK_NAME12}] Clearing pending fallback due to provider auto-retry signal`, {
63089
63163
  sessionID,
63090
63164
  pendingFallbackModel: state3.pendingFallbackModel
63091
63165
  });
63092
63166
  state3.pendingFallbackModel = undefined;
63093
63167
  } else {
63094
- log(`[${HOOK_NAME11}] session.status retry skipped (pending fallback in progress)`, {
63168
+ log(`[${HOOK_NAME12}] session.status retry skipped (pending fallback in progress)`, {
63095
63169
  sessionID,
63096
63170
  pendingFallbackModel: state3.pendingFallbackModel
63097
63171
  });
63098
63172
  return;
63099
63173
  }
63100
63174
  }
63101
- log(`[${HOOK_NAME11}] Detected provider auto-retry signal in session.status`, {
63175
+ log(`[${HOOK_NAME12}] Detected provider auto-retry signal in session.status`, {
63102
63176
  sessionID,
63103
63177
  model: state3.currentModel,
63104
63178
  retryAttempt: status.attempt
@@ -63134,7 +63208,7 @@ function createEventHandler(deps, helpers) {
63134
63208
  const sessionID = sessionInfo?.id;
63135
63209
  const model = sessionInfo?.model;
63136
63210
  if (sessionID && model) {
63137
- log(`[${HOOK_NAME11}] Session created with model`, { sessionID, model });
63211
+ log(`[${HOOK_NAME12}] Session created with model`, { sessionID, model });
63138
63212
  sessionStates2.set(sessionID, createFallbackState(model));
63139
63213
  sessionLastAccess.set(sessionID, Date.now());
63140
63214
  }
@@ -63143,7 +63217,7 @@ function createEventHandler(deps, helpers) {
63143
63217
  const sessionInfo = props?.info;
63144
63218
  const sessionID = sessionInfo?.id;
63145
63219
  if (sessionID) {
63146
- log(`[${HOOK_NAME11}] Cleaning up session state`, { sessionID });
63220
+ log(`[${HOOK_NAME12}] Cleaning up session state`, { sessionID });
63147
63221
  cancelledSessions.delete(sessionID);
63148
63222
  sessionStates2.delete(sessionID);
63149
63223
  sessionLastAccess.delete(sessionID);
@@ -63163,7 +63237,7 @@ function createEventHandler(deps, helpers) {
63163
63237
  }
63164
63238
  cancelledSessions.add(sessionID);
63165
63239
  resetRetryState(sessionID);
63166
- log(`[${HOOK_NAME11}] Cleared fallback retry state on session.stop`, { sessionID });
63240
+ log(`[${HOOK_NAME12}] Cleared fallback retry state on session.stop`, { sessionID });
63167
63241
  };
63168
63242
  const handleMessageUpdated = (props) => {
63169
63243
  const info = props?.info;
@@ -63179,11 +63253,11 @@ function createEventHandler(deps, helpers) {
63179
63253
  return;
63180
63254
  if (cancelledSessions.has(sessionID)) {
63181
63255
  resetRetryState(sessionID);
63182
- log(`[${HOOK_NAME11}] Cleared fallback retry state for cancelled session on idle`, { sessionID });
63256
+ log(`[${HOOK_NAME12}] Cleared fallback retry state for cancelled session on idle`, { sessionID });
63183
63257
  return;
63184
63258
  }
63185
63259
  if (sessionAwaitingFallbackResult.has(sessionID)) {
63186
- log(`[${HOOK_NAME11}] session.idle while awaiting fallback result; keeping timeout armed`, { sessionID });
63260
+ log(`[${HOOK_NAME12}] session.idle while awaiting fallback result; keeping timeout armed`, { sessionID });
63187
63261
  return;
63188
63262
  }
63189
63263
  const hadTimeout = sessionFallbackTimeouts.has(sessionID);
@@ -63195,7 +63269,7 @@ function createEventHandler(deps, helpers) {
63195
63269
  state3.pendingFallbackModel = undefined;
63196
63270
  }
63197
63271
  if (hadTimeout) {
63198
- log(`[${HOOK_NAME11}] Cleared fallback timeout after session completion`, { sessionID });
63272
+ log(`[${HOOK_NAME12}] Cleared fallback timeout after session completion`, { sessionID });
63199
63273
  }
63200
63274
  };
63201
63275
  const handleSessionError = async (props) => {
@@ -63203,18 +63277,18 @@ function createEventHandler(deps, helpers) {
63203
63277
  const error51 = props?.error;
63204
63278
  const agent = props?.agent;
63205
63279
  if (!sessionID) {
63206
- log(`[${HOOK_NAME11}] session.error without sessionID, skipping`);
63280
+ log(`[${HOOK_NAME12}] session.error without sessionID, skipping`);
63207
63281
  return;
63208
63282
  }
63209
63283
  const resolvedAgent = await helpers.resolveAgentForSessionFromContext(sessionID, agent);
63210
63284
  if (isAbortError(error51)) {
63211
63285
  cancelledSessions.add(sessionID);
63212
63286
  resetRetryState(sessionID);
63213
- log(`[${HOOK_NAME11}] session.error matched cancellation; cleared retry state`, { sessionID, resolvedAgent });
63287
+ log(`[${HOOK_NAME12}] session.error matched cancellation; cleared retry state`, { sessionID, resolvedAgent });
63214
63288
  return;
63215
63289
  }
63216
63290
  if (sessionRetryInFlight.has(sessionID)) {
63217
- log(`[${HOOK_NAME11}] session.error skipped - retry in flight`, {
63291
+ log(`[${HOOK_NAME12}] session.error skipped - retry in flight`, {
63218
63292
  sessionID,
63219
63293
  retryInFlight: true
63220
63294
  });
@@ -63222,7 +63296,7 @@ function createEventHandler(deps, helpers) {
63222
63296
  }
63223
63297
  sessionAwaitingFallbackResult.delete(sessionID);
63224
63298
  helpers.clearSessionFallbackTimeout(sessionID);
63225
- log(`[${HOOK_NAME11}] session.error received`, {
63299
+ log(`[${HOOK_NAME12}] session.error received`, {
63226
63300
  sessionID,
63227
63301
  agent,
63228
63302
  resolvedAgent,
@@ -63231,7 +63305,7 @@ function createEventHandler(deps, helpers) {
63231
63305
  errorType: classifyErrorType(error51)
63232
63306
  });
63233
63307
  if (!isRetryableError(error51, config2.retry_on_errors)) {
63234
- log(`[${HOOK_NAME11}] Error not retryable, skipping fallback`, {
63308
+ log(`[${HOOK_NAME12}] Error not retryable, skipping fallback`, {
63235
63309
  sessionID,
63236
63310
  retryable: false,
63237
63311
  statusCode: extractStatusCode(error51, config2.retry_on_errors),
@@ -63243,7 +63317,7 @@ function createEventHandler(deps, helpers) {
63243
63317
  let state3 = sessionStates2.get(sessionID);
63244
63318
  const fallbackModels = getFallbackModelsForSession(sessionID, resolvedAgent, pluginConfig);
63245
63319
  if (fallbackModels.length === 0) {
63246
- log(`[${HOOK_NAME11}] No fallback models configured`, { sessionID, agent });
63320
+ log(`[${HOOK_NAME12}] No fallback models configured`, { sessionID, agent });
63247
63321
  return;
63248
63322
  }
63249
63323
  if (!state3) {
@@ -63255,7 +63329,7 @@ function createEventHandler(deps, helpers) {
63255
63329
  pluginConfig
63256
63330
  });
63257
63331
  if (!initialModel) {
63258
- log(`[${HOOK_NAME11}] No model info available, cannot fallback`, { sessionID });
63332
+ log(`[${HOOK_NAME12}] No model info available, cannot fallback`, { sessionID });
63259
63333
  return;
63260
63334
  }
63261
63335
  state3 = createFallbackState(initialModel);
@@ -63395,7 +63469,7 @@ function createMessageUpdateHandler(deps, helpers) {
63395
63469
  }
63396
63470
  const hasVisible = await checkVisibleResponse(ctx, sessionID, info);
63397
63471
  if (!hasVisible) {
63398
- log(`[${HOOK_NAME11}] Assistant update observed without visible final response; keeping fallback timeout`, {
63472
+ log(`[${HOOK_NAME12}] Assistant update observed without visible final response; keeping fallback timeout`, {
63399
63473
  sessionID,
63400
63474
  model
63401
63475
  });
@@ -63408,17 +63482,17 @@ function createMessageUpdateHandler(deps, helpers) {
63408
63482
  if (state3?.pendingFallbackModel) {
63409
63483
  state3.pendingFallbackModel = undefined;
63410
63484
  }
63411
- log(`[${HOOK_NAME11}] Assistant response observed; cleared fallback timeout`, { sessionID, model });
63485
+ log(`[${HOOK_NAME12}] Assistant response observed; cleared fallback timeout`, { sessionID, model });
63412
63486
  return;
63413
63487
  }
63414
63488
  if (sessionID && role === "assistant" && error51) {
63415
63489
  sessionAwaitingFallbackResult.delete(sessionID);
63416
63490
  if (sessionRetryInFlight.has(sessionID) && !retrySignal) {
63417
- log(`[${HOOK_NAME11}] message.updated fallback skipped (retry in flight)`, { sessionID });
63491
+ log(`[${HOOK_NAME12}] message.updated fallback skipped (retry in flight)`, { sessionID });
63418
63492
  return;
63419
63493
  }
63420
63494
  if (retrySignal && sessionRetryInFlight.has(sessionID) && timeoutEnabled) {
63421
- log(`[${HOOK_NAME11}] Overriding in-flight retry due to provider auto-retry signal`, {
63495
+ log(`[${HOOK_NAME12}] Overriding in-flight retry due to provider auto-retry signal`, {
63422
63496
  sessionID,
63423
63497
  model
63424
63498
  });
@@ -63426,12 +63500,12 @@ function createMessageUpdateHandler(deps, helpers) {
63426
63500
  sessionRetryInFlight.delete(sessionID);
63427
63501
  }
63428
63502
  if (retrySignal && timeoutEnabled) {
63429
- log(`[${HOOK_NAME11}] Detected provider auto-retry signal`, { sessionID, model });
63503
+ log(`[${HOOK_NAME12}] Detected provider auto-retry signal`, { sessionID, model });
63430
63504
  }
63431
63505
  if (!retrySignal) {
63432
63506
  helpers.clearSessionFallbackTimeout(sessionID);
63433
63507
  }
63434
- log(`[${HOOK_NAME11}] message.updated with assistant error`, {
63508
+ log(`[${HOOK_NAME12}] message.updated with assistant error`, {
63435
63509
  sessionID,
63436
63510
  model,
63437
63511
  statusCode: extractStatusCode(error51, config2.retry_on_errors),
@@ -63439,7 +63513,7 @@ function createMessageUpdateHandler(deps, helpers) {
63439
63513
  errorType: classifyErrorType(error51)
63440
63514
  });
63441
63515
  if (!isRetryableError(error51, config2.retry_on_errors)) {
63442
- log(`[${HOOK_NAME11}] message.updated error not retryable, skipping fallback`, {
63516
+ log(`[${HOOK_NAME12}] message.updated error not retryable, skipping fallback`, {
63443
63517
  sessionID,
63444
63518
  statusCode: extractStatusCode(error51, config2.retry_on_errors),
63445
63519
  errorName: extractErrorName(error51),
@@ -63463,7 +63537,7 @@ function createMessageUpdateHandler(deps, helpers) {
63463
63537
  pluginConfig
63464
63538
  });
63465
63539
  if (!initialModel) {
63466
- log(`[${HOOK_NAME11}] message.updated missing model info, cannot fallback`, {
63540
+ log(`[${HOOK_NAME12}] message.updated missing model info, cannot fallback`, {
63467
63541
  sessionID,
63468
63542
  errorName: extractErrorName(error51),
63469
63543
  errorType: classifyErrorType(error51)
@@ -63477,13 +63551,13 @@ function createMessageUpdateHandler(deps, helpers) {
63477
63551
  sessionLastAccess.set(sessionID, Date.now());
63478
63552
  if (state3.pendingFallbackModel) {
63479
63553
  if (retrySignal && timeoutEnabled) {
63480
- log(`[${HOOK_NAME11}] Clearing pending fallback due to provider auto-retry signal`, {
63554
+ log(`[${HOOK_NAME12}] Clearing pending fallback due to provider auto-retry signal`, {
63481
63555
  sessionID,
63482
63556
  pendingFallbackModel: state3.pendingFallbackModel
63483
63557
  });
63484
63558
  state3.pendingFallbackModel = undefined;
63485
63559
  } else {
63486
- log(`[${HOOK_NAME11}] message.updated fallback skipped (pending fallback in progress)`, {
63560
+ log(`[${HOOK_NAME12}] message.updated fallback skipped (pending fallback in progress)`, {
63487
63561
  sessionID,
63488
63562
  pendingFallbackModel: state3.pendingFallbackModel
63489
63563
  });
@@ -64489,7 +64563,7 @@ function createSessionRecoveryHook(ctx, options) {
64489
64563
  }
64490
64564
  // src/hooks/start-work/start-work-hook.ts
64491
64565
  init_logger();
64492
- var HOOK_NAME12 = "start-work";
64566
+ var HOOK_NAME13 = "start-work";
64493
64567
  var KEYWORD_PATTERN = /\b(ultrawork|ulw)\b/gi;
64494
64568
  function extractUserRequestPlanName(promptText) {
64495
64569
  const userRequestMatch = promptText.match(/<user-request>\s*([\s\S]*?)\s*<\/user-request>/i);
@@ -64519,7 +64593,7 @@ function createStartWorkHook(ctx) {
64519
64593
  if (!isStartWorkCommand) {
64520
64594
  return;
64521
64595
  }
64522
- log(`[${HOOK_NAME12}] Processing start-work command`, {
64596
+ log(`[${HOOK_NAME13}] Processing start-work command`, {
64523
64597
  sessionID: input.sessionID
64524
64598
  });
64525
64599
  updateSessionAgent(input.sessionID, "architect");
@@ -64529,7 +64603,7 @@ function createStartWorkHook(ctx) {
64529
64603
  let contextInfo = "";
64530
64604
  const explicitPlanName = extractUserRequestPlanName(promptText);
64531
64605
  if (explicitPlanName) {
64532
- log(`[${HOOK_NAME12}] Explicit plan name requested: ${explicitPlanName}`, {
64606
+ log(`[${HOOK_NAME13}] Explicit plan name requested: ${explicitPlanName}`, {
64533
64607
  sessionID: input.sessionID
64534
64608
  });
64535
64609
  const allPlans = findOraclePlans(ctx.directory);
@@ -64670,7 +64744,7 @@ Ask the user which plan to work on. Present the options above and wait for their
64670
64744
  ---
64671
64745
  ${contextInfo}`;
64672
64746
  }
64673
- log(`[${HOOK_NAME12}] Context injected`, {
64747
+ log(`[${HOOK_NAME13}] Context injected`, {
64674
64748
  sessionID: input.sessionID,
64675
64749
  hasExistingState: !!existingState
64676
64750
  });
@@ -64756,7 +64830,7 @@ function isQuestionTool(properties) {
64756
64830
 
64757
64831
  // src/hooks/stop-continuation-guard/hook.ts
64758
64832
  init_logger();
64759
- var HOOK_NAME13 = "stop-continuation-guard";
64833
+ var HOOK_NAME14 = "stop-continuation-guard";
64760
64834
  function createStopContinuationGuardHook(ctx, options) {
64761
64835
  const stoppedSessions = new Set;
64762
64836
  const isSessionAwaitingUser = async (sessionID) => {
@@ -64769,22 +64843,22 @@ function createStopContinuationGuardHook(ctx, options) {
64769
64843
  });
64770
64844
  return hasPendingQuestionMessage(normalizeSDKResponse(resp, []));
64771
64845
  } catch (error51) {
64772
- log(`[${HOOK_NAME13}] Messages fetch failed, assuming not awaiting user`, { sessionID, error: String(error51) });
64846
+ log(`[${HOOK_NAME14}] Messages fetch failed, assuming not awaiting user`, { sessionID, error: String(error51) });
64773
64847
  return false;
64774
64848
  }
64775
64849
  };
64776
64850
  const stop = async (sessionID) => {
64777
64851
  stoppedSessions.add(sessionID);
64778
- log(`[${HOOK_NAME13}] Continuation stopped for session`, { sessionID });
64852
+ log(`[${HOOK_NAME14}] Continuation stopped for session`, { sessionID });
64779
64853
  if (!options?.backgroundManager)
64780
64854
  return;
64781
64855
  if (await isSessionAwaitingUser(sessionID)) {
64782
- log(`[${HOOK_NAME13}] Background cancellation suppressed: awaiting user`, { sessionID });
64856
+ log(`[${HOOK_NAME14}] Background cancellation suppressed: awaiting user`, { sessionID });
64783
64857
  return;
64784
64858
  }
64785
64859
  const cancelled = options.backgroundManager.cancelAllForSession(sessionID);
64786
64860
  if (cancelled > 0) {
64787
- log(`[${HOOK_NAME13}] Cancelled ${cancelled} background task(s) for session`, { sessionID });
64861
+ log(`[${HOOK_NAME14}] Cancelled ${cancelled} background task(s) for session`, { sessionID });
64788
64862
  }
64789
64863
  };
64790
64864
  const isStopped = (sessionID) => {
@@ -64792,7 +64866,7 @@ function createStopContinuationGuardHook(ctx, options) {
64792
64866
  };
64793
64867
  const clear = (sessionID) => {
64794
64868
  stoppedSessions.delete(sessionID);
64795
- log(`[${HOOK_NAME13}] Continuation guard cleared for session`, { sessionID });
64869
+ log(`[${HOOK_NAME14}] Continuation guard cleared for session`, { sessionID });
64796
64870
  };
64797
64871
  const event = async ({
64798
64872
  event: event2
@@ -64802,7 +64876,7 @@ function createStopContinuationGuardHook(ctx, options) {
64802
64876
  const sessionInfo = props?.info;
64803
64877
  if (sessionInfo?.id) {
64804
64878
  clear(sessionInfo.id);
64805
- log(`[${HOOK_NAME13}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
64879
+ log(`[${HOOK_NAME14}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
64806
64880
  }
64807
64881
  }
64808
64882
  };
@@ -64811,7 +64885,7 @@ function createStopContinuationGuardHook(ctx, options) {
64811
64885
  }) => {
64812
64886
  if (sessionID && stoppedSessions.has(sessionID)) {
64813
64887
  clear(sessionID);
64814
- log(`[${HOOK_NAME13}] Cleared stop state on new user message`, { sessionID });
64888
+ log(`[${HOOK_NAME14}] Cleared stop state on new user message`, { sessionID });
64815
64889
  }
64816
64890
  };
64817
64891
  return {
@@ -64826,7 +64900,7 @@ function createStopContinuationGuardHook(ctx, options) {
64826
64900
  init_logger();
64827
64901
 
64828
64902
  // src/hooks/task-continuation-enforcer/constants.ts
64829
- var HOOK_NAME14 = "task-continuation-enforcer";
64903
+ var HOOK_NAME15 = "task-continuation-enforcer";
64830
64904
  var DEFAULT_SKIP_AGENTS = ["oracle", "compaction"];
64831
64905
  var EXPLORER_AGENTS = ["trinity", "operator"];
64832
64906
  function hasNonExplorerBgTasks(tasks) {
@@ -64973,21 +65047,21 @@ async function injectContinuation(args) {
64973
65047
  config: config2
64974
65048
  } = args;
64975
65049
  if (subagentSessions.has(sessionID)) {
64976
- log(`[${HOOK_NAME14}] Skipped injection: subagent session`, { sessionID });
65050
+ log(`[${HOOK_NAME15}] Skipped injection: subagent session`, { sessionID });
64977
65051
  return;
64978
65052
  }
64979
65053
  const state3 = sessionStateStore.getExistingState(sessionID);
64980
65054
  if (state3?.isRecovering) {
64981
- log(`[${HOOK_NAME14}] Skipped injection: in recovery`, { sessionID });
65055
+ log(`[${HOOK_NAME15}] Skipped injection: in recovery`, { sessionID });
64982
65056
  return;
64983
65057
  }
64984
65058
  if (isAwaitingUser(state3)) {
64985
- log(`[${HOOK_NAME14}] Skipped injection: awaiting user`, { sessionID });
65059
+ log(`[${HOOK_NAME15}] Skipped injection: awaiting user`, { sessionID });
64986
65060
  return;
64987
65061
  }
64988
65062
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
64989
65063
  if (hasRunningBgTasks) {
64990
- log(`[${HOOK_NAME14}] Skipped injection: background tasks running`, { sessionID });
65064
+ log(`[${HOOK_NAME15}] Skipped injection: background tasks running`, { sessionID });
64991
65065
  return;
64992
65066
  }
64993
65067
  const earlyInjectionState = sessionStateStore.getExistingState(sessionID);
@@ -64995,7 +65069,7 @@ async function injectContinuation(args) {
64995
65069
  const now = Date.now();
64996
65070
  const elapsedMs = now - earlyInjectionState.lastInjectedAt;
64997
65071
  if (elapsedMs < CONTINUATION_COOLDOWN_MS3) {
64998
- log(`[${HOOK_NAME14}] Skipped injection: cooldown active`, {
65072
+ log(`[${HOOK_NAME15}] Skipped injection: cooldown active`, {
64999
65073
  sessionID,
65000
65074
  cooldownMs: CONTINUATION_COOLDOWN_MS3,
65001
65075
  elapsedMs
@@ -65013,10 +65087,10 @@ async function injectContinuation(args) {
65013
65087
  if (!existsSync42(taskDir)) {
65014
65088
  const hadNonExplorerBgTasks = backgroundManager ? hasNonExplorerBgTasks(backgroundManager.getTasksByParentSession(sessionID)) : false;
65015
65089
  if (hadNonExplorerBgTasks) {
65016
- log(`[${HOOK_NAME14}] Bootstrap injection: no task dir (hadNonExplorerBgTasks)`, { sessionID, taskDir });
65090
+ log(`[${HOOK_NAME15}] Bootstrap injection: no task dir (hadNonExplorerBgTasks)`, { sessionID, taskDir });
65017
65091
  isBootstrap = true;
65018
65092
  } else {
65019
- log(`[${HOOK_NAME14}] Skipped injection: no task dir`, { sessionID, taskDir });
65093
+ log(`[${HOOK_NAME15}] Skipped injection: no task dir`, { sessionID, taskDir });
65020
65094
  return;
65021
65095
  }
65022
65096
  total = 0;
@@ -65036,16 +65110,16 @@ async function injectContinuation(args) {
65036
65110
  if (total === 0) {
65037
65111
  const hadNonExplorerBgTasks = backgroundManager ? hasNonExplorerBgTasks(backgroundManager.getTasksByParentSession(sessionID)) : false;
65038
65112
  if (hadNonExplorerBgTasks) {
65039
- log(`[${HOOK_NAME14}] Bootstrap injection: no tasks (hadNonExplorerBgTasks)`, { sessionID });
65113
+ log(`[${HOOK_NAME15}] Bootstrap injection: no tasks (hadNonExplorerBgTasks)`, { sessionID });
65040
65114
  isBootstrap = true;
65041
65115
  } else {
65042
- log(`[${HOOK_NAME14}] Skipped injection: no tasks`, { sessionID });
65116
+ log(`[${HOOK_NAME15}] Skipped injection: no tasks`, { sessionID });
65043
65117
  return;
65044
65118
  }
65045
65119
  }
65046
65120
  }
65047
65121
  } catch (error51) {
65048
- log(`[${HOOK_NAME14}] Failed to fetch tasks`, { sessionID, error: String(error51) });
65122
+ log(`[${HOOK_NAME15}] Failed to fetch tasks`, { sessionID, error: String(error51) });
65049
65123
  return;
65050
65124
  }
65051
65125
  const stateForBootstrap = sessionStateStore.getExistingState(sessionID);
@@ -65056,7 +65130,7 @@ async function injectContinuation(args) {
65056
65130
  const staleAfterMs = getStaleAfterMs(config2);
65057
65131
  const freshIncompleteCount = isBootstrap ? 1 : filterFreshIncompleteTasks(getIncompleteTasks(filteredTasks), taskDir, staleAfterMs).length;
65058
65132
  if (!isBootstrap && freshIncompleteCount === 0) {
65059
- log(`[${HOOK_NAME14}] Skipped injection: only stale tasks remain`, { sessionID, total });
65133
+ log(`[${HOOK_NAME15}] Skipped injection: only stale tasks remain`, { sessionID, total });
65060
65134
  return;
65061
65135
  }
65062
65136
  let agentName = resolvedInfo?.agent;
@@ -65079,11 +65153,11 @@ async function injectContinuation(args) {
65079
65153
  tools = tools ?? previousMessage?.tools;
65080
65154
  }
65081
65155
  if (agentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(agentName))) {
65082
- log(`[${HOOK_NAME14}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
65156
+ log(`[${HOOK_NAME15}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
65083
65157
  return;
65084
65158
  }
65085
65159
  if (!hasWritePermission(tools)) {
65086
- log(`[${HOOK_NAME14}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
65160
+ log(`[${HOOK_NAME15}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
65087
65161
  return;
65088
65162
  }
65089
65163
  let prompt;
@@ -65117,11 +65191,11 @@ ${taskList}${staleNote}`;
65117
65191
  if (injectionState) {
65118
65192
  injectionState.inFlight = false;
65119
65193
  }
65120
- log(`[${HOOK_NAME14}] Skipped injection: awaiting user (race)`, { sessionID });
65194
+ log(`[${HOOK_NAME15}] Skipped injection: awaiting user (race)`, { sessionID });
65121
65195
  return;
65122
65196
  }
65123
65197
  try {
65124
- log(`[${HOOK_NAME14}] Injecting continuation`, {
65198
+ log(`[${HOOK_NAME15}] Injecting continuation`, {
65125
65199
  sessionID,
65126
65200
  agent: agentName,
65127
65201
  model,
@@ -65136,14 +65210,14 @@ ${taskList}${staleNote}`;
65136
65210
  },
65137
65211
  query: { directory: ctx.directory }
65138
65212
  });
65139
- log(`[${HOOK_NAME14}] Injection successful`, { sessionID });
65213
+ log(`[${HOOK_NAME15}] Injection successful`, { sessionID });
65140
65214
  if (injectionState) {
65141
65215
  injectionState.inFlight = false;
65142
65216
  injectionState.lastInjectedAt = Date.now();
65143
65217
  injectionState.consecutiveFailures = 0;
65144
65218
  }
65145
65219
  } catch (error51) {
65146
- log(`[${HOOK_NAME14}] Injection failed`, { sessionID, error: String(error51) });
65220
+ log(`[${HOOK_NAME15}] Injection failed`, { sessionID, error: String(error51) });
65147
65221
  if (injectionState) {
65148
65222
  injectionState.inFlight = false;
65149
65223
  injectionState.lastInjectedAt = Date.now();
@@ -65178,7 +65252,7 @@ function startCountdown(args) {
65178
65252
  } = args;
65179
65253
  const state3 = sessionStateStore.getState(sessionID);
65180
65254
  if (state3.countdownTimer || state3.countdownInterval) {
65181
- log(`[${HOOK_NAME14}] Skipped: countdown already active`, { sessionID });
65255
+ log(`[${HOOK_NAME15}] Skipped: countdown already active`, { sessionID });
65182
65256
  return;
65183
65257
  }
65184
65258
  let secondsRemaining = COUNTDOWN_SECONDS;
@@ -65202,7 +65276,7 @@ function startCountdown(args) {
65202
65276
  config: config2
65203
65277
  });
65204
65278
  }, COUNTDOWN_SECONDS * 1000);
65205
- log(`[${HOOK_NAME14}] Countdown started`, {
65279
+ log(`[${HOOK_NAME15}] Countdown started`, {
65206
65280
  sessionID,
65207
65281
  seconds: COUNTDOWN_SECONDS,
65208
65282
  incompleteCount
@@ -65220,36 +65294,36 @@ async function handleSessionIdle(args) {
65220
65294
  isContinuationStopped,
65221
65295
  config: config2
65222
65296
  } = args;
65223
- log(`[${HOOK_NAME14}] session.idle`, { sessionID });
65297
+ log(`[${HOOK_NAME15}] session.idle`, { sessionID });
65224
65298
  if (subagentSessions.has(sessionID)) {
65225
- log(`[${HOOK_NAME14}] Skipped: subagent session`, { sessionID });
65299
+ log(`[${HOOK_NAME15}] Skipped: subagent session`, { sessionID });
65226
65300
  return;
65227
65301
  }
65228
65302
  const state3 = sessionStateStore.getState(sessionID);
65229
65303
  if (state3.countdownTimer || state3.countdownInterval) {
65230
- log(`[${HOOK_NAME14}] Skipped: countdown already active`, { sessionID });
65304
+ log(`[${HOOK_NAME15}] Skipped: countdown already active`, { sessionID });
65231
65305
  return;
65232
65306
  }
65233
65307
  if (state3.isRecovering) {
65234
- log(`[${HOOK_NAME14}] Skipped: in recovery`, { sessionID });
65308
+ log(`[${HOOK_NAME15}] Skipped: in recovery`, { sessionID });
65235
65309
  return;
65236
65310
  }
65237
65311
  if (state3.abortDetectedAt) {
65238
65312
  const timeSinceAbort = Date.now() - state3.abortDetectedAt;
65239
65313
  if (timeSinceAbort < ABORT_WINDOW_MS) {
65240
- log(`[${HOOK_NAME14}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
65314
+ log(`[${HOOK_NAME15}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
65241
65315
  state3.abortDetectedAt = undefined;
65242
65316
  return;
65243
65317
  }
65244
65318
  state3.abortDetectedAt = undefined;
65245
65319
  }
65246
65320
  if (isAwaitingUser(state3)) {
65247
- log(`[${HOOK_NAME14}] Skipped: awaiting user`, { sessionID });
65321
+ log(`[${HOOK_NAME15}] Skipped: awaiting user`, { sessionID });
65248
65322
  return;
65249
65323
  }
65250
65324
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
65251
65325
  if (hasRunningBgTasks) {
65252
- log(`[${HOOK_NAME14}] Skipped: background tasks running`, { sessionID });
65326
+ log(`[${HOOK_NAME15}] Skipped: background tasks running`, { sessionID });
65253
65327
  return;
65254
65328
  }
65255
65329
  try {
@@ -65259,15 +65333,15 @@ async function handleSessionIdle(args) {
65259
65333
  });
65260
65334
  const messages = normalizeSDKResponse(messagesResp, []);
65261
65335
  if (isLastAssistantMessageAborted(messages)) {
65262
- log(`[${HOOK_NAME14}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
65336
+ log(`[${HOOK_NAME15}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
65263
65337
  return;
65264
65338
  }
65265
65339
  if (isAwaitingUser(state3, messages)) {
65266
- log(`[${HOOK_NAME14}] Skipped: awaiting user (pending question)`, { sessionID });
65340
+ log(`[${HOOK_NAME15}] Skipped: awaiting user (pending question)`, { sessionID });
65267
65341
  return;
65268
65342
  }
65269
65343
  } catch (error51) {
65270
- log(`[${HOOK_NAME14}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
65344
+ log(`[${HOOK_NAME15}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
65271
65345
  }
65272
65346
  let incompleteCount = 0;
65273
65347
  let total = 0;
@@ -65278,12 +65352,12 @@ async function handleSessionIdle(args) {
65278
65352
  if (!existsSync43(taskDir)) {
65279
65353
  const hadNonExplorerBgTasks = backgroundManager ? hasNonExplorerBgTasks(backgroundManager.getTasksByParentSession(sessionID)) : false;
65280
65354
  if (hadNonExplorerBgTasks) {
65281
- log(`[${HOOK_NAME14}] No task dir \u2014 bootstrap (hadBgTasks)`, { sessionID, taskDir });
65355
+ log(`[${HOOK_NAME15}] No task dir \u2014 bootstrap (hadBgTasks)`, { sessionID, taskDir });
65282
65356
  isBootstrap = true;
65283
65357
  total = 0;
65284
65358
  incompleteCount = 1;
65285
65359
  } else {
65286
- log(`[${HOOK_NAME14}] No task dir`, { sessionID, taskDir });
65360
+ log(`[${HOOK_NAME15}] No task dir`, { sessionID, taskDir });
65287
65361
  return;
65288
65362
  }
65289
65363
  } else {
@@ -65303,11 +65377,11 @@ async function handleSessionIdle(args) {
65303
65377
  if (total === 0) {
65304
65378
  const hadNonExplorerBgTasks = backgroundManager ? hasNonExplorerBgTasks(backgroundManager.getTasksByParentSession(sessionID)) : false;
65305
65379
  if (hadNonExplorerBgTasks) {
65306
- log(`[${HOOK_NAME14}] No tasks \u2014 bootstrap (hadNonExplorerBgTasks)`, { sessionID });
65380
+ log(`[${HOOK_NAME15}] No tasks \u2014 bootstrap (hadNonExplorerBgTasks)`, { sessionID });
65307
65381
  isBootstrap = true;
65308
65382
  incompleteCount = 1;
65309
65383
  } else {
65310
- log(`[${HOOK_NAME14}] No tasks`, { sessionID });
65384
+ log(`[${HOOK_NAME15}] No tasks`, { sessionID });
65311
65385
  return;
65312
65386
  }
65313
65387
  } else {
@@ -65317,7 +65391,7 @@ async function handleSessionIdle(args) {
65317
65391
  const staleIncompleteCount = incompleteTasks.filter((t) => isTaskStale(join52(taskDir, `${t.id}.json`), staleAfterMs)).length;
65318
65392
  const activeIncompleteCount = incompleteCount - staleIncompleteCount;
65319
65393
  if (activeIncompleteCount === 0 && staleIncompleteCount > 0) {
65320
- log(`[${HOOK_NAME14}] Skipped: only stale tasks remain`, {
65394
+ log(`[${HOOK_NAME15}] Skipped: only stale tasks remain`, {
65321
65395
  sessionID,
65322
65396
  staleIncompleteCount,
65323
65397
  staleAfterMs
@@ -65327,11 +65401,11 @@ async function handleSessionIdle(args) {
65327
65401
  }
65328
65402
  }
65329
65403
  } catch (error51) {
65330
- log(`[${HOOK_NAME14}] Task fetch failed`, { sessionID, error: String(error51) });
65404
+ log(`[${HOOK_NAME15}] Task fetch failed`, { sessionID, error: String(error51) });
65331
65405
  return;
65332
65406
  }
65333
65407
  if (!isBootstrap && incompleteCount === 0) {
65334
- log(`[${HOOK_NAME14}] All tasks complete`, { sessionID, total });
65408
+ log(`[${HOOK_NAME15}] All tasks complete`, { sessionID, total });
65335
65409
  return;
65336
65410
  }
65337
65411
  if (isBootstrap) {
@@ -65339,18 +65413,18 @@ async function handleSessionIdle(args) {
65339
65413
  s._bootstrap = true;
65340
65414
  }
65341
65415
  if (state3.inFlight) {
65342
- log(`[${HOOK_NAME14}] Skipped: injection in flight`, { sessionID });
65416
+ log(`[${HOOK_NAME15}] Skipped: injection in flight`, { sessionID });
65343
65417
  return;
65344
65418
  }
65345
65419
  if ((state3.consecutiveFailures ?? 0) >= MAX_CONSECUTIVE_FAILURES && state3.lastInjectedAt && Date.now() - state3.lastInjectedAt >= FAILURE_RESET_WINDOW_MS) {
65346
65420
  state3.consecutiveFailures = 0;
65347
- log(`[${HOOK_NAME14}] Reset consecutive failures after recovery window`, {
65421
+ log(`[${HOOK_NAME15}] Reset consecutive failures after recovery window`, {
65348
65422
  sessionID,
65349
65423
  failureResetWindowMs: FAILURE_RESET_WINDOW_MS
65350
65424
  });
65351
65425
  }
65352
65426
  if ((state3.consecutiveFailures ?? 0) >= MAX_CONSECUTIVE_FAILURES) {
65353
- log(`[${HOOK_NAME14}] Skipped: max consecutive failures reached`, {
65427
+ log(`[${HOOK_NAME15}] Skipped: max consecutive failures reached`, {
65354
65428
  sessionID,
65355
65429
  consecutiveFailures: state3.consecutiveFailures ?? 0,
65356
65430
  maxConsecutiveFailures: MAX_CONSECUTIVE_FAILURES
@@ -65359,7 +65433,7 @@ async function handleSessionIdle(args) {
65359
65433
  }
65360
65434
  const effectiveCooldown = CONTINUATION_COOLDOWN_MS3 * 2 ** Math.min(state3.consecutiveFailures ?? 0, 5);
65361
65435
  if (state3.lastInjectedAt && Date.now() - state3.lastInjectedAt < effectiveCooldown) {
65362
- log(`[${HOOK_NAME14}] Skipped: cooldown active`, {
65436
+ log(`[${HOOK_NAME15}] Skipped: cooldown active`, {
65363
65437
  sessionID,
65364
65438
  effectiveCooldown,
65365
65439
  consecutiveFailures: state3.consecutiveFailures ?? 0
@@ -65389,24 +65463,24 @@ async function handleSessionIdle(args) {
65389
65463
  }
65390
65464
  }
65391
65465
  } catch (error51) {
65392
- log(`[${HOOK_NAME14}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
65466
+ log(`[${HOOK_NAME15}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
65393
65467
  }
65394
- log(`[${HOOK_NAME14}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
65468
+ log(`[${HOOK_NAME15}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
65395
65469
  const resolvedAgentName = resolvedInfo?.agent;
65396
65470
  if (resolvedAgentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
65397
- log(`[${HOOK_NAME14}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
65471
+ log(`[${HOOK_NAME15}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
65398
65472
  return;
65399
65473
  }
65400
65474
  if (hasCompactionMessage && !resolvedInfo?.agent) {
65401
- log(`[${HOOK_NAME14}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
65475
+ log(`[${HOOK_NAME15}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
65402
65476
  return;
65403
65477
  }
65404
65478
  if (isContinuationStopped?.(sessionID)) {
65405
- log(`[${HOOK_NAME14}] Skipped: continuation stopped for session`, { sessionID });
65479
+ log(`[${HOOK_NAME15}] Skipped: continuation stopped for session`, { sessionID });
65406
65480
  return;
65407
65481
  }
65408
65482
  if (isAwaitingUser(state3)) {
65409
- log(`[${HOOK_NAME14}] Skipped: awaiting user (race)`, { sessionID });
65483
+ log(`[${HOOK_NAME15}] Skipped: awaiting user (race)`, { sessionID });
65410
65484
  return;
65411
65485
  }
65412
65486
  startCountdown({
@@ -65439,7 +65513,7 @@ function handleNonIdleEvent(args) {
65439
65513
  if (state3?.countdownStartedAt) {
65440
65514
  const elapsed = Date.now() - state3.countdownStartedAt;
65441
65515
  if (elapsed < COUNTDOWN_GRACE_PERIOD_MS) {
65442
- log(`[${HOOK_NAME14}] Ignoring user message in grace period`, { sessionID, elapsed });
65516
+ log(`[${HOOK_NAME15}] Ignoring user message in grace period`, { sessionID, elapsed });
65443
65517
  return;
65444
65518
  }
65445
65519
  }
@@ -65485,7 +65559,7 @@ function handleNonIdleEvent(args) {
65485
65559
  const sessionInfo = properties?.info;
65486
65560
  if (sessionInfo?.id) {
65487
65561
  sessionStateStore.cleanup(sessionInfo.id);
65488
- log(`[${HOOK_NAME14}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
65562
+ log(`[${HOOK_NAME15}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
65489
65563
  }
65490
65564
  return;
65491
65565
  }
@@ -65511,10 +65585,10 @@ function createTaskContinuationHandler(args) {
65511
65585
  if (error51?.name === "MessageAbortedError" || error51?.name === "AbortError") {
65512
65586
  const state3 = sessionStateStore.getState(sessionID);
65513
65587
  state3.abortDetectedAt = Date.now();
65514
- log(`[${HOOK_NAME14}] Abort detected via session.error`, { sessionID, errorName: error51.name });
65588
+ log(`[${HOOK_NAME15}] Abort detected via session.error`, { sessionID, errorName: error51.name });
65515
65589
  }
65516
65590
  sessionStateStore.cancelCountdown(sessionID);
65517
- log(`[${HOOK_NAME14}] session.error`, { sessionID });
65591
+ log(`[${HOOK_NAME15}] session.error`, { sessionID });
65518
65592
  return;
65519
65593
  }
65520
65594
  if (event.type === "session.idle") {
@@ -65634,13 +65708,13 @@ function createTaskContinuationEnforcer(ctx, options = {}) {
65634
65708
  const state3 = sessionStateStore.getState(sessionID);
65635
65709
  state3.isRecovering = true;
65636
65710
  sessionStateStore.cancelCountdown(sessionID);
65637
- log(`[${HOOK_NAME14}] Session marked as recovering`, { sessionID });
65711
+ log(`[${HOOK_NAME15}] Session marked as recovering`, { sessionID });
65638
65712
  };
65639
65713
  const markRecoveryComplete = (sessionID) => {
65640
65714
  const state3 = sessionStateStore.getExistingState(sessionID);
65641
65715
  if (state3) {
65642
65716
  state3.isRecovering = false;
65643
- log(`[${HOOK_NAME14}] Session recovery complete`, { sessionID });
65717
+ log(`[${HOOK_NAME15}] Session recovery complete`, { sessionID });
65644
65718
  }
65645
65719
  };
65646
65720
  const handler = createTaskContinuationHandler({
@@ -65653,7 +65727,7 @@ function createTaskContinuationEnforcer(ctx, options = {}) {
65653
65727
  });
65654
65728
  const cancelAllCountdowns = () => {
65655
65729
  sessionStateStore.cancelAllCountdowns();
65656
- log(`[${HOOK_NAME14}] All countdowns cancelled`);
65730
+ log(`[${HOOK_NAME15}] All countdowns cancelled`);
65657
65731
  };
65658
65732
  return {
65659
65733
  handler,
@@ -65664,8 +65738,8 @@ function createTaskContinuationEnforcer(ctx, options = {}) {
65664
65738
  };
65665
65739
  }
65666
65740
  // src/hooks/task-edit-guard/constants.ts
65667
- var HOOK_NAME15 = "task-edit-guard";
65668
- var BLOCKED_PATTERNS = [
65741
+ var HOOK_NAME16 = "task-edit-guard";
65742
+ var BLOCKED_PATTERNS2 = [
65669
65743
  /sed\s+.*\.matrixx\/plans/,
65670
65744
  /sed\s+.*\.matrixx\/tasks/,
65671
65745
  /python3?\s+.*\.matrixx\/plans/,
@@ -65698,7 +65772,7 @@ function createTaskEditGuardHook(ctx) {
65698
65772
  if (filePath) {
65699
65773
  const normalized = filePath.toLowerCase().replace(/\\/g, "/");
65700
65774
  if (normalized.includes(".matrixx/plans")) {
65701
- log(`[${HOOK_NAME15}] BLOCKED generic Write/Edit to .matrixx/plans \u2014 use plan_*`, {
65775
+ log(`[${HOOK_NAME16}] BLOCKED generic Write/Edit to .matrixx/plans \u2014 use plan_*`, {
65702
65776
  sessionID: input.sessionID,
65703
65777
  tool: input.tool,
65704
65778
  filePath
@@ -65714,7 +65788,7 @@ function createTaskEditGuardHook(ctx) {
65714
65788
  if (filePath) {
65715
65789
  const normalized = filePath.toLowerCase().replace(/\\/g, "/");
65716
65790
  if (normalized.includes(".matrixx/plans")) {
65717
- log(`[${HOOK_NAME15}] BLOCKED generic Read to .matrixx/plans \u2014 use plan_read`, {
65791
+ log(`[${HOOK_NAME16}] BLOCKED generic Read to .matrixx/plans \u2014 use plan_read`, {
65718
65792
  sessionID: input.sessionID,
65719
65793
  tool: input.tool,
65720
65794
  filePath
@@ -65732,15 +65806,15 @@ function createTaskEditGuardHook(ctx) {
65732
65806
  return;
65733
65807
  const lower = cmd.toLowerCase();
65734
65808
  if (lower.includes("grep") && !lower.includes("sed") && !lower.includes("python")) {
65735
- const isOnlyGrep = BLOCKED_PATTERNS.every((rx) => !rx.test(cmd));
65809
+ const isOnlyGrep = BLOCKED_PATTERNS2.every((rx) => !rx.test(cmd));
65736
65810
  if (isOnlyGrep)
65737
65811
  return;
65738
65812
  }
65739
- const hit = BLOCKED_PATTERNS.some((rx) => rx.test(cmd));
65813
+ const hit = BLOCKED_PATTERNS2.some((rx) => rx.test(cmd));
65740
65814
  if (!hit)
65741
65815
  return;
65742
65816
  ctx.directory;
65743
- log(`[${HOOK_NAME15}] BLOCKED raw bash edit`, {
65817
+ log(`[${HOOK_NAME16}] BLOCKED raw bash edit`, {
65744
65818
  sessionID: input.sessionID,
65745
65819
  command: cmd.slice(0, 120)
65746
65820
  });
@@ -65749,7 +65823,7 @@ function createTaskEditGuardHook(ctx) {
65749
65823
  };
65750
65824
  }
65751
65825
  // src/hooks/task-notepad/constants.ts
65752
- var HOOK_NAME16 = "task-notepad";
65826
+ var HOOK_NAME17 = "task-notepad";
65753
65827
  var PLAN_DIR_NAME = ".matrixx";
65754
65828
  var PLANS_SUBDIR = "plans";
65755
65829
  var NOTEPAD_DIR_NAME = "notepads";
@@ -65778,7 +65852,7 @@ function createTaskNotepadHook(ctx) {
65778
65852
  const plan = resolveActivePlan(ctx.directory);
65779
65853
  if (inProgress) {
65780
65854
  if (!plan) {
65781
- log(`[${HOOK_NAME16}] No active plan, skipping notepad creation`, {
65855
+ log(`[${HOOK_NAME17}] No active plan, skipping notepad creation`, {
65782
65856
  sessionID: input.sessionID
65783
65857
  });
65784
65858
  } else {
@@ -65805,7 +65879,7 @@ async function fetchTodos(ctx, sessionID) {
65805
65879
  }
65806
65880
  return [];
65807
65881
  } catch (err) {
65808
- log(`[${HOOK_NAME16}] ${TODO_FETCH_ERROR_FRAGMENT}`, {
65882
+ log(`[${HOOK_NAME17}] ${TODO_FETCH_ERROR_FRAGMENT}`, {
65809
65883
  sessionID,
65810
65884
  error: String(err)
65811
65885
  });
@@ -65850,7 +65924,7 @@ function ensureNotepad(directory, planName, todo) {
65850
65924
  mkdirSync16(dirname11(notepadPath), { recursive: true });
65851
65925
  const content = renderNotepad(todo);
65852
65926
  writeFileSync18(notepadPath, content, "utf-8");
65853
- log(`[${HOOK_NAME16}] Created task notepad`, { plan: planName, taskID: todo.id });
65927
+ log(`[${HOOK_NAME17}] Created task notepad`, { plan: planName, taskID: todo.id });
65854
65928
  }
65855
65929
  function appendCompletion(directory, planName, todo) {
65856
65930
  const notepadPath = notepadPathFor(directory, planName, todo.id);
@@ -65866,7 +65940,7 @@ function appendCompletion(directory, planName, todo) {
65866
65940
  - status: completed
65867
65941
  `;
65868
65942
  writeFileSync18(notepadPath, updated, "utf-8");
65869
- log(`[${HOOK_NAME16}] Appended completion to notepad`, { plan: planName, taskID: todo.id });
65943
+ log(`[${HOOK_NAME17}] Appended completion to notepad`, { plan: planName, taskID: todo.id });
65870
65944
  }
65871
65945
  function notepadPathFor(directory, planName, taskID) {
65872
65946
  return join53(directory, PLAN_DIR_NAME, NOTEPAD_DIR_NAME, planName, TASKS_SUBDIR, `${taskID}${NOTEPAD_EXTENSION}`);
@@ -66453,7 +66527,7 @@ function createThinkingBlockValidatorHook() {
66453
66527
  init_logger();
66454
66528
 
66455
66529
  // src/hooks/todo-continuation-enforcer/constants.ts
66456
- var HOOK_NAME17 = "todo-continuation-enforcer";
66530
+ var HOOK_NAME18 = "todo-continuation-enforcer";
66457
66531
  var DEFAULT_SKIP_AGENTS2 = ["oracle", "compaction"];
66458
66532
  var CONTINUATION_PROMPT3 = `${createSystemDirective(SystemDirectiveTypes.TODO_CONTINUATION)}
66459
66533
 
@@ -66518,21 +66592,21 @@ async function injectContinuation2(args) {
66518
66592
  sessionStateStore
66519
66593
  } = args;
66520
66594
  if (subagentSessions.has(sessionID)) {
66521
- log(`[${HOOK_NAME17}] Skipped injection: subagent session`, { sessionID });
66595
+ log(`[${HOOK_NAME18}] Skipped injection: subagent session`, { sessionID });
66522
66596
  return;
66523
66597
  }
66524
66598
  const state3 = sessionStateStore.getExistingState(sessionID);
66525
66599
  if (state3?.isRecovering) {
66526
- log(`[${HOOK_NAME17}] Skipped injection: in recovery`, { sessionID });
66600
+ log(`[${HOOK_NAME18}] Skipped injection: in recovery`, { sessionID });
66527
66601
  return;
66528
66602
  }
66529
66603
  if (isAwaitingUser(state3)) {
66530
- log(`[${HOOK_NAME17}] Skipped injection: awaiting user`, { sessionID });
66604
+ log(`[${HOOK_NAME18}] Skipped injection: awaiting user`, { sessionID });
66531
66605
  return;
66532
66606
  }
66533
66607
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
66534
66608
  if (hasRunningBgTasks) {
66535
- log(`[${HOOK_NAME17}] Skipped injection: background tasks running`, { sessionID });
66609
+ log(`[${HOOK_NAME18}] Skipped injection: background tasks running`, { sessionID });
66536
66610
  return;
66537
66611
  }
66538
66612
  let todos = [];
@@ -66540,12 +66614,12 @@ async function injectContinuation2(args) {
66540
66614
  const response = await ctx.client.session.todo({ path: { id: sessionID } });
66541
66615
  todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
66542
66616
  } catch (error51) {
66543
- log(`[${HOOK_NAME17}] Failed to fetch todos`, { sessionID, error: String(error51) });
66617
+ log(`[${HOOK_NAME18}] Failed to fetch todos`, { sessionID, error: String(error51) });
66544
66618
  return;
66545
66619
  }
66546
66620
  const freshIncompleteCount = getIncompleteCount(todos);
66547
66621
  if (freshIncompleteCount === 0) {
66548
- log(`[${HOOK_NAME17}] Skipped injection: no incomplete todos`, { sessionID });
66622
+ log(`[${HOOK_NAME18}] Skipped injection: no incomplete todos`, { sessionID });
66549
66623
  return;
66550
66624
  }
66551
66625
  let agentName = resolvedInfo?.agent;
@@ -66568,11 +66642,11 @@ async function injectContinuation2(args) {
66568
66642
  tools = tools ?? previousMessage?.tools;
66569
66643
  }
66570
66644
  if (agentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(agentName))) {
66571
- log(`[${HOOK_NAME17}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
66645
+ log(`[${HOOK_NAME18}] Skipped: agent in skipAgents list`, { sessionID, agent: agentName });
66572
66646
  return;
66573
66647
  }
66574
66648
  if (!hasWritePermission2(tools)) {
66575
- log(`[${HOOK_NAME17}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
66649
+ log(`[${HOOK_NAME18}] Skipped: agent lacks write permission`, { sessionID, agent: agentName });
66576
66650
  return;
66577
66651
  }
66578
66652
  const incompleteTodos = todos.filter((todo) => todo.status !== "completed" && todo.status !== "cancelled");
@@ -66589,11 +66663,11 @@ ${todoList}`;
66589
66663
  injectionState.inFlight = true;
66590
66664
  }
66591
66665
  if (isAwaitingUser(sessionStateStore.getExistingState(sessionID))) {
66592
- log(`[${HOOK_NAME17}] Skipped injection: awaiting user (race)`, { sessionID });
66666
+ log(`[${HOOK_NAME18}] Skipped injection: awaiting user (race)`, { sessionID });
66593
66667
  return;
66594
66668
  }
66595
66669
  try {
66596
- log(`[${HOOK_NAME17}] Injecting continuation`, {
66670
+ log(`[${HOOK_NAME18}] Injecting continuation`, {
66597
66671
  sessionID,
66598
66672
  agent: agentName,
66599
66673
  model,
@@ -66608,14 +66682,14 @@ ${todoList}`;
66608
66682
  },
66609
66683
  query: { directory: ctx.directory }
66610
66684
  });
66611
- log(`[${HOOK_NAME17}] Injection successful`, { sessionID });
66685
+ log(`[${HOOK_NAME18}] Injection successful`, { sessionID });
66612
66686
  if (injectionState) {
66613
66687
  injectionState.inFlight = false;
66614
66688
  injectionState.lastInjectedAt = Date.now();
66615
66689
  injectionState.consecutiveFailures = 0;
66616
66690
  }
66617
66691
  } catch (error51) {
66618
- log(`[${HOOK_NAME17}] Injection failed`, { sessionID, error: String(error51) });
66692
+ log(`[${HOOK_NAME18}] Injection failed`, { sessionID, error: String(error51) });
66619
66693
  if (injectionState) {
66620
66694
  injectionState.inFlight = false;
66621
66695
  injectionState.lastInjectedAt = Date.now();
@@ -66649,7 +66723,7 @@ function startCountdown2(args) {
66649
66723
  } = args;
66650
66724
  const state3 = sessionStateStore.getState(sessionID);
66651
66725
  if (state3.countdownTimer || state3.countdownInterval) {
66652
- log(`[${HOOK_NAME17}] Skipped: countdown already active`, { sessionID });
66726
+ log(`[${HOOK_NAME18}] Skipped: countdown already active`, { sessionID });
66653
66727
  return;
66654
66728
  }
66655
66729
  let secondsRemaining = COUNTDOWN_SECONDS2;
@@ -66672,7 +66746,7 @@ function startCountdown2(args) {
66672
66746
  sessionStateStore
66673
66747
  });
66674
66748
  }, COUNTDOWN_SECONDS2 * 1000);
66675
- log(`[${HOOK_NAME17}] Countdown started`, {
66749
+ log(`[${HOOK_NAME18}] Countdown started`, {
66676
66750
  sessionID,
66677
66751
  seconds: COUNTDOWN_SECONDS2,
66678
66752
  incompleteCount
@@ -66689,36 +66763,36 @@ async function handleSessionIdle2(args) {
66689
66763
  skipAgents = DEFAULT_SKIP_AGENTS2,
66690
66764
  isContinuationStopped
66691
66765
  } = args;
66692
- log(`[${HOOK_NAME17}] session.idle`, { sessionID });
66766
+ log(`[${HOOK_NAME18}] session.idle`, { sessionID });
66693
66767
  if (subagentSessions.has(sessionID)) {
66694
- log(`[${HOOK_NAME17}] Skipped: subagent session`, { sessionID });
66768
+ log(`[${HOOK_NAME18}] Skipped: subagent session`, { sessionID });
66695
66769
  return;
66696
66770
  }
66697
66771
  const state3 = sessionStateStore.getState(sessionID);
66698
66772
  if (state3.countdownTimer || state3.countdownInterval) {
66699
- log(`[${HOOK_NAME17}] Skipped: countdown already active`, { sessionID });
66773
+ log(`[${HOOK_NAME18}] Skipped: countdown already active`, { sessionID });
66700
66774
  return;
66701
66775
  }
66702
66776
  if (state3.isRecovering) {
66703
- log(`[${HOOK_NAME17}] Skipped: in recovery`, { sessionID });
66777
+ log(`[${HOOK_NAME18}] Skipped: in recovery`, { sessionID });
66704
66778
  return;
66705
66779
  }
66706
66780
  if (state3.abortDetectedAt) {
66707
66781
  const timeSinceAbort = Date.now() - state3.abortDetectedAt;
66708
66782
  if (timeSinceAbort < ABORT_WINDOW_MS2) {
66709
- log(`[${HOOK_NAME17}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
66783
+ log(`[${HOOK_NAME18}] Skipped: abort detected via event ${timeSinceAbort}ms ago`, { sessionID });
66710
66784
  state3.abortDetectedAt = undefined;
66711
66785
  return;
66712
66786
  }
66713
66787
  state3.abortDetectedAt = undefined;
66714
66788
  }
66715
66789
  if (isAwaitingUser(state3)) {
66716
- log(`[${HOOK_NAME17}] Skipped: awaiting user`, { sessionID });
66790
+ log(`[${HOOK_NAME18}] Skipped: awaiting user`, { sessionID });
66717
66791
  return;
66718
66792
  }
66719
66793
  const hasRunningBgTasks = backgroundManager ? backgroundManager.getTasksByParentSession(sessionID).some((task) => task.status === "running") : false;
66720
66794
  if (hasRunningBgTasks) {
66721
- log(`[${HOOK_NAME17}] Skipped: background tasks running`, { sessionID });
66795
+ log(`[${HOOK_NAME18}] Skipped: background tasks running`, { sessionID });
66722
66796
  return;
66723
66797
  }
66724
66798
  try {
@@ -66728,46 +66802,46 @@ async function handleSessionIdle2(args) {
66728
66802
  });
66729
66803
  const messages = normalizeSDKResponse(messagesResp, []);
66730
66804
  if (isLastAssistantMessageAborted2(messages)) {
66731
- log(`[${HOOK_NAME17}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
66805
+ log(`[${HOOK_NAME18}] Skipped: last assistant message was aborted (API fallback)`, { sessionID });
66732
66806
  return;
66733
66807
  }
66734
66808
  if (isAwaitingUser(state3, messages)) {
66735
- log(`[${HOOK_NAME17}] Skipped: awaiting user (pending question)`, { sessionID });
66809
+ log(`[${HOOK_NAME18}] Skipped: awaiting user (pending question)`, { sessionID });
66736
66810
  return;
66737
66811
  }
66738
66812
  } catch (error51) {
66739
- log(`[${HOOK_NAME17}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
66813
+ log(`[${HOOK_NAME18}] Messages fetch failed, continuing`, { sessionID, error: String(error51) });
66740
66814
  }
66741
66815
  let todos = [];
66742
66816
  try {
66743
66817
  const response = await ctx.client.session.todo({ path: { id: sessionID } });
66744
66818
  todos = normalizeSDKResponse(response, [], { preferResponseOnMissingData: true });
66745
66819
  } catch (error51) {
66746
- log(`[${HOOK_NAME17}] Todo fetch failed`, { sessionID, error: String(error51) });
66820
+ log(`[${HOOK_NAME18}] Todo fetch failed`, { sessionID, error: String(error51) });
66747
66821
  return;
66748
66822
  }
66749
66823
  if (!todos || todos.length === 0) {
66750
- log(`[${HOOK_NAME17}] No todos`, { sessionID });
66824
+ log(`[${HOOK_NAME18}] No todos`, { sessionID });
66751
66825
  return;
66752
66826
  }
66753
66827
  const incompleteCount = getIncompleteCount(todos);
66754
66828
  if (incompleteCount === 0) {
66755
- log(`[${HOOK_NAME17}] All todos complete`, { sessionID, total: todos.length });
66829
+ log(`[${HOOK_NAME18}] All todos complete`, { sessionID, total: todos.length });
66756
66830
  return;
66757
66831
  }
66758
66832
  if (state3.inFlight) {
66759
- log(`[${HOOK_NAME17}] Skipped: injection in flight`, { sessionID });
66833
+ log(`[${HOOK_NAME18}] Skipped: injection in flight`, { sessionID });
66760
66834
  return;
66761
66835
  }
66762
66836
  if (state3.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES2 && state3.lastInjectedAt && Date.now() - state3.lastInjectedAt >= FAILURE_RESET_WINDOW_MS2) {
66763
66837
  state3.consecutiveFailures = 0;
66764
- log(`[${HOOK_NAME17}] Reset consecutive failures after recovery window`, {
66838
+ log(`[${HOOK_NAME18}] Reset consecutive failures after recovery window`, {
66765
66839
  sessionID,
66766
66840
  failureResetWindowMs: FAILURE_RESET_WINDOW_MS2
66767
66841
  });
66768
66842
  }
66769
66843
  if (state3.consecutiveFailures >= MAX_CONSECUTIVE_FAILURES2) {
66770
- log(`[${HOOK_NAME17}] Skipped: max consecutive failures reached`, {
66844
+ log(`[${HOOK_NAME18}] Skipped: max consecutive failures reached`, {
66771
66845
  sessionID,
66772
66846
  consecutiveFailures: state3.consecutiveFailures,
66773
66847
  maxConsecutiveFailures: MAX_CONSECUTIVE_FAILURES2
@@ -66776,7 +66850,7 @@ async function handleSessionIdle2(args) {
66776
66850
  }
66777
66851
  const effectiveCooldown = CONTINUATION_COOLDOWN_MS4 * 2 ** Math.min(state3.consecutiveFailures, 5);
66778
66852
  if (state3.lastInjectedAt && Date.now() - state3.lastInjectedAt < effectiveCooldown) {
66779
- log(`[${HOOK_NAME17}] Skipped: cooldown active`, {
66853
+ log(`[${HOOK_NAME18}] Skipped: cooldown active`, {
66780
66854
  sessionID,
66781
66855
  effectiveCooldown,
66782
66856
  consecutiveFailures: state3.consecutiveFailures
@@ -66806,24 +66880,24 @@ async function handleSessionIdle2(args) {
66806
66880
  }
66807
66881
  }
66808
66882
  } catch (error51) {
66809
- log(`[${HOOK_NAME17}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
66883
+ log(`[${HOOK_NAME18}] Failed to fetch messages for agent check`, { sessionID, error: String(error51) });
66810
66884
  }
66811
- log(`[${HOOK_NAME17}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
66885
+ log(`[${HOOK_NAME18}] Agent check`, { sessionID, agentName: resolvedInfo?.agent, skipAgents, hasCompactionMessage });
66812
66886
  const resolvedAgentName = resolvedInfo?.agent;
66813
66887
  if (resolvedAgentName && skipAgents.some((s) => getAgentConfigKey(s) === getAgentConfigKey(resolvedAgentName))) {
66814
- log(`[${HOOK_NAME17}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
66888
+ log(`[${HOOK_NAME18}] Skipped: agent in skipAgents list`, { sessionID, agent: resolvedAgentName });
66815
66889
  return;
66816
66890
  }
66817
66891
  if (hasCompactionMessage && !resolvedInfo?.agent) {
66818
- log(`[${HOOK_NAME17}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
66892
+ log(`[${HOOK_NAME18}] Skipped: compaction occurred but no agent info resolved`, { sessionID });
66819
66893
  return;
66820
66894
  }
66821
66895
  if (isContinuationStopped?.(sessionID)) {
66822
- log(`[${HOOK_NAME17}] Skipped: continuation stopped for session`, { sessionID });
66896
+ log(`[${HOOK_NAME18}] Skipped: continuation stopped for session`, { sessionID });
66823
66897
  return;
66824
66898
  }
66825
66899
  if (isAwaitingUser(state3)) {
66826
- log(`[${HOOK_NAME17}] Skipped: awaiting user (race)`, { sessionID });
66900
+ log(`[${HOOK_NAME18}] Skipped: awaiting user (race)`, { sessionID });
66827
66901
  return;
66828
66902
  }
66829
66903
  startCountdown2({
@@ -66855,7 +66929,7 @@ function handleNonIdleEvent2(args) {
66855
66929
  if (state3?.countdownStartedAt) {
66856
66930
  const elapsed = Date.now() - state3.countdownStartedAt;
66857
66931
  if (elapsed < COUNTDOWN_GRACE_PERIOD_MS2) {
66858
- log(`[${HOOK_NAME17}] Ignoring user message in grace period`, { sessionID, elapsed });
66932
+ log(`[${HOOK_NAME18}] Ignoring user message in grace period`, { sessionID, elapsed });
66859
66933
  return;
66860
66934
  }
66861
66935
  }
@@ -66901,7 +66975,7 @@ function handleNonIdleEvent2(args) {
66901
66975
  const sessionInfo = properties?.info;
66902
66976
  if (sessionInfo?.id) {
66903
66977
  sessionStateStore.cleanup(sessionInfo.id);
66904
- log(`[${HOOK_NAME17}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
66978
+ log(`[${HOOK_NAME18}] Session deleted: cleaned up`, { sessionID: sessionInfo.id });
66905
66979
  }
66906
66980
  return;
66907
66981
  }
@@ -66926,10 +67000,10 @@ function createTodoContinuationHandler(args) {
66926
67000
  if (error51?.name === "MessageAbortedError" || error51?.name === "AbortError") {
66927
67001
  const state3 = sessionStateStore.getState(sessionID);
66928
67002
  state3.abortDetectedAt = Date.now();
66929
- log(`[${HOOK_NAME17}] Abort detected via session.error`, { sessionID, errorName: error51.name });
67003
+ log(`[${HOOK_NAME18}] Abort detected via session.error`, { sessionID, errorName: error51.name });
66930
67004
  }
66931
67005
  sessionStateStore.cancelCountdown(sessionID);
66932
- log(`[${HOOK_NAME17}] session.error`, { sessionID });
67006
+ log(`[${HOOK_NAME18}] session.error`, { sessionID });
66933
67007
  return;
66934
67008
  }
66935
67009
  if (event.type === "session.idle") {
@@ -67047,13 +67121,13 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
67047
67121
  const state3 = sessionStateStore.getState(sessionID);
67048
67122
  state3.isRecovering = true;
67049
67123
  sessionStateStore.cancelCountdown(sessionID);
67050
- log(`[${HOOK_NAME17}] Session marked as recovering`, { sessionID });
67124
+ log(`[${HOOK_NAME18}] Session marked as recovering`, { sessionID });
67051
67125
  };
67052
67126
  const markRecoveryComplete = (sessionID) => {
67053
67127
  const state3 = sessionStateStore.getExistingState(sessionID);
67054
67128
  if (state3) {
67055
67129
  state3.isRecovering = false;
67056
- log(`[${HOOK_NAME17}] Session recovery complete`, { sessionID });
67130
+ log(`[${HOOK_NAME18}] Session recovery complete`, { sessionID });
67057
67131
  }
67058
67132
  };
67059
67133
  const handler = createTodoContinuationHandler({
@@ -67065,7 +67139,7 @@ function createTodoContinuationEnforcer(ctx, options = {}) {
67065
67139
  });
67066
67140
  const cancelAllCountdowns = () => {
67067
67141
  sessionStateStore.cancelAllCountdowns();
67068
- log(`[${HOOK_NAME17}] All countdowns cancelled`);
67142
+ log(`[${HOOK_NAME18}] All countdowns cancelled`);
67069
67143
  };
67070
67144
  return {
67071
67145
  handler,
@@ -67299,7 +67373,7 @@ This is a reminder only. No automatic action was taken.`;
67299
67373
  }
67300
67374
  // src/hooks/unstable-agent-babysitter/unstable-agent-babysitter-hook.ts
67301
67375
  init_logger();
67302
- var HOOK_NAME18 = "unstable-agent-babysitter";
67376
+ var HOOK_NAME19 = "unstable-agent-babysitter";
67303
67377
  var DEFAULT_TIMEOUT_MS = 120000;
67304
67378
  var COOLDOWN_MS = 5 * 60 * 1000;
67305
67379
  async function resolveMainSessionTarget(ctx, sessionID) {
@@ -67319,7 +67393,7 @@ async function resolveMainSessionTarget(ctx, sessionID) {
67319
67393
  }
67320
67394
  }
67321
67395
  } catch (error51) {
67322
- log(`[${HOOK_NAME18}] Failed to resolve main session agent`, { sessionID, error: String(error51) });
67396
+ log(`[${HOOK_NAME19}] Failed to resolve main session agent`, { sessionID, error: String(error51) });
67323
67397
  }
67324
67398
  return { agent, model };
67325
67399
  }
@@ -67352,7 +67426,7 @@ async function getThinkingSummary(ctx, sessionID) {
67352
67426
  return combined;
67353
67427
  return `${combined.slice(0, THINKING_SUMMARY_MAX_CHARS)}...`;
67354
67428
  } catch (error51) {
67355
- log(`[${HOOK_NAME18}] Failed to fetch thinking summary`, { sessionID, error: String(error51) });
67429
+ log(`[${HOOK_NAME19}] Failed to fetch thinking summary`, { sessionID, error: String(error51) });
67356
67430
  return null;
67357
67431
  }
67358
67432
  }
@@ -67401,9 +67475,9 @@ function createUnstableAgentBabysitterHook(ctx, options) {
67401
67475
  query: { directory: ctx.directory }
67402
67476
  });
67403
67477
  reminderCooldowns.set(task.id, now);
67404
- log(`[${HOOK_NAME18}] Reminder injected`, { taskId: task.id, sessionID: mainSessionID });
67478
+ log(`[${HOOK_NAME19}] Reminder injected`, { taskId: task.id, sessionID: mainSessionID });
67405
67479
  } catch (error51) {
67406
- log(`[${HOOK_NAME18}] Reminder injection failed`, { taskId: task.id, error: String(error51) });
67480
+ log(`[${HOOK_NAME19}] Reminder injection failed`, { taskId: task.id, error: String(error51) });
67407
67481
  }
67408
67482
  }
67409
67483
  };
@@ -84356,8 +84430,13 @@ function createBddValidateContractTool() {
84356
84430
  }
84357
84431
  });
84358
84432
  }
84359
- // src/tools/glob/cli.ts
84360
- import { stat } from "fs/promises";
84433
+ // src/tools/github-search/cli.ts
84434
+ import { spawn as spawn11, spawnSync as spawnSync3 } from "child_process";
84435
+ import { existsSync as existsSync59, mkdirSync as mkdirSync17 } from "fs";
84436
+ import { tmpdir as tmpdir5 } from "os";
84437
+ import { join as join62 } from "path";
84438
+
84439
+ // src/tools/grep/cli.ts
84361
84440
  var {spawn: spawn10 } = globalThis.Bun;
84362
84441
 
84363
84442
  // src/tools/grep/constants.ts
@@ -84552,8 +84631,367 @@ var RG_SAFETY_FLAGS = [
84552
84631
  ];
84553
84632
  var GREP_SAFETY_FLAGS = ["-n", "-H", "--color=never"];
84554
84633
 
84555
- // src/tools/glob/constants.ts
84634
+ // src/tools/grep/cli.ts
84635
+ function buildRgArgs(options) {
84636
+ const args = [
84637
+ ...RG_SAFETY_FLAGS,
84638
+ `--max-depth=${Math.min(options.maxDepth ?? DEFAULT_MAX_DEPTH, DEFAULT_MAX_DEPTH)}`,
84639
+ `--max-filesize=${options.maxFilesize ?? DEFAULT_MAX_FILESIZE}`,
84640
+ `--max-count=${Math.min(options.maxCount ?? DEFAULT_MAX_COUNT, DEFAULT_MAX_COUNT)}`,
84641
+ `--max-columns=${Math.min(options.maxColumns ?? DEFAULT_MAX_COLUMNS, DEFAULT_MAX_COLUMNS)}`
84642
+ ];
84643
+ if (options.context !== undefined && options.context > 0) {
84644
+ args.push(`-C${Math.min(options.context, 10)}`);
84645
+ }
84646
+ if (options.caseSensitive)
84647
+ args.push("--case-sensitive");
84648
+ if (options.wholeWord)
84649
+ args.push("-w");
84650
+ if (options.fixedStrings)
84651
+ args.push("-F");
84652
+ if (options.multiline)
84653
+ args.push("-U");
84654
+ if (options.hidden)
84655
+ args.push("--hidden");
84656
+ if (options.noIgnore)
84657
+ args.push("--no-ignore");
84658
+ if (options.fileType?.length) {
84659
+ for (const type of options.fileType) {
84660
+ args.push(`--type=${type}`);
84661
+ }
84662
+ }
84663
+ if (options.globs) {
84664
+ for (const glob of options.globs) {
84665
+ args.push(`--glob=${glob}`);
84666
+ }
84667
+ }
84668
+ if (options.excludeGlobs) {
84669
+ for (const glob of options.excludeGlobs) {
84670
+ args.push(`--glob=!${glob}`);
84671
+ }
84672
+ }
84673
+ return args;
84674
+ }
84675
+ function buildGrepArgs(options) {
84676
+ const args = [...GREP_SAFETY_FLAGS, "-r"];
84677
+ if (options.context !== undefined && options.context > 0) {
84678
+ args.push(`-C${Math.min(options.context, 10)}`);
84679
+ }
84680
+ if (!options.caseSensitive)
84681
+ args.push("-i");
84682
+ if (options.wholeWord)
84683
+ args.push("-w");
84684
+ if (options.fixedStrings)
84685
+ args.push("-F");
84686
+ if (options.globs?.length) {
84687
+ for (const glob of options.globs) {
84688
+ args.push(`--include=${glob}`);
84689
+ }
84690
+ }
84691
+ if (options.excludeGlobs?.length) {
84692
+ for (const glob of options.excludeGlobs) {
84693
+ args.push(`--exclude=${glob}`);
84694
+ }
84695
+ }
84696
+ args.push("--exclude-dir=.git", "--exclude-dir=node_modules");
84697
+ return args;
84698
+ }
84699
+ function buildArgs(options, backend) {
84700
+ return backend === "rg" ? buildRgArgs(options) : buildGrepArgs(options);
84701
+ }
84702
+ function parseOutput(output) {
84703
+ if (!output.trim())
84704
+ return [];
84705
+ const matches = [];
84706
+ const lines = output.split(`
84707
+ `);
84708
+ for (const line of lines) {
84709
+ if (!line.trim())
84710
+ continue;
84711
+ const match = line.match(/^(.+?):(\d+):(.*)$/);
84712
+ if (match) {
84713
+ matches.push({
84714
+ file: match[1],
84715
+ line: parseInt(match[2], 10),
84716
+ text: match[3]
84717
+ });
84718
+ }
84719
+ }
84720
+ return matches;
84721
+ }
84722
+ async function runRg(options) {
84723
+ const cli = resolveGrepCli();
84724
+ const args = buildArgs(options, cli.backend);
84725
+ const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3);
84726
+ if (cli.backend === "rg") {
84727
+ args.push("--", options.pattern);
84728
+ } else {
84729
+ args.push("-e", options.pattern);
84730
+ }
84731
+ const paths = options.paths?.length ? options.paths : ["."];
84732
+ args.push(...paths);
84733
+ const proc = spawn10([cli.path, ...args], {
84734
+ stdout: "pipe",
84735
+ stderr: "pipe"
84736
+ });
84737
+ const timeoutPromise = new Promise((_, reject) => {
84738
+ const id = setTimeout(() => {
84739
+ proc.kill();
84740
+ reject(new Error(`Search timeout after ${timeout}ms`));
84741
+ }, timeout);
84742
+ proc.exited.then(() => clearTimeout(id));
84743
+ });
84744
+ try {
84745
+ const stdout = await Promise.race([new Response(proc.stdout).text(), timeoutPromise]);
84746
+ const stderr = await new Response(proc.stderr).text();
84747
+ const exitCode = await proc.exited;
84748
+ const truncated = stdout.length >= DEFAULT_MAX_OUTPUT_BYTES2;
84749
+ const outputToProcess = truncated ? stdout.substring(0, DEFAULT_MAX_OUTPUT_BYTES2) : stdout;
84750
+ if (exitCode > 1 && stderr.trim()) {
84751
+ return {
84752
+ matches: [],
84753
+ totalMatches: 0,
84754
+ filesSearched: 0,
84755
+ truncated: false,
84756
+ error: stderr.trim()
84757
+ };
84758
+ }
84759
+ const matches = parseOutput(outputToProcess);
84760
+ const filesSearched = new Set(matches.map((m) => m.file)).size;
84761
+ return {
84762
+ matches,
84763
+ totalMatches: matches.length,
84764
+ filesSearched,
84765
+ truncated
84766
+ };
84767
+ } catch (e) {
84768
+ return {
84769
+ matches: [],
84770
+ totalMatches: 0,
84771
+ filesSearched: 0,
84772
+ truncated: false,
84773
+ error: e instanceof Error ? e.message : String(e)
84774
+ };
84775
+ }
84776
+ }
84777
+
84778
+ // src/tools/github-search/constants.ts
84556
84779
  var DEFAULT_TIMEOUT_MS4 = 60000;
84780
+ var DEFAULT_MAX_OUTPUT_CHARS = 20000;
84781
+ var MAX_LIMIT = 30;
84782
+ var CLONE_DEPTH = 1;
84783
+ var TMP_PREFIX = "matrixx-gh-";
84784
+
84785
+ // src/tools/github-search/cli.ts
84786
+ function findExecutable2(name) {
84787
+ const cmd = process.platform === "win32" ? "where" : "which";
84788
+ try {
84789
+ const result = spawnSync3(cmd, [name], { encoding: "utf-8", timeout: 5000 });
84790
+ if (result.status === 0 && result.stdout.trim()) {
84791
+ return result.stdout.trim().split(`
84792
+ `)[0]?.trim() ?? null;
84793
+ }
84794
+ } catch {}
84795
+ return null;
84796
+ }
84797
+ async function runCmd(cmd, args, cwd, timeout) {
84798
+ const proc = spawn11(cmd, args, { cwd, timeout });
84799
+ let stdout = "";
84800
+ let stderr = "";
84801
+ proc.stdout?.on("data", (d) => {
84802
+ stdout += d.toString();
84803
+ });
84804
+ proc.stderr?.on("data", (d) => {
84805
+ stderr += d.toString();
84806
+ });
84807
+ const exitCode = await new Promise((resolve8) => {
84808
+ const timer = setTimeout(() => {
84809
+ try {
84810
+ proc.kill();
84811
+ } catch {}
84812
+ resolve8(124);
84813
+ }, timeout);
84814
+ proc.on("close", (code) => {
84815
+ clearTimeout(timer);
84816
+ resolve8(code ?? 0);
84817
+ });
84818
+ proc.on("error", () => {
84819
+ clearTimeout(timer);
84820
+ resolve8(127);
84821
+ });
84822
+ });
84823
+ return { stdout, stderr, exitCode };
84824
+ }
84825
+ function sanitizeRepo(repo) {
84826
+ return repo.replace(/[^a-zA-Z0-9-_]/g, "-").slice(0, 80);
84827
+ }
84828
+ function parseGhSearchOutput(stdout, fallbackRepo) {
84829
+ const trimmed = stdout.trim();
84830
+ if (!trimmed)
84831
+ return [];
84832
+ try {
84833
+ const parsed = JSON.parse(trimmed);
84834
+ const items = Array.isArray(parsed) ? parsed : [];
84835
+ return items.map((item) => ({
84836
+ repo: item.repository?.nameWithOwner ?? fallbackRepo,
84837
+ path: item.path ?? "",
84838
+ sha: item.sha,
84839
+ url: item.url
84840
+ }));
84841
+ } catch {
84842
+ return [];
84843
+ }
84844
+ }
84845
+ async function codeSearch(query, repo, language, limit) {
84846
+ const gh = findExecutable2("gh");
84847
+ if (!gh) {
84848
+ return {
84849
+ hits: [],
84850
+ truncated: false,
84851
+ mode: "code-search",
84852
+ error: "gh CLI not found in PATH. Install from https://cli.github.com and authenticate with `gh auth login`."
84853
+ };
84854
+ }
84855
+ const parts = [query];
84856
+ if (language)
84857
+ parts.push(`language:${language}`);
84858
+ if (repo)
84859
+ parts.push(`repo:${repo}`);
84860
+ const searchQuery = parts.join(" ");
84861
+ const capped = Math.min(Math.max(limit, 1), MAX_LIMIT);
84862
+ const args = ["search", "code", searchQuery, "--limit", String(capped), "--json", "repository,path,sha,url"];
84863
+ const { stdout, stderr, exitCode } = await runCmd(gh, args, process.cwd(), DEFAULT_TIMEOUT_MS4);
84864
+ if (exitCode !== 0) {
84865
+ const hint = /auth|login|token/i.test(stderr) ? " Run `gh auth login` and retry." : "";
84866
+ return {
84867
+ hits: [],
84868
+ truncated: false,
84869
+ mode: "code-search",
84870
+ error: `gh search code failed: ${stderr.trim() || `exit ${exitCode}`}.${hint}`
84871
+ };
84872
+ }
84873
+ const hits = parseGhSearchOutput(stdout, repo ?? "").slice(0, capped);
84874
+ return { hits, truncated: false, mode: "code-search" };
84875
+ }
84876
+ async function cloneAndSearch(query, repo) {
84877
+ const gh = findExecutable2("gh");
84878
+ const git = findExecutable2("git");
84879
+ const cloneBin = gh ?? git;
84880
+ if (!cloneBin) {
84881
+ return {
84882
+ hits: [],
84883
+ truncated: false,
84884
+ mode: "clone-search",
84885
+ error: "Neither gh nor git found in PATH. Install GitHub CLI (https://cli.github.com) or git."
84886
+ };
84887
+ }
84888
+ const dest = join62(tmpdir5(), `${TMP_PREFIX}${sanitizeRepo(repo)}`);
84889
+ if (!existsSync59(dest)) {
84890
+ mkdirSync17(dest, { recursive: true });
84891
+ const cloneArgs = gh ? ["repo", "clone", repo, dest, "--", "--depth", String(CLONE_DEPTH)] : ["clone", "--depth", String(CLONE_DEPTH), `https://github.com/${repo}.git`, dest];
84892
+ const cloned = await runCmd(cloneBin, cloneArgs, process.cwd(), DEFAULT_TIMEOUT_MS4);
84893
+ if (cloned.exitCode !== 0) {
84894
+ return {
84895
+ hits: [],
84896
+ truncated: false,
84897
+ mode: "clone-search",
84898
+ error: `Clone failed for ${repo}: ${cloned.stderr.trim() || `exit ${cloned.exitCode}`}`
84899
+ };
84900
+ }
84901
+ }
84902
+ const shaRes = await runCmd("git", ["rev-parse", "HEAD"], dest, 15000);
84903
+ const sha = shaRes.exitCode === 0 ? shaRes.stdout.trim() : undefined;
84904
+ const rgResult = await runRg({ pattern: query, paths: [dest], context: 0 });
84905
+ if (rgResult.error) {
84906
+ return { hits: [], truncated: false, mode: "clone-search", error: rgResult.error };
84907
+ }
84908
+ const hits = rgResult.matches.map((m) => {
84909
+ const rel = m.file.startsWith(dest) ? m.file.slice(dest.length + 1) : m.file;
84910
+ return {
84911
+ repo,
84912
+ path: rel,
84913
+ sha,
84914
+ line: m.line,
84915
+ text: m.text,
84916
+ url: sha ? `https://github.com/${repo}/blob/${sha}/${rel}#L${m.line}` : undefined
84917
+ };
84918
+ });
84919
+ return { hits, truncated: rgResult.truncated, mode: "clone-search" };
84920
+ }
84921
+ async function githubSearch(opts) {
84922
+ const query = opts.query.trim();
84923
+ if (!query) {
84924
+ return { hits: [], truncated: false, mode: "code-search", error: "query must not be empty" };
84925
+ }
84926
+ if (opts.repo) {
84927
+ return cloneAndSearch(query, opts.repo.trim());
84928
+ }
84929
+ return codeSearch(query, undefined, opts.language?.trim() || undefined, opts.limit ?? 10);
84930
+ }
84931
+
84932
+ // src/tools/github-search/result-formatter.ts
84933
+ function formatGithubSearchResult(result) {
84934
+ if (result.error) {
84935
+ return `Error: ${result.error}`;
84936
+ }
84937
+ if (result.hits.length === 0) {
84938
+ return "No matches found";
84939
+ }
84940
+ const lines = [];
84941
+ lines.push(`Found ${result.hits.length} match(es) [${result.mode}]`);
84942
+ lines.push("");
84943
+ for (const hit of result.hits) {
84944
+ const loc = hit.line ? `${hit.path}#L${hit.line}` : hit.path;
84945
+ lines.push(`${hit.repo} ${loc}`);
84946
+ if (hit.url)
84947
+ lines.push(` ${hit.url}`);
84948
+ if (hit.text?.trim())
84949
+ lines.push(` ${hit.text.trim().slice(0, 300)}`);
84950
+ lines.push("");
84951
+ }
84952
+ let out = lines.join(`
84953
+ `);
84954
+ if (out.length > DEFAULT_MAX_OUTPUT_CHARS) {
84955
+ out = `${out.slice(0, DEFAULT_MAX_OUTPUT_CHARS)}
84956
+ [Output truncated due to size limit]`;
84957
+ } else if (result.truncated) {
84958
+ out += "[Output truncated due to size limit]";
84959
+ }
84960
+ return out;
84961
+ }
84962
+
84963
+ // src/tools/github-search/tools.ts
84964
+ function createGithubSearchTools(_ctx) {
84965
+ const github_search = tool({
84966
+ description: "Search public GitHub code using local CLIs only (gh, git, rg). " + "No third-party remote services. " + "Without repo: runs `gh search code` for discovery. " + "With repo (owner/name): clones shallow to TMPDIR and searches locally with permalinks. " + "Requires gh CLI + auth for discovery (`gh auth login`).",
84967
+ args: {
84968
+ query: tool.schema.string().describe('Code pattern to search for (e.g. "useQuery(", "staleTime:")'),
84969
+ repo: tool.schema.string().optional().describe('Repository in owner/name format for deep local search (e.g. "tanstack/query")'),
84970
+ language: tool.schema.string().optional().describe('Language filter for discovery mode (e.g. "TypeScript")'),
84971
+ limit: tool.schema.number().optional().describe(`Max results for discovery mode (1-${MAX_LIMIT}, default 10)`)
84972
+ },
84973
+ execute: async (args) => {
84974
+ try {
84975
+ const result = await githubSearch({
84976
+ query: args.query,
84977
+ repo: args.repo,
84978
+ language: args.language,
84979
+ limit: args.limit
84980
+ });
84981
+ return formatGithubSearchResult(result);
84982
+ } catch (e) {
84983
+ return `Error: ${e instanceof Error ? e.message : String(e)}`;
84984
+ }
84985
+ }
84986
+ });
84987
+ return { github_search };
84988
+ }
84989
+ // src/tools/glob/cli.ts
84990
+ import { stat } from "fs/promises";
84991
+ var {spawn: spawn12 } = globalThis.Bun;
84992
+
84993
+ // src/tools/glob/constants.ts
84994
+ var DEFAULT_TIMEOUT_MS5 = 60000;
84557
84995
  var DEFAULT_LIMIT = 100;
84558
84996
  var DEFAULT_MAX_DEPTH2 = 20;
84559
84997
  var DEFAULT_MAX_OUTPUT_BYTES3 = 10 * 1024 * 1024;
@@ -84564,7 +85002,7 @@ var RG_FILES_FLAGS = [
84564
85002
  ];
84565
85003
 
84566
85004
  // src/tools/glob/cli.ts
84567
- function buildRgArgs(options) {
85005
+ function buildRgArgs2(options) {
84568
85006
  const args = [
84569
85007
  ...RG_FILES_FLAGS,
84570
85008
  `--max-depth=${Math.min(options.maxDepth ?? DEFAULT_MAX_DEPTH2, DEFAULT_MAX_DEPTH2)}`
@@ -84616,14 +85054,14 @@ async function getFileMtime(filePath) {
84616
85054
  }
84617
85055
  async function runRgFiles(options, resolvedCli) {
84618
85056
  const cli = resolvedCli ?? resolveGrepCli();
84619
- const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS4, DEFAULT_TIMEOUT_MS4);
85057
+ const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS5, DEFAULT_TIMEOUT_MS5);
84620
85058
  const limit = Math.min(options.limit ?? DEFAULT_LIMIT, DEFAULT_LIMIT);
84621
85059
  const isRg = cli.backend === "rg";
84622
85060
  const isWindows = process.platform === "win32";
84623
85061
  let command;
84624
85062
  let cwd;
84625
85063
  if (isRg) {
84626
- const args = buildRgArgs(options);
85064
+ const args = buildRgArgs2(options);
84627
85065
  const paths = options.paths?.length ? options.paths : ["."];
84628
85066
  args.push(...paths);
84629
85067
  command = [cli.path, ...args];
@@ -84637,7 +85075,7 @@ async function runRgFiles(options, resolvedCli) {
84637
85075
  cwd = paths[0] || ".";
84638
85076
  command = [cli.path, ...args];
84639
85077
  }
84640
- const proc = spawn10(command, {
85078
+ const proc = spawn12(command, {
84641
85079
  stdout: "pipe",
84642
85080
  stderr: "pipe",
84643
85081
  cwd
@@ -84746,151 +85184,6 @@ function createGlobTools(ctx) {
84746
85184
  });
84747
85185
  return { glob };
84748
85186
  }
84749
- // src/tools/grep/cli.ts
84750
- var {spawn: spawn11 } = globalThis.Bun;
84751
- function buildRgArgs2(options) {
84752
- const args = [
84753
- ...RG_SAFETY_FLAGS,
84754
- `--max-depth=${Math.min(options.maxDepth ?? DEFAULT_MAX_DEPTH, DEFAULT_MAX_DEPTH)}`,
84755
- `--max-filesize=${options.maxFilesize ?? DEFAULT_MAX_FILESIZE}`,
84756
- `--max-count=${Math.min(options.maxCount ?? DEFAULT_MAX_COUNT, DEFAULT_MAX_COUNT)}`,
84757
- `--max-columns=${Math.min(options.maxColumns ?? DEFAULT_MAX_COLUMNS, DEFAULT_MAX_COLUMNS)}`
84758
- ];
84759
- if (options.context !== undefined && options.context > 0) {
84760
- args.push(`-C${Math.min(options.context, 10)}`);
84761
- }
84762
- if (options.caseSensitive)
84763
- args.push("--case-sensitive");
84764
- if (options.wholeWord)
84765
- args.push("-w");
84766
- if (options.fixedStrings)
84767
- args.push("-F");
84768
- if (options.multiline)
84769
- args.push("-U");
84770
- if (options.hidden)
84771
- args.push("--hidden");
84772
- if (options.noIgnore)
84773
- args.push("--no-ignore");
84774
- if (options.fileType?.length) {
84775
- for (const type of options.fileType) {
84776
- args.push(`--type=${type}`);
84777
- }
84778
- }
84779
- if (options.globs) {
84780
- for (const glob of options.globs) {
84781
- args.push(`--glob=${glob}`);
84782
- }
84783
- }
84784
- if (options.excludeGlobs) {
84785
- for (const glob of options.excludeGlobs) {
84786
- args.push(`--glob=!${glob}`);
84787
- }
84788
- }
84789
- return args;
84790
- }
84791
- function buildGrepArgs(options) {
84792
- const args = [...GREP_SAFETY_FLAGS, "-r"];
84793
- if (options.context !== undefined && options.context > 0) {
84794
- args.push(`-C${Math.min(options.context, 10)}`);
84795
- }
84796
- if (!options.caseSensitive)
84797
- args.push("-i");
84798
- if (options.wholeWord)
84799
- args.push("-w");
84800
- if (options.fixedStrings)
84801
- args.push("-F");
84802
- if (options.globs?.length) {
84803
- for (const glob of options.globs) {
84804
- args.push(`--include=${glob}`);
84805
- }
84806
- }
84807
- if (options.excludeGlobs?.length) {
84808
- for (const glob of options.excludeGlobs) {
84809
- args.push(`--exclude=${glob}`);
84810
- }
84811
- }
84812
- args.push("--exclude-dir=.git", "--exclude-dir=node_modules");
84813
- return args;
84814
- }
84815
- function buildArgs(options, backend) {
84816
- return backend === "rg" ? buildRgArgs2(options) : buildGrepArgs(options);
84817
- }
84818
- function parseOutput(output) {
84819
- if (!output.trim())
84820
- return [];
84821
- const matches = [];
84822
- const lines = output.split(`
84823
- `);
84824
- for (const line of lines) {
84825
- if (!line.trim())
84826
- continue;
84827
- const match = line.match(/^(.+?):(\d+):(.*)$/);
84828
- if (match) {
84829
- matches.push({
84830
- file: match[1],
84831
- line: parseInt(match[2], 10),
84832
- text: match[3]
84833
- });
84834
- }
84835
- }
84836
- return matches;
84837
- }
84838
- async function runRg(options) {
84839
- const cli = resolveGrepCli();
84840
- const args = buildArgs(options, cli.backend);
84841
- const timeout = Math.min(options.timeout ?? DEFAULT_TIMEOUT_MS3, DEFAULT_TIMEOUT_MS3);
84842
- if (cli.backend === "rg") {
84843
- args.push("--", options.pattern);
84844
- } else {
84845
- args.push("-e", options.pattern);
84846
- }
84847
- const paths = options.paths?.length ? options.paths : ["."];
84848
- args.push(...paths);
84849
- const proc = spawn11([cli.path, ...args], {
84850
- stdout: "pipe",
84851
- stderr: "pipe"
84852
- });
84853
- const timeoutPromise = new Promise((_, reject) => {
84854
- const id = setTimeout(() => {
84855
- proc.kill();
84856
- reject(new Error(`Search timeout after ${timeout}ms`));
84857
- }, timeout);
84858
- proc.exited.then(() => clearTimeout(id));
84859
- });
84860
- try {
84861
- const stdout = await Promise.race([new Response(proc.stdout).text(), timeoutPromise]);
84862
- const stderr = await new Response(proc.stderr).text();
84863
- const exitCode = await proc.exited;
84864
- const truncated = stdout.length >= DEFAULT_MAX_OUTPUT_BYTES2;
84865
- const outputToProcess = truncated ? stdout.substring(0, DEFAULT_MAX_OUTPUT_BYTES2) : stdout;
84866
- if (exitCode > 1 && stderr.trim()) {
84867
- return {
84868
- matches: [],
84869
- totalMatches: 0,
84870
- filesSearched: 0,
84871
- truncated: false,
84872
- error: stderr.trim()
84873
- };
84874
- }
84875
- const matches = parseOutput(outputToProcess);
84876
- const filesSearched = new Set(matches.map((m) => m.file)).size;
84877
- return {
84878
- matches,
84879
- totalMatches: matches.length,
84880
- filesSearched,
84881
- truncated
84882
- };
84883
- } catch (e) {
84884
- return {
84885
- matches: [],
84886
- totalMatches: 0,
84887
- filesSearched: 0,
84888
- truncated: false,
84889
- error: e instanceof Error ? e.message : String(e)
84890
- };
84891
- }
84892
- }
84893
-
84894
85187
  // src/tools/grep/result-formatter.ts
84895
85188
  function formatGrepResult(result) {
84896
85189
  if (result.error) {
@@ -84992,17 +85285,17 @@ var HandoffSchema = exports_external.object({
84992
85285
  sections: HandoffSectionSchema
84993
85286
  });
84994
85287
  // src/features/handoff/storage.ts
84995
- import { existsSync as existsSync59, mkdirSync as mkdirSync17, readFileSync as readFileSync40, renameSync as renameSync4, writeFileSync as writeFileSync22 } from "fs";
84996
- import { join as join62 } from "path";
85288
+ import { existsSync as existsSync60, mkdirSync as mkdirSync18, readFileSync as readFileSync40, renameSync as renameSync4, writeFileSync as writeFileSync22 } from "fs";
85289
+ import { join as join63 } from "path";
84997
85290
  function getHandoffFilePath(directory) {
84998
- return join62(directory, HANDOFF_FILE_PATH);
85291
+ return join63(directory, HANDOFF_FILE_PATH);
84999
85292
  }
85000
85293
  function getHandoffConsumedFilePath(directory) {
85001
- return join62(directory, HANDOFF_CONSUMED_FILE_PATH);
85294
+ return join63(directory, HANDOFF_CONSUMED_FILE_PATH);
85002
85295
  }
85003
85296
  function handoffFileExists(directory) {
85004
85297
  try {
85005
- return existsSync59(getHandoffFilePath(directory));
85298
+ return existsSync60(getHandoffFilePath(directory));
85006
85299
  } catch {
85007
85300
  return false;
85008
85301
  }
@@ -85010,7 +85303,7 @@ function handoffFileExists(directory) {
85010
85303
  function readHandoffFile(directory) {
85011
85304
  const filePath = getHandoffFilePath(directory);
85012
85305
  try {
85013
- if (!existsSync59(filePath))
85306
+ if (!existsSync60(filePath))
85014
85307
  return null;
85015
85308
  return readFileSync40(filePath, "utf-8");
85016
85309
  } catch {
@@ -85036,7 +85329,7 @@ function writeHandoffFile(directory, content) {
85036
85329
  }
85037
85330
  function ensureHandoffDir(directory) {
85038
85331
  try {
85039
- mkdirSync17(join62(directory, ".matrixx"), { recursive: true });
85332
+ mkdirSync18(join63(directory, ".matrixx"), { recursive: true });
85040
85333
  return true;
85041
85334
  } catch {
85042
85335
  return false;
@@ -85191,15 +85484,15 @@ ${body}
85191
85484
  }
85192
85485
 
85193
85486
  // src/tools/handoff/list-handler.ts
85194
- import { existsSync as existsSync60, readdirSync as readdirSync19, statSync as statSync12 } from "fs";
85195
- import { join as join63 } from "path";
85487
+ import { existsSync as existsSync61, readdirSync as readdirSync19, statSync as statSync12 } from "fs";
85488
+ import { join as join64 } from "path";
85196
85489
  var MATRIX_DIR = ".matrixx";
85197
85490
  function isHandoffFile(name) {
85198
85491
  return name === "handoff.md" || name.startsWith("handoff.consumed");
85199
85492
  }
85200
85493
  function handleList(directory) {
85201
- const matrixxDir = join63(directory, MATRIX_DIR);
85202
- if (!existsSync60(matrixxDir)) {
85494
+ const matrixxDir = join64(directory, MATRIX_DIR);
85495
+ if (!existsSync61(matrixxDir)) {
85203
85496
  return "No handoffs found in .matrixx/";
85204
85497
  }
85205
85498
  let entries;
@@ -85216,7 +85509,7 @@ function handleList(directory) {
85216
85509
  lines.push(`Handoff files in ${matrixxDir}:`);
85217
85510
  lines.push("");
85218
85511
  for (const name of handoffs) {
85219
- const fullPath = join63(matrixxDir, name);
85512
+ const fullPath = join64(matrixxDir, name);
85220
85513
  let size = "?";
85221
85514
  let mtime = "?";
85222
85515
  try {
@@ -85313,7 +85606,7 @@ function createHandoffTools(ctx) {
85313
85606
  init_logger();
85314
85607
 
85315
85608
  // src/tools/interactive-bash/constants.ts
85316
- var DEFAULT_TIMEOUT_MS5 = 60000;
85609
+ var DEFAULT_TIMEOUT_MS6 = 60000;
85317
85610
  var BLOCKED_TMUX_SUBCOMMANDS = [
85318
85611
  "capture-pane",
85319
85612
  "capturep",
@@ -85410,7 +85703,7 @@ The Bash tool can execute these commands directly. Do NOT retry with interactive
85410
85703
  });
85411
85704
  const timeoutPromise = new Promise((_, reject) => {
85412
85705
  const id = setTimeout(() => {
85413
- const timeoutError = new Error(`Timeout after ${DEFAULT_TIMEOUT_MS5}ms`);
85706
+ const timeoutError = new Error(`Timeout after ${DEFAULT_TIMEOUT_MS6}ms`);
85414
85707
  try {
85415
85708
  proc.kill();
85416
85709
  proc.exited.catch((err) => {
@@ -85418,7 +85711,7 @@ The Bash tool can execute these commands directly. Do NOT retry with interactive
85418
85711
  });
85419
85712
  } catch {}
85420
85713
  reject(timeoutError);
85421
- }, DEFAULT_TIMEOUT_MS5);
85714
+ }, DEFAULT_TIMEOUT_MS6);
85422
85715
  proc.exited.then(() => clearTimeout(id)).catch(() => clearTimeout(id));
85423
85716
  });
85424
85717
  const [stdout, stderr, exitCode] = await Promise.race([
@@ -85440,9 +85733,9 @@ The Bash tool can execute these commands directly. Do NOT retry with interactive
85440
85733
  }
85441
85734
  });
85442
85735
  // src/tools/pdf-extract-figures/tools.ts
85443
- import { existsSync as existsSync61, mkdtempSync, writeFileSync as writeFileSync23 } from "fs";
85444
- import { tmpdir as tmpdir5 } from "os";
85445
- import { join as join64 } from "path";
85736
+ import { existsSync as existsSync62, mkdtempSync, writeFileSync as writeFileSync23 } from "fs";
85737
+ import { tmpdir as tmpdir6 } from "os";
85738
+ import { join as join65 } from "path";
85446
85739
  var PYTHON_SCRIPT = `#!/usr/bin/env python3
85447
85740
  import sys
85448
85741
  import json
@@ -85544,7 +85837,7 @@ function validateArgs(args) {
85544
85837
  if (!args.file_path) {
85545
85838
  return "Error: 'file_path' is required.";
85546
85839
  }
85547
- if (!existsSync61(args.file_path)) {
85840
+ if (!existsSync62(args.file_path)) {
85548
85841
  return `Error: File not found: ${args.file_path}`;
85549
85842
  }
85550
85843
  if (!args.file_path.toLowerCase().endsWith(".pdf")) {
@@ -85593,11 +85886,11 @@ async function runExtraction(args) {
85593
85886
  }
85594
85887
  let tmpDir;
85595
85888
  try {
85596
- tmpDir = mkdtempSync(join64(tmpdir5(), "matrixx-pdf-extract-"));
85889
+ tmpDir = mkdtempSync(join65(tmpdir6(), "matrixx-pdf-extract-"));
85597
85890
  } catch {
85598
85891
  return "Error: Could not create temporary directory.";
85599
85892
  }
85600
- const scriptPath = join64(tmpDir, "extract_pdf_figures.py");
85893
+ const scriptPath = join65(tmpDir, "extract_pdf_figures.py");
85601
85894
  writeFileSync23(scriptPath, PYTHON_SCRIPT);
85602
85895
  const payload = buildArgsPayload(args);
85603
85896
  log("[pdf_extract_figures] Running extraction", { file_path: args.file_path, payload });
@@ -85645,11 +85938,11 @@ function createPdfExtractFiguresTool() {
85645
85938
  return { pdf_extract_figures };
85646
85939
  }
85647
85940
  // src/tools/plan/plan-create.ts
85648
- import { existsSync as existsSync62 } from "fs";
85941
+ import { existsSync as existsSync63 } from "fs";
85649
85942
  import { basename as basename7 } from "path";
85650
85943
 
85651
85944
  // src/tools/plan/types.ts
85652
- import { basename as basename6, isAbsolute as isAbsolute5, join as join65, relative as relative4, resolve as resolve8 } from "path";
85945
+ import { basename as basename6, isAbsolute as isAbsolute5, join as join66, relative as relative4, resolve as resolve8 } from "path";
85653
85946
 
85654
85947
  // src/tools/plan/constants.ts
85655
85948
  var PLANS_DIR3 = ".matrixx/plans";
@@ -85675,8 +85968,8 @@ function validatePlanFilePath(filePath, directory) {
85675
85968
  if (!PLAN_FILENAME_KEBAB_REGEX.test(base)) {
85676
85969
  return { error: `fileName must match kebab-case ^[a-z0-9-]+\\.md$: ${base}` };
85677
85970
  }
85678
- const resolved = isAbsolute5(filePath) ? resolve8(filePath) : resolve8(join65(directory, filePath));
85679
- const plansDir = resolve8(join65(directory, PLANS_DIR3));
85971
+ const resolved = isAbsolute5(filePath) ? resolve8(filePath) : resolve8(join66(directory, filePath));
85972
+ const plansDir = resolve8(join66(directory, PLANS_DIR3));
85680
85973
  const relToPlans = relative4(plansDir, resolved);
85681
85974
  if (relToPlans.startsWith("..") || isAbsolute5(relToPlans)) {
85682
85975
  return { error: `filePath must be inside ${PLANS_DIR3}: ${filePath}` };
@@ -85719,7 +86012,7 @@ Uses atomicWrite (.tmp.pid+rename) + upsertMetadataComment. Warns if file alread
85719
86012
  }
85720
86013
  const resolved = validation.resolved;
85721
86014
  ensurePlanDir(directory);
85722
- if (existsSync62(resolved)) {
86015
+ if (existsSync63(resolved)) {
85723
86016
  return JSON.stringify({
85724
86017
  error: "file_exists",
85725
86018
  message: `Plan file already exists: ${resolved}. Use plan_read + plan_update to modify.`,
@@ -85750,7 +86043,7 @@ Uses atomicWrite (.tmp.pid+rename) + upsertMetadataComment. Warns if file alread
85750
86043
  });
85751
86044
  }
85752
86045
  // src/tools/plan/plan-delete.ts
85753
- import { existsSync as existsSync63, unlinkSync as unlinkSync10 } from "fs";
86046
+ import { existsSync as existsSync64, unlinkSync as unlinkSync10 } from "fs";
85754
86047
  function createPlanDeleteTool(ctx) {
85755
86048
  return tool({
85756
86049
  description: `Delete a plan file under .matrixx/plans/*.md. Scoped unlink inside PLANS_DIR, refuses outside paths.`,
@@ -85766,7 +86059,7 @@ function createPlanDeleteTool(ctx) {
85766
86059
  return JSON.stringify({ error: "invalid_file_path", message: validation.error });
85767
86060
  }
85768
86061
  const resolved = validation.resolved;
85769
- if (!existsSync63(resolved)) {
86062
+ if (!existsSync64(resolved)) {
85770
86063
  return JSON.stringify({ error: "file_not_found", message: `File not found: ${resolved}` });
85771
86064
  }
85772
86065
  unlinkSync10(resolved);
@@ -85779,8 +86072,8 @@ function createPlanDeleteTool(ctx) {
85779
86072
  });
85780
86073
  }
85781
86074
  // src/tools/plan/plan-list.ts
85782
- import { existsSync as existsSync64, readdirSync as readdirSync20, statSync as statSync13 } from "fs";
85783
- import { join as join66 } from "path";
86075
+ import { existsSync as existsSync65, readdirSync as readdirSync20, statSync as statSync13 } from "fs";
86076
+ import { join as join67 } from "path";
85784
86077
  function createPlanListTool(ctx) {
85785
86078
  return tool({
85786
86079
  description: `List plan files under .matrixx/plans/*.md. Filters *.md with kebab-case, sorts by mtime.`,
@@ -85788,13 +86081,13 @@ function createPlanListTool(ctx) {
85788
86081
  execute: async (_args, context) => {
85789
86082
  try {
85790
86083
  const directory = resolveDirectory(context?.directory, ctx?.directory);
85791
- const plansDir = join66(directory, PLANS_DIR3);
85792
- if (!existsSync64(plansDir)) {
86084
+ const plansDir = join67(directory, PLANS_DIR3);
86085
+ if (!existsSync65(plansDir)) {
85793
86086
  return JSON.stringify({ plans: [] });
85794
86087
  }
85795
86088
  const entries = readdirSync20(plansDir).filter((f) => f.endsWith(".md") && PLAN_FILENAME_KEBAB_REGEX.test(f));
85796
86089
  const plans = entries.map((fileName) => {
85797
- const filePath = join66(plansDir, fileName);
86090
+ const filePath = join67(plansDir, fileName);
85798
86091
  try {
85799
86092
  const stat2 = statSync13(filePath);
85800
86093
  return {
@@ -85817,7 +86110,7 @@ function createPlanListTool(ctx) {
85817
86110
  });
85818
86111
  }
85819
86112
  // src/tools/plan/plan-read.ts
85820
- import { existsSync as existsSync65, statSync as statSync14 } from "fs";
86113
+ import { existsSync as existsSync66, statSync as statSync14 } from "fs";
85821
86114
  function createPlanReadTool(ctx) {
85822
86115
  return tool({
85823
86116
  description: `Read a plan file from .matrixx/plans/*.md with hashline-tagged output (1#AB|content) + lsp-style. Enforces scoping and ${MAX_PLAN_FILE_BYTES2} byte cap.`,
@@ -85833,7 +86126,7 @@ function createPlanReadTool(ctx) {
85833
86126
  return JSON.stringify({ error: "invalid_file_path", message: validation.error });
85834
86127
  }
85835
86128
  const resolved = validation.resolved;
85836
- if (!existsSync65(resolved)) {
86129
+ if (!existsSync66(resolved)) {
85837
86130
  return JSON.stringify({ error: "file_not_found", message: `File not found: ${resolved}` });
85838
86131
  }
85839
86132
  try {
@@ -85865,7 +86158,7 @@ function createPlanReadTool(ctx) {
85865
86158
  });
85866
86159
  }
85867
86160
  // src/tools/plan/plan-update.ts
85868
- import { existsSync as existsSync66 } from "fs";
86161
+ import { existsSync as existsSync67 } from "fs";
85869
86162
 
85870
86163
  // src/features/tool-metadata-store/store.ts
85871
86164
  var pendingStore = new Map;
@@ -86928,7 +87221,7 @@ function createPlanUpdateTool(ctx) {
86928
87221
  return JSON.stringify({ error: "invalid_file_path", message: validation.error });
86929
87222
  }
86930
87223
  const resolved = validation.resolved;
86931
- if (!existsSync66(resolved)) {
87224
+ if (!existsSync67(resolved)) {
86932
87225
  return JSON.stringify({ error: "file_not_found", message: `File not found: ${resolved}` });
86933
87226
  }
86934
87227
  const result = await executeHashlineEditTool({ filePath: resolved, edits }, context, ctx);
@@ -86941,17 +87234,17 @@ function createPlanUpdateTool(ctx) {
86941
87234
  });
86942
87235
  }
86943
87236
  // src/tools/preset/tools.ts
86944
- import { existsSync as existsSync67, mkdirSync as mkdirSync18, readFileSync as readFileSync41, writeFileSync as writeFileSync24 } from "fs";
87237
+ import { existsSync as existsSync68, mkdirSync as mkdirSync19, readFileSync as readFileSync41, writeFileSync as writeFileSync24 } from "fs";
86945
87238
  import { homedir as homedir10 } from "os";
86946
- import { dirname as dirname15, join as join67 } from "path";
87239
+ import { dirname as dirname15, join as join68 } from "path";
86947
87240
 
86948
87241
  // src/features/preset-state/manager.ts
86949
87242
  init_logger();
86950
- var HOOK_NAME19 = "preset-state";
87243
+ var HOOK_NAME20 = "preset-state";
86951
87244
  var presetBySession = new Map;
86952
87245
  function setSessionPreset(sessionID, presetName) {
86953
87246
  presetBySession.set(sessionID, presetName);
86954
- log(`[${HOOK_NAME19}] Preset set for session`, { sessionID, presetName });
87247
+ log(`[${HOOK_NAME20}] Preset set for session`, { sessionID, presetName });
86955
87248
  }
86956
87249
  function getSessionPreset(sessionID) {
86957
87250
  return presetBySession.get(sessionID);
@@ -86962,14 +87255,14 @@ var ACTIONS = ["list", "show", "set"];
86962
87255
  var SCOPES = ["global", "project"];
86963
87256
  function resolveConfigPath(scope, directory) {
86964
87257
  if (scope === "global") {
86965
- return join67(homedir10(), ".config", "opencode", "matrixx.jsonc");
87258
+ return join68(homedir10(), ".config", "opencode", "matrixx.jsonc");
86966
87259
  }
86967
- return join67(directory ?? process.cwd(), ".opencode", "matrixx.jsonc");
87260
+ return join68(directory ?? process.cwd(), ".opencode", "matrixx.jsonc");
86968
87261
  }
86969
87262
  function persistActivePreset(presetName, scope, directory) {
86970
87263
  const configPath = resolveConfigPath(scope, directory);
86971
87264
  let raw = {};
86972
- if (existsSync67(configPath)) {
87265
+ if (existsSync68(configPath)) {
86973
87266
  const content = readFileSync41(configPath, "utf-8");
86974
87267
  const parsed = parseJsoncSafe(content);
86975
87268
  if (parsed.errors.length > 0) {
@@ -86980,7 +87273,7 @@ function persistActivePreset(presetName, scope, directory) {
86980
87273
  }
86981
87274
  raw = parsed.data ?? {};
86982
87275
  } else {
86983
- mkdirSync18(dirname15(configPath), { recursive: true });
87276
+ mkdirSync19(dirname15(configPath), { recursive: true });
86984
87277
  }
86985
87278
  raw.active_preset = presetName;
86986
87279
  writeFileSync24(configPath, `${JSON.stringify(raw, null, 2)}
@@ -87083,9 +87376,9 @@ function createPresetTool(options) {
87083
87376
  return { preset };
87084
87377
  }
87085
87378
  // src/tools/session-manager/constants.ts
87086
- import { join as join68 } from "path";
87087
- var TODO_DIR = join68(getOpenCodeConfigDir({ binary: "opencode" }), "todos");
87088
- var TRANSCRIPT_DIR2 = join68(getOpenCodeConfigDir({ binary: "opencode" }), "transcripts");
87379
+ import { join as join69 } from "path";
87380
+ var TODO_DIR = join69(getOpenCodeConfigDir({ binary: "opencode" }), "todos");
87381
+ var TRANSCRIPT_DIR2 = join69(getOpenCodeConfigDir({ binary: "opencode" }), "transcripts");
87089
87382
  var SESSION_LIST_DESCRIPTION = `List all OpenCode sessions with optional filtering.
87090
87383
 
87091
87384
  Returns a list of available session IDs with metadata including message count, date range, and agents used.
@@ -87157,9 +87450,9 @@ Agents Used: build, oracle, librarian
87157
87450
  Has Todos: Yes (12 items, 8 completed)
87158
87451
  Has Transcript: Yes (234 entries)`;
87159
87452
  // src/tools/session-manager/storage.ts
87160
- import { existsSync as existsSync68 } from "fs";
87453
+ import { existsSync as existsSync69 } from "fs";
87161
87454
  import { readdir, readFile as readFile3 } from "fs/promises";
87162
- import { join as join69 } from "path";
87455
+ import { join as join70 } from "path";
87163
87456
  var sdkClient = null;
87164
87457
  function setStorageClient(client2) {
87165
87458
  sdkClient = client2;
@@ -87178,7 +87471,7 @@ async function getMainSessions(options) {
87178
87471
  return [];
87179
87472
  }
87180
87473
  }
87181
- if (!existsSync68(SESSION_STORAGE))
87474
+ if (!existsSync69(SESSION_STORAGE))
87182
87475
  return [];
87183
87476
  const sessions = [];
87184
87477
  try {
@@ -87186,13 +87479,13 @@ async function getMainSessions(options) {
87186
87479
  for (const projectDir of projectDirs) {
87187
87480
  if (!projectDir.isDirectory())
87188
87481
  continue;
87189
- const projectPath = join69(SESSION_STORAGE, projectDir.name);
87482
+ const projectPath = join70(SESSION_STORAGE, projectDir.name);
87190
87483
  const sessionFiles = await readdir(projectPath);
87191
87484
  for (const file3 of sessionFiles) {
87192
87485
  if (!file3.endsWith(".json"))
87193
87486
  continue;
87194
87487
  try {
87195
- const content = await readFile3(join69(projectPath, file3), "utf-8");
87488
+ const content = await readFile3(join70(projectPath, file3), "utf-8");
87196
87489
  const meta3 = JSON.parse(content);
87197
87490
  if (meta3.parentID)
87198
87491
  continue;
@@ -87217,7 +87510,7 @@ async function getAllSessions() {
87217
87510
  return [];
87218
87511
  }
87219
87512
  }
87220
- if (!existsSync68(MESSAGE_STORAGE))
87513
+ if (!existsSync69(MESSAGE_STORAGE))
87221
87514
  return [];
87222
87515
  const sessions = [];
87223
87516
  async function scanDirectory(dir) {
@@ -87225,7 +87518,7 @@ async function getAllSessions() {
87225
87518
  const entries = await readdir(dir, { withFileTypes: true });
87226
87519
  for (const entry of entries) {
87227
87520
  if (entry.isDirectory()) {
87228
- const sessionPath = join69(dir, entry.name);
87521
+ const sessionPath = join70(dir, entry.name);
87229
87522
  const files = await readdir(sessionPath);
87230
87523
  if (files.some((f) => f.endsWith(".json"))) {
87231
87524
  sessions.push(entry.name);
@@ -87286,7 +87579,7 @@ async function readSessionMessages(sessionID) {
87286
87579
  }
87287
87580
  }
87288
87581
  const messageDir = getMessageDir(sessionID);
87289
- if (!messageDir || !existsSync68(messageDir))
87582
+ if (!messageDir || !existsSync69(messageDir))
87290
87583
  return [];
87291
87584
  const messages2 = [];
87292
87585
  try {
@@ -87295,7 +87588,7 @@ async function readSessionMessages(sessionID) {
87295
87588
  if (!file3.endsWith(".json"))
87296
87589
  continue;
87297
87590
  try {
87298
- const content = await readFile3(join69(messageDir, file3), "utf-8");
87591
+ const content = await readFile3(join70(messageDir, file3), "utf-8");
87299
87592
  const meta3 = JSON.parse(content);
87300
87593
  const parts = await readParts2(meta3.id);
87301
87594
  messages2.push({
@@ -87319,8 +87612,8 @@ async function readSessionMessages(sessionID) {
87319
87612
  });
87320
87613
  }
87321
87614
  async function readParts2(messageID) {
87322
- const partDir = join69(PART_STORAGE, messageID);
87323
- if (!existsSync68(partDir))
87615
+ const partDir = join70(PART_STORAGE, messageID);
87616
+ if (!existsSync69(partDir))
87324
87617
  return [];
87325
87618
  const parts = [];
87326
87619
  try {
@@ -87329,7 +87622,7 @@ async function readParts2(messageID) {
87329
87622
  if (!file3.endsWith(".json"))
87330
87623
  continue;
87331
87624
  try {
87332
- const content = await readFile3(join69(partDir, file3), "utf-8");
87625
+ const content = await readFile3(join70(partDir, file3), "utf-8");
87333
87626
  parts.push(JSON.parse(content));
87334
87627
  } catch {}
87335
87628
  }
@@ -87353,14 +87646,14 @@ async function readSessionTodos(sessionID) {
87353
87646
  return [];
87354
87647
  }
87355
87648
  }
87356
- if (!existsSync68(TODO_DIR))
87649
+ if (!existsSync69(TODO_DIR))
87357
87650
  return [];
87358
87651
  try {
87359
87652
  const allFiles = await readdir(TODO_DIR);
87360
87653
  const todoFiles = allFiles.filter((f) => f.includes(sessionID) && f.endsWith(".json"));
87361
87654
  for (const file3 of todoFiles) {
87362
87655
  try {
87363
- const content = await readFile3(join69(TODO_DIR, file3), "utf-8");
87656
+ const content = await readFile3(join70(TODO_DIR, file3), "utf-8");
87364
87657
  const data = JSON.parse(content);
87365
87658
  if (Array.isArray(data)) {
87366
87659
  return data.map((item) => ({
@@ -87378,10 +87671,10 @@ async function readSessionTodos(sessionID) {
87378
87671
  return [];
87379
87672
  }
87380
87673
  async function readSessionTranscript(sessionID) {
87381
- if (!existsSync68(TRANSCRIPT_DIR2))
87674
+ if (!existsSync69(TRANSCRIPT_DIR2))
87382
87675
  return 0;
87383
- const transcriptFile = join69(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
87384
- if (!existsSync68(transcriptFile))
87676
+ const transcriptFile = join70(TRANSCRIPT_DIR2, `${sessionID}.jsonl`);
87677
+ if (!existsSync69(transcriptFile))
87385
87678
  return 0;
87386
87679
  try {
87387
87680
  const content = await readFile3(transcriptFile, "utf-8");
@@ -87881,17 +88174,17 @@ function createSkillTool(options = {}) {
87881
88174
  }
87882
88175
  var skill = createSkillTool();
87883
88176
  // src/tools/slashcommand/command-discovery.ts
87884
- import { existsSync as existsSync69, readdirSync as readdirSync21, readFileSync as readFileSync42 } from "fs";
87885
- import { basename as basename8, join as join70 } from "path";
88177
+ import { existsSync as existsSync70, readdirSync as readdirSync21, readFileSync as readFileSync42 } from "fs";
88178
+ import { basename as basename8, join as join71 } from "path";
87886
88179
  function discoverCommandsFromDir2(commandsDir, scope) {
87887
- if (!existsSync69(commandsDir))
88180
+ if (!existsSync70(commandsDir))
87888
88181
  return [];
87889
88182
  const entries = readdirSync21(commandsDir, { withFileTypes: true });
87890
88183
  const commands2 = [];
87891
88184
  for (const entry of entries) {
87892
88185
  if (!isMarkdownFile(entry))
87893
88186
  continue;
87894
- const commandPath = join70(commandsDir, entry.name);
88187
+ const commandPath = join71(commandsDir, entry.name);
87895
88188
  const commandName = basename8(entry.name, ".md");
87896
88189
  try {
87897
88190
  const content = readFileSync42(commandPath, "utf-8");
@@ -87918,8 +88211,8 @@ function discoverCommandsFromDir2(commandsDir, scope) {
87918
88211
  }
87919
88212
  function discoverCommandsSync(directory) {
87920
88213
  const configDir = getOpenCodeConfigDir({ binary: "opencode" });
87921
- const opencodeGlobalDir = join70(configDir, "command");
87922
- const opencodeProjectDir = join70(directory ?? process.cwd(), ".opencode", "command");
88214
+ const opencodeGlobalDir = join71(configDir, "command");
88215
+ const opencodeProjectDir = join71(directory ?? process.cwd(), ".opencode", "command");
87923
88216
  const opencodeGlobalCommands = discoverCommandsFromDir2(opencodeGlobalDir, "opencode");
87924
88217
  const opencodeProjectCommands = discoverCommandsFromDir2(opencodeProjectDir, "opencode-project");
87925
88218
  const builtinCommandsMap = loadBuiltinCommands();
@@ -88723,11 +89016,11 @@ function createBackgroundOutput(manager, client2) {
88723
89016
  });
88724
89017
  }
88725
89018
  // src/tools/background-task/create-background-revive.ts
88726
- import { existsSync as existsSync71 } from "fs";
89019
+ import { existsSync as existsSync72 } from "fs";
88727
89020
 
88728
89021
  // src/features/background-agent/handle-index.ts
88729
- import { existsSync as existsSync70, mkdirSync as mkdirSync19, readdirSync as readdirSync22, readFileSync as readFileSync43, renameSync as renameSync5, unlinkSync as unlinkSync11, writeFileSync as writeFileSync25 } from "fs";
88730
- import { dirname as dirname17, join as join71 } from "path";
89022
+ import { existsSync as existsSync71, mkdirSync as mkdirSync20, readdirSync as readdirSync22, readFileSync as readFileSync43, renameSync as renameSync5, unlinkSync as unlinkSync11, writeFileSync as writeFileSync25 } from "fs";
89023
+ import { dirname as dirname17, join as join72 } from "path";
88731
89024
 
88732
89025
  // src/features/background-agent/constants.ts
88733
89026
  var TASK_TTL_MS = 30 * 60 * 1000;
@@ -88778,14 +89071,14 @@ var BgHandleSchema = exports_external.object({
88778
89071
  completedAt: exports_external.number().optional()
88779
89072
  }).strict();
88780
89073
  function getBgHandleDir(directory) {
88781
- return join71(directory, ".matrixx", BG_HANDLE_DIR_NAME);
89074
+ return join72(directory, ".matrixx", BG_HANDLE_DIR_NAME);
88782
89075
  }
88783
89076
  function getHandlePath(directory, taskId) {
88784
- return join71(getBgHandleDir(directory), `${taskId}.json`);
89077
+ return join72(getBgHandleDir(directory), `${taskId}.json`);
88785
89078
  }
88786
89079
  function ensureDir3(dirPath) {
88787
- if (!existsSync70(dirPath)) {
88788
- mkdirSync19(dirPath, { recursive: true });
89080
+ if (!existsSync71(dirPath)) {
89081
+ mkdirSync20(dirPath, { recursive: true });
88789
89082
  }
88790
89083
  }
88791
89084
  function toHandle(task) {
@@ -88815,7 +89108,7 @@ function writeHandle(directory, task) {
88815
89108
  renameSync5(tempPath, filePath);
88816
89109
  } catch (error95) {
88817
89110
  try {
88818
- if (existsSync70(tempPath)) {
89111
+ if (existsSync71(tempPath)) {
88819
89112
  unlinkSync11(tempPath);
88820
89113
  }
88821
89114
  } catch {}
@@ -88824,7 +89117,7 @@ function writeHandle(directory, task) {
88824
89117
  }
88825
89118
  function readHandleFile(filePath) {
88826
89119
  try {
88827
- if (!existsSync70(filePath)) {
89120
+ if (!existsSync71(filePath)) {
88828
89121
  return null;
88829
89122
  }
88830
89123
  const parsed = JSON.parse(readFileSync43(filePath, "utf-8"));
@@ -88836,7 +89129,7 @@ function readHandleFile(filePath) {
88836
89129
  }
88837
89130
  function readHandles(directory) {
88838
89131
  const dir = getBgHandleDir(directory);
88839
- if (!existsSync70(dir)) {
89132
+ if (!existsSync71(dir)) {
88840
89133
  return [];
88841
89134
  }
88842
89135
  const handles = [];
@@ -88844,7 +89137,7 @@ function readHandles(directory) {
88844
89137
  if (!name.startsWith(BG_HANDLE_FILE_PREFIX) || !name.endsWith(".json")) {
88845
89138
  continue;
88846
89139
  }
88847
- const handle = readHandleFile(join71(dir, name));
89140
+ const handle = readHandleFile(join72(dir, name));
88848
89141
  if (handle) {
88849
89142
  handles.push(handle);
88850
89143
  }
@@ -88853,7 +89146,7 @@ function readHandles(directory) {
88853
89146
  }
88854
89147
  function sweepStaleHandles(directory, ttlMs = TASK_TTL_MS, now = Date.now()) {
88855
89148
  const dir = getBgHandleDir(directory);
88856
- if (!existsSync70(dir)) {
89149
+ if (!existsSync71(dir)) {
88857
89150
  return 0;
88858
89151
  }
88859
89152
  let removed = 0;
@@ -88861,7 +89154,7 @@ function sweepStaleHandles(directory, ttlMs = TASK_TTL_MS, now = Date.now()) {
88861
89154
  if (!name.startsWith(BG_HANDLE_FILE_PREFIX) || !name.endsWith(".json")) {
88862
89155
  continue;
88863
89156
  }
88864
- const filePath = join71(dir, name);
89157
+ const filePath = join72(dir, name);
88865
89158
  const handle = readHandleFile(filePath);
88866
89159
  const timestamp = handle?.completedAt ?? handle?.startedAt ?? handle?.queuedAt;
88867
89160
  if (handle && timestamp !== undefined && now - timestamp <= ttlMs) {
@@ -88929,7 +89222,7 @@ Revive one with background_revive(taskId="<id>", prompt="<new instruction>").`;
88929
89222
  }
88930
89223
  function resolveFailure(identifier, message, directory) {
88931
89224
  if (message.includes("not found for revive")) {
88932
- const handleOnDisk = existsSync71(getHandlePath(directory, identifier));
89225
+ const handleOnDisk = existsSync72(getHandlePath(directory, identifier));
88933
89226
  return { reason: handleOnDisk ? "unknown-task" : "expired", detail: message };
88934
89227
  }
88935
89228
  if (message.includes("still active"))
@@ -89344,9 +89637,9 @@ function createAssemblyTool(options) {
89344
89637
  });
89345
89638
  }
89346
89639
  // src/tools/dcp-switch-profile/tools.ts
89347
- import { existsSync as existsSync72, writeFileSync as writeFileSync26 } from "fs";
89640
+ import { existsSync as existsSync73, writeFileSync as writeFileSync26 } from "fs";
89348
89641
  import { homedir as homedir11 } from "os";
89349
- import { join as join72 } from "path";
89642
+ import { join as join73 } from "path";
89350
89643
 
89351
89644
  // src/config/schema/dcp.ts
89352
89645
  var DcpCompressOverrideSchema = exports_external.object({
@@ -89546,11 +89839,11 @@ var DcpConfigSchema = exports_external.object({
89546
89839
  });
89547
89840
 
89548
89841
  // src/tools/dcp-switch-profile/tools.ts
89549
- var DCP_PLUGIN_DIR = join72(homedir11(), ".config", "opencode", "node_modules", "@tarquinen", "opencode-dcp");
89550
- var DCP_SYMLINK = join72(homedir11(), ".config", "opencode", "dcp.jsonc");
89842
+ var DCP_PLUGIN_DIR = join73(homedir11(), ".config", "opencode", "node_modules", "@tarquinen", "opencode-dcp");
89843
+ var DCP_SYMLINK = join73(homedir11(), ".config", "opencode", "dcp.jsonc");
89551
89844
  var VALID_PROFILES = ["economy", "balanced", "performance", "ultimate", "brutal"];
89552
89845
  function checkDcpInstalled() {
89553
- if (!existsSync72(DCP_PLUGIN_DIR)) {
89846
+ if (!existsSync73(DCP_PLUGIN_DIR)) {
89554
89847
  return `DCP is not installed at ${DCP_PLUGIN_DIR}. Install it with: npm install --prefix ~/.config/opencode @tarquinen/opencode-dcp`;
89555
89848
  }
89556
89849
  return null;
@@ -92671,8 +92964,8 @@ Original error: ${createResult.error}`;
92671
92964
  });
92672
92965
  }
92673
92966
  // src/tools/task/task-cleanup.ts
92674
- import { existsSync as existsSync73, readdirSync as readdirSync23, readFileSync as readFileSync44, unlinkSync as unlinkSync12 } from "fs";
92675
- import { join as join73 } from "path";
92967
+ import { existsSync as existsSync74, readdirSync as readdirSync23, readFileSync as readFileSync44, unlinkSync as unlinkSync12 } from "fs";
92968
+ import { join as join74 } from "path";
92676
92969
  init_logger();
92677
92970
  function parseOlderThan(value) {
92678
92971
  const match = value.trim().match(/^(\d+)(d|h|m)$/);
@@ -92732,14 +93025,14 @@ Returns counts: {deleted, remaining, deletedIds}`,
92732
93025
  }
92733
93026
  const directory = context?.directory ?? ctx?.directory ?? process.cwd();
92734
93027
  const taskDir = getTaskDir(config4, directory);
92735
- if (!existsSync73(taskDir)) {
93028
+ if (!existsSync74(taskDir)) {
92736
93029
  return JSON.stringify({ deleted: 0, remaining: 0, deletedIds: [] });
92737
93030
  }
92738
93031
  const files = readdirSync23(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-"));
92739
93032
  const total = files.length;
92740
93033
  const deletedIds = [];
92741
93034
  for (const file3 of files) {
92742
- const filePath = join73(taskDir, file3);
93035
+ const filePath = join74(taskDir, file3);
92743
93036
  let raw;
92744
93037
  try {
92745
93038
  const content = readFileSync44(filePath, "utf-8");
@@ -92776,7 +93069,7 @@ Returns counts: {deleted, remaining, deletedIds}`,
92776
93069
  }
92777
93070
  // src/tools/task/task-create.ts
92778
93071
  import { readdirSync as readdirSync24, statSync as statSync15 } from "fs";
92779
- import { join as join74 } from "path";
93072
+ import { join as join75 } from "path";
92780
93073
  init_logger();
92781
93074
  function createTaskCreateTool(config4, ctx) {
92782
93075
  return tool({
@@ -92845,7 +93138,7 @@ async function handleCreate2(args, config4, ctx, context) {
92845
93138
  projectRoot: directory
92846
93139
  };
92847
93140
  const validatedTask = TaskObjectSchema.parse(task);
92848
- writeJsonAtomic(join74(taskDir, `${taskId}.json`), validatedTask);
93141
+ writeJsonAtomic(join75(taskDir, `${taskId}.json`), validatedTask);
92849
93142
  return JSON.stringify({
92850
93143
  task: {
92851
93144
  id: validatedTask.id,
@@ -92874,7 +93167,7 @@ function findDuplicateTask(taskDir, subject, directory) {
92874
93167
  return null;
92875
93168
  }
92876
93169
  for (const file3 of files) {
92877
- const filePath = join74(taskDir, file3);
93170
+ const filePath = join75(taskDir, file3);
92878
93171
  let existing;
92879
93172
  try {
92880
93173
  existing = readJsonSafe(filePath, TaskObjectSchema);
@@ -92905,7 +93198,7 @@ function findDuplicateTask(taskDir, subject, directory) {
92905
93198
  return null;
92906
93199
  }
92907
93200
  // src/tools/task/task-get.ts
92908
- import { join as join75 } from "path";
93201
+ import { join as join76 } from "path";
92909
93202
  function parseTaskId(id) {
92910
93203
  if (!TASK_ID_PATTERN.test(id))
92911
93204
  return null;
@@ -92931,7 +93224,7 @@ Returns null if the task does not exist or the file is invalid.`,
92931
93224
  }
92932
93225
  const directory = context?.directory ?? ctx?.directory ?? process.cwd();
92933
93226
  const taskDir = getTaskDir(config4, directory);
92934
- const taskPath = join75(taskDir, `${taskId}.json`);
93227
+ const taskPath = join76(taskDir, `${taskId}.json`);
92935
93228
  const task = readJsonSafe(taskPath, TaskObjectSchema);
92936
93229
  return JSON.stringify({ task: task ?? null });
92937
93230
  } catch (error95) {
@@ -92944,8 +93237,8 @@ Returns null if the task does not exist or the file is invalid.`,
92944
93237
  });
92945
93238
  }
92946
93239
  // src/tools/task/task-list.ts
92947
- import { existsSync as existsSync74, readdirSync as readdirSync25 } from "fs";
92948
- import { join as join76 } from "path";
93240
+ import { existsSync as existsSync75, readdirSync as readdirSync25 } from "fs";
93241
+ import { join as join77 } from "path";
92949
93242
  function createTaskList(config4, ctx) {
92950
93243
  return tool({
92951
93244
  description: `[TRACKING \u2014 local progress records only. Spawns nothing, executes nothing.]
@@ -92964,7 +93257,7 @@ Optional \`parentID\` filter: when provided, returns only tasks whose parentID m
92964
93257
  const parentID = args.parentID;
92965
93258
  const directory = context?.directory ?? ctx?.directory ?? process.cwd();
92966
93259
  const taskDir = getTaskDir(config4, directory);
92967
- if (!existsSync74(taskDir)) {
93260
+ if (!existsSync75(taskDir)) {
92968
93261
  return JSON.stringify({ tasks: [] });
92969
93262
  }
92970
93263
  const files = readdirSync25(taskDir).filter((f) => f.endsWith(".json") && f.startsWith("T-")).map((f) => f.replace(".json", ""));
@@ -92973,7 +93266,7 @@ Optional \`parentID\` filter: when provided, returns only tasks whose parentID m
92973
93266
  }
92974
93267
  const allTasks = [];
92975
93268
  for (const fileId of files) {
92976
- const task = readJsonSafe(join76(taskDir, `${fileId}.json`), TaskObjectSchema);
93269
+ const task = readJsonSafe(join77(taskDir, `${fileId}.json`), TaskObjectSchema);
92977
93270
  if (task) {
92978
93271
  allTasks.push(task);
92979
93272
  }
@@ -93002,7 +93295,7 @@ Optional \`parentID\` filter: when provided, returns only tasks whose parentID m
93002
93295
  });
93003
93296
  }
93004
93297
  // src/tools/task/task-update.ts
93005
- import { join as join77 } from "path";
93298
+ import { join as join78 } from "path";
93006
93299
  function parseTaskId2(id) {
93007
93300
  if (!TASK_ID_PATTERN.test(id))
93008
93301
  return null;
@@ -93049,7 +93342,7 @@ async function handleUpdate(args, config4, ctx, context) {
93049
93342
  return JSON.stringify({ error: "task_lock_unavailable" });
93050
93343
  }
93051
93344
  try {
93052
- const taskPath = join77(taskDir, `${taskId}.json`);
93345
+ const taskPath = join78(taskDir, `${taskId}.json`);
93053
93346
  const task = readJsonSafe(taskPath, TaskObjectSchema);
93054
93347
  if (!task) {
93055
93348
  return JSON.stringify({ error: "task_not_found" });
@@ -93227,6 +93520,7 @@ function createToolGuardHooks(args) {
93227
93520
  const qualityGate = isHookEnabled("quality-gate") ? safeHook("quality-gate", () => createQualityGateHook()) : null;
93228
93521
  const taskNotepad = isHookEnabled("task-notepad") ? safeHook("task-notepad", () => createTaskNotepadHook(ctx)) : null;
93229
93522
  const taskEditGuard = isHookEnabled("task-edit-guard") ? safeHook("task-edit-guard", () => createTaskEditGuardHook(ctx)) : null;
93523
+ const documentReaderGuard = isHookEnabled("document-reader-guard") ? safeHook("document-reader-guard", () => createDocumentReaderGuardHook(ctx)) : null;
93230
93524
  const evolutionWatcher = evolutionEnabled && isHookEnabled("evolution-watcher") ? safeHook("evolution-watcher", () => createEvolutionWatcherHook(ctx, pluginConfig.evolution)) : null;
93231
93525
  return {
93232
93526
  backgroundTaskBlocker,
@@ -93249,6 +93543,7 @@ function createToolGuardHooks(args) {
93249
93543
  qualityGate,
93250
93544
  taskNotepad,
93251
93545
  taskEditGuard,
93546
+ documentReaderGuard,
93252
93547
  evolutionWatcher
93253
93548
  };
93254
93549
  }
@@ -93813,19 +94108,19 @@ function detectRepetitiveToolUse(window2) {
93813
94108
  }
93814
94109
 
93815
94110
  // src/features/background-agent/message-dir.ts
93816
- import { existsSync as existsSync75, readdirSync as readdirSync26, readFileSync as readFileSync45 } from "fs";
93817
- import { join as join78 } from "path";
94111
+ import { existsSync as existsSync76, readdirSync as readdirSync26, readFileSync as readFileSync45 } from "fs";
94112
+ import { join as join79 } from "path";
93818
94113
  var MESSAGE_DIR_CACHE_CAP = 1000;
93819
94114
  var messageDirCache = new Map;
93820
94115
  function computeMessageDir(sessionID) {
93821
- if (!existsSync75(MESSAGE_STORAGE))
94116
+ if (!existsSync76(MESSAGE_STORAGE))
93822
94117
  return null;
93823
- const directPath = join78(MESSAGE_STORAGE, sessionID);
93824
- if (existsSync75(directPath))
94118
+ const directPath = join79(MESSAGE_STORAGE, sessionID);
94119
+ if (existsSync76(directPath))
93825
94120
  return directPath;
93826
94121
  for (const dir of readdirSync26(MESSAGE_STORAGE)) {
93827
- const sessionPath = join78(MESSAGE_STORAGE, dir, sessionID);
93828
- if (existsSync75(sessionPath))
94122
+ const sessionPath = join79(MESSAGE_STORAGE, dir, sessionID);
94123
+ if (existsSync76(sessionPath))
93829
94124
  return sessionPath;
93830
94125
  }
93831
94126
  return null;
@@ -93863,7 +94158,7 @@ function findNearestMessageExcludingCompaction(messageDir) {
93863
94158
  const parsedByFile = new Map;
93864
94159
  for (const file3 of files) {
93865
94160
  try {
93866
- const content = readFileSync45(join78(messageDir, file3), "utf-8");
94161
+ const content = readFileSync45(join79(messageDir, file3), "utf-8");
93867
94162
  const parsed = JSON.parse(content);
93868
94163
  parsedByFile.set(file3, parsed);
93869
94164
  if (hasFullAgentAndModel(parsed)) {
@@ -96505,12 +96800,12 @@ function decideCloseAction(state3, sessionId, sessionMappings) {
96505
96800
  return { type: "close", paneId: mapping.paneId, sessionId };
96506
96801
  }
96507
96802
  // src/features/tmux-subagent/pane-state-querier.ts
96508
- var {spawn: spawn12 } = globalThis.Bun;
96803
+ var {spawn: spawn13 } = globalThis.Bun;
96509
96804
  async function queryWindowState(sourcePaneId) {
96510
96805
  const tmux2 = await getTmuxPath();
96511
96806
  if (!tmux2)
96512
96807
  return null;
96513
- const proc = spawn12([
96808
+ const proc = spawn13([
96514
96809
  tmux2,
96515
96810
  "list-panes",
96516
96811
  "-t",
@@ -98418,7 +98713,7 @@ function createBddContractAgent(model) {
98418
98713
  createBddContractAgent.mode = MODE2;
98419
98714
 
98420
98715
  // src/agents/builtin-agents/resolve-file-uri.ts
98421
- import { existsSync as existsSync76, readFileSync as readFileSync47 } from "fs";
98716
+ import { existsSync as existsSync77, readFileSync as readFileSync47 } from "fs";
98422
98717
  import { homedir as homedir12 } from "os";
98423
98718
  import { isAbsolute as isAbsolute6, resolve as resolve9 } from "path";
98424
98719
  function resolvePromptAppend(promptAppend, configDir) {
@@ -98433,7 +98728,7 @@ function resolvePromptAppend(promptAppend, configDir) {
98433
98728
  } catch {
98434
98729
  return `[WARNING: Malformed file URI (invalid percent-encoding): ${promptAppend}]`;
98435
98730
  }
98436
- if (!existsSync76(filePath)) {
98731
+ if (!existsSync77(filePath)) {
98437
98732
  return `[WARNING: Could not resolve file URI: ${promptAppend}]`;
98438
98733
  }
98439
98734
  try {
@@ -100054,7 +100349,7 @@ var CONSTRUCT_PROMPT_METADATA = {
100054
100349
  triggers: []
100055
100350
  };
100056
100351
  function createConstructAgent(model) {
100057
- const restrictions = createAgentToolAllowlist(["read"]);
100352
+ const restrictions = createAgentToolAllowlist(["read", "document_reader__convert_to_markdown"]);
100058
100353
  return {
100059
100354
  description: "Analyze media files (PDFs, images, diagrams) that require interpretation beyond raw text. Extracts specific information or summaries from documents, describes visual content. Use when you need analyzed/extracted data rather than literal file contents. (Multimodal-Looker - Matrixx)",
100060
100355
  mode: MODE6,
@@ -100078,7 +100373,7 @@ When NOT to use you:
100078
100373
 
100079
100374
  How you work:
100080
100375
  1. Receive a file path and a goal describing what to extract
100081
- 2. Read and analyze the file deeply
100376
+ 2. For PDF/DOCX/XLSX/PPTX/media call document_reader__convert_to_markdown with file:///absolute/path, otherwise use Read
100082
100377
  3. Return ONLY the relevant extracted information
100083
100378
  4. The main agent never processes the raw file - you save context tokens
100084
100379
 
@@ -100444,7 +100739,7 @@ context7_query-docs(libraryId: id, query: "specific topic")
100444
100739
  Tool 1: context7_resolve-library-id("library-name")
100445
100740
  \u2192 then context7_query-docs(libraryId: id, query: "specific-topic")
100446
100741
  Tool 2: webfetch(relevant_pages_from_sitemap) // Targeted, not random
100447
- Tool 3: white_rabbit_searchGitHub(query: "usage pattern", language: ["TypeScript"])
100742
+ Tool 3: github_search(query: "usage pattern", language: "TypeScript")
100448
100743
  \`\`\`
100449
100744
 
100450
100745
  **Output**: Summarize findings with links to official docs (versioned if applicable) and real-world examples.
@@ -100474,7 +100769,7 @@ Step 4: Construct permalink
100474
100769
  **Parallel acceleration (4+ calls)**:
100475
100770
  \`\`\`
100476
100771
  Tool 1: gh repo clone owner/repo \${TMPDIR:-/tmp}/repo -- --depth 1
100477
- Tool 2: white_rabbit_searchGitHub(query: "function_name", repo: "owner/repo")
100772
+ Tool 2: github_search(query: "function_name", repo: "owner/repo")
100478
100773
  Tool 3: gh api repos/owner/repo/commits/HEAD --jq '.sha'
100479
100774
  Tool 4: context7_get-library-docs(id, topic: "relevant-api")
100480
100775
  \`\`\`
@@ -100513,8 +100808,8 @@ Tool 1: context7_resolve-library-id \u2192 context7_query-docs
100513
100808
  Tool 2: webfetch(targeted_doc_pages_from_sitemap)
100514
100809
 
100515
100810
  // Code Search
100516
- Tool 3: white_rabbit_searchGitHub(query: "pattern1", language: [...])
100517
- Tool 4: white_rabbit_searchGitHub(query: "pattern2", useRegexp: true)
100811
+ Tool 3: github_search(query: "pattern1", language: "TypeScript")
100812
+ Tool 4: github_search(query: "pattern2", repo: "owner/repo")
100518
100813
 
100519
100814
  // Source Analysis
100520
100815
  Tool 5: gh repo clone owner/repo \${TMPDIR:-/tmp}/repo -- --depth 1
@@ -100570,7 +100865,7 @@ https://github.com/tanstack/query/blob/abc123def/packages/react-query/src/useQue
100570
100865
  | **Sitemap Discovery** | webfetch | \`webfetch(docs_url + "/sitemap.xml")\` to understand doc structure |
100571
100866
  | **Read Doc Page** | webfetch | \`webfetch(specific_doc_page)\` for targeted documentation |
100572
100867
  | **Latest Info** | websearch_exa | \`websearch_exa_web_search_exa("query ${new Date().getFullYear()}")\` |
100573
- | **Fast Code Search** | white_rabbit | \`white_rabbit_searchGitHub(query, language, useRegexp)\` |
100868
+ | **Fast Code Search** | github_search | \`github_search(query, language, repo, limit)\` |
100574
100869
  | **Deep Code Search** | gh CLI | \`gh search code "query" --repo owner/repo\` |
100575
100870
  | **Clone Repo** | gh CLI | \`gh repo clone owner/repo \${TMPDIR:-/tmp}/name -- --depth 1\` |
100576
100871
  | **Issues/PRs** | gh CLI | \`gh search issues/prs "query" --repo owner/repo\` |
@@ -100606,16 +100901,16 @@ Use OS-appropriate temp directory:
100606
100901
  **Doc Discovery is SEQUENTIAL** (websearch \u2192 version check \u2192 sitemap \u2192 investigate).
100607
100902
  **Main phase is PARALLEL** once you know where to look.
100608
100903
 
100609
- **Always vary queries** when using white_rabbit:
100904
+ **Always vary queries** when using github_search:
100610
100905
  \`\`\`
100611
100906
  // GOOD: Different angles
100612
- white_rabbit_searchGitHub(query: "useQuery(", language: ["TypeScript"])
100613
- white_rabbit_searchGitHub(query: "queryOptions", language: ["TypeScript"])
100614
- white_rabbit_searchGitHub(query: "staleTime:", language: ["TypeScript"])
100907
+ github_search(query: "useQuery(", language: "TypeScript")
100908
+ github_search(query: "queryOptions", language: "TypeScript")
100909
+ github_search(query: "staleTime:", language: "TypeScript")
100615
100910
 
100616
100911
  // BAD: Same pattern
100617
- white_rabbit_searchGitHub(query: "useQuery")
100618
- white_rabbit_searchGitHub(query: "useQuery")
100912
+ github_search(query: "useQuery")
100913
+ github_search(query: "useQuery")
100619
100914
  \`\`\`
100620
100915
 
100621
100916
  ---
@@ -100625,7 +100920,7 @@ white_rabbit_searchGitHub(query: "useQuery")
100625
100920
  | Failure | Recovery Action |
100626
100921
  |---------|-----------------|
100627
100922
  | context7 not found | Clone repo, read source + README directly |
100628
- | white_rabbit no results | Broaden query, try concept instead of exact name |
100923
+ | github_search no results | Broaden query, try concept instead of exact name |
100629
100924
  | gh API rate limit | Use cloned repo in temp directory |
100630
100925
  | Repo not found | Search for forks or mirrors |
100631
100926
  | Sitemap not found | Try \`/sitemap-0.xml\`, \`/sitemap_index.xml\`, or fetch docs index page and parse navigation |
@@ -100636,7 +100931,7 @@ white_rabbit_searchGitHub(query: "useQuery")
100636
100931
 
100637
100932
  ## COMMUNICATION RULES
100638
100933
 
100639
- 1. **NO TOOL NAMES**: Say "I'll search the codebase" not "I'll use white_rabbit"
100934
+ 1. **NO TOOL NAMES**: Say "I'll search the codebase" not "I'll use github_search"
100640
100935
  2. **NO PREAMBLE**: Answer directly, skip "I'll help you with..."
100641
100936
  3. **ALWAYS CITE**: Every code claim needs a permalink
100642
100937
  4. **USE MARKDOWN**: Code blocks with language identifiers
@@ -101613,6 +101908,7 @@ Use the right tool for the job:
101613
101908
  - **Text patterns** (strings, comments, logs): ctx_search (indexed KB) -> ctx_batch_execute / ctx_execute for multi-file analysis. ast_grep_search for structural patterns. grep/glob only as fallback when ctx_* unavailable.
101614
101909
  - **File patterns** (find by name/extension): ctx_search (indexed KB) -> LSP / ast_grep_search. glob only as fallback when ctx_* unavailable.
101615
101910
  - **History/evolution** (when added, who changed): git commands
101911
+ - **OSS reference** (how others implement X in public repos): github_search (local gh/git/rg CLIs, no third-party services)
101616
101912
 
101617
101913
  Flood with parallel calls. Cross-validate findings across multiple tools.`
101618
101914
  };
@@ -104052,16 +104348,8 @@ function createWebsearchConfig(config4) {
104052
104348
  }
104053
104349
  var websearch = createWebsearchConfig();
104054
104350
 
104055
- // src/mcp/white-rabbit.ts
104056
- var white_rabbit = {
104057
- type: "remote",
104058
- url: "https://mcp.grep.app",
104059
- enabled: true,
104060
- oauth: false
104061
- };
104062
-
104063
104351
  // src/mcp/types.ts
104064
- var McpNameSchema = exports_external.enum(["websearch", "context7", "white_rabbit", "document_reader"]);
104352
+ var McpNameSchema = exports_external.enum(["websearch", "context7", "document_reader"]);
104065
104353
  var AnyMcpNameSchema = exports_external.string().min(1);
104066
104354
 
104067
104355
  // src/mcp/index.ts
@@ -104110,9 +104398,6 @@ function createBuiltinMcps(disabledMcps = [], config4, options = {}) {
104110
104398
  if (!disabledMcps.includes("context7")) {
104111
104399
  mcps.context7 = context7;
104112
104400
  }
104113
- if (!disabledMcps.includes("white_rabbit")) {
104114
- mcps.white_rabbit = white_rabbit;
104115
- }
104116
104401
  if (!disabledMcps.includes("document_reader")) {
104117
104402
  if (commandAvailable("uvx")) {
104118
104403
  mcps.document_reader = document_reader;
@@ -104213,7 +104498,7 @@ function applyToolConfig(params) {
104213
104498
  const denyTaskTools = !isTaskSystem ? { "task_*": "deny", task: "deny" } : {};
104214
104499
  params.config.tools = {
104215
104500
  ...params.config.tools,
104216
- "white_rabbit_*": false,
104501
+ github_search: false,
104217
104502
  LspHover: false,
104218
104503
  LspCodeActions: false,
104219
104504
  LspCodeActionResolve: false,
@@ -104225,7 +104510,11 @@ function applyToolConfig(params) {
104225
104510
  const questionPermission = isCliRunMode ? "deny" : "allow";
104226
104511
  const operator = agentByKey(params.agentResult, "operator");
104227
104512
  if (operator) {
104228
- operator.permission = { ...operator.permission, "white_rabbit_*": "allow" };
104513
+ operator.permission = { ...operator.permission, github_search: "allow" };
104514
+ }
104515
+ const trinity = agentByKey(params.agentResult, "trinity");
104516
+ if (trinity) {
104517
+ trinity.permission = { ...trinity.permission, github_search: "allow" };
104229
104518
  }
104230
104519
  const construct = agentByKey(params.agentResult, "construct");
104231
104520
  if (construct) {
@@ -104476,6 +104765,7 @@ function createToolRegistry(args) {
104476
104765
  const allTools = {
104477
104766
  ...builtinTools,
104478
104767
  ...createGrepTools(ctx),
104768
+ ...createGithubSearchTools(ctx),
104479
104769
  ...createGlobTools(ctx),
104480
104770
  ...createAstGrepTools(ctx),
104481
104771
  ...createSessionManagerTools(ctx),
@@ -104942,6 +105232,7 @@ var HookNameEnum = exports_external.enum([
104942
105232
  "context-mode-enforcer",
104943
105233
  "context-mode-enforcer",
104944
105234
  "task-edit-guard",
105235
+ "document-reader-guard",
104945
105236
  "failure-counter"
104946
105237
  ]);
104947
105238
  var HookNameSchema = exports_external.union([
@@ -105776,22 +106067,22 @@ function createToolExecuteAfterHandler2(args) {
105776
106067
 
105777
106068
  // src/features/assembly-state/manager.ts
105778
106069
  init_logger();
105779
- var HOOK_NAME20 = "assembly-state";
106070
+ var HOOK_NAME21 = "assembly-state";
105780
106071
  var disabledSessions = new Set;
105781
106072
  function isAssemblyDisabled(sessionID) {
105782
106073
  return disabledSessions.has(sessionID);
105783
106074
  }
105784
106075
  function disableAssembly(sessionID) {
105785
106076
  disabledSessions.add(sessionID);
105786
- log(`[${HOOK_NAME20}] Assembly disabled for session`, { sessionID });
106077
+ log(`[${HOOK_NAME21}] Assembly disabled for session`, { sessionID });
105787
106078
  disabledSessions.add(sessionID);
105788
- log(`[${HOOK_NAME20}] Consensus disabled for session`, { sessionID });
106079
+ log(`[${HOOK_NAME21}] Consensus disabled for session`, { sessionID });
105789
106080
  }
105790
106081
  function enableAssembly(sessionID) {
105791
106082
  disabledSessions.delete(sessionID);
105792
- log(`[${HOOK_NAME20}] Assembly enabled for session`, { sessionID });
106083
+ log(`[${HOOK_NAME21}] Assembly enabled for session`, { sessionID });
105793
106084
  disabledSessions.delete(sessionID);
105794
- log(`[${HOOK_NAME20}] Consensus enabled for session`, { sessionID });
106085
+ log(`[${HOOK_NAME21}] Consensus enabled for session`, { sessionID });
105795
106086
  }
105796
106087
  // src/plugin/session-agent-resolver.ts
105797
106088
  async function resolveSessionAgent(client2, sessionId, timeoutMs = 5000) {