@harness-engineering/cli 1.14.0 → 1.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +240 -39
  2. package/dist/agents/skills/claude-code/harness-autopilot/skill.yaml +6 -0
  3. package/dist/agents/skills/claude-code/harness-product-spec/SKILL.md +5 -5
  4. package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +240 -39
  5. package/dist/agents/skills/gemini-cli/harness-autopilot/skill.yaml +6 -0
  6. package/dist/agents/skills/gemini-cli/harness-product-spec/SKILL.md +5 -5
  7. package/dist/agents/skills/package.json +1 -0
  8. package/dist/agents/skills/vitest.config.mts +5 -0
  9. package/dist/{agents-md-YTYQDA3P.js → agents-md-ZGNIDWAF.js} +1 -1
  10. package/dist/{architecture-JQZYM4US.js → architecture-ZLIH5533.js} +2 -2
  11. package/dist/bin/harness-mcp.js +11 -11
  12. package/dist/bin/harness.js +16 -14
  13. package/dist/{check-phase-gate-L3RADYWO.js → check-phase-gate-ZOXVBDCN.js} +3 -3
  14. package/dist/{chunk-7IP4JIFL.js → chunk-2BKLWLY6.js} +4 -4
  15. package/dist/{chunk-OSXBPAMK.js → chunk-3ZZKVN62.js} +1 -1
  16. package/dist/{chunk-O5OJVPL6.js → chunk-B2HKP423.js} +1 -1
  17. package/dist/{chunk-YPYGXRDR.js → chunk-EDXIVMAP.js} +4 -4
  18. package/dist/{chunk-XKECDXJS.js → chunk-J4RAX7YB.js} +738 -186
  19. package/dist/{chunk-6KTUUFRN.js → chunk-LGYBN7Y6.js} +1 -1
  20. package/dist/{chunk-3C2MLBPJ.js → chunk-N25INEIX.js} +1 -1
  21. package/dist/{chunk-NLVUVUGD.js → chunk-ND2ENWDM.js} +1 -1
  22. package/dist/{chunk-YZD2MRNQ.js → chunk-NNHDDXYT.js} +379 -119
  23. package/dist/{chunk-S2FXOWOR.js → chunk-OFXQSFOW.js} +2 -2
  24. package/dist/{chunk-OXLLOSSR.js → chunk-VEPAJXBW.js} +2 -2
  25. package/dist/{chunk-TPOTOBR7.js → chunk-YLXFKVJE.js} +3 -3
  26. package/dist/{chunk-ABQHQ6I5.js → chunk-Z2OOPXJO.js} +1238 -133
  27. package/dist/{ci-workflow-EQZFVX3P.js → ci-workflow-765LSHRD.js} +1 -1
  28. package/dist/{dist-HWXF2C3R.js → dist-ALQDD67R.js} +47 -1
  29. package/dist/{docs-7ECGYMAV.js → docs-NRMQCOJ6.js} +3 -3
  30. package/dist/{engine-EG4EH4IX.js → engine-3RB7MXPP.js} +1 -1
  31. package/dist/{entropy-5USWKLVS.js → entropy-6AGX2ZUN.js} +2 -2
  32. package/dist/{feedback-UTBXZZHF.js → feedback-MY4QZIFD.js} +1 -1
  33. package/dist/{generate-agent-definitions-3PM5EU7V.js → generate-agent-definitions-ZAE726AU.js} +1 -1
  34. package/dist/index.d.ts +8 -8
  35. package/dist/index.js +13 -13
  36. package/dist/{loader-ZPALXIVR.js → loader-UUTVMQCC.js} +1 -1
  37. package/dist/{mcp-362EZHF4.js → mcp-VU5FMO52.js} +11 -11
  38. package/dist/{performance-OQAFMJUD.js → performance-2D7G6NMJ.js} +2 -2
  39. package/dist/{review-pipeline-C4GCFVGP.js → review-pipeline-RAQ55ISU.js} +1 -1
  40. package/dist/{runtime-7YLVK453.js → runtime-BCK5RRZQ.js} +1 -1
  41. package/dist/{security-PZOX7AQS.js → security-2RPQEN62.js} +1 -1
  42. package/dist/{validate-FD3Z6VJD.js → validate-KBYQAEWE.js} +2 -2
  43. package/dist/{validate-cross-check-WNJM6H2D.js → validate-cross-check-OABMREW4.js} +1 -1
  44. package/package.json +5 -3
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  detectEntropyDefinition,
3
3
  handleDetectEntropy
4
- } from "./chunk-OXLLOSSR.js";
4
+ } from "./chunk-VEPAJXBW.js";
5
5
  import {
6
6
  checkPerformanceDefinition,
7
7
  getCriticalPathsDefinition,
@@ -11,7 +11,7 @@ import {
11
11
  handleGetPerfBaselines,
12
12
  handleUpdatePerfBaselines,
13
13
  updatePerfBaselinesDefinition
14
- } from "./chunk-YPYGXRDR.js";
14
+ } from "./chunk-EDXIVMAP.js";
15
15
  import {
16
16
  analyzeDiffDefinition,
17
17
  createSelfReviewDefinition,
@@ -19,15 +19,15 @@ import {
19
19
  handleCreateSelfReview,
20
20
  handleRequestPeerReview,
21
21
  requestPeerReviewDefinition
22
- } from "./chunk-TPOTOBR7.js";
22
+ } from "./chunk-YLXFKVJE.js";
23
23
  import {
24
24
  handleRunSecurityScan,
25
25
  runSecurityScanDefinition
26
- } from "./chunk-OSXBPAMK.js";
26
+ } from "./chunk-3ZZKVN62.js";
27
27
  import {
28
28
  handleRunCodeReview,
29
29
  runCodeReviewDefinition
30
- } from "./chunk-NLVUVUGD.js";
30
+ } from "./chunk-ND2ENWDM.js";
31
31
  import {
32
32
  GENERATED_HEADER_CLAUDE,
33
33
  GENERATED_HEADER_GEMINI,
@@ -38,24 +38,24 @@ import {
38
38
  import {
39
39
  handleValidateProject,
40
40
  validateToolDefinition
41
- } from "./chunk-S2FXOWOR.js";
41
+ } from "./chunk-OFXQSFOW.js";
42
42
  import {
43
43
  loadGraphStore
44
44
  } from "./chunk-FTMXDOR6.js";
45
45
  import {
46
46
  checkDependenciesDefinition,
47
47
  handleCheckDependencies
48
- } from "./chunk-3C2MLBPJ.js";
48
+ } from "./chunk-N25INEIX.js";
49
49
  import {
50
50
  resolveProjectConfig
51
51
  } from "./chunk-H7Y5CKTM.js";
52
52
  import {
53
53
  checkDocsDefinition,
54
54
  handleCheckDocs
55
- } from "./chunk-7IP4JIFL.js";
55
+ } from "./chunk-2BKLWLY6.js";
56
56
  import {
57
57
  resolveConfig
58
- } from "./chunk-O5OJVPL6.js";
58
+ } from "./chunk-B2HKP423.js";
59
59
  import {
60
60
  resultToMcpResponse
61
61
  } from "./chunk-IDZNPTYD.js";
@@ -428,7 +428,7 @@ ${skippedMsg}`
428
428
  async function handleInitProject(input) {
429
429
  const i = input;
430
430
  try {
431
- const { TemplateEngine } = await import("./engine-EG4EH4IX.js");
431
+ const { TemplateEngine } = await import("./engine-3RB7MXPP.js");
432
432
  const engine = new TemplateEngine(resolveTemplatesDir());
433
433
  const safePath = sanitizePath(i.path);
434
434
  const detected = tryDetectFramework(engine, safePath, i);
@@ -449,7 +449,7 @@ var listPersonasDefinition = {
449
449
  inputSchema: { type: "object", properties: {} }
450
450
  };
451
451
  async function handleListPersonas() {
452
- const { listPersonas } = await import("./loader-ZPALXIVR.js");
452
+ const { listPersonas } = await import("./loader-UUTVMQCC.js");
453
453
  const result = listPersonas(resolvePersonasDir());
454
454
  return resultToMcpResponse(result);
455
455
  }
@@ -473,10 +473,10 @@ async function handleGeneratePersonaArtifacts(input) {
473
473
  if (!/^[a-z0-9][a-z0-9._-]*$/i.test(input.name)) {
474
474
  return resultToMcpResponse(Err(new Error(`Invalid persona name: ${input.name}`)));
475
475
  }
476
- const { loadPersona } = await import("./loader-ZPALXIVR.js");
477
- const { generateRuntime } = await import("./runtime-7YLVK453.js");
478
- const { generateAgentsMd } = await import("./agents-md-YTYQDA3P.js");
479
- const { generateCIWorkflow } = await import("./ci-workflow-EQZFVX3P.js");
476
+ const { loadPersona } = await import("./loader-UUTVMQCC.js");
477
+ const { generateRuntime } = await import("./runtime-BCK5RRZQ.js");
478
+ const { generateAgentsMd } = await import("./agents-md-ZGNIDWAF.js");
479
+ const { generateCIWorkflow } = await import("./ci-workflow-765LSHRD.js");
480
480
  const personasDir = resolvePersonasDir();
481
481
  const filePath = path3.join(personasDir, `${input.name}.yaml`);
482
482
  if (!filePath.startsWith(personasDir)) {
@@ -531,7 +531,7 @@ async function handleRunPersona(input) {
531
531
  if (!/^[a-z0-9][a-z0-9._-]*$/i.test(input.persona)) {
532
532
  return resultToMcpResponse(Err(new Error(`Invalid persona name: ${input.persona}`)));
533
533
  }
534
- const { loadPersona } = await import("./loader-ZPALXIVR.js");
534
+ const { loadPersona } = await import("./loader-UUTVMQCC.js");
535
535
  const { runPersona } = await import("./runner-VMYLHWOC.js");
536
536
  const { executeSkill } = await import("./skill-executor-XZLYZYAK.js");
537
537
  const personasDir = resolvePersonasDir();
@@ -1142,6 +1142,26 @@ async function getLearningsResource(projectRoot) {
1142
1142
  return sections.length > 0 ? sections.join("\n\n---\n\n") : "No learnings files found.";
1143
1143
  }
1144
1144
 
1145
+ // src/mcp/tools/roadmap-auto-sync.ts
1146
+ import * as fs10 from "fs";
1147
+ import * as path11 from "path";
1148
+ async function autoSyncRoadmap(projectPath) {
1149
+ try {
1150
+ const roadmapFile = path11.join(projectPath, "docs", "roadmap.md");
1151
+ if (!fs10.existsSync(roadmapFile)) return;
1152
+ const { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges } = await import("./dist-ALQDD67R.js");
1153
+ const raw = fs10.readFileSync(roadmapFile, "utf-8");
1154
+ const parseResult = parseRoadmap(raw);
1155
+ if (!parseResult.ok) return;
1156
+ const roadmap = parseResult.value;
1157
+ const syncResult = syncRoadmap({ projectPath, roadmap });
1158
+ if (!syncResult.ok || syncResult.value.length === 0) return;
1159
+ applySyncChanges(roadmap, syncResult.value);
1160
+ fs10.writeFileSync(roadmapFile, serializeRoadmap(roadmap), "utf-8");
1161
+ } catch {
1162
+ }
1163
+ }
1164
+
1145
1165
  // src/mcp/utils.ts
1146
1166
  function mcpError(text) {
1147
1167
  return { content: [{ type: "text", text }], isError: true };
@@ -1215,12 +1235,12 @@ var manageStateDefinition = {
1215
1235
  }
1216
1236
  };
1217
1237
  async function handleShow(projectPath, input) {
1218
- const { loadState } = await import("./dist-HWXF2C3R.js");
1238
+ const { loadState } = await import("./dist-ALQDD67R.js");
1219
1239
  return resultToMcpResponse(await loadState(projectPath, input.stream, input.session));
1220
1240
  }
1221
1241
  async function handleLearn(projectPath, input) {
1222
1242
  if (!input.learning) return mcpError("Error: learning is required for learn action");
1223
- const { appendLearning } = await import("./dist-HWXF2C3R.js");
1243
+ const { appendLearning } = await import("./dist-ALQDD67R.js");
1224
1244
  const result = await appendLearning(
1225
1245
  projectPath,
1226
1246
  input.learning,
@@ -1235,7 +1255,7 @@ async function handleLearn(projectPath, input) {
1235
1255
  async function handleFailure(projectPath, input) {
1236
1256
  if (!input.description) return mcpError("Error: description is required for failure action");
1237
1257
  if (!input.failureType) return mcpError("Error: failureType is required for failure action");
1238
- const { appendFailure } = await import("./dist-HWXF2C3R.js");
1258
+ const { appendFailure } = await import("./dist-ALQDD67R.js");
1239
1259
  const result = await appendFailure(
1240
1260
  projectPath,
1241
1261
  input.description,
@@ -1248,34 +1268,36 @@ async function handleFailure(projectPath, input) {
1248
1268
  return resultToMcpResponse(Ok({ recorded: true }));
1249
1269
  }
1250
1270
  async function handleArchive(projectPath, input) {
1251
- const { archiveFailures } = await import("./dist-HWXF2C3R.js");
1271
+ const { archiveFailures } = await import("./dist-ALQDD67R.js");
1252
1272
  const result = await archiveFailures(projectPath, input.stream, input.session);
1253
1273
  if (!result.ok) return resultToMcpResponse(result);
1254
1274
  return resultToMcpResponse(Ok({ archived: true }));
1255
1275
  }
1256
1276
  async function handleReset(projectPath, input) {
1257
- const { saveState, DEFAULT_STATE } = await import("./dist-HWXF2C3R.js");
1277
+ const { saveState, DEFAULT_STATE } = await import("./dist-ALQDD67R.js");
1258
1278
  const result = await saveState(projectPath, { ...DEFAULT_STATE }, input.stream, input.session);
1259
1279
  if (!result.ok) return resultToMcpResponse(result);
1260
1280
  return resultToMcpResponse(Ok({ reset: true }));
1261
1281
  }
1262
1282
  async function handleGate(projectPath, _input) {
1263
- const { runMechanicalGate } = await import("./dist-HWXF2C3R.js");
1283
+ const { runMechanicalGate } = await import("./dist-ALQDD67R.js");
1264
1284
  return resultToMcpResponse(await runMechanicalGate(projectPath));
1265
1285
  }
1266
1286
  async function handleSaveHandoff(projectPath, input) {
1267
1287
  if (!input.handoff) return mcpError("Error: handoff is required for save-handoff action");
1268
- const { saveHandoff } = await import("./dist-HWXF2C3R.js");
1288
+ const { saveHandoff } = await import("./dist-ALQDD67R.js");
1269
1289
  const result = await saveHandoff(
1270
1290
  projectPath,
1271
1291
  input.handoff,
1272
1292
  input.stream,
1273
1293
  input.session
1274
1294
  );
1275
- return resultToMcpResponse(result.ok ? Ok({ saved: true }) : result);
1295
+ if (!result.ok) return resultToMcpResponse(result);
1296
+ await autoSyncRoadmap(projectPath);
1297
+ return resultToMcpResponse(Ok({ saved: true }));
1276
1298
  }
1277
1299
  async function handleLoadHandoff(projectPath, input) {
1278
- const { loadHandoff } = await import("./dist-HWXF2C3R.js");
1300
+ const { loadHandoff } = await import("./dist-ALQDD67R.js");
1279
1301
  return resultToMcpResponse(await loadHandoff(projectPath, input.stream, input.session));
1280
1302
  }
1281
1303
  async function handleAppendEntry(projectPath, input) {
@@ -1283,7 +1305,7 @@ async function handleAppendEntry(projectPath, input) {
1283
1305
  if (!input.section) return mcpError("Error: section is required for append_entry action");
1284
1306
  if (!input.authorSkill) return mcpError("Error: authorSkill is required for append_entry action");
1285
1307
  if (!input.content) return mcpError("Error: content is required for append_entry action");
1286
- const { appendSessionEntry } = await import("./dist-HWXF2C3R.js");
1308
+ const { appendSessionEntry } = await import("./dist-ALQDD67R.js");
1287
1309
  const result = await appendSessionEntry(
1288
1310
  projectPath,
1289
1311
  input.session,
@@ -1299,7 +1321,7 @@ async function handleUpdateEntryStatus(projectPath, input) {
1299
1321
  if (!input.entryId) return mcpError("Error: entryId is required for update_entry_status action");
1300
1322
  if (!input.newStatus)
1301
1323
  return mcpError("Error: newStatus is required for update_entry_status action");
1302
- const { updateSessionEntryStatus } = await import("./dist-HWXF2C3R.js");
1324
+ const { updateSessionEntryStatus } = await import("./dist-ALQDD67R.js");
1303
1325
  const result = await updateSessionEntryStatus(
1304
1326
  projectPath,
1305
1327
  input.session,
@@ -1312,7 +1334,7 @@ async function handleUpdateEntryStatus(projectPath, input) {
1312
1334
  async function handleReadSection(projectPath, input) {
1313
1335
  if (!input.session) return mcpError("Error: session is required for read_section action");
1314
1336
  if (!input.section) return mcpError("Error: section is required for read_section action");
1315
- const { readSessionSection } = await import("./dist-HWXF2C3R.js");
1337
+ const { readSessionSection } = await import("./dist-ALQDD67R.js");
1316
1338
  const result = await readSessionSection(
1317
1339
  projectPath,
1318
1340
  input.session,
@@ -1322,15 +1344,16 @@ async function handleReadSection(projectPath, input) {
1322
1344
  }
1323
1345
  async function handleReadSections(projectPath, input) {
1324
1346
  if (!input.session) return mcpError("Error: session is required for read_sections action");
1325
- const { readSessionSections } = await import("./dist-HWXF2C3R.js");
1347
+ const { readSessionSections } = await import("./dist-ALQDD67R.js");
1326
1348
  const result = await readSessionSections(projectPath, input.session);
1327
1349
  return resultToMcpResponse(result);
1328
1350
  }
1329
1351
  async function handleArchiveSession(projectPath, input) {
1330
1352
  if (!input.session) return mcpError("Error: session is required for archive_session action");
1331
- const { archiveSession } = await import("./dist-HWXF2C3R.js");
1353
+ const { archiveSession } = await import("./dist-ALQDD67R.js");
1332
1354
  const result = await archiveSession(projectPath, input.session);
1333
1355
  if (!result.ok) return resultToMcpResponse(result);
1356
+ await autoSyncRoadmap(projectPath);
1334
1357
  return resultToMcpResponse(Ok({ archived: true }));
1335
1358
  }
1336
1359
  var ACTION_HANDLERS = {
@@ -1371,7 +1394,7 @@ var listStreamsDefinition = {
1371
1394
  };
1372
1395
  async function handleListStreams(input) {
1373
1396
  try {
1374
- const { listStreams, loadStreamIndex } = await import("./dist-HWXF2C3R.js");
1397
+ const { listStreams, loadStreamIndex } = await import("./dist-ALQDD67R.js");
1375
1398
  const projectPath = sanitizePath(input.path);
1376
1399
  const indexResult = await loadStreamIndex(projectPath);
1377
1400
  const streamsResult = await listStreams(projectPath);
@@ -1409,7 +1432,7 @@ var checkPhaseGateDefinition = {
1409
1432
  };
1410
1433
  async function handleCheckPhaseGate(input) {
1411
1434
  try {
1412
- const { runCheckPhaseGate } = await import("./check-phase-gate-L3RADYWO.js");
1435
+ const { runCheckPhaseGate } = await import("./check-phase-gate-ZOXVBDCN.js");
1413
1436
  const result = await runCheckPhaseGate({ cwd: sanitizePath(input.path) });
1414
1437
  if (result.ok) {
1415
1438
  return { content: [{ type: "text", text: JSON.stringify(result.value) }] };
@@ -1429,7 +1452,7 @@ async function handleCheckPhaseGate(input) {
1429
1452
  }
1430
1453
 
1431
1454
  // src/mcp/tools/cross-check.ts
1432
- import * as path11 from "path";
1455
+ import * as path12 from "path";
1433
1456
  var validateCrossCheckDefinition = {
1434
1457
  name: "validate_cross_check",
1435
1458
  description: "Validate plan-to-implementation coverage: checks that specs have plans and plans have implementations, detects staleness",
@@ -1465,15 +1488,15 @@ async function handleValidateCrossCheck(input) {
1465
1488
  };
1466
1489
  }
1467
1490
  try {
1468
- const { runCrossCheck } = await import("./validate-cross-check-WNJM6H2D.js");
1469
- const specsDir = path11.resolve(projectPath, input.specsDir ?? "docs/specs");
1491
+ const { runCrossCheck } = await import("./validate-cross-check-OABMREW4.js");
1492
+ const specsDir = path12.resolve(projectPath, input.specsDir ?? "docs/specs");
1470
1493
  if (!specsDir.startsWith(projectPath)) {
1471
1494
  return {
1472
1495
  content: [{ type: "text", text: "Error: specsDir escapes project root" }],
1473
1496
  isError: true
1474
1497
  };
1475
1498
  }
1476
- const plansDir = path11.resolve(projectPath, input.plansDir ?? "docs/plans");
1499
+ const plansDir = path12.resolve(projectPath, input.plansDir ?? "docs/plans");
1477
1500
  if (!plansDir.startsWith(projectPath)) {
1478
1501
  return {
1479
1502
  content: [{ type: "text", text: "Error: plansDir escapes project root" }],
@@ -1504,14 +1527,14 @@ async function handleValidateCrossCheck(input) {
1504
1527
 
1505
1528
  // src/commands/generate-slash-commands.ts
1506
1529
  import { Command } from "commander";
1507
- import fs11 from "fs";
1508
- import path13 from "path";
1530
+ import fs12 from "fs";
1531
+ import path14 from "path";
1509
1532
  import os from "os";
1510
1533
  import readline from "readline";
1511
1534
 
1512
1535
  // src/slash-commands/normalize.ts
1513
- import fs10 from "fs";
1514
- import path12 from "path";
1536
+ import fs11 from "fs";
1537
+ import path13 from "path";
1515
1538
  import { parse as parse3 } from "yaml";
1516
1539
 
1517
1540
  // src/slash-commands/normalize-name.ts
@@ -1531,7 +1554,7 @@ function normalizeName(skillName) {
1531
1554
  function readSkillYaml(yamlPath) {
1532
1555
  let raw;
1533
1556
  try {
1534
- raw = fs10.readFileSync(yamlPath, "utf-8");
1557
+ raw = fs11.readFileSync(yamlPath, "utf-8");
1535
1558
  } catch {
1536
1559
  return null;
1537
1560
  }
@@ -1591,10 +1614,10 @@ function buildProcessLines(meta) {
1591
1614
  ];
1592
1615
  }
1593
1616
  function buildSpec(meta, normalized, entry, skillsDir, source) {
1594
- const skillMdPath = path12.join(skillsDir, entry.name, "SKILL.md");
1595
- const skillMdContent = fs10.existsSync(skillMdPath) ? fs10.readFileSync(skillMdPath, "utf-8") : "";
1596
- const skillMdRelative = path12.relative(process.cwd(), skillMdPath).replaceAll("\\", "/");
1597
- const skillYamlRelative = path12.relative(process.cwd(), path12.join(skillsDir, entry.name, "skill.yaml")).replaceAll("\\", "/");
1617
+ const skillMdPath = path13.join(skillsDir, entry.name, "SKILL.md");
1618
+ const skillMdContent = fs11.existsSync(skillMdPath) ? fs11.readFileSync(skillMdPath, "utf-8") : "";
1619
+ const skillMdRelative = path13.relative(process.cwd(), skillMdPath).replaceAll("\\", "/");
1620
+ const skillYamlRelative = path13.relative(process.cwd(), path13.join(skillsDir, entry.name, "skill.yaml")).replaceAll("\\", "/");
1598
1621
  const args = (meta.cli?.args ?? []).map((a) => ({
1599
1622
  name: a.name,
1600
1623
  description: a.description ?? "",
@@ -1631,11 +1654,11 @@ function normalizeSkills(skillSources, platforms) {
1631
1654
  const specs = [];
1632
1655
  const nameMap = /* @__PURE__ */ new Map();
1633
1656
  for (const { dir: skillsDir, source } of skillSources) {
1634
- if (!fs10.existsSync(skillsDir)) continue;
1635
- const entries = fs10.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
1657
+ if (!fs11.existsSync(skillsDir)) continue;
1658
+ const entries = fs11.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
1636
1659
  for (const entry of entries) {
1637
- const yamlPath = path12.join(skillsDir, entry.name, "skill.yaml");
1638
- if (!fs10.existsSync(yamlPath)) continue;
1660
+ const yamlPath = path13.join(skillsDir, entry.name, "skill.yaml");
1661
+ if (!fs11.existsSync(yamlPath)) continue;
1639
1662
  const result = readSkillYaml(yamlPath);
1640
1663
  if (!result) continue;
1641
1664
  if (!result.success) {
@@ -1746,13 +1769,13 @@ function renderGemini(spec, skillMdContent, skillYamlContent) {
1746
1769
  // src/commands/generate-slash-commands.ts
1747
1770
  function resolveOutputDir(platform, opts) {
1748
1771
  if (opts.output) {
1749
- return path13.join(opts.output, "harness");
1772
+ return path14.join(opts.output, "harness");
1750
1773
  }
1751
1774
  if (opts.global) {
1752
1775
  const home = os.homedir();
1753
- return platform === "claude-code" ? path13.join(home, ".claude", "commands", "harness") : path13.join(home, ".gemini", "commands", "harness");
1776
+ return platform === "claude-code" ? path14.join(home, ".claude", "commands", "harness") : path14.join(home, ".gemini", "commands", "harness");
1754
1777
  }
1755
- return platform === "claude-code" ? path13.join("agents", "commands", "claude-code", "harness") : path13.join("agents", "commands", "gemini-cli", "harness");
1778
+ return platform === "claude-code" ? path14.join("agents", "commands", "claude-code", "harness") : path14.join("agents", "commands", "gemini-cli", "harness");
1756
1779
  }
1757
1780
  function fileExtension(platform) {
1758
1781
  return platform === "claude-code" ? ".md" : ".toml";
@@ -1777,12 +1800,12 @@ function resolveSkillSources(opts) {
1777
1800
  sources.push({ dir: projectDir, source: "project" });
1778
1801
  }
1779
1802
  const communityDir = resolveCommunitySkillsDir();
1780
- if (fs11.existsSync(communityDir)) {
1803
+ if (fs12.existsSync(communityDir)) {
1781
1804
  sources.push({ dir: communityDir, source: "community" });
1782
1805
  }
1783
1806
  if (opts.includeGlobal || sources.length === 0) {
1784
1807
  const globalDir = resolveGlobalSkillsDir();
1785
- if (!projectDir || path13.resolve(globalDir) !== path13.resolve(projectDir)) {
1808
+ if (!projectDir || path14.resolve(globalDir) !== path14.resolve(projectDir)) {
1786
1809
  sources.push({ dir: globalDir, source: "global" });
1787
1810
  }
1788
1811
  }
@@ -1807,7 +1830,7 @@ function generateSlashCommands(opts) {
1807
1830
  executionContext: spec.prompt.executionContext.split("\n").map((line) => {
1808
1831
  if (line.startsWith("@")) {
1809
1832
  const relPath = line.slice(1);
1810
- return `@${path13.resolve(relPath)}`;
1833
+ return `@${path14.resolve(relPath)}`;
1811
1834
  }
1812
1835
  return line;
1813
1836
  }).join("\n")
@@ -1815,10 +1838,10 @@ function generateSlashCommands(opts) {
1815
1838
  } : spec;
1816
1839
  rendered.set(filename, renderClaudeCode(renderSpec));
1817
1840
  } else {
1818
- const mdPath = path13.join(spec.skillsBaseDir, spec.sourceDir, "SKILL.md");
1819
- const yamlPath = path13.join(spec.skillsBaseDir, spec.sourceDir, "skill.yaml");
1820
- const mdContent = fs11.existsSync(mdPath) ? fs11.readFileSync(mdPath, "utf-8") : "";
1821
- const yamlContent = fs11.existsSync(yamlPath) ? fs11.readFileSync(yamlPath, "utf-8") : "";
1841
+ const mdPath = path14.join(spec.skillsBaseDir, spec.sourceDir, "SKILL.md");
1842
+ const yamlPath = path14.join(spec.skillsBaseDir, spec.sourceDir, "skill.yaml");
1843
+ const mdContent = fs12.existsSync(mdPath) ? fs12.readFileSync(mdPath, "utf-8") : "";
1844
+ const yamlContent = fs12.existsSync(yamlPath) ? fs12.readFileSync(yamlPath, "utf-8") : "";
1822
1845
  rendered.set(filename, renderGemini(spec, mdContent, yamlContent));
1823
1846
  }
1824
1847
  }
@@ -1844,9 +1867,9 @@ async function handleOrphanDeletion(results, opts) {
1844
1867
  const shouldDelete = opts.yes || await confirmDeletion(result.removed);
1845
1868
  if (shouldDelete) {
1846
1869
  for (const filename of result.removed) {
1847
- const filePath = path13.join(result.outputDir, filename);
1848
- if (fs11.existsSync(filePath)) {
1849
- fs11.unlinkSync(filePath);
1870
+ const filePath = path14.join(result.outputDir, filename);
1871
+ if (fs12.existsSync(filePath)) {
1872
+ fs12.unlinkSync(filePath);
1850
1873
  }
1851
1874
  }
1852
1875
  }
@@ -1972,7 +1995,7 @@ async function handleGenerateSlashCommands(input) {
1972
1995
  // src/mcp/resources/state.ts
1973
1996
  async function getStateResource(projectRoot) {
1974
1997
  try {
1975
- const { loadState, migrateToStreams } = await import("./dist-HWXF2C3R.js");
1998
+ const { loadState, migrateToStreams } = await import("./dist-ALQDD67R.js");
1976
1999
  await migrateToStreams(projectRoot);
1977
2000
  const result = await loadState(projectRoot);
1978
2001
  if (result.ok) {
@@ -2519,7 +2542,7 @@ async function handleGetImpact(input) {
2519
2542
  }
2520
2543
 
2521
2544
  // src/mcp/tools/graph/ingest-source.ts
2522
- import * as path14 from "path";
2545
+ import * as path15 from "path";
2523
2546
  var ingestSourceDefinition = {
2524
2547
  name: "ingest_source",
2525
2548
  description: "Ingest sources into the project knowledge graph. Supports code analysis, knowledge documents, git history, or all at once.",
@@ -2539,10 +2562,10 @@ var ingestSourceDefinition = {
2539
2562
  async function handleIngestSource(input) {
2540
2563
  try {
2541
2564
  const projectPath = sanitizePath(input.path);
2542
- const graphDir = path14.join(projectPath, ".harness", "graph");
2565
+ const graphDir = path15.join(projectPath, ".harness", "graph");
2543
2566
  const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-B26DFXMP.js");
2544
- const fs14 = await import("fs/promises");
2545
- await fs14.mkdir(graphDir, { recursive: true });
2567
+ const fs15 = await import("fs/promises");
2568
+ await fs15.mkdir(graphDir, { recursive: true });
2546
2569
  const store = new GraphStore();
2547
2570
  await store.load(graphDir);
2548
2571
  const results = [];
@@ -2674,8 +2697,8 @@ async function handleAskGraph(input) {
2674
2697
  }
2675
2698
 
2676
2699
  // src/mcp/resources/graph.ts
2677
- import * as fs12 from "fs/promises";
2678
- import * as path15 from "path";
2700
+ import * as fs13 from "fs/promises";
2701
+ import * as path16 from "path";
2679
2702
  var MAX_ITEMS = 5e3;
2680
2703
  function formatStaleness(isoTimestamp) {
2681
2704
  const then = new Date(isoTimestamp).getTime();
@@ -2698,11 +2721,11 @@ async function getGraphResource(projectRoot) {
2698
2721
  message: "No knowledge graph found. Run harness scan to build one."
2699
2722
  });
2700
2723
  }
2701
- const graphDir = path15.join(projectRoot, ".harness", "graph");
2702
- const metadataPath = path15.join(graphDir, "metadata.json");
2724
+ const graphDir = path16.join(projectRoot, ".harness", "graph");
2725
+ const metadataPath = path16.join(graphDir, "metadata.json");
2703
2726
  let lastScanTimestamp = null;
2704
2727
  try {
2705
- const raw = JSON.parse(await fs12.readFile(metadataPath, "utf-8"));
2728
+ const raw = JSON.parse(await fs13.readFile(metadataPath, "utf-8"));
2706
2729
  lastScanTimestamp = raw.lastScanTimestamp ?? null;
2707
2730
  } catch {
2708
2731
  }
@@ -2791,7 +2814,7 @@ var generateAgentDefinitionsDefinition = {
2791
2814
  }
2792
2815
  };
2793
2816
  async function handleGenerateAgentDefinitions(input) {
2794
- const { generateAgentDefinitions } = await import("./generate-agent-definitions-3PM5EU7V.js");
2817
+ const { generateAgentDefinitions } = await import("./generate-agent-definitions-ZAE726AU.js");
2795
2818
  const platforms = input.platform === "all" || !input.platform ? ["claude-code", "gemini-cli"] : [input.platform];
2796
2819
  const results = generateAgentDefinitions({
2797
2820
  platforms: [...platforms],
@@ -2802,8 +2825,8 @@ async function handleGenerateAgentDefinitions(input) {
2802
2825
  }
2803
2826
 
2804
2827
  // src/mcp/tools/roadmap.ts
2805
- import * as fs13 from "fs";
2806
- import * as path16 from "path";
2828
+ import * as fs14 from "fs";
2829
+ import * as path17 from "path";
2807
2830
  var manageRoadmapDefinition = {
2808
2831
  name: "manage_roadmap",
2809
2832
  description: "Manage the project roadmap: show, add, update, remove, sync features, or query by filter. Reads and writes docs/roadmap.md.",
@@ -2858,21 +2881,21 @@ var manageRoadmapDefinition = {
2858
2881
  }
2859
2882
  };
2860
2883
  function roadmapPath(projectRoot) {
2861
- return path16.join(projectRoot, "docs", "roadmap.md");
2884
+ return path17.join(projectRoot, "docs", "roadmap.md");
2862
2885
  }
2863
2886
  function readRoadmapFile(projectRoot) {
2864
2887
  const filePath = roadmapPath(projectRoot);
2865
2888
  try {
2866
- return fs13.readFileSync(filePath, "utf-8");
2889
+ return fs14.readFileSync(filePath, "utf-8");
2867
2890
  } catch {
2868
2891
  return null;
2869
2892
  }
2870
2893
  }
2871
2894
  function writeRoadmapFile(projectRoot, content) {
2872
2895
  const filePath = roadmapPath(projectRoot);
2873
- const dir = path16.dirname(filePath);
2874
- fs13.mkdirSync(dir, { recursive: true });
2875
- fs13.writeFileSync(filePath, content, "utf-8");
2896
+ const dir = path17.dirname(filePath);
2897
+ fs14.mkdirSync(dir, { recursive: true });
2898
+ fs14.writeFileSync(filePath, content, "utf-8");
2876
2899
  }
2877
2900
  function roadmapNotFoundError() {
2878
2901
  return {
@@ -3077,18 +3100,7 @@ function handleSync(projectPath, input, deps) {
3077
3100
  return resultToMcpResponse(Ok2({ changes: [], message: "Roadmap is up to date." }));
3078
3101
  }
3079
3102
  if (input.apply) {
3080
- for (const change of changes) {
3081
- for (const m of roadmap.milestones) {
3082
- const feature = m.features.find(
3083
- (f) => f.name.toLowerCase() === change.feature.toLowerCase()
3084
- );
3085
- if (feature) {
3086
- feature.status = change.to;
3087
- break;
3088
- }
3089
- }
3090
- }
3091
- roadmap.frontmatter.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
3103
+ deps.applySyncChanges(roadmap, changes);
3092
3104
  writeRoadmapFile(projectPath, serializeRoadmap(roadmap));
3093
3105
  return resultToMcpResponse(Ok2({ changes, applied: true, roadmap }));
3094
3106
  }
@@ -3096,10 +3108,10 @@ function handleSync(projectPath, input, deps) {
3096
3108
  }
3097
3109
  async function handleManageRoadmap(input) {
3098
3110
  try {
3099
- const { parseRoadmap, serializeRoadmap, syncRoadmap } = await import("./dist-HWXF2C3R.js");
3111
+ const { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges } = await import("./dist-ALQDD67R.js");
3100
3112
  const { Ok: Ok2 } = await import("./dist-USY2C5JL.js");
3101
3113
  const projectPath = sanitizePath(input.path);
3102
- const deps = { parseRoadmap, serializeRoadmap, syncRoadmap, Ok: Ok2 };
3114
+ const deps = { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges, Ok: Ok2 };
3103
3115
  switch (input.action) {
3104
3116
  case "show":
3105
3117
  return handleShow2(projectPath, input, deps);
@@ -3524,7 +3536,7 @@ async function handleTransition(validInput, projectPath, id) {
3524
3536
  const transition = transitionResult.data;
3525
3537
  const prompt = renderTransition(transition);
3526
3538
  try {
3527
- const { saveHandoff } = await import("./dist-HWXF2C3R.js");
3539
+ const { saveHandoff } = await import("./dist-ALQDD67R.js");
3528
3540
  await saveHandoff(
3529
3541
  projectPath,
3530
3542
  {
@@ -3594,7 +3606,7 @@ async function handleEmitInteraction(input) {
3594
3606
  }
3595
3607
  async function recordInteraction(projectPath, id, type, decision, stream) {
3596
3608
  try {
3597
- const { loadState, saveState } = await import("./dist-HWXF2C3R.js");
3609
+ const { loadState, saveState } = await import("./dist-ALQDD67R.js");
3598
3610
  const stateResult = await loadState(projectPath, stream);
3599
3611
  if (stateResult.ok) {
3600
3612
  const state = stateResult.value;
@@ -3633,10 +3645,14 @@ var gatherContextDefinition = {
3633
3645
  type: "array",
3634
3646
  items: {
3635
3647
  type: "string",
3636
- enum: ["state", "learnings", "handoff", "graph", "validation", "sessions"]
3648
+ enum: ["state", "learnings", "handoff", "graph", "validation", "sessions", "events"]
3637
3649
  },
3638
3650
  description: "Which constituents to include (default: all)"
3639
3651
  },
3652
+ includeEvents: {
3653
+ type: "boolean",
3654
+ description: "Include recent events timeline. Default: true when session is provided, false otherwise. Can also be controlled via include array."
3655
+ },
3640
3656
  mode: {
3641
3657
  type: "string",
3642
3658
  enum: ["summary", "detailed"],
@@ -3649,6 +3665,11 @@ var gatherContextDefinition = {
3649
3665
  session: {
3650
3666
  type: "string",
3651
3667
  description: "Session slug for session-scoped state. When provided, state/learnings/handoff/failures are read from .harness/sessions/<session>/ instead of .harness/. Omit for global fallback."
3668
+ },
3669
+ depth: {
3670
+ type: "string",
3671
+ enum: ["index", "summary", "full"],
3672
+ description: 'Retrieval depth for learnings. "index" returns one-line summaries, "summary" (default) returns full entries, "full" returns entries with linked context.'
3652
3673
  }
3653
3674
  },
3654
3675
  required: ["path", "intent"]
@@ -3674,18 +3695,19 @@ async function handleGatherContext(input) {
3674
3695
  input.include ?? ["state", "learnings", "handoff", "graph", "validation"]
3675
3696
  );
3676
3697
  const errors = [];
3677
- const statePromise = includeSet.has("state") ? import("./dist-HWXF2C3R.js").then(
3698
+ const statePromise = includeSet.has("state") ? import("./dist-ALQDD67R.js").then(
3678
3699
  (core) => core.loadState(projectPath, void 0, input.session)
3679
3700
  ) : Promise.resolve(null);
3680
- const learningsPromise = includeSet.has("learnings") ? import("./dist-HWXF2C3R.js").then(
3701
+ const learningsPromise = includeSet.has("learnings") ? import("./dist-ALQDD67R.js").then(
3681
3702
  (core) => core.loadBudgetedLearnings(projectPath, {
3682
3703
  intent: input.intent,
3683
3704
  tokenBudget: input.learningsBudget ?? 1e3,
3684
3705
  ...input.skill !== void 0 && { skill: input.skill },
3685
- ...input.session !== void 0 && { session: input.session }
3706
+ ...input.session !== void 0 && { session: input.session },
3707
+ ...input.depth !== void 0 && { depth: input.depth }
3686
3708
  })
3687
3709
  ) : Promise.resolve(null);
3688
- const handoffPromise = includeSet.has("handoff") ? import("./dist-HWXF2C3R.js").then(
3710
+ const handoffPromise = includeSet.has("handoff") ? import("./dist-ALQDD67R.js").then(
3689
3711
  (core) => core.loadHandoff(projectPath, void 0, input.session)
3690
3712
  ) : Promise.resolve(null);
3691
3713
  const graphPromise = includeSet.has("graph") ? (async () => {
@@ -3729,11 +3751,19 @@ async function handleGatherContext(input) {
3729
3751
  context: contextBlocks
3730
3752
  };
3731
3753
  })() : Promise.resolve(null);
3732
- const sessionsPromise = includeSet.has("sessions") && input.session ? import("./dist-HWXF2C3R.js").then(
3754
+ const sessionsPromise = includeSet.has("sessions") && input.session ? import("./dist-ALQDD67R.js").then(
3733
3755
  (core) => core.readSessionSections(projectPath, input.session)
3734
3756
  ) : Promise.resolve(null);
3757
+ const shouldIncludeEvents = input.includeEvents !== void 0 ? input.includeEvents : includeSet.has("events") || !!input.session && !input.include;
3758
+ const eventsPromise = shouldIncludeEvents ? import("./dist-ALQDD67R.js").then(async (core) => {
3759
+ const result = await core.loadEvents(projectPath, {
3760
+ session: input.session
3761
+ });
3762
+ if (!result.ok) return null;
3763
+ return core.formatEventTimeline(result.value);
3764
+ }) : Promise.resolve(null);
3735
3765
  const validationPromise = includeSet.has("validation") ? (async () => {
3736
- const { handleValidateProject: handleValidateProject2 } = await import("./validate-FD3Z6VJD.js");
3766
+ const { handleValidateProject: handleValidateProject2 } = await import("./validate-KBYQAEWE.js");
3737
3767
  const result = await handleValidateProject2({ path: projectPath });
3738
3768
  const first = result.content[0];
3739
3769
  return first ? JSON.parse(first.text) : null;
@@ -3744,14 +3774,16 @@ async function handleGatherContext(input) {
3744
3774
  handoffResult,
3745
3775
  graphResult,
3746
3776
  validationResult,
3747
- sessionsResult
3777
+ sessionsResult,
3778
+ eventsResult
3748
3779
  ] = await Promise.allSettled([
3749
3780
  statePromise,
3750
3781
  learningsPromise,
3751
3782
  handoffPromise,
3752
3783
  graphPromise,
3753
3784
  validationPromise,
3754
- sessionsPromise
3785
+ sessionsPromise,
3786
+ eventsPromise
3755
3787
  ]);
3756
3788
  function extract(settled, name) {
3757
3789
  if (settled.status === "rejected") {
@@ -3766,6 +3798,7 @@ async function handleGatherContext(input) {
3766
3798
  const graphContextRaw = extract(graphResult, "graph");
3767
3799
  const validationRaw = extract(validationResult, "validation");
3768
3800
  const sessionsRaw = extract(sessionsResult, "sessions");
3801
+ const eventsTimeline = extract(eventsResult, "events");
3769
3802
  const state = stateRaw && typeof stateRaw === "object" && "ok" in stateRaw ? stateRaw.ok ? stateRaw.value : (() => {
3770
3803
  errors.push(`state: ${stateRaw.error.message}`);
3771
3804
  return null;
@@ -3813,6 +3846,7 @@ async function handleGatherContext(input) {
3813
3846
  graphContext: outputGraphContext,
3814
3847
  validation: outputValidation,
3815
3848
  sessionSections: sessionSections ?? null,
3849
+ events: eventsTimeline || null,
3816
3850
  meta: {
3817
3851
  assembledIn,
3818
3852
  graphAvailable: graphContext !== null,
@@ -3823,7 +3857,7 @@ async function handleGatherContext(input) {
3823
3857
  };
3824
3858
  if (input.session) {
3825
3859
  try {
3826
- const core = await import("./dist-HWXF2C3R.js");
3860
+ const core = await import("./dist-ALQDD67R.js");
3827
3861
  core.updateSessionIndex(
3828
3862
  projectPath,
3829
3863
  input.session,
@@ -3893,7 +3927,7 @@ async function handleAssessProject(input) {
3893
3927
  let validateResult = null;
3894
3928
  if (checksToRun.has("validate")) {
3895
3929
  try {
3896
- const { handleValidateProject: handleValidateProject2 } = await import("./validate-FD3Z6VJD.js");
3930
+ const { handleValidateProject: handleValidateProject2 } = await import("./validate-KBYQAEWE.js");
3897
3931
  const result = await handleValidateProject2({ path: projectPath });
3898
3932
  const first = result.content[0];
3899
3933
  const parsed = first ? JSON.parse(first.text) : {};
@@ -3918,7 +3952,7 @@ async function handleAssessProject(input) {
3918
3952
  parallelChecks.push(
3919
3953
  (async () => {
3920
3954
  try {
3921
- const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-JQZYM4US.js");
3955
+ const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-ZLIH5533.js");
3922
3956
  const result = await handleCheckDependencies2({ path: projectPath });
3923
3957
  const first = result.content[0];
3924
3958
  const parsed = first ? JSON.parse(first.text) : {};
@@ -3945,7 +3979,7 @@ async function handleAssessProject(input) {
3945
3979
  parallelChecks.push(
3946
3980
  (async () => {
3947
3981
  try {
3948
- const { handleCheckDocs: handleCheckDocs2 } = await import("./docs-7ECGYMAV.js");
3982
+ const { handleCheckDocs: handleCheckDocs2 } = await import("./docs-NRMQCOJ6.js");
3949
3983
  const result = await handleCheckDocs2({ path: projectPath, scope: "coverage" });
3950
3984
  const first = result.content[0];
3951
3985
  const parsed = first ? JSON.parse(first.text) : {};
@@ -3972,7 +4006,7 @@ async function handleAssessProject(input) {
3972
4006
  parallelChecks.push(
3973
4007
  (async () => {
3974
4008
  try {
3975
- const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-5USWKLVS.js");
4009
+ const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-6AGX2ZUN.js");
3976
4010
  const result = await handleDetectEntropy2({ path: projectPath, type: "all" });
3977
4011
  const first = result.content[0];
3978
4012
  const parsed = first ? JSON.parse(first.text) : {};
@@ -3999,7 +4033,7 @@ async function handleAssessProject(input) {
3999
4033
  parallelChecks.push(
4000
4034
  (async () => {
4001
4035
  try {
4002
- const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-PZOX7AQS.js");
4036
+ const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-2RPQEN62.js");
4003
4037
  const result = await handleRunSecurityScan2({ path: projectPath });
4004
4038
  const first = result.content[0];
4005
4039
  const parsed = first ? JSON.parse(first.text) : {};
@@ -4031,7 +4065,7 @@ async function handleAssessProject(input) {
4031
4065
  parallelChecks.push(
4032
4066
  (async () => {
4033
4067
  try {
4034
- const { handleCheckPerformance: handleCheckPerformance2 } = await import("./performance-OQAFMJUD.js");
4068
+ const { handleCheckPerformance: handleCheckPerformance2 } = await import("./performance-2D7G6NMJ.js");
4035
4069
  const result = await handleCheckPerformance2({ path: projectPath });
4036
4070
  const first = result.content[0];
4037
4071
  const parsed = first ? JSON.parse(first.text) : {};
@@ -4220,7 +4254,7 @@ async function handleReviewChanges(input) {
4220
4254
  }
4221
4255
  }
4222
4256
  async function runQuickReview(projectPath, diff, diffLines, downgraded) {
4223
- const { handleAnalyzeDiff: handleAnalyzeDiff2 } = await import("./feedback-UTBXZZHF.js");
4257
+ const { handleAnalyzeDiff: handleAnalyzeDiff2 } = await import("./feedback-MY4QZIFD.js");
4224
4258
  const result = await handleAnalyzeDiff2({ diff, path: projectPath });
4225
4259
  const firstContent = result.content[0];
4226
4260
  if (!firstContent) throw new Error("Empty analyze_diff response");
@@ -4251,7 +4285,7 @@ function extractFileCount(diffParsed) {
4251
4285
  return files?.length ?? 0;
4252
4286
  }
4253
4287
  async function runStandardReview(projectPath, diff, diffLines, downgraded) {
4254
- const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-UTBXZZHF.js");
4288
+ const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-MY4QZIFD.js");
4255
4289
  const [diffResult, reviewResult] = await Promise.all([
4256
4290
  handleAnalyzeDiff2({ diff, path: projectPath }),
4257
4291
  handleCreateSelfReview2({ path: projectPath, diff })
@@ -4283,7 +4317,7 @@ async function runStandardReview(projectPath, diff, diffLines, downgraded) {
4283
4317
  };
4284
4318
  }
4285
4319
  async function runDeepReview(projectPath, diff, diffLines, _downgraded) {
4286
- const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-C4GCFVGP.js");
4320
+ const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-RAQ55ISU.js");
4287
4321
  const result = await handleRunCodeReview2({ path: projectPath, diff });
4288
4322
  const deepContent = result.content[0];
4289
4323
  if (!deepContent) throw new Error("Empty code review response");
@@ -4569,7 +4603,7 @@ async function handleDetectStaleConstraints(input) {
4569
4603
  ]
4570
4604
  };
4571
4605
  }
4572
- const { detectStaleConstraints } = await import("./dist-HWXF2C3R.js");
4606
+ const { detectStaleConstraints } = await import("./dist-ALQDD67R.js");
4573
4607
  const result = detectStaleConstraints(
4574
4608
  store,
4575
4609
  windowDays,
@@ -4666,6 +4700,226 @@ async function handleSearchSkills(input) {
4666
4700
  };
4667
4701
  }
4668
4702
 
4703
+ // src/mcp/tools/code-nav.ts
4704
+ var codeOutlineDefinition = {
4705
+ name: "code_outline",
4706
+ description: "Get a structural skeleton of a file or files matching a glob: exports, classes, functions, types with signatures and line numbers. No implementation bodies. 4-8x token savings vs full file read.",
4707
+ inputSchema: {
4708
+ type: "object",
4709
+ properties: {
4710
+ path: {
4711
+ type: "string",
4712
+ description: "Absolute file path or directory path. When a directory, outlines all supported files within it."
4713
+ },
4714
+ glob: {
4715
+ type: "string",
4716
+ description: 'Optional glob pattern to filter files (e.g. "*.ts", "src/**/*.py"). Only used when path is a directory.'
4717
+ }
4718
+ },
4719
+ required: ["path"]
4720
+ }
4721
+ };
4722
+ async function handleCodeOutline(input) {
4723
+ let targetPath;
4724
+ try {
4725
+ targetPath = sanitizePath(input.path);
4726
+ } catch (error) {
4727
+ return {
4728
+ content: [
4729
+ {
4730
+ type: "text",
4731
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`
4732
+ }
4733
+ ],
4734
+ isError: true
4735
+ };
4736
+ }
4737
+ try {
4738
+ const { getOutline, formatOutline, EXTENSION_MAP } = await import("./dist-ALQDD67R.js");
4739
+ const { stat } = await import("fs/promises");
4740
+ const stats = await stat(targetPath).catch(() => null);
4741
+ if (stats?.isFile()) {
4742
+ const outline = await getOutline(targetPath);
4743
+ return { content: [{ type: "text", text: formatOutline(outline) }] };
4744
+ }
4745
+ if (stats?.isDirectory()) {
4746
+ const { glob } = await import("glob");
4747
+ const exts = Object.keys(EXTENSION_MAP).map((e) => e.slice(1));
4748
+ const pattern = input.glob ?? `**/*.{${exts.join(",")}}`;
4749
+ const files = await glob(pattern, { cwd: targetPath, absolute: true });
4750
+ const results = [];
4751
+ const MAX_FILES = 50;
4752
+ for (const file of files.slice(0, MAX_FILES)) {
4753
+ const outline = await getOutline(file);
4754
+ results.push(formatOutline(outline));
4755
+ }
4756
+ if (files.length > MAX_FILES) {
4757
+ results.push(
4758
+ `
4759
+ ... and ${files.length - MAX_FILES} more files (use a narrower glob to see them)`
4760
+ );
4761
+ }
4762
+ return { content: [{ type: "text", text: results.join("\n\n") }] };
4763
+ }
4764
+ return {
4765
+ content: [{ type: "text", text: `Error: Path not found: ${targetPath}` }],
4766
+ isError: true
4767
+ };
4768
+ } catch (error) {
4769
+ return {
4770
+ content: [
4771
+ {
4772
+ type: "text",
4773
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`
4774
+ }
4775
+ ],
4776
+ isError: true
4777
+ };
4778
+ }
4779
+ }
4780
+ var codeSearchDefinition = {
4781
+ name: "code_search",
4782
+ description: "Search for symbols (functions, classes, types, variables) by name or pattern across a directory. Returns matching locations with file, line, kind, and one-line context. 6-12x token savings vs grep + read.",
4783
+ inputSchema: {
4784
+ type: "object",
4785
+ properties: {
4786
+ query: {
4787
+ type: "string",
4788
+ description: "Symbol name or substring to search for (case-insensitive)."
4789
+ },
4790
+ directory: {
4791
+ type: "string",
4792
+ description: "Absolute path to directory to search in."
4793
+ },
4794
+ glob: {
4795
+ type: "string",
4796
+ description: 'Optional glob pattern to filter files (e.g. "*.ts").'
4797
+ }
4798
+ },
4799
+ required: ["query", "directory"]
4800
+ }
4801
+ };
4802
+ async function handleCodeSearch(input) {
4803
+ let directory;
4804
+ try {
4805
+ directory = sanitizePath(input.directory);
4806
+ } catch (error) {
4807
+ return {
4808
+ content: [
4809
+ {
4810
+ type: "text",
4811
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`
4812
+ }
4813
+ ],
4814
+ isError: true
4815
+ };
4816
+ }
4817
+ try {
4818
+ const { searchSymbols } = await import("./dist-ALQDD67R.js");
4819
+ const result = await searchSymbols(input.query, directory, input.glob);
4820
+ const lines = [`Search: "${result.query}" \u2014 ${result.matches.length} matches`];
4821
+ for (const match of result.matches) {
4822
+ const { symbol } = match;
4823
+ lines.push(
4824
+ ` ${symbol.file}:${symbol.line} [${symbol.kind}] ${symbol.name} \u2014 ${match.context}`
4825
+ );
4826
+ }
4827
+ if (result.skipped.length > 0) {
4828
+ lines.push(`
4829
+ Skipped ${result.skipped.length} files (unsupported or parse failed)`);
4830
+ }
4831
+ return { content: [{ type: "text", text: lines.join("\n") }] };
4832
+ } catch (error) {
4833
+ return {
4834
+ content: [
4835
+ {
4836
+ type: "text",
4837
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`
4838
+ }
4839
+ ],
4840
+ isError: true
4841
+ };
4842
+ }
4843
+ }
4844
+ var codeUnfoldDefinition = {
4845
+ name: "code_unfold",
4846
+ description: "Extract the complete implementation of a specific symbol (function, class, type) or a line range from a file. Uses AST boundaries for precise extraction. 2-4x token savings vs full file read.",
4847
+ inputSchema: {
4848
+ type: "object",
4849
+ properties: {
4850
+ path: {
4851
+ type: "string",
4852
+ description: "Absolute path to the file."
4853
+ },
4854
+ symbol: {
4855
+ type: "string",
4856
+ description: "Name of the symbol to extract (function, class, type, etc.). Mutually exclusive with startLine/endLine."
4857
+ },
4858
+ startLine: {
4859
+ type: "number",
4860
+ description: "Start line number (1-indexed). Used with endLine for range extraction. Mutually exclusive with symbol."
4861
+ },
4862
+ endLine: {
4863
+ type: "number",
4864
+ description: "End line number (1-indexed, inclusive). Used with startLine for range extraction."
4865
+ }
4866
+ },
4867
+ required: ["path"]
4868
+ }
4869
+ };
4870
+ async function handleCodeUnfold(input) {
4871
+ let filePath;
4872
+ try {
4873
+ filePath = sanitizePath(input.path);
4874
+ } catch (error) {
4875
+ return {
4876
+ content: [
4877
+ {
4878
+ type: "text",
4879
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`
4880
+ }
4881
+ ],
4882
+ isError: true
4883
+ };
4884
+ }
4885
+ try {
4886
+ if (input.symbol) {
4887
+ const { unfoldSymbol } = await import("./dist-ALQDD67R.js");
4888
+ const result = await unfoldSymbol(filePath, input.symbol);
4889
+ const header = result.warning ? `${result.file}:${result.startLine}-${result.endLine} ${result.warning}
4890
+ ` : `${result.file}:${result.startLine}-${result.endLine}
4891
+ `;
4892
+ return { content: [{ type: "text", text: header + result.content }] };
4893
+ }
4894
+ if (input.startLine != null && input.endLine != null) {
4895
+ const { unfoldRange } = await import("./dist-ALQDD67R.js");
4896
+ const result = await unfoldRange(filePath, input.startLine, input.endLine);
4897
+ const header = `${result.file}:${result.startLine}-${result.endLine}
4898
+ `;
4899
+ return { content: [{ type: "text", text: header + result.content }] };
4900
+ }
4901
+ return {
4902
+ content: [
4903
+ {
4904
+ type: "text",
4905
+ text: 'Error: Provide either "symbol" or "startLine" + "endLine".'
4906
+ }
4907
+ ],
4908
+ isError: true
4909
+ };
4910
+ } catch (error) {
4911
+ return {
4912
+ content: [
4913
+ {
4914
+ type: "text",
4915
+ text: `Error: ${error instanceof Error ? error.message : String(error)}`
4916
+ }
4917
+ ],
4918
+ isError: true
4919
+ };
4920
+ }
4921
+ }
4922
+
4669
4923
  // src/mcp/server.ts
4670
4924
  var TOOL_DEFINITIONS = [
4671
4925
  validateToolDefinition,
@@ -4713,7 +4967,10 @@ var TOOL_DEFINITIONS = [
4713
4967
  checkTaskIndependenceDefinition,
4714
4968
  predictConflictsDefinition,
4715
4969
  detectStaleConstraintsDefinition,
4716
- searchSkillsDefinition
4970
+ searchSkillsDefinition,
4971
+ codeOutlineDefinition,
4972
+ codeSearchDefinition,
4973
+ codeUnfoldDefinition
4717
4974
  ];
4718
4975
  var TOOL_HANDLERS = {
4719
4976
  validate_project: handleValidateProject,
@@ -4761,7 +5018,10 @@ var TOOL_HANDLERS = {
4761
5018
  check_task_independence: handleCheckTaskIndependence,
4762
5019
  predict_conflicts: handlePredictConflicts,
4763
5020
  detect_stale_constraints: handleDetectStaleConstraints,
4764
- search_skills: handleSearchSkills
5021
+ search_skills: handleSearchSkills,
5022
+ code_outline: handleCodeOutline,
5023
+ code_search: handleCodeSearch,
5024
+ code_unfold: handleCodeUnfold
4765
5025
  };
4766
5026
  var RESOURCE_DEFINITIONS = [
4767
5027
  {
@@ -4847,7 +5107,7 @@ async function appendUpdateNotification(result, resolvedRoot) {
4847
5107
  shouldRunCheck,
4848
5108
  readCheckState,
4849
5109
  spawnBackgroundCheck
4850
- } = await import("./dist-HWXF2C3R.js");
5110
+ } = await import("./dist-ALQDD67R.js");
4851
5111
  const { CLI_VERSION } = await import("./version-KFFPOQAX.js");
4852
5112
  const configInterval = readConfigInterval(resolvedRoot);
4853
5113
  const DEFAULT_INTERVAL = 864e5;