@wrongstack/core 0.307.0 → 0.307.1

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.
@@ -6222,6 +6222,225 @@ function maybeAppendPendingNextSteps(ctx, res) {
6222
6222
  return { ...res, content };
6223
6223
  }
6224
6224
 
6225
+ // src/prompts/prompt-journal.ts
6226
+ import * as fs5 from "node:fs/promises";
6227
+ import * as path11 from "node:path";
6228
+ async function ensureGitignore(projectRoot) {
6229
+ const gitignorePath = path11.join(projectRoot, ".gitignore");
6230
+ try {
6231
+ let content = "";
6232
+ try {
6233
+ content = await fs5.readFile(gitignorePath, "utf8");
6234
+ } catch {
6235
+ content = "";
6236
+ }
6237
+ if (!content.includes(".wrongstack") && !content.includes(".wrongstack/")) {
6238
+ const addition = content.endsWith("\n") || content.length === 0 ? ".wrongstack/\n" : "\n.wrongstack/\n";
6239
+ await fs5.writeFile(gitignorePath, content + addition, "utf8");
6240
+ }
6241
+ } catch {
6242
+ }
6243
+ }
6244
+ function sessionFileId(sessionId) {
6245
+ const leaf = sessionId.split(/[\\/]/u).pop();
6246
+ return leaf && leaf.trim().length > 0 ? leaf : "general";
6247
+ }
6248
+ async function recordPromptJournalEntry(opts) {
6249
+ const now = /* @__PURE__ */ new Date();
6250
+ const timestamp = now.toISOString();
6251
+ const dateStr = timestamp.slice(0, 10);
6252
+ const monthStr = dateStr.slice(0, 7);
6253
+ const sessionId = opts.sessionId && opts.sessionId.trim() ? opts.sessionId.trim() : "general";
6254
+ const id = `pmt_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
6255
+ const content = opts.content ?? "";
6256
+ const lines = content.split("\n");
6257
+ const characterCount = content.length;
6258
+ const lineCount = lines.length;
6259
+ const tokenEstimate = Math.ceil(characterCount / 4);
6260
+ const entry = {
6261
+ id,
6262
+ timestamp,
6263
+ sessionId,
6264
+ projectRoot: opts.projectRoot,
6265
+ role: opts.role ?? (opts.category === "system_prompt" ? "system" : "user"),
6266
+ category: opts.category,
6267
+ content,
6268
+ rawContent: opts.rawContent,
6269
+ metadata: {
6270
+ model: opts.model,
6271
+ provider: opts.provider,
6272
+ iterationIndex: opts.iterationIndex,
6273
+ tokenEstimate,
6274
+ characterCount,
6275
+ lineCount,
6276
+ activeTools: opts.activeTools,
6277
+ contextFiles: opts.contextFiles,
6278
+ durationMs: opts.durationMs,
6279
+ decisionReason: opts.decisionReason,
6280
+ tags: opts.tags
6281
+ }
6282
+ };
6283
+ const basePromptsDir = path11.join(opts.projectRoot, ".wrongstack", "prompts");
6284
+ const dayDir = path11.join(basePromptsDir, monthStr, dateStr);
6285
+ try {
6286
+ await fs5.mkdir(dayDir, { recursive: true });
6287
+ await ensureGitignore(opts.projectRoot);
6288
+ const sessionJsonlFile = path11.join(dayDir, `session-${sessionFileId(sessionId)}.jsonl`);
6289
+ await fs5.appendFile(sessionJsonlFile, JSON.stringify(entry) + "\n", "utf8");
6290
+ const sessionMdFile = path11.join(dayDir, `session-${sessionFileId(sessionId)}.md`);
6291
+ const mdSection = formatEntryMarkdown(entry);
6292
+ await fs5.appendFile(sessionMdFile, mdSection, "utf8");
6293
+ const dailySummaryFile = path11.join(dayDir, "daily-summary.md");
6294
+ await updateDailySummary(dailySummaryFile, dateStr, entry);
6295
+ await updateRootCatalog(basePromptsDir, monthStr, dateStr, sessionId, entry);
6296
+ } catch (err) {
6297
+ console.error?.(`Failed to write hierarchical prompt journal: ${err}`);
6298
+ }
6299
+ return entry;
6300
+ }
6301
+ function formatEntryMarkdown(entry) {
6302
+ const tagList = [
6303
+ `**Category:** \`${entry.category}\``,
6304
+ `**Role:** \`${entry.role}\``,
6305
+ entry.metadata.model ? `**Model:** \`${entry.metadata.model}\`` : null,
6306
+ `**Tokens (est):** ~${entry.metadata.tokenEstimate}`,
6307
+ entry.metadata.iterationIndex !== void 0 ? `**Iteration:** #${entry.metadata.iterationIndex}` : null,
6308
+ `**Session:** \`${entry.sessionId}\``
6309
+ ].filter(Boolean).join(" | ");
6310
+ let md = `
6311
+ ### \u{1F4DD} [${entry.timestamp}] \`${entry.id}\`
6312
+ ${tagList}
6313
+
6314
+ `;
6315
+ if (entry.metadata.decisionReason) {
6316
+ md += `> **Rationale:** ${entry.metadata.decisionReason}
6317
+
6318
+ `;
6319
+ }
6320
+ if (entry.metadata.activeTools && entry.metadata.activeTools.length > 0) {
6321
+ md += `*Active Tools:* \`${entry.metadata.activeTools.join("`, `")}\`
6322
+
6323
+ `;
6324
+ }
6325
+ if (entry.rawContent && entry.rawContent !== entry.content) {
6326
+ md += `**Raw Input:**
6327
+ \`\`\`text
6328
+ ${entry.rawContent.trim()}
6329
+ \`\`\`
6330
+
6331
+ `;
6332
+ md += `**Refined / Injected Prompt:**
6333
+ \`\`\`text
6334
+ ${entry.content.trim()}
6335
+ \`\`\`
6336
+
6337
+ `;
6338
+ } else {
6339
+ md += `**Prompt Content:**
6340
+ \`\`\`text
6341
+ ${entry.content.trim()}
6342
+ \`\`\`
6343
+
6344
+ `;
6345
+ }
6346
+ md += `---
6347
+ `;
6348
+ return md;
6349
+ }
6350
+ async function updateDailySummary(summaryFile, dateStr, entry) {
6351
+ try {
6352
+ let content = "";
6353
+ try {
6354
+ content = await fs5.readFile(summaryFile, "utf8");
6355
+ } catch {
6356
+ content = `# \u{1F4C5} Daily Prompt Summary \u2014 ${dateStr}
6357
+
6358
+ | Time | ID | Session | Category | Model | Tokens | Rationale |
6359
+ | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
6360
+ `;
6361
+ }
6362
+ const time = entry.timestamp.slice(11, 19);
6363
+ const model = entry.metadata.model ?? "-";
6364
+ const reason = entry.metadata.decisionReason ? entry.metadata.decisionReason.slice(0, 40) : "-";
6365
+ const row = `| ${time} | [\`${entry.id}\`](session-${sessionFileId(entry.sessionId)}.md) | \`${entry.sessionId}\` | \`${entry.category}\` | ${model} | ~${entry.metadata.tokenEstimate} | ${reason} |
6366
+ `;
6367
+ await fs5.writeFile(summaryFile, content + row, "utf8");
6368
+ } catch {
6369
+ }
6370
+ }
6371
+ async function updateRootCatalog(baseDir, monthStr, dateStr, sessionId, entry) {
6372
+ const indexJsonFile = path11.join(baseDir, "index.json");
6373
+ const indexMdFile = path11.join(baseDir, "index.md");
6374
+ let catalog;
6375
+ try {
6376
+ const raw = await fs5.readFile(indexJsonFile, "utf8");
6377
+ catalog = JSON.parse(raw);
6378
+ } catch {
6379
+ catalog = {
6380
+ updatedAt: entry.timestamp,
6381
+ totalPrompts: 0,
6382
+ totalTokensEstimated: 0,
6383
+ months: {}
6384
+ };
6385
+ }
6386
+ catalog.updatedAt = entry.timestamp;
6387
+ catalog.totalPrompts += 1;
6388
+ catalog.totalTokensEstimated += entry.metadata.tokenEstimate;
6389
+ if (!catalog.months[monthStr]) {
6390
+ catalog.months[monthStr] = { days: {} };
6391
+ }
6392
+ const monthData = catalog.months[monthStr];
6393
+ if (!monthData.days[dateStr]) {
6394
+ monthData.days[dateStr] = { sessions: {} };
6395
+ }
6396
+ const dayData = monthData.days[dateStr];
6397
+ if (!dayData.sessions[sessionId]) {
6398
+ dayData.sessions[sessionId] = {
6399
+ promptCount: 0,
6400
+ tokenEstimate: 0,
6401
+ lastTimestamp: entry.timestamp,
6402
+ categories: {}
6403
+ };
6404
+ }
6405
+ const sessionData = dayData.sessions[sessionId];
6406
+ sessionData.promptCount += 1;
6407
+ sessionData.tokenEstimate += entry.metadata.tokenEstimate;
6408
+ sessionData.lastTimestamp = entry.timestamp;
6409
+ sessionData.categories[entry.category] = (sessionData.categories[entry.category] ?? 0) + 1;
6410
+ try {
6411
+ await fs5.writeFile(indexJsonFile, JSON.stringify(catalog, null, 2), "utf8");
6412
+ let md = `# \u{1F5C2}\uFE0F Prompt Journal Navigation Index
6413
+
6414
+ `;
6415
+ md += `* **Total Prompts Logged:** ${catalog.totalPrompts}
6416
+ `;
6417
+ md += `* **Total Tokens (est):** ~${catalog.totalTokensEstimated.toLocaleString()}
6418
+ `;
6419
+ md += `* **Last Recorded Activity:** ${catalog.updatedAt}
6420
+
6421
+ `;
6422
+ md += `## \u{1F4C5} Recorded Dates & Sessions
6423
+
6424
+ `;
6425
+ md += `| Date | Session | Prompts | Tokens (est) | Daily Log | Session Log |
6426
+ `;
6427
+ md += `| :--- | :--- | :--- | :--- | :--- | :--- |
6428
+ `;
6429
+ for (const [m, mObj] of Object.entries(catalog.months).sort().reverse()) {
6430
+ for (const [d, dObj] of Object.entries(mObj.days).sort().reverse()) {
6431
+ for (const [sId, sData] of Object.entries(dObj.sessions)) {
6432
+ const dailyLink = `[daily-summary.md](./${m}/${d}/daily-summary.md)`;
6433
+ const sessionLink = `[session-${sessionFileId(sId)}.md](./${m}/${d}/session-${sessionFileId(sId)}.md)`;
6434
+ md += `| **${d}** | \`${sId}\` | ${sData.promptCount} | ~${sData.tokenEstimate.toLocaleString()} | ${dailyLink} | ${sessionLink} |
6435
+ `;
6436
+ }
6437
+ }
6438
+ }
6439
+ await fs5.writeFile(indexMdFile, md, "utf8");
6440
+ } catch {
6441
+ }
6442
+ }
6443
+
6225
6444
  // src/core/streaming-response-builder.ts
6226
6445
  import { randomUUID as randomUUID4 } from "node:crypto";
6227
6446
 
@@ -7037,6 +7256,36 @@ function providerBoundToRequest(request) {
7037
7256
  function toError(err) {
7038
7257
  return err instanceof Error ? err : new Error(String(err));
7039
7258
  }
7259
+ function recordSelfHealingRetry(a, err, reason) {
7260
+ const projectRoot = a.ctx.projectRoot;
7261
+ if (!projectRoot) return;
7262
+ void recordPromptJournalEntry({
7263
+ projectRoot,
7264
+ sessionId: resolveEventSessionId(a.ctx),
7265
+ category: "self_healing_retry",
7266
+ content: toErrorMessage(err),
7267
+ decisionReason: reason,
7268
+ model: a.ctx.model,
7269
+ provider: a.ctx.provider?.id,
7270
+ activeTools: a.ctx.tools?.map((tool) => tool.name) ?? []
7271
+ }).catch(() => {
7272
+ });
7273
+ }
7274
+ function recordAutonomousContinue(a, text) {
7275
+ const projectRoot = a.ctx.projectRoot;
7276
+ if (!projectRoot) return;
7277
+ void recordPromptJournalEntry({
7278
+ projectRoot,
7279
+ sessionId: resolveEventSessionId(a.ctx),
7280
+ category: "autonomous_next_step",
7281
+ content: text,
7282
+ decisionReason: "text-marker autonomous continue",
7283
+ model: a.ctx.model,
7284
+ provider: a.ctx.provider?.id,
7285
+ activeTools: a.ctx.tools?.map((tool) => tool.name) ?? []
7286
+ }).catch(() => {
7287
+ });
7288
+ }
7040
7289
  function signalAbortReason(signal) {
7041
7290
  const r = signal.reason;
7042
7291
  if (r instanceof Error) return r.message || r.name;
@@ -7327,6 +7576,7 @@ ${text}` : text;
7327
7576
  }
7328
7577
  if (extDecision.model) a.ctx.model = extDecision.model;
7329
7578
  a.logger.info("Extension requested retry; retrying turn");
7579
+ recordSelfHealingRetry(a, err, "extension-requested provider retry");
7330
7580
  continue;
7331
7581
  }
7332
7582
  }
@@ -7356,6 +7606,7 @@ ${text}` : text;
7356
7606
  }
7357
7607
  if (recovered.model) a.ctx.model = recovered.model;
7358
7608
  a.logger.info(`Recovered provider error via ${recovered.reason}; retrying turn`);
7609
+ recordSelfHealingRetry(a, err, recovered.reason);
7359
7610
  continue;
7360
7611
  }
7361
7612
  recoveryRetries = 0;
@@ -7415,6 +7666,7 @@ ${text}` : text;
7415
7666
  continue;
7416
7667
  }
7417
7668
  if (autonomousContinue && responseResult.directive === "continue") {
7669
+ recordAutonomousContinue(a, finalText);
7418
7670
  await a.extensions.runAfterIteration(a.ctx, i);
7419
7671
  continue;
7420
7672
  }
@@ -7651,6 +7903,7 @@ var RUNTIME_CAPABILITY_MANIFEST = [
7651
7903
  "codebase-skeleton",
7652
7904
  "codebase-repo-map",
7653
7905
  "codebase-impact-analysis",
7906
+ "codebase-invariant-check",
7654
7907
  "dead-code-scan",
7655
7908
  "diff",
7656
7909
  "json",
@@ -10535,8 +10788,8 @@ var InputBuilder = class {
10535
10788
  async registerFile(input) {
10536
10789
  const ref = await this.store.add({ ...input, kind: "file" });
10537
10790
  this.refs.push(ref);
10538
- const path15 = ref.meta.filename ?? ref.meta.label ?? String(ref.seq);
10539
- return `[file:${path15}]`;
10791
+ const path16 = ref.meta.filename ?? ref.meta.label ?? String(ref.seq);
10792
+ return `[file:${path16}]`;
10540
10793
  }
10541
10794
  /**
10542
10795
  * Whether `appendPaste(text)` would collapse the text to a placeholder
@@ -10583,8 +10836,8 @@ function paragraphLabel(text) {
10583
10836
 
10584
10837
  // src/core/instruction-bundle.ts
10585
10838
  import { statSync as statSync2 } from "node:fs";
10586
- import * as fs5 from "node:fs/promises";
10587
- import * as path11 from "node:path";
10839
+ import * as fs6 from "node:fs/promises";
10840
+ import * as path12 from "node:path";
10588
10841
  import { fileURLToPath as fileURLToPath2 } from "node:url";
10589
10842
  async function loadInstructionBundle(paths) {
10590
10843
  let bundle = {};
@@ -10640,17 +10893,17 @@ function resolveSystemInstructionFile(paths) {
10640
10893
  }
10641
10894
  function sanitizeSystemInstructionFile(file) {
10642
10895
  const trimmed = file.trim();
10643
- if (trimmed.length === 0 || trimmed !== path11.basename(trimmed) || path11.extname(trimmed).toLowerCase() !== ".md") {
10896
+ if (trimmed.length === 0 || trimmed !== path12.basename(trimmed) || path12.extname(trimmed).toLowerCase() !== ".md") {
10644
10897
  throw new Error(`Invalid system instruction file: ${file}`);
10645
10898
  }
10646
10899
  return trimmed;
10647
10900
  }
10648
10901
  async function readInstructionDir(dir, options) {
10649
10902
  const [json, identity, leaderAfterTask, sections] = await Promise.all([
10650
- readInstructionJson(path11.join(dir, "instructions.json")),
10651
- readOptionalText(path11.join(dir, options.systemFile)),
10652
- readOptionalText(path11.join(dir, "leader-after-task.md")),
10653
- readSections(path11.join(dir, "sections"))
10903
+ readInstructionJson(path12.join(dir, "instructions.json")),
10904
+ readOptionalText(path12.join(dir, options.systemFile)),
10905
+ readOptionalText(path12.join(dir, "leader-after-task.md")),
10906
+ readSections(path12.join(dir, "sections"))
10654
10907
  ]);
10655
10908
  const fromMarkdown = {
10656
10909
  system: {
@@ -10669,13 +10922,13 @@ async function readSections(root) {
10669
10922
  async function readSectionsInto(root, dir, out) {
10670
10923
  let entries;
10671
10924
  try {
10672
- entries = await fs5.readdir(dir, { withFileTypes: true });
10925
+ entries = await fs6.readdir(dir, { withFileTypes: true });
10673
10926
  } catch {
10674
10927
  return;
10675
10928
  }
10676
10929
  await Promise.all(
10677
10930
  entries.map(async (entry) => {
10678
- const file = path11.join(dir, entry.name);
10931
+ const file = path12.join(dir, entry.name);
10679
10932
  if (entry.isDirectory()) {
10680
10933
  await readSectionsInto(root, file, out);
10681
10934
  return;
@@ -10683,7 +10936,7 @@ async function readSectionsInto(root, dir, out) {
10683
10936
  if (!entry.isFile() || !entry.name.endsWith(".md")) return;
10684
10937
  const text = await readOptionalText(file);
10685
10938
  if (text === void 0) return;
10686
- const rel = path11.relative(root, file).replace(/\\/g, "/").replace(/\.md$/i, "");
10939
+ const rel = path12.relative(root, file).replace(/\\/g, "/").replace(/\.md$/i, "");
10687
10940
  const key = rel.split("/").join(".").replace(/-/g, ".");
10688
10941
  out[key] = text;
10689
10942
  })
@@ -10692,7 +10945,7 @@ async function readSectionsInto(root, dir, out) {
10692
10945
  async function readInstructionJson(file) {
10693
10946
  let raw;
10694
10947
  try {
10695
- raw = await fs5.readFile(file, "utf8");
10948
+ raw = await fs6.readFile(file, "utf8");
10696
10949
  } catch {
10697
10950
  return {};
10698
10951
  }
@@ -10724,18 +10977,18 @@ function normalizeInstructionBundle(value) {
10724
10977
  }
10725
10978
  async function readOptionalText(file) {
10726
10979
  try {
10727
- const text = await fs5.readFile(file, "utf8");
10980
+ const text = await fs6.readFile(file, "utf8");
10728
10981
  return text.trimEnd();
10729
10982
  } catch {
10730
10983
  return void 0;
10731
10984
  }
10732
10985
  }
10733
10986
  function defaultBundledInstructionDir() {
10734
- const here = path11.dirname(fileURLToPath2(import.meta.url));
10987
+ const here = path12.dirname(fileURLToPath2(import.meta.url));
10735
10988
  return firstExistingDirSync([
10736
- path11.resolve(here, "../../instructions"),
10737
- path11.resolve(here, "../instructions"),
10738
- path11.resolve(here, "instructions")
10989
+ path12.resolve(here, "../../instructions"),
10990
+ path12.resolve(here, "../instructions"),
10991
+ path12.resolve(here, "instructions")
10739
10992
  ]);
10740
10993
  }
10741
10994
  function definedPick(obj, keys) {
@@ -10789,13 +11042,13 @@ function flattenSystemPromptRegions(regions) {
10789
11042
 
10790
11043
  // src/core/modes/default.ts
10791
11044
  import { readFileSync as readFileSync6, statSync as statSync3 } from "node:fs";
10792
- import * as path12 from "node:path";
11045
+ import * as path13 from "node:path";
10793
11046
  import { fileURLToPath as fileURLToPath3 } from "node:url";
10794
11047
  var PROMPT = readBundledInstructionFile("system.md");
10795
11048
  var LEADER_AFTER_TASK_PROMPT = readBundledInstructionFile("leader-after-task.md");
10796
11049
  function readBundledInstructionFile(name) {
10797
11050
  for (const dir of bundledInstructionDirCandidates()) {
10798
- const file = path12.join(dir, name);
11051
+ const file = path13.join(dir, name);
10799
11052
  try {
10800
11053
  return readFileSync6(file, "utf8").trimEnd();
10801
11054
  } catch {
@@ -10804,12 +11057,12 @@ function readBundledInstructionFile(name) {
10804
11057
  return "";
10805
11058
  }
10806
11059
  function bundledInstructionDirCandidates() {
10807
- const here = path12.dirname(fileURLToPath3(import.meta.url));
11060
+ const here = path13.dirname(fileURLToPath3(import.meta.url));
10808
11061
  const candidates = [
10809
- path12.resolve(here, "../../../instructions"),
10810
- path12.resolve(here, "../../instructions"),
10811
- path12.resolve(here, "../instructions"),
10812
- path12.resolve(here, "instructions")
11062
+ path13.resolve(here, "../../../instructions"),
11063
+ path13.resolve(here, "../../instructions"),
11064
+ path13.resolve(here, "../instructions"),
11065
+ path13.resolve(here, "instructions")
10813
11066
  ];
10814
11067
  return candidates.sort((a, b) => Number(!isDirectory(a)) - Number(!isDirectory(b)));
10815
11068
  }
@@ -10823,12 +11076,12 @@ function isDirectory(candidate) {
10823
11076
 
10824
11077
  // src/core/system-prompt-environment.ts
10825
11078
  import * as os3 from "node:os";
10826
- import * as path14 from "node:path";
11079
+ import * as path15 from "node:path";
10827
11080
 
10828
11081
  // src/core/system-prompt-environment-probes.ts
10829
11082
  import { spawn as spawn2 } from "node:child_process";
10830
- import * as fs6 from "node:fs/promises";
10831
- import * as path13 from "node:path";
11083
+ import * as fs7 from "node:fs/promises";
11084
+ import * as path14 from "node:path";
10832
11085
 
10833
11086
  // src/utils/child-env.ts
10834
11087
  var ALLOWED_KEYS = /* @__PURE__ */ new Set([
@@ -10969,7 +11222,7 @@ function buildChildEnv(optsOrSessionId) {
10969
11222
  // src/core/system-prompt-environment-probes.ts
10970
11223
  async function dirExists(p) {
10971
11224
  try {
10972
- const stat3 = await fs6.stat(p);
11225
+ const stat3 = await fs7.stat(p);
10973
11226
  return stat3.isDirectory();
10974
11227
  } catch {
10975
11228
  return false;
@@ -11056,7 +11309,7 @@ async function detectLanguages(root) {
11056
11309
  const hits = await Promise.all(
11057
11310
  checks.map(async ([marker, lang]) => {
11058
11311
  try {
11059
- await fs6.access(path13.join(root, marker));
11312
+ await fs7.access(path14.join(root, marker));
11060
11313
  return lang;
11061
11314
  } catch {
11062
11315
  return null;
@@ -11142,7 +11395,7 @@ async function buildEnvironment(ctx, env) {
11142
11395
  const effShell = effectiveShell(os3.platform(), process.env["WRONGSTACK_SHELL"]);
11143
11396
  const shell = effShell === "posix" ? process.env.SHELL ?? process.env.ComSpec ?? "unknown" : SHELL_DISPLAY[effShell];
11144
11397
  const node = process.version;
11145
- const isGit = await dirExists(path14.join(ctx.projectRoot, ".git"));
11398
+ const isGit = await dirExists(path15.join(ctx.projectRoot, ".git"));
11146
11399
  const [git, langs] = await Promise.all([
11147
11400
  isGit ? gitStatus(ctx.projectRoot) : Promise.resolve("not a git repo"),
11148
11401
  detectLanguages(ctx.projectRoot)
@@ -11618,16 +11871,16 @@ ${skillBodyCache}`);
11618
11871
  }
11619
11872
 
11620
11873
  // src/core/system-prompt-plan.ts
11621
- import * as fs7 from "node:fs/promises";
11874
+ import * as fs8 from "node:fs/promises";
11622
11875
  async function readActivePlanBlock(args) {
11623
11876
  const { planPath, cache } = args;
11624
11877
  if (!planPath) return { text: "", cache };
11625
11878
  try {
11626
- const stat3 = await fs7.stat(planPath);
11879
+ const stat3 = await fs8.stat(planPath);
11627
11880
  if (cache && cache.path === planPath && cache.mtimeMs === stat3.mtimeMs) {
11628
11881
  return { text: cache.text, cache };
11629
11882
  }
11630
- const raw = await fs7.readFile(planPath, "utf8");
11883
+ const raw = await fs8.readFile(planPath, "utf8");
11631
11884
  const text = formatActivePlan(raw);
11632
11885
  return { text, cache: { path: planPath, mtimeMs: stat3.mtimeMs, text } };
11633
11886
  } catch {
@@ -1793,6 +1793,7 @@ var RUNTIME_CAPABILITY_MANIFEST = [
1793
1793
  "codebase-skeleton",
1794
1794
  "codebase-repo-map",
1795
1795
  "codebase-impact-analysis",
1796
+ "codebase-invariant-check",
1796
1797
  "dead-code-scan",
1797
1798
  "diff",
1798
1799
  "json",
@@ -2718,13 +2719,50 @@ var HEAVY_BUDGET = {
2718
2719
  maxIterations: 8e3,
2719
2720
  maxToolCalls: 2e4
2720
2721
  };
2722
+ var INDEX_READ = [
2723
+ "codebase-stats",
2724
+ "codebase-search",
2725
+ "codebase-skeleton",
2726
+ "codebase-repo-map",
2727
+ "codebase-incoming-calls",
2728
+ "codebase-outgoing-calls"
2729
+ ];
2721
2730
  var TOOLS = {
2731
+ /** Index-backed code discovery. Spread onto code-facing presets, not browser. */
2732
+ index: INDEX_READ,
2722
2733
  /** Pure read/inspect — safe for analysis and review agents. */
2723
2734
  read: ["read", "grep", "glob", "search", "tree", "mailbox"],
2724
2735
  /** Read + structured inspection (logs, diffs, json, dependency audit). */
2725
- inspect: ["read", "grep", "glob", "search", "tree", "json", "diff", "logs", "audit", "mailbox"],
2736
+ inspect: [
2737
+ "read",
2738
+ "grep",
2739
+ "glob",
2740
+ "search",
2741
+ "tree",
2742
+ ...INDEX_READ,
2743
+ "json",
2744
+ "diff",
2745
+ "logs",
2746
+ "audit",
2747
+ "mailbox"
2748
+ ],
2726
2749
  /** Read + edit (no shell). For agents that write code/docs but don't run it. */
2727
- write: ["read", "grep", "glob", "search", "tree", "write", "edit", "replace", "patch", "mailbox"],
2750
+ write: [
2751
+ "read",
2752
+ "grep",
2753
+ "glob",
2754
+ "search",
2755
+ "tree",
2756
+ ...INDEX_READ,
2757
+ "codebase-impact-analysis",
2758
+ "codebase-ast-replace",
2759
+ "codebase-invariant-check",
2760
+ "write",
2761
+ "edit",
2762
+ "replace",
2763
+ "patch",
2764
+ "mailbox"
2765
+ ],
2728
2766
  /** Full build loop: edit + run (lint/format/typecheck/test/bash). */
2729
2767
  build: [
2730
2768
  "read",
@@ -2732,6 +2770,11 @@ var TOOLS = {
2732
2770
  "glob",
2733
2771
  "search",
2734
2772
  "tree",
2773
+ ...INDEX_READ,
2774
+ "codebase-impact-analysis",
2775
+ "codebase-ast-replace",
2776
+ "codebase-invariant-check",
2777
+ "codebase-targeted-test",
2735
2778
  "diff",
2736
2779
  "write",
2737
2780
  "edit",
@@ -2758,7 +2801,18 @@ var TOOLS = {
2758
2801
  /** Dependency management + CVE audit. */
2759
2802
  deps: ["read", "grep", "glob", "install", "outdated", "audit", "json", "mailbox"],
2760
2803
  /** Documentation authoring. */
2761
- docs: ["read", "grep", "glob", "search", "tree", "write", "edit", "document", "mailbox"],
2804
+ docs: [
2805
+ "read",
2806
+ "grep",
2807
+ "glob",
2808
+ "search",
2809
+ "tree",
2810
+ ...INDEX_READ,
2811
+ "write",
2812
+ "edit",
2813
+ "document",
2814
+ "mailbox"
2815
+ ],
2762
2816
  /** Web research. */
2763
2817
  research: ["read", "grep", "glob", "search", "fetch", "mailbox"]
2764
2818
  };
@@ -2775,7 +2829,7 @@ var DISCOVERY_AGENTS = [
2775
2829
  id: "explore",
2776
2830
  name: "Explore",
2777
2831
  role: "explore",
2778
- tools: [...TOOLS.read],
2832
+ tools: [...TOOLS.read, ...TOOLS.index],
2779
2833
  prompt: agentPrompt("explore")
2780
2834
  },
2781
2835
  budget: MEDIUM_BUDGET,
@@ -2803,7 +2857,7 @@ var DISCOVERY_AGENTS = [
2803
2857
  id: "search",
2804
2858
  name: "Search",
2805
2859
  role: "search",
2806
- tools: [...TOOLS.read, "codebase-search"],
2860
+ tools: [...TOOLS.read, ...TOOLS.index],
2807
2861
  prompt: agentPrompt("search")
2808
2862
  },
2809
2863
  budget: MEDIUM_BUDGET,
@@ -2854,7 +2908,7 @@ var DISCOVERY_AGENTS = [
2854
2908
  ];
2855
2909
 
2856
2910
  // src/coordination/agents/phase2-planning.ts
2857
- var PLAN_TOOLS = [...TOOLS.read, "plan", "todo"];
2911
+ var PLAN_TOOLS = [...TOOLS.read, ...TOOLS.index, "plan", "todo"];
2858
2912
  var PLANNING_AGENTS = [
2859
2913
  {
2860
2914
  config: {