@harness-engineering/cli 1.13.1 → 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 (147) 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-brainstorming/SKILL.md +39 -0
  4. package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +44 -0
  5. package/dist/agents/skills/claude-code/harness-execution/SKILL.md +44 -0
  6. package/dist/agents/skills/claude-code/harness-planning/SKILL.md +39 -0
  7. package/dist/agents/skills/claude-code/harness-product-spec/SKILL.md +5 -5
  8. package/dist/agents/skills/claude-code/harness-release-readiness/SKILL.md +3 -3
  9. package/dist/agents/skills/claude-code/harness-verification/SKILL.md +35 -0
  10. package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +11 -3
  11. package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +240 -39
  12. package/dist/agents/skills/gemini-cli/harness-autopilot/skill.yaml +6 -0
  13. package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +39 -0
  14. package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +44 -0
  15. package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +44 -0
  16. package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +39 -0
  17. package/dist/agents/skills/gemini-cli/harness-product-spec/SKILL.md +5 -5
  18. package/dist/agents/skills/gemini-cli/harness-release-readiness/SKILL.md +3 -3
  19. package/dist/agents/skills/gemini-cli/harness-verification/SKILL.md +35 -0
  20. package/dist/agents/skills/gemini-cli/initialize-harness-project/SKILL.md +11 -3
  21. package/dist/agents/skills/package.json +1 -0
  22. package/dist/agents/skills/vitest.config.mts +5 -0
  23. package/dist/agents-md-ZGNIDWAF.js +8 -0
  24. package/dist/{architecture-2R5Z4ZAF.js → architecture-ZLIH5533.js} +4 -4
  25. package/dist/bin/harness-mcp.js +14 -14
  26. package/dist/bin/harness.js +27 -25
  27. package/dist/{check-phase-gate-2OFZ7OWW.js → check-phase-gate-ZOXVBDCN.js} +4 -4
  28. package/dist/{chunk-ND6PNADU.js → chunk-2BKLWLY6.js} +9 -9
  29. package/dist/{chunk-65FRIL4D.js → chunk-3ZZKVN62.js} +1 -1
  30. package/dist/{chunk-C2ERUR3L.js → chunk-7MJAPE3Z.js} +165 -49
  31. package/dist/{chunk-Z77YQRQT.js → chunk-B2HKP423.js} +16 -5
  32. package/dist/{chunk-QPEH2QPG.js → chunk-DBSOCI3G.js} +53 -54
  33. package/dist/{chunk-TKJZKICB.js → chunk-EDXIVMAP.js} +7 -7
  34. package/dist/{chunk-MHBMTPW7.js → chunk-ERS5EVUZ.js} +9 -0
  35. package/dist/{chunk-JSTQ3AWB.js → chunk-FIAPHX37.js} +1 -1
  36. package/dist/{chunk-IMFVFNJE.js → chunk-FTMXDOR6.js} +1 -1
  37. package/dist/{chunk-72GHBOL2.js → chunk-GZKSBLQL.js} +1 -1
  38. package/dist/{chunk-K6XAPGML.js → chunk-H7Y5CKTM.js} +1 -1
  39. package/dist/{chunk-SSKDAOX5.js → chunk-J4RAX7YB.js} +1164 -516
  40. package/dist/{chunk-UAX4I5ZE.js → chunk-LGYBN7Y6.js} +2 -2
  41. package/dist/{chunk-QY4T6YAZ.js → chunk-N25INEIX.js} +4 -4
  42. package/dist/{chunk-4ZMOCPYO.js → chunk-ND2ENWDM.js} +1 -1
  43. package/dist/{chunk-NERR4TAO.js → chunk-NNHDDXYT.js} +1250 -765
  44. package/dist/{chunk-NKDM3FMH.js → chunk-OD3S2NHN.js} +1 -1
  45. package/dist/{chunk-NOPU4RZ4.js → chunk-OFXQSFOW.js} +3 -3
  46. package/dist/{chunk-TS3XWPW5.js → chunk-RCWZBSK5.js} +1 -1
  47. package/dist/{chunk-VUCPTQ6G.js → chunk-SD3SQOZ2.js} +1 -1
  48. package/dist/{chunk-DZS7CJKL.js → chunk-VEPAJXBW.js} +45 -47
  49. package/dist/{chunk-IM32EEDM.js → chunk-YLXFKVJE.js} +9 -9
  50. package/dist/{chunk-Q6AB7W5Z.js → chunk-YQ6KC6TE.js} +1 -1
  51. package/dist/{chunk-PQ5YK4AY.js → chunk-Z2OOPXJO.js} +2740 -1221
  52. package/dist/ci-workflow-765LSHRD.js +8 -0
  53. package/dist/{dist-2B363XUH.js → dist-ALQDD67R.js} +64 -2
  54. package/dist/{dist-HXHWB7SV.js → dist-B26DFXMP.js} +571 -478
  55. package/dist/{dist-L7LAAQAS.js → dist-DZ63LLUD.js} +1 -1
  56. package/dist/{dist-D4RYGUZE.js → dist-USY2C5JL.js} +3 -1
  57. package/dist/{docs-FZOPM4GK.js → docs-NRMQCOJ6.js} +4 -4
  58. package/dist/engine-3RB7MXPP.js +8 -0
  59. package/dist/{entropy-LVHJMFGH.js → entropy-6AGX2ZUN.js} +3 -3
  60. package/dist/{feedback-IHLVLMRD.js → feedback-MY4QZIFD.js} +1 -1
  61. package/dist/{generate-agent-definitions-64S3CLEZ.js → generate-agent-definitions-ZAE726AU.js} +4 -4
  62. package/dist/{graph-loader-GJZ4FN4Y.js → graph-loader-2M2HXDQI.js} +1 -1
  63. package/dist/index.d.ts +156 -17
  64. package/dist/index.js +24 -24
  65. package/dist/loader-UUTVMQCC.js +10 -0
  66. package/dist/{mcp-JQUI7BVZ.js → mcp-VU5FMO52.js} +14 -14
  67. package/dist/{performance-ZTVSUANN.js → performance-2D7G6NMJ.js} +3 -3
  68. package/dist/{review-pipeline-76JHKGSV.js → review-pipeline-RAQ55ISU.js} +1 -1
  69. package/dist/runtime-BCK5RRZQ.js +9 -0
  70. package/dist/{security-FWQZF2IZ.js → security-2RPQEN62.js} +1 -1
  71. package/dist/templates/axum/Cargo.toml.hbs +8 -0
  72. package/dist/templates/axum/src/main.rs +12 -0
  73. package/dist/templates/axum/template.json +16 -0
  74. package/dist/templates/django/manage.py.hbs +19 -0
  75. package/dist/templates/django/requirements.txt.hbs +1 -0
  76. package/dist/templates/django/src/settings.py.hbs +44 -0
  77. package/dist/templates/django/src/urls.py +6 -0
  78. package/dist/templates/django/src/wsgi.py.hbs +9 -0
  79. package/dist/templates/django/template.json +21 -0
  80. package/dist/templates/express/package.json.hbs +15 -0
  81. package/dist/templates/express/src/app.ts +12 -0
  82. package/dist/templates/express/src/lib/.gitkeep +0 -0
  83. package/dist/templates/express/template.json +16 -0
  84. package/dist/templates/fastapi/requirements.txt.hbs +2 -0
  85. package/dist/templates/fastapi/src/main.py +8 -0
  86. package/dist/templates/fastapi/template.json +20 -0
  87. package/dist/templates/gin/go.mod.hbs +5 -0
  88. package/dist/templates/gin/main.go +15 -0
  89. package/dist/templates/gin/template.json +19 -0
  90. package/dist/templates/go-base/.golangci.yml +16 -0
  91. package/dist/templates/go-base/AGENTS.md.hbs +35 -0
  92. package/dist/templates/go-base/go.mod.hbs +3 -0
  93. package/dist/templates/go-base/harness.config.json.hbs +17 -0
  94. package/dist/templates/go-base/main.go +7 -0
  95. package/dist/templates/go-base/template.json +14 -0
  96. package/dist/templates/java-base/AGENTS.md.hbs +35 -0
  97. package/dist/templates/java-base/checkstyle.xml +20 -0
  98. package/dist/templates/java-base/harness.config.json.hbs +16 -0
  99. package/dist/templates/java-base/pom.xml.hbs +39 -0
  100. package/dist/templates/java-base/src/main/java/App.java.hbs +5 -0
  101. package/dist/templates/java-base/template.json +13 -0
  102. package/dist/templates/nestjs/nest-cli.json +5 -0
  103. package/dist/templates/nestjs/package.json.hbs +18 -0
  104. package/dist/templates/nestjs/src/app.module.ts +8 -0
  105. package/dist/templates/nestjs/src/lib/.gitkeep +0 -0
  106. package/dist/templates/nestjs/src/main.ts +11 -0
  107. package/dist/templates/nestjs/template.json +16 -0
  108. package/dist/templates/nextjs/template.json +15 -1
  109. package/dist/templates/python-base/.python-version +1 -0
  110. package/dist/templates/python-base/AGENTS.md.hbs +32 -0
  111. package/dist/templates/python-base/harness.config.json.hbs +16 -0
  112. package/dist/templates/python-base/pyproject.toml.hbs +18 -0
  113. package/dist/templates/python-base/ruff.toml +5 -0
  114. package/dist/templates/python-base/src/__init__.py +0 -0
  115. package/dist/templates/python-base/template.json +13 -0
  116. package/dist/templates/react-vite/index.html +12 -0
  117. package/dist/templates/react-vite/package.json.hbs +18 -0
  118. package/dist/templates/react-vite/src/App.tsx +7 -0
  119. package/dist/templates/react-vite/src/lib/.gitkeep +0 -0
  120. package/dist/templates/react-vite/src/main.tsx +9 -0
  121. package/dist/templates/react-vite/template.json +19 -0
  122. package/dist/templates/react-vite/vite.config.ts +6 -0
  123. package/dist/templates/rust-base/AGENTS.md.hbs +35 -0
  124. package/dist/templates/rust-base/Cargo.toml.hbs +6 -0
  125. package/dist/templates/rust-base/clippy.toml +2 -0
  126. package/dist/templates/rust-base/harness.config.json.hbs +17 -0
  127. package/dist/templates/rust-base/src/main.rs +3 -0
  128. package/dist/templates/rust-base/template.json +14 -0
  129. package/dist/templates/spring-boot/pom.xml.hbs +50 -0
  130. package/dist/templates/spring-boot/src/main/java/Application.java.hbs +19 -0
  131. package/dist/templates/spring-boot/template.json +15 -0
  132. package/dist/templates/vue/index.html +12 -0
  133. package/dist/templates/vue/package.json.hbs +16 -0
  134. package/dist/templates/vue/src/App.vue +7 -0
  135. package/dist/templates/vue/src/lib/.gitkeep +0 -0
  136. package/dist/templates/vue/src/main.ts +4 -0
  137. package/dist/templates/vue/template.json +19 -0
  138. package/dist/templates/vue/vite.config.ts +6 -0
  139. package/dist/{validate-GCHZJIL7.js → validate-KBYQAEWE.js} +4 -4
  140. package/dist/validate-cross-check-OABMREW4.js +8 -0
  141. package/package.json +7 -5
  142. package/dist/agents-md-XU3BHE22.js +0 -8
  143. package/dist/ci-workflow-EHV65NQB.js +0 -8
  144. package/dist/engine-OL4T6NZS.js +0 -8
  145. package/dist/loader-DPYFB6R6.js +0 -10
  146. package/dist/runtime-X7U6SC7K.js +0 -9
  147. package/dist/validate-cross-check-STFHYMAZ.js +0 -8
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  detectEntropyDefinition,
3
3
  handleDetectEntropy
4
- } from "./chunk-DZS7CJKL.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-TKJZKICB.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-IM32EEDM.js";
22
+ } from "./chunk-YLXFKVJE.js";
23
23
  import {
24
24
  handleRunSecurityScan,
25
25
  runSecurityScanDefinition
26
- } from "./chunk-65FRIL4D.js";
26
+ } from "./chunk-3ZZKVN62.js";
27
27
  import {
28
28
  handleRunCodeReview,
29
29
  runCodeReviewDefinition
30
- } from "./chunk-4ZMOCPYO.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-NOPU4RZ4.js";
41
+ } from "./chunk-OFXQSFOW.js";
42
42
  import {
43
43
  loadGraphStore
44
- } from "./chunk-IMFVFNJE.js";
44
+ } from "./chunk-FTMXDOR6.js";
45
45
  import {
46
46
  checkDependenciesDefinition,
47
47
  handleCheckDependencies
48
- } from "./chunk-QY4T6YAZ.js";
48
+ } from "./chunk-N25INEIX.js";
49
49
  import {
50
50
  resolveProjectConfig
51
- } from "./chunk-K6XAPGML.js";
51
+ } from "./chunk-H7Y5CKTM.js";
52
52
  import {
53
53
  checkDocsDefinition,
54
54
  handleCheckDocs
55
- } from "./chunk-ND6PNADU.js";
55
+ } from "./chunk-2BKLWLY6.js";
56
56
  import {
57
57
  resolveConfig
58
- } from "./chunk-Z77YQRQT.js";
58
+ } from "./chunk-B2HKP423.js";
59
59
  import {
60
60
  resultToMcpResponse
61
61
  } from "./chunk-IDZNPTYD.js";
@@ -82,7 +82,7 @@ import {
82
82
  import {
83
83
  Err,
84
84
  Ok
85
- } from "./chunk-MHBMTPW7.js";
85
+ } from "./chunk-ERS5EVUZ.js";
86
86
 
87
87
  // src/mcp/server.ts
88
88
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
@@ -109,7 +109,7 @@ var generateLinterDefinition = {
109
109
  };
110
110
  async function handleGenerateLinter(input) {
111
111
  try {
112
- const { generate } = await import("./dist-L7LAAQAS.js");
112
+ const { generate } = await import("./dist-DZ63LLUD.js");
113
113
  const result = await generate({
114
114
  configPath: sanitizePath(input.configPath),
115
115
  ...input.outputDir !== void 0 && { outputDir: sanitizePath(input.outputDir) }
@@ -143,7 +143,7 @@ var validateLinterConfigDefinition = {
143
143
  };
144
144
  async function handleValidateLinterConfig(input) {
145
145
  try {
146
- const { validate } = await import("./dist-L7LAAQAS.js");
146
+ const { validate } = await import("./dist-DZ63LLUD.js");
147
147
  const result = await validate({ configPath: sanitizePath(input.configPath) });
148
148
  if ("success" in result && result.success) {
149
149
  return { content: [{ type: "text", text: JSON.stringify(result) }] };
@@ -163,7 +163,179 @@ async function handleValidateLinterConfig(input) {
163
163
  }
164
164
 
165
165
  // src/mcp/tools/init.ts
166
+ import * as path2 from "path";
167
+ import * as fs2 from "fs";
168
+
169
+ // src/templates/post-write.ts
170
+ import * as fs from "fs";
166
171
  import * as path from "path";
172
+
173
+ // src/templates/agents-append.ts
174
+ var FRAMEWORK_SECTIONS = {
175
+ nextjs: {
176
+ title: "Next.js Conventions",
177
+ content: [
178
+ "- Use the App Router (`src/app/`) for all routes",
179
+ '- Server Components by default; add `"use client"` only when needed',
180
+ "- Use `next/image` for images and `next/link` for navigation",
181
+ "- API routes go in `src/app/api/`",
182
+ "- Run `next dev` for development, `next build` for production"
183
+ ].join("\n")
184
+ },
185
+ "react-vite": {
186
+ title: "React + Vite Conventions",
187
+ content: [
188
+ "- Component files use `.tsx` extension in `src/`",
189
+ "- Use Vite for dev server and bundling (`npm run dev`)",
190
+ "- Prefer function components with hooks",
191
+ "- CSS modules or styled-components for styling",
192
+ "- Tests use Vitest (`npm test`)"
193
+ ].join("\n")
194
+ },
195
+ vue: {
196
+ title: "Vue Conventions",
197
+ content: [
198
+ "- Single File Components (`.vue`) in `src/`",
199
+ "- Use `<script setup>` with Composition API",
200
+ "- Vite for dev server and bundling (`npm run dev`)",
201
+ "- Vue Router for routing, Pinia for state management",
202
+ "- Tests use Vitest (`npm test`)"
203
+ ].join("\n")
204
+ },
205
+ express: {
206
+ title: "Express Conventions",
207
+ content: [
208
+ "- Entry point at `src/app.ts`",
209
+ "- Routes in `src/routes/`, middleware in `src/middleware/`",
210
+ "- Use `express.json()` for body parsing",
211
+ "- Error handling via centralized error middleware",
212
+ "- Tests use Vitest with supertest (`npm test`)"
213
+ ].join("\n")
214
+ },
215
+ nestjs: {
216
+ title: "NestJS Conventions",
217
+ content: [
218
+ "- Module-based architecture: each feature in its own module",
219
+ "- Use decorators (`@Controller`, `@Injectable`, `@Module`)",
220
+ "- Entry point at `src/main.ts`, root module at `src/app.module.ts`",
221
+ "- Use Nest CLI for generating components (`nest g`)",
222
+ "- Tests use Vitest (`npm test`)"
223
+ ].join("\n")
224
+ },
225
+ fastapi: {
226
+ title: "FastAPI Conventions",
227
+ content: [
228
+ "- Entry point at `src/main.py` with FastAPI app instance",
229
+ "- Use Pydantic models for request/response validation",
230
+ "- Async endpoints preferred; sync is acceptable for CPU-bound work",
231
+ "- Run with `uvicorn src.main:app --reload` for development",
232
+ "- Tests use pytest (`pytest`)"
233
+ ].join("\n")
234
+ },
235
+ django: {
236
+ title: "Django Conventions",
237
+ content: [
238
+ "- Settings at `src/settings.py`, URLs at `src/urls.py`",
239
+ "- Use `manage.py` for management commands",
240
+ "- Apps in `src/` directory; each app has models, views, urls",
241
+ "- Run with `python manage.py runserver` for development",
242
+ "- Tests use pytest with pytest-django (`pytest`)"
243
+ ].join("\n")
244
+ },
245
+ gin: {
246
+ title: "Gin Conventions",
247
+ content: [
248
+ "- Entry point at `main.go` with Gin router setup",
249
+ "- Group routes by feature using `router.Group()`",
250
+ "- Use middleware for logging, auth, error recovery",
251
+ "- Run with `go run main.go` for development",
252
+ "- Tests use `go test ./...`"
253
+ ].join("\n")
254
+ },
255
+ axum: {
256
+ title: "Axum Conventions",
257
+ content: [
258
+ "- Entry point at `src/main.rs` with Axum router",
259
+ "- Use extractors for request parsing (`Path`, `Query`, `Json`)",
260
+ "- Shared state via `Extension` or `State`",
261
+ "- Run with `cargo run` for development",
262
+ "- Tests use `cargo test`"
263
+ ].join("\n")
264
+ },
265
+ "spring-boot": {
266
+ title: "Spring Boot Conventions",
267
+ content: [
268
+ "- Entry point annotated with `@SpringBootApplication`",
269
+ "- Controllers in `controller/` package, services in `service/`",
270
+ "- Use constructor injection for dependencies",
271
+ "- Run with `mvn spring-boot:run` for development",
272
+ "- Tests use JUnit 5 with Spring Boot Test (`mvn test`)"
273
+ ].join("\n")
274
+ }
275
+ };
276
+ function buildFrameworkSection(framework) {
277
+ const entry = FRAMEWORK_SECTIONS[framework];
278
+ if (!entry) return "";
279
+ return `## ${entry.title}
280
+
281
+ <!-- framework: ${framework} -->
282
+ ${entry.content}
283
+ `;
284
+ }
285
+ function appendFrameworkSection(existingContent, framework, _language) {
286
+ if (!framework) return existingContent;
287
+ const startMarker = `<!-- harness:framework-conventions:${framework} -->`;
288
+ const endMarker = `<!-- /harness:framework-conventions:${framework} -->`;
289
+ if (existingContent.includes(startMarker)) return existingContent;
290
+ const section = buildFrameworkSection(framework);
291
+ if (!section) return existingContent;
292
+ const block = `
293
+ ${startMarker}
294
+ ${section}${endMarker}
295
+ `;
296
+ return existingContent.trimEnd() + "\n" + block;
297
+ }
298
+
299
+ // src/templates/post-write.ts
300
+ function persistToolingConfig(targetDir, resolveResult, framework) {
301
+ const configPath = path.join(targetDir, "harness.config.json");
302
+ if (!fs.existsSync(configPath)) return;
303
+ try {
304
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
305
+ const overlayMeta = resolveResult.overlayMetadata;
306
+ if (framework) {
307
+ config.template = config.template || {};
308
+ config.template.framework = framework;
309
+ }
310
+ if (overlayMeta?.tooling) {
311
+ config.tooling = { ...config.tooling, ...overlayMeta.tooling };
312
+ delete config.tooling.lockFile;
313
+ } else if (resolveResult.metadata.tooling && !config.tooling) {
314
+ config.tooling = { ...resolveResult.metadata.tooling };
315
+ delete config.tooling.lockFile;
316
+ }
317
+ if (config.template?.level === null || config.template?.level === void 0) {
318
+ delete config.template.level;
319
+ }
320
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
321
+ } catch {
322
+ }
323
+ }
324
+ function appendFrameworkAgents(targetDir, framework, language) {
325
+ if (!framework) return;
326
+ const agentsPath = path.join(targetDir, "AGENTS.md");
327
+ if (!fs.existsSync(agentsPath)) return;
328
+ try {
329
+ const existing = fs.readFileSync(agentsPath, "utf-8");
330
+ const updated = appendFrameworkSection(existing, framework, language);
331
+ if (updated !== existing) {
332
+ fs.writeFileSync(agentsPath, updated);
333
+ }
334
+ } catch {
335
+ }
336
+ }
337
+
338
+ // src/mcp/tools/init.ts
167
339
  var initProjectDefinition = {
168
340
  name: "init_project",
169
341
  description: "Scaffold a new harness engineering project from a template",
@@ -175,54 +347,109 @@ var initProjectDefinition = {
175
347
  level: {
176
348
  type: "string",
177
349
  enum: ["basic", "intermediate", "advanced"],
178
- description: "Adoption level"
350
+ description: "Adoption level (JS/TS only)"
179
351
  },
180
- framework: { type: "string", description: "Framework overlay (e.g., nextjs)" }
352
+ framework: { type: "string", description: "Framework overlay (e.g., nextjs, fastapi, gin)" },
353
+ language: {
354
+ type: "string",
355
+ enum: ["typescript", "python", "go", "rust", "java"],
356
+ description: "Target language"
357
+ }
181
358
  },
182
359
  required: ["path"]
183
360
  }
184
361
  };
362
+ function mcpText(text, isError = false) {
363
+ return { content: [{ type: "text", text }], isError };
364
+ }
365
+ function findFrameworkLanguage(engine, framework) {
366
+ const templates = engine.listTemplates();
367
+ if (!templates.ok || !templates.value) return void 0;
368
+ return templates.value.find((t) => t.framework === framework)?.language;
369
+ }
370
+ function tryDetectFramework(engine, safePath, input) {
371
+ if (input.framework || input.language || !fs2.existsSync(safePath)) return null;
372
+ const result = engine.detectFramework(safePath);
373
+ if (!result.ok || !result.value || result.value.length === 0) return null;
374
+ const candidates = result.value.map((c) => `${c.framework} (${c.language}, score: ${c.score})`).join(", ");
375
+ return mcpText(
376
+ `Detected frameworks: ${candidates}
377
+
378
+ Re-invoke with --framework <name> to scaffold, or specify --language for a bare scaffold.`
379
+ );
380
+ }
381
+ function checkFrameworkLanguageConflict(engine, input) {
382
+ if (!input.framework || !input.language) return null;
383
+ const fwLang = findFrameworkLanguage(engine, input.framework);
384
+ if (fwLang && fwLang !== input.language) {
385
+ return mcpText(
386
+ `Framework "${input.framework}" is a ${fwLang} framework, but language "${input.language}" was specified. Use language "${fwLang}" instead.`,
387
+ true
388
+ );
389
+ }
390
+ return null;
391
+ }
392
+ function inferLanguage(engine, input) {
393
+ if (input.language) return input.language;
394
+ if (!input.framework) return void 0;
395
+ return findFrameworkLanguage(engine, input.framework);
396
+ }
397
+ function scaffoldMcp(engine, safePath, i, language) {
398
+ const isNonJs = language && language !== "typescript";
399
+ const level = isNonJs ? void 0 : i.level ?? "basic";
400
+ const resolveResult = engine.resolveTemplate(level, i.framework, language);
401
+ if (!resolveResult.ok) return resultToMcpResponse(resolveResult);
402
+ const renderResult = engine.render(resolveResult.value, {
403
+ projectName: i.name ?? path2.basename(safePath),
404
+ level: level ?? "",
405
+ ...i.framework !== void 0 && { framework: i.framework },
406
+ ...language !== void 0 && { language }
407
+ });
408
+ if (!renderResult.ok) return resultToMcpResponse(renderResult);
409
+ const writeResult = engine.write(renderResult.value, safePath, {
410
+ overwrite: false,
411
+ ...language !== void 0 && { language }
412
+ });
413
+ if (writeResult.ok) {
414
+ persistToolingConfig(safePath, resolveResult.value, i.framework);
415
+ appendFrameworkAgents(safePath, i.framework, language);
416
+ }
417
+ if (writeResult.ok && writeResult.value.skippedConfigs.length > 0) {
418
+ const skippedMsg = writeResult.value.skippedConfigs.map((f) => ` - ${f}`).join("\n");
419
+ return mcpText(
420
+ `Files written: ${writeResult.value.written.join(", ")}
421
+
422
+ Skipped existing config files (add harness dependencies manually):
423
+ ${skippedMsg}`
424
+ );
425
+ }
426
+ return resultToMcpResponse(writeResult);
427
+ }
185
428
  async function handleInitProject(input) {
429
+ const i = input;
186
430
  try {
187
- const { TemplateEngine } = await import("./engine-OL4T6NZS.js");
188
- const templatesDir = resolveTemplatesDir();
189
- const engine = new TemplateEngine(templatesDir);
190
- const level = input.level ?? "basic";
191
- const resolveResult = engine.resolveTemplate(level, input.framework);
192
- if (!resolveResult.ok) return resultToMcpResponse(resolveResult);
193
- const safePath = sanitizePath(input.path);
194
- const renderResult = engine.render(resolveResult.value, {
195
- projectName: input.name ?? path.basename(safePath),
196
- level,
197
- ...input.framework !== void 0 && { framework: input.framework }
198
- });
199
- if (!renderResult.ok) return resultToMcpResponse(renderResult);
200
- const writeResult = engine.write(renderResult.value, safePath, {
201
- overwrite: false
202
- });
203
- return resultToMcpResponse(writeResult);
431
+ const { TemplateEngine } = await import("./engine-3RB7MXPP.js");
432
+ const engine = new TemplateEngine(resolveTemplatesDir());
433
+ const safePath = sanitizePath(i.path);
434
+ const detected = tryDetectFramework(engine, safePath, i);
435
+ if (detected) return detected;
436
+ const conflict = checkFrameworkLanguageConflict(engine, i);
437
+ if (conflict) return conflict;
438
+ return scaffoldMcp(engine, safePath, i, inferLanguage(engine, i));
204
439
  } catch (error) {
205
- return {
206
- content: [
207
- {
208
- type: "text",
209
- text: `Init failed: ${error instanceof Error ? error.message : String(error)}`
210
- }
211
- ],
212
- isError: true
213
- };
440
+ return mcpText(`Init failed: ${error instanceof Error ? error.message : String(error)}`, true);
214
441
  }
215
442
  }
216
443
 
217
444
  // src/mcp/tools/persona.ts
218
- import * as path2 from "path";
445
+ import * as path3 from "path";
219
446
  var listPersonasDefinition = {
220
447
  name: "list_personas",
221
448
  description: "List available agent personas",
222
449
  inputSchema: { type: "object", properties: {} }
223
450
  };
224
451
  async function handleListPersonas() {
225
- const { listPersonas } = await import("./loader-DPYFB6R6.js");
452
+ const { listPersonas } = await import("./loader-UUTVMQCC.js");
226
453
  const result = listPersonas(resolvePersonasDir());
227
454
  return resultToMcpResponse(result);
228
455
  }
@@ -246,12 +473,12 @@ async function handleGeneratePersonaArtifacts(input) {
246
473
  if (!/^[a-z0-9][a-z0-9._-]*$/i.test(input.name)) {
247
474
  return resultToMcpResponse(Err(new Error(`Invalid persona name: ${input.name}`)));
248
475
  }
249
- const { loadPersona } = await import("./loader-DPYFB6R6.js");
250
- const { generateRuntime } = await import("./runtime-X7U6SC7K.js");
251
- const { generateAgentsMd } = await import("./agents-md-XU3BHE22.js");
252
- const { generateCIWorkflow } = await import("./ci-workflow-EHV65NQB.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");
253
480
  const personasDir = resolvePersonasDir();
254
- const filePath = path2.join(personasDir, `${input.name}.yaml`);
481
+ const filePath = path3.join(personasDir, `${input.name}.yaml`);
255
482
  if (!filePath.startsWith(personasDir)) {
256
483
  return resultToMcpResponse(Err(new Error(`Invalid persona path: ${input.name}`)));
257
484
  }
@@ -304,11 +531,11 @@ async function handleRunPersona(input) {
304
531
  if (!/^[a-z0-9][a-z0-9._-]*$/i.test(input.persona)) {
305
532
  return resultToMcpResponse(Err(new Error(`Invalid persona name: ${input.persona}`)));
306
533
  }
307
- const { loadPersona } = await import("./loader-DPYFB6R6.js");
534
+ const { loadPersona } = await import("./loader-UUTVMQCC.js");
308
535
  const { runPersona } = await import("./runner-VMYLHWOC.js");
309
536
  const { executeSkill } = await import("./skill-executor-XZLYZYAK.js");
310
537
  const personasDir = resolvePersonasDir();
311
- const filePath = path2.join(personasDir, `${input.persona}.yaml`);
538
+ const filePath = path3.join(personasDir, `${input.persona}.yaml`);
312
539
  if (!filePath.startsWith(personasDir)) {
313
540
  return resultToMcpResponse(Err(new Error(`Invalid persona path: ${input.persona}`)));
314
541
  }
@@ -470,8 +697,8 @@ async function handleRunAgentTask(input) {
470
697
  }
471
698
 
472
699
  // src/mcp/tools/skill.ts
473
- import * as fs3 from "fs";
474
- import * as path5 from "path";
700
+ import * as fs5 from "fs";
701
+ import * as path6 from "path";
475
702
 
476
703
  // src/skill/dispatcher.ts
477
704
  var TIER_1_SKILLS = /* @__PURE__ */ new Set([
@@ -546,26 +773,26 @@ function formatSuggestions(suggestions) {
546
773
  }
547
774
 
548
775
  // src/skill/index-builder.ts
549
- import fs from "fs";
550
- import path3 from "path";
776
+ import fs3 from "fs";
777
+ import path4 from "path";
551
778
  import crypto from "crypto";
552
779
  import { parse } from "yaml";
553
780
  function computeSkillsDirHash(skillsDirs) {
554
781
  const hash = crypto.createHash("sha256");
555
782
  for (const dir of skillsDirs) {
556
- if (!fs.existsSync(dir)) continue;
557
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
783
+ if (!fs3.existsSync(dir)) continue;
784
+ for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
558
785
  if (!entry.isDirectory()) continue;
559
- const yamlPath = path3.join(dir, entry.name, "skill.yaml");
560
- if (!fs.existsSync(yamlPath)) continue;
561
- const stat = fs.statSync(yamlPath);
786
+ const yamlPath = path4.join(dir, entry.name, "skill.yaml");
787
+ if (!fs3.existsSync(yamlPath)) continue;
788
+ const stat = fs3.statSync(yamlPath);
562
789
  hash.update(`${yamlPath}:${stat.mtimeMs}`);
563
790
  }
564
791
  }
565
792
  return hash.digest("hex");
566
793
  }
567
794
  function parseSkillEntry(yamlPath, source, tierOverrides) {
568
- const raw = fs.readFileSync(yamlPath, "utf-8");
795
+ const raw = fs3.readFileSync(yamlPath, "utf-8");
569
796
  const parsed = parse(raw);
570
797
  const result = SkillMetadataSchema.safeParse(parsed);
571
798
  if (!result.success) return null;
@@ -584,12 +811,12 @@ function parseSkillEntry(yamlPath, source, tierOverrides) {
584
811
  };
585
812
  }
586
813
  function scanDirectory(dir, source, index, tierOverrides) {
587
- if (!fs.existsSync(dir)) return;
588
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
814
+ if (!fs3.existsSync(dir)) return;
815
+ for (const entry of fs3.readdirSync(dir, { withFileTypes: true })) {
589
816
  if (!entry.isDirectory()) continue;
590
817
  if (index.skills[entry.name]) continue;
591
- const yamlPath = path3.join(dir, entry.name, "skill.yaml");
592
- if (!fs.existsSync(yamlPath)) continue;
818
+ const yamlPath = path4.join(dir, entry.name, "skill.yaml");
819
+ if (!fs3.existsSync(yamlPath)) continue;
593
820
  try {
594
821
  const parsed = parseSkillEntry(yamlPath, source, tierOverrides);
595
822
  if (parsed) index.skills[entry.name] = parsed;
@@ -613,25 +840,25 @@ function buildIndex(platform, _projectRoot, tierOverrides) {
613
840
  return index;
614
841
  }
615
842
  function loadOrRebuildIndex(platform, projectRoot, tierOverrides) {
616
- const indexPath = path3.join(projectRoot, ".harness", "skills-index.json");
843
+ const indexPath = path4.join(projectRoot, ".harness", "skills-index.json");
617
844
  const skillsDirs = resolveAllSkillsDirs(platform);
618
845
  const currentHash = computeSkillsDirHash(skillsDirs);
619
- if (fs.existsSync(indexPath)) {
846
+ if (fs3.existsSync(indexPath)) {
620
847
  try {
621
- const existing = JSON.parse(fs.readFileSync(indexPath, "utf-8"));
848
+ const existing = JSON.parse(fs3.readFileSync(indexPath, "utf-8"));
622
849
  if (existing.hash === currentHash) return existing;
623
850
  } catch {
624
851
  }
625
852
  }
626
853
  const index = buildIndex(platform, projectRoot, tierOverrides);
627
- fs.mkdirSync(path3.dirname(indexPath), { recursive: true });
628
- fs.writeFileSync(indexPath, JSON.stringify(index, null, 2));
854
+ fs3.mkdirSync(path4.dirname(indexPath), { recursive: true });
855
+ fs3.writeFileSync(indexPath, JSON.stringify(index, null, 2));
629
856
  return index;
630
857
  }
631
858
 
632
859
  // src/skill/stack-profile.ts
633
- import fs2 from "fs";
634
- import path4 from "path";
860
+ import fs4 from "fs";
861
+ import path5 from "path";
635
862
  var SIGNAL_DOMAIN_MAP = {
636
863
  "prisma/schema.prisma": ["database"],
637
864
  "drizzle.config.ts": ["database"],
@@ -679,8 +906,8 @@ function generateStackProfile(projectRoot) {
679
906
  const signals = {};
680
907
  const domainSet = /* @__PURE__ */ new Set();
681
908
  for (const [pattern, domains] of Object.entries(SIGNAL_DOMAIN_MAP)) {
682
- const fullPath = path4.join(projectRoot, pattern);
683
- const exists = fs2.existsSync(fullPath);
909
+ const fullPath = path5.join(projectRoot, pattern);
910
+ const exists = fs4.existsSync(fullPath);
684
911
  signals[pattern] = exists;
685
912
  if (exists) {
686
913
  for (const domain of domains) domainSet.add(domain);
@@ -693,16 +920,16 @@ function generateStackProfile(projectRoot) {
693
920
  };
694
921
  }
695
922
  function loadOrGenerateProfile(projectRoot) {
696
- const profilePath = path4.join(projectRoot, ".harness", "stack-profile.json");
697
- if (fs2.existsSync(profilePath)) {
923
+ const profilePath = path5.join(projectRoot, ".harness", "stack-profile.json");
924
+ if (fs4.existsSync(profilePath)) {
698
925
  try {
699
- return JSON.parse(fs2.readFileSync(profilePath, "utf-8"));
926
+ return JSON.parse(fs4.readFileSync(profilePath, "utf-8"));
700
927
  } catch {
701
928
  }
702
929
  }
703
930
  const profile = generateStackProfile(projectRoot);
704
- fs2.mkdirSync(path4.dirname(profilePath), { recursive: true });
705
- fs2.writeFileSync(profilePath, JSON.stringify(profile, null, 2));
931
+ fs4.mkdirSync(path5.dirname(profilePath), { recursive: true });
932
+ fs4.writeFileSync(profilePath, JSON.stringify(profile, null, 2));
706
933
  return profile;
707
934
  }
708
935
 
@@ -731,23 +958,23 @@ async function handleRunSkill(input) {
731
958
  if (!/^[a-z0-9][a-z0-9._-]*$/i.test(input.skill)) {
732
959
  return resultToMcpResponse(Err(new Error(`Invalid skill name: ${input.skill}`)));
733
960
  }
734
- const skillDir = path5.join(skillsDir, input.skill);
961
+ const skillDir = path6.join(skillsDir, input.skill);
735
962
  if (!skillDir.startsWith(skillsDir)) {
736
963
  return resultToMcpResponse(Err(new Error(`Invalid skill path: ${input.skill}`)));
737
964
  }
738
- if (!fs3.existsSync(skillDir)) {
965
+ if (!fs5.existsSync(skillDir)) {
739
966
  return resultToMcpResponse(Err(new Error(`Skill not found: ${input.skill}`)));
740
967
  }
741
- const skillMdPath = path5.join(skillDir, "SKILL.md");
742
- if (!fs3.existsSync(skillMdPath)) {
968
+ const skillMdPath = path6.join(skillDir, "SKILL.md");
969
+ if (!fs5.existsSync(skillMdPath)) {
743
970
  return resultToMcpResponse(Err(new Error(`SKILL.md not found for skill: ${input.skill}`)));
744
971
  }
745
- let content = fs3.readFileSync(skillMdPath, "utf-8");
972
+ let content = fs5.readFileSync(skillMdPath, "utf-8");
746
973
  if (input.path) {
747
974
  const projectPath = sanitizePath(input.path);
748
- const stateFile = path5.join(projectPath, ".harness", "state.json");
749
- if (fs3.existsSync(stateFile)) {
750
- const stateContent = fs3.readFileSync(stateFile, "utf-8");
975
+ const stateFile = path6.join(projectPath, ".harness", "state.json");
976
+ if (fs5.existsSync(stateFile)) {
977
+ const stateContent = fs5.readFileSync(stateFile, "utf-8");
751
978
  content += `
752
979
 
753
980
  ---
@@ -826,22 +1053,22 @@ async function handleCreateSkill(input) {
826
1053
  }
827
1054
 
828
1055
  // src/mcp/resources/skills.ts
829
- import * as fs4 from "fs";
830
- import * as path6 from "path";
1056
+ import * as fs6 from "fs";
1057
+ import * as path7 from "path";
831
1058
  import * as yaml from "yaml";
832
1059
  async function getSkillsResource(projectRoot) {
833
- const skillsDir = path6.join(projectRoot, "agents", "skills", "claude-code");
1060
+ const skillsDir = path7.join(projectRoot, "agents", "skills", "claude-code");
834
1061
  const skills = [];
835
- if (!fs4.existsSync(skillsDir)) {
1062
+ if (!fs6.existsSync(skillsDir)) {
836
1063
  return JSON.stringify(skills, null, 2);
837
1064
  }
838
- const entries = fs4.readdirSync(skillsDir, { withFileTypes: true });
1065
+ const entries = fs6.readdirSync(skillsDir, { withFileTypes: true });
839
1066
  for (const entry of entries) {
840
1067
  if (!entry.isDirectory()) continue;
841
- const skillYamlPath = path6.join(skillsDir, entry.name, "skill.yaml");
842
- if (!fs4.existsSync(skillYamlPath)) continue;
1068
+ const skillYamlPath = path7.join(skillsDir, entry.name, "skill.yaml");
1069
+ if (!fs6.existsSync(skillYamlPath)) continue;
843
1070
  try {
844
- const content = fs4.readFileSync(skillYamlPath, "utf-8");
1071
+ const content = fs6.readFileSync(skillYamlPath, "utf-8");
845
1072
  const parsed = yaml.parse(content);
846
1073
  skills.push({
847
1074
  name: parsed.name,
@@ -857,14 +1084,14 @@ async function getSkillsResource(projectRoot) {
857
1084
  }
858
1085
 
859
1086
  // src/mcp/resources/rules.ts
860
- import * as fs5 from "fs";
861
- import * as path7 from "path";
1087
+ import * as fs7 from "fs";
1088
+ import * as path8 from "path";
862
1089
  async function getRulesResource(projectRoot) {
863
1090
  const rules = [];
864
- const configPath = path7.join(projectRoot, "harness.config.json");
865
- if (fs5.existsSync(configPath)) {
1091
+ const configPath = path8.join(projectRoot, "harness.config.json");
1092
+ if (fs7.existsSync(configPath)) {
866
1093
  try {
867
- const config = JSON.parse(fs5.readFileSync(configPath, "utf-8"));
1094
+ const config = JSON.parse(fs7.readFileSync(configPath, "utf-8"));
868
1095
  if (config.layers) {
869
1096
  rules.push({ type: "layer-enforcement", config: config.layers });
870
1097
  }
@@ -877,10 +1104,10 @@ async function getRulesResource(projectRoot) {
877
1104
  } catch {
878
1105
  }
879
1106
  }
880
- const linterPath = path7.join(projectRoot, ".harness", "linter.json");
881
- if (fs5.existsSync(linterPath)) {
1107
+ const linterPath = path8.join(projectRoot, ".harness", "linter.json");
1108
+ if (fs7.existsSync(linterPath)) {
882
1109
  try {
883
- const linterConfig = JSON.parse(fs5.readFileSync(linterPath, "utf-8"));
1110
+ const linterConfig = JSON.parse(fs7.readFileSync(linterPath, "utf-8"));
884
1111
  rules.push({ type: "linter", config: linterConfig });
885
1112
  } catch {
886
1113
  }
@@ -889,32 +1116,57 @@ async function getRulesResource(projectRoot) {
889
1116
  }
890
1117
 
891
1118
  // src/mcp/resources/project.ts
892
- import * as fs6 from "fs";
893
- import * as path8 from "path";
1119
+ import * as fs8 from "fs";
1120
+ import * as path9 from "path";
894
1121
  async function getProjectResource(projectRoot) {
895
- const agentsPath = path8.join(projectRoot, "AGENTS.md");
896
- if (fs6.existsSync(agentsPath)) {
897
- return fs6.readFileSync(agentsPath, "utf-8");
1122
+ const agentsPath = path9.join(projectRoot, "AGENTS.md");
1123
+ if (fs8.existsSync(agentsPath)) {
1124
+ return fs8.readFileSync(agentsPath, "utf-8");
898
1125
  }
899
1126
  return "# No AGENTS.md found";
900
1127
  }
901
1128
 
902
1129
  // src/mcp/resources/learnings.ts
903
- import * as fs7 from "fs";
904
- import * as path9 from "path";
1130
+ import * as fs9 from "fs";
1131
+ import * as path10 from "path";
905
1132
  async function getLearningsResource(projectRoot) {
906
1133
  const sections = [];
907
- const reviewPath = path9.join(projectRoot, ".harness", "review-learnings.md");
908
- if (fs7.existsSync(reviewPath)) {
909
- sections.push("## Review Learnings\n\n" + fs7.readFileSync(reviewPath, "utf-8"));
1134
+ const reviewPath = path10.join(projectRoot, ".harness", "review-learnings.md");
1135
+ if (fs9.existsSync(reviewPath)) {
1136
+ sections.push("## Review Learnings\n\n" + fs9.readFileSync(reviewPath, "utf-8"));
910
1137
  }
911
- const antiPath = path9.join(projectRoot, ".harness", "anti-patterns.md");
912
- if (fs7.existsSync(antiPath)) {
913
- sections.push("## Anti-Pattern Log\n\n" + fs7.readFileSync(antiPath, "utf-8"));
1138
+ const antiPath = path10.join(projectRoot, ".harness", "anti-patterns.md");
1139
+ if (fs9.existsSync(antiPath)) {
1140
+ sections.push("## Anti-Pattern Log\n\n" + fs9.readFileSync(antiPath, "utf-8"));
914
1141
  }
915
1142
  return sections.length > 0 ? sections.join("\n\n---\n\n") : "No learnings files found.";
916
1143
  }
917
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
+
1165
+ // src/mcp/utils.ts
1166
+ function mcpError(text) {
1167
+ return { content: [{ type: "text", text }], isError: true };
1168
+ }
1169
+
918
1170
  // src/mcp/tools/state.ts
919
1171
  var manageStateDefinition = {
920
1172
  name: "manage_state",
@@ -933,7 +1185,12 @@ var manageStateDefinition = {
933
1185
  "reset",
934
1186
  "gate",
935
1187
  "save-handoff",
936
- "load-handoff"
1188
+ "load-handoff",
1189
+ "append_entry",
1190
+ "update_entry_status",
1191
+ "read_section",
1192
+ "read_sections",
1193
+ "archive_session"
937
1194
  ],
938
1195
  description: "Action to perform"
939
1196
  },
@@ -950,138 +1207,178 @@ var manageStateDefinition = {
950
1207
  session: {
951
1208
  type: "string",
952
1209
  description: "Session slug for session-scoped state (takes priority over stream when provided)"
1210
+ },
1211
+ section: {
1212
+ type: "string",
1213
+ enum: ["terminology", "decisions", "constraints", "risks", "openQuestions", "evidence"],
1214
+ description: "Session section name (terminology, decisions, constraints, risks, openQuestions, evidence)"
1215
+ },
1216
+ authorSkill: {
1217
+ type: "string",
1218
+ description: "Name of the skill authoring the entry (required for append_entry)"
1219
+ },
1220
+ content: {
1221
+ type: "string",
1222
+ description: "Entry content text (required for append_entry)"
1223
+ },
1224
+ entryId: {
1225
+ type: "string",
1226
+ description: "ID of the entry to update (required for update_entry_status)"
1227
+ },
1228
+ newStatus: {
1229
+ type: "string",
1230
+ enum: ["active", "resolved", "superseded"],
1231
+ description: "New status for the entry: active, resolved, or superseded (required for update_entry_status)"
953
1232
  }
954
1233
  },
955
1234
  required: ["path", "action"]
956
1235
  }
957
1236
  };
1237
+ async function handleShow(projectPath, input) {
1238
+ const { loadState } = await import("./dist-ALQDD67R.js");
1239
+ return resultToMcpResponse(await loadState(projectPath, input.stream, input.session));
1240
+ }
1241
+ async function handleLearn(projectPath, input) {
1242
+ if (!input.learning) return mcpError("Error: learning is required for learn action");
1243
+ const { appendLearning } = await import("./dist-ALQDD67R.js");
1244
+ const result = await appendLearning(
1245
+ projectPath,
1246
+ input.learning,
1247
+ input.skillName,
1248
+ input.outcome,
1249
+ input.stream,
1250
+ input.session
1251
+ );
1252
+ if (!result.ok) return resultToMcpResponse(result);
1253
+ return resultToMcpResponse(Ok({ recorded: true }));
1254
+ }
1255
+ async function handleFailure(projectPath, input) {
1256
+ if (!input.description) return mcpError("Error: description is required for failure action");
1257
+ if (!input.failureType) return mcpError("Error: failureType is required for failure action");
1258
+ const { appendFailure } = await import("./dist-ALQDD67R.js");
1259
+ const result = await appendFailure(
1260
+ projectPath,
1261
+ input.description,
1262
+ input.skillName ?? "unknown",
1263
+ input.failureType,
1264
+ input.stream,
1265
+ input.session
1266
+ );
1267
+ if (!result.ok) return resultToMcpResponse(result);
1268
+ return resultToMcpResponse(Ok({ recorded: true }));
1269
+ }
1270
+ async function handleArchive(projectPath, input) {
1271
+ const { archiveFailures } = await import("./dist-ALQDD67R.js");
1272
+ const result = await archiveFailures(projectPath, input.stream, input.session);
1273
+ if (!result.ok) return resultToMcpResponse(result);
1274
+ return resultToMcpResponse(Ok({ archived: true }));
1275
+ }
1276
+ async function handleReset(projectPath, input) {
1277
+ const { saveState, DEFAULT_STATE } = await import("./dist-ALQDD67R.js");
1278
+ const result = await saveState(projectPath, { ...DEFAULT_STATE }, input.stream, input.session);
1279
+ if (!result.ok) return resultToMcpResponse(result);
1280
+ return resultToMcpResponse(Ok({ reset: true }));
1281
+ }
1282
+ async function handleGate(projectPath, _input) {
1283
+ const { runMechanicalGate } = await import("./dist-ALQDD67R.js");
1284
+ return resultToMcpResponse(await runMechanicalGate(projectPath));
1285
+ }
1286
+ async function handleSaveHandoff(projectPath, input) {
1287
+ if (!input.handoff) return mcpError("Error: handoff is required for save-handoff action");
1288
+ const { saveHandoff } = await import("./dist-ALQDD67R.js");
1289
+ const result = await saveHandoff(
1290
+ projectPath,
1291
+ input.handoff,
1292
+ input.stream,
1293
+ input.session
1294
+ );
1295
+ if (!result.ok) return resultToMcpResponse(result);
1296
+ await autoSyncRoadmap(projectPath);
1297
+ return resultToMcpResponse(Ok({ saved: true }));
1298
+ }
1299
+ async function handleLoadHandoff(projectPath, input) {
1300
+ const { loadHandoff } = await import("./dist-ALQDD67R.js");
1301
+ return resultToMcpResponse(await loadHandoff(projectPath, input.stream, input.session));
1302
+ }
1303
+ async function handleAppendEntry(projectPath, input) {
1304
+ if (!input.session) return mcpError("Error: session is required for append_entry action");
1305
+ if (!input.section) return mcpError("Error: section is required for append_entry action");
1306
+ if (!input.authorSkill) return mcpError("Error: authorSkill is required for append_entry action");
1307
+ if (!input.content) return mcpError("Error: content is required for append_entry action");
1308
+ const { appendSessionEntry } = await import("./dist-ALQDD67R.js");
1309
+ const result = await appendSessionEntry(
1310
+ projectPath,
1311
+ input.session,
1312
+ input.section,
1313
+ input.authorSkill,
1314
+ input.content
1315
+ );
1316
+ return resultToMcpResponse(result);
1317
+ }
1318
+ async function handleUpdateEntryStatus(projectPath, input) {
1319
+ if (!input.session) return mcpError("Error: session is required for update_entry_status action");
1320
+ if (!input.section) return mcpError("Error: section is required for update_entry_status action");
1321
+ if (!input.entryId) return mcpError("Error: entryId is required for update_entry_status action");
1322
+ if (!input.newStatus)
1323
+ return mcpError("Error: newStatus is required for update_entry_status action");
1324
+ const { updateSessionEntryStatus } = await import("./dist-ALQDD67R.js");
1325
+ const result = await updateSessionEntryStatus(
1326
+ projectPath,
1327
+ input.session,
1328
+ input.section,
1329
+ input.entryId,
1330
+ input.newStatus
1331
+ );
1332
+ return resultToMcpResponse(result);
1333
+ }
1334
+ async function handleReadSection(projectPath, input) {
1335
+ if (!input.session) return mcpError("Error: session is required for read_section action");
1336
+ if (!input.section) return mcpError("Error: section is required for read_section action");
1337
+ const { readSessionSection } = await import("./dist-ALQDD67R.js");
1338
+ const result = await readSessionSection(
1339
+ projectPath,
1340
+ input.session,
1341
+ input.section
1342
+ );
1343
+ return resultToMcpResponse(result);
1344
+ }
1345
+ async function handleReadSections(projectPath, input) {
1346
+ if (!input.session) return mcpError("Error: session is required for read_sections action");
1347
+ const { readSessionSections } = await import("./dist-ALQDD67R.js");
1348
+ const result = await readSessionSections(projectPath, input.session);
1349
+ return resultToMcpResponse(result);
1350
+ }
1351
+ async function handleArchiveSession(projectPath, input) {
1352
+ if (!input.session) return mcpError("Error: session is required for archive_session action");
1353
+ const { archiveSession } = await import("./dist-ALQDD67R.js");
1354
+ const result = await archiveSession(projectPath, input.session);
1355
+ if (!result.ok) return resultToMcpResponse(result);
1356
+ await autoSyncRoadmap(projectPath);
1357
+ return resultToMcpResponse(Ok({ archived: true }));
1358
+ }
1359
+ var ACTION_HANDLERS = {
1360
+ show: handleShow,
1361
+ learn: handleLearn,
1362
+ failure: handleFailure,
1363
+ archive: handleArchive,
1364
+ reset: handleReset,
1365
+ gate: handleGate,
1366
+ "save-handoff": handleSaveHandoff,
1367
+ "load-handoff": handleLoadHandoff,
1368
+ append_entry: handleAppendEntry,
1369
+ update_entry_status: handleUpdateEntryStatus,
1370
+ read_section: handleReadSection,
1371
+ read_sections: handleReadSections,
1372
+ archive_session: handleArchiveSession
1373
+ };
958
1374
  async function handleManageState(input) {
959
1375
  try {
960
- const {
961
- loadState,
962
- saveState,
963
- appendLearning,
964
- appendFailure,
965
- archiveFailures,
966
- runMechanicalGate,
967
- DEFAULT_STATE
968
- } = await import("./dist-2B363XUH.js");
969
1376
  const projectPath = sanitizePath(input.path);
970
- switch (input.action) {
971
- case "show": {
972
- const result = await loadState(projectPath, input.stream, input.session);
973
- return resultToMcpResponse(result);
974
- }
975
- case "learn": {
976
- if (!input.learning) {
977
- return {
978
- content: [
979
- { type: "text", text: "Error: learning is required for learn action" }
980
- ],
981
- isError: true
982
- };
983
- }
984
- const result = await appendLearning(
985
- projectPath,
986
- input.learning,
987
- input.skillName,
988
- input.outcome,
989
- input.stream,
990
- input.session
991
- );
992
- if (!result.ok) return resultToMcpResponse(result);
993
- return resultToMcpResponse(Ok({ recorded: true }));
994
- }
995
- case "failure": {
996
- if (!input.description) {
997
- return {
998
- content: [
999
- { type: "text", text: "Error: description is required for failure action" }
1000
- ],
1001
- isError: true
1002
- };
1003
- }
1004
- if (!input.failureType) {
1005
- return {
1006
- content: [
1007
- {
1008
- type: "text",
1009
- text: "Error: failureType is required for failure action"
1010
- }
1011
- ],
1012
- isError: true
1013
- };
1014
- }
1015
- const result = await appendFailure(
1016
- projectPath,
1017
- input.description,
1018
- input.skillName ?? "unknown",
1019
- input.failureType,
1020
- input.stream,
1021
- input.session
1022
- );
1023
- if (!result.ok) return resultToMcpResponse(result);
1024
- return resultToMcpResponse(Ok({ recorded: true }));
1025
- }
1026
- case "archive": {
1027
- const result = await archiveFailures(projectPath, input.stream, input.session);
1028
- if (!result.ok) return resultToMcpResponse(result);
1029
- return resultToMcpResponse(Ok({ archived: true }));
1030
- }
1031
- case "reset": {
1032
- const result = await saveState(
1033
- projectPath,
1034
- { ...DEFAULT_STATE },
1035
- input.stream,
1036
- input.session
1037
- );
1038
- if (!result.ok) return resultToMcpResponse(result);
1039
- return resultToMcpResponse(Ok({ reset: true }));
1040
- }
1041
- case "gate": {
1042
- const result = await runMechanicalGate(projectPath);
1043
- return resultToMcpResponse(result);
1044
- }
1045
- case "save-handoff": {
1046
- if (!input.handoff) {
1047
- return {
1048
- content: [
1049
- { type: "text", text: "Error: handoff is required for save-handoff action" }
1050
- ],
1051
- isError: true
1052
- };
1053
- }
1054
- const { saveHandoff } = await import("./dist-2B363XUH.js");
1055
- const result = await saveHandoff(
1056
- projectPath,
1057
- input.handoff,
1058
- input.stream,
1059
- input.session
1060
- );
1061
- return resultToMcpResponse(result.ok ? Ok({ saved: true }) : result);
1062
- }
1063
- case "load-handoff": {
1064
- const { loadHandoff } = await import("./dist-2B363XUH.js");
1065
- const result = await loadHandoff(projectPath, input.stream, input.session);
1066
- return resultToMcpResponse(result);
1067
- }
1068
- default: {
1069
- return {
1070
- content: [{ type: "text", text: `Error: unknown action` }],
1071
- isError: true
1072
- };
1073
- }
1074
- }
1377
+ const handler = ACTION_HANDLERS[input.action];
1378
+ if (!handler) return mcpError("Error: unknown action");
1379
+ return await handler(projectPath, input);
1075
1380
  } catch (error) {
1076
- return {
1077
- content: [
1078
- {
1079
- type: "text",
1080
- text: `Error: ${error instanceof Error ? error.message : String(error)}`
1081
- }
1082
- ],
1083
- isError: true
1084
- };
1381
+ return mcpError(`Error: ${error instanceof Error ? error.message : String(error)}`);
1085
1382
  }
1086
1383
  }
1087
1384
  var listStreamsDefinition = {
@@ -1097,7 +1394,7 @@ var listStreamsDefinition = {
1097
1394
  };
1098
1395
  async function handleListStreams(input) {
1099
1396
  try {
1100
- const { listStreams, loadStreamIndex } = await import("./dist-2B363XUH.js");
1397
+ const { listStreams, loadStreamIndex } = await import("./dist-ALQDD67R.js");
1101
1398
  const projectPath = sanitizePath(input.path);
1102
1399
  const indexResult = await loadStreamIndex(projectPath);
1103
1400
  const streamsResult = await listStreams(projectPath);
@@ -1135,7 +1432,7 @@ var checkPhaseGateDefinition = {
1135
1432
  };
1136
1433
  async function handleCheckPhaseGate(input) {
1137
1434
  try {
1138
- const { runCheckPhaseGate } = await import("./check-phase-gate-2OFZ7OWW.js");
1435
+ const { runCheckPhaseGate } = await import("./check-phase-gate-ZOXVBDCN.js");
1139
1436
  const result = await runCheckPhaseGate({ cwd: sanitizePath(input.path) });
1140
1437
  if (result.ok) {
1141
1438
  return { content: [{ type: "text", text: JSON.stringify(result.value) }] };
@@ -1155,7 +1452,7 @@ async function handleCheckPhaseGate(input) {
1155
1452
  }
1156
1453
 
1157
1454
  // src/mcp/tools/cross-check.ts
1158
- import * as path10 from "path";
1455
+ import * as path12 from "path";
1159
1456
  var validateCrossCheckDefinition = {
1160
1457
  name: "validate_cross_check",
1161
1458
  description: "Validate plan-to-implementation coverage: checks that specs have plans and plans have implementations, detects staleness",
@@ -1191,15 +1488,15 @@ async function handleValidateCrossCheck(input) {
1191
1488
  };
1192
1489
  }
1193
1490
  try {
1194
- const { runCrossCheck } = await import("./validate-cross-check-STFHYMAZ.js");
1195
- const specsDir = path10.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");
1196
1493
  if (!specsDir.startsWith(projectPath)) {
1197
1494
  return {
1198
1495
  content: [{ type: "text", text: "Error: specsDir escapes project root" }],
1199
1496
  isError: true
1200
1497
  };
1201
1498
  }
1202
- const plansDir = path10.resolve(projectPath, input.plansDir ?? "docs/plans");
1499
+ const plansDir = path12.resolve(projectPath, input.plansDir ?? "docs/plans");
1203
1500
  if (!plansDir.startsWith(projectPath)) {
1204
1501
  return {
1205
1502
  content: [{ type: "text", text: "Error: plansDir escapes project root" }],
@@ -1230,14 +1527,14 @@ async function handleValidateCrossCheck(input) {
1230
1527
 
1231
1528
  // src/commands/generate-slash-commands.ts
1232
1529
  import { Command } from "commander";
1233
- import fs9 from "fs";
1234
- import path12 from "path";
1530
+ import fs12 from "fs";
1531
+ import path14 from "path";
1235
1532
  import os from "os";
1236
1533
  import readline from "readline";
1237
1534
 
1238
1535
  // src/slash-commands/normalize.ts
1239
- import fs8 from "fs";
1240
- import path11 from "path";
1536
+ import fs11 from "fs";
1537
+ import path13 from "path";
1241
1538
  import { parse as parse3 } from "yaml";
1242
1539
 
1243
1540
  // src/slash-commands/normalize-name.ts
@@ -1254,113 +1551,125 @@ function normalizeName(skillName) {
1254
1551
  }
1255
1552
 
1256
1553
  // src/slash-commands/normalize.ts
1554
+ function readSkillYaml(yamlPath) {
1555
+ let raw;
1556
+ try {
1557
+ raw = fs11.readFileSync(yamlPath, "utf-8");
1558
+ } catch {
1559
+ return null;
1560
+ }
1561
+ return SkillMetadataSchema.safeParse(parse3(raw));
1562
+ }
1563
+ function shouldSkipSkill(meta, platforms) {
1564
+ const matchesPlatform = platforms.some((p) => meta.platforms.includes(p));
1565
+ if (!matchesPlatform) return true;
1566
+ if (meta.tier === 3 || meta.internal) return true;
1567
+ return false;
1568
+ }
1569
+ function checkNameCollision(normalized, metaName, source, nameMap) {
1570
+ const existing = nameMap.get(normalized);
1571
+ if (!existing) {
1572
+ nameMap.set(normalized, { skillName: metaName, source });
1573
+ return "ok";
1574
+ }
1575
+ if (existing.source === source) {
1576
+ throw new Error(
1577
+ `Name collision: skills "${existing.skillName}" and "${metaName}" both normalize to "${normalized}"`
1578
+ );
1579
+ }
1580
+ return "skip";
1581
+ }
1582
+ function buildContextLines(meta) {
1583
+ const lines = [];
1584
+ if (meta.cognitive_mode) lines.push(`Cognitive mode: ${meta.cognitive_mode}`);
1585
+ if (meta.type) lines.push(`Type: ${meta.type}`);
1586
+ if (meta.state?.persistent) {
1587
+ const files = meta.state.files?.join(", ") ?? "";
1588
+ lines.push(`State: persistent${files ? ` (files: ${files})` : ""}`);
1589
+ }
1590
+ return lines;
1591
+ }
1592
+ function buildObjectiveLines(meta) {
1593
+ const lines = [meta.description];
1594
+ if (meta.phases && meta.phases.length > 0) {
1595
+ lines.push("", "Phases:");
1596
+ for (const phase of meta.phases) {
1597
+ const req = phase.required !== false ? "" : " (optional)";
1598
+ lines.push(`- ${phase.name}: ${phase.description}${req}`);
1599
+ }
1600
+ }
1601
+ return lines;
1602
+ }
1603
+ function buildProcessLines(meta) {
1604
+ if (meta.mcp?.tool) {
1605
+ return [
1606
+ `1. Try: invoke mcp__harness__${meta.mcp.tool} with skill: "${meta.name}"`,
1607
+ `2. If MCP unavailable: read SKILL.md and follow its workflow directly`,
1608
+ `3. Pass through any arguments provided by the user`
1609
+ ];
1610
+ }
1611
+ return [
1612
+ `1. Read SKILL.md and follow its workflow directly`,
1613
+ `2. Pass through any arguments provided by the user`
1614
+ ];
1615
+ }
1616
+ function buildSpec(meta, normalized, entry, skillsDir, source) {
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("\\", "/");
1621
+ const args = (meta.cli?.args ?? []).map((a) => ({
1622
+ name: a.name,
1623
+ description: a.description ?? "",
1624
+ required: a.required ?? false
1625
+ }));
1626
+ const tools = [...meta.tools ?? []];
1627
+ if (!tools.includes("Read")) tools.push("Read");
1628
+ const executionContextLines = [];
1629
+ if (skillMdContent) {
1630
+ executionContextLines.push(`@${skillMdRelative}`, `@${skillYamlRelative}`);
1631
+ }
1632
+ return {
1633
+ name: normalized,
1634
+ namespace: "harness",
1635
+ fullName: `harness:${normalized}`,
1636
+ description: meta.description,
1637
+ version: meta.version,
1638
+ ...meta.cognitive_mode ? { cognitiveMode: meta.cognitive_mode } : {},
1639
+ tools,
1640
+ args,
1641
+ skillYamlName: meta.name,
1642
+ sourceDir: entry.name,
1643
+ skillsBaseDir: skillsDir,
1644
+ source,
1645
+ prompt: {
1646
+ context: buildContextLines(meta).join("\n"),
1647
+ objective: buildObjectiveLines(meta).join("\n"),
1648
+ executionContext: executionContextLines.join("\n"),
1649
+ process: buildProcessLines(meta).join("\n")
1650
+ }
1651
+ };
1652
+ }
1257
1653
  function normalizeSkills(skillSources, platforms) {
1258
1654
  const specs = [];
1259
1655
  const nameMap = /* @__PURE__ */ new Map();
1260
1656
  for (const { dir: skillsDir, source } of skillSources) {
1261
- if (!fs8.existsSync(skillsDir)) continue;
1262
- const entries = fs8.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());
1263
1659
  for (const entry of entries) {
1264
- const yamlPath = path11.join(skillsDir, entry.name, "skill.yaml");
1265
- if (!fs8.existsSync(yamlPath)) continue;
1266
- let raw;
1267
- try {
1268
- raw = fs8.readFileSync(yamlPath, "utf-8");
1269
- } catch {
1270
- continue;
1271
- }
1272
- const parsed = parse3(raw);
1273
- const result = SkillMetadataSchema.safeParse(parsed);
1660
+ const yamlPath = path13.join(skillsDir, entry.name, "skill.yaml");
1661
+ if (!fs11.existsSync(yamlPath)) continue;
1662
+ const result = readSkillYaml(yamlPath);
1663
+ if (!result) continue;
1274
1664
  if (!result.success) {
1275
1665
  console.warn(`Skipping ${entry.name}: invalid skill.yaml`);
1276
1666
  continue;
1277
1667
  }
1278
1668
  const meta = result.data;
1279
- const matchesPlatform = platforms.some((p) => meta.platforms.includes(p));
1280
- if (!matchesPlatform) continue;
1281
- const tier = meta.tier;
1282
- const isInternal = meta.internal;
1283
- if (tier === 3 || isInternal) continue;
1669
+ if (shouldSkipSkill(meta, platforms)) continue;
1284
1670
  const normalized = normalizeName(meta.name);
1285
- const existing = nameMap.get(normalized);
1286
- if (existing) {
1287
- if (existing.source === source) {
1288
- throw new Error(
1289
- `Name collision: skills "${existing.skillName}" and "${meta.name}" both normalize to "${normalized}"`
1290
- );
1291
- }
1292
- continue;
1293
- }
1294
- nameMap.set(normalized, { skillName: meta.name, source });
1295
- const skillMdPath = path11.join(skillsDir, entry.name, "SKILL.md");
1296
- const skillMdContent = fs8.existsSync(skillMdPath) ? fs8.readFileSync(skillMdPath, "utf-8") : "";
1297
- const skillMdRelative = path11.relative(process.cwd(), path11.join(skillsDir, entry.name, "SKILL.md")).replaceAll("\\", "/");
1298
- const skillYamlRelative = path11.relative(process.cwd(), path11.join(skillsDir, entry.name, "skill.yaml")).replaceAll("\\", "/");
1299
- const args = (meta.cli?.args ?? []).map((a) => ({
1300
- name: a.name,
1301
- description: a.description ?? "",
1302
- required: a.required ?? false
1303
- }));
1304
- const tools = [...meta.tools ?? []];
1305
- if (!tools.includes("Read")) {
1306
- tools.push("Read");
1307
- }
1308
- const contextLines = [];
1309
- if (meta.cognitive_mode) {
1310
- contextLines.push(`Cognitive mode: ${meta.cognitive_mode}`);
1311
- }
1312
- if (meta.type) {
1313
- contextLines.push(`Type: ${meta.type}`);
1314
- }
1315
- if (meta.state?.persistent) {
1316
- const files = meta.state.files?.join(", ") ?? "";
1317
- contextLines.push(`State: persistent${files ? ` (files: ${files})` : ""}`);
1318
- }
1319
- const objectiveLines = [meta.description];
1320
- if (meta.phases && meta.phases.length > 0) {
1321
- objectiveLines.push("");
1322
- objectiveLines.push("Phases:");
1323
- for (const phase of meta.phases) {
1324
- const req = phase.required !== false ? "" : " (optional)";
1325
- objectiveLines.push(`- ${phase.name}: ${phase.description}${req}`);
1326
- }
1327
- }
1328
- const executionContextLines = [];
1329
- if (skillMdContent) {
1330
- executionContextLines.push(`@${skillMdRelative}`);
1331
- executionContextLines.push(`@${skillYamlRelative}`);
1332
- }
1333
- const processLines = [];
1334
- if (meta.mcp?.tool) {
1335
- processLines.push(
1336
- `1. Try: invoke mcp__harness__${meta.mcp.tool} with skill: "${meta.name}"`
1337
- );
1338
- processLines.push(`2. If MCP unavailable: read SKILL.md and follow its workflow directly`);
1339
- processLines.push(`3. Pass through any arguments provided by the user`);
1340
- } else {
1341
- processLines.push(`1. Read SKILL.md and follow its workflow directly`);
1342
- processLines.push(`2. Pass through any arguments provided by the user`);
1343
- }
1344
- specs.push({
1345
- name: normalized,
1346
- namespace: "harness",
1347
- fullName: `harness:${normalized}`,
1348
- description: meta.description,
1349
- version: meta.version,
1350
- ...meta.cognitive_mode ? { cognitiveMode: meta.cognitive_mode } : {},
1351
- tools,
1352
- args,
1353
- skillYamlName: meta.name,
1354
- sourceDir: entry.name,
1355
- skillsBaseDir: skillsDir,
1356
- source,
1357
- prompt: {
1358
- context: contextLines.join("\n"),
1359
- objective: objectiveLines.join("\n"),
1360
- executionContext: executionContextLines.join("\n"),
1361
- process: processLines.join("\n")
1362
- }
1363
- });
1671
+ if (checkNameCollision(normalized, meta.name, source, nameMap) === "skip") continue;
1672
+ specs.push(buildSpec(meta, normalized, entry, skillsDir, source));
1364
1673
  }
1365
1674
  }
1366
1675
  return specs;
@@ -1460,13 +1769,13 @@ function renderGemini(spec, skillMdContent, skillYamlContent) {
1460
1769
  // src/commands/generate-slash-commands.ts
1461
1770
  function resolveOutputDir(platform, opts) {
1462
1771
  if (opts.output) {
1463
- return path12.join(opts.output, "harness");
1772
+ return path14.join(opts.output, "harness");
1464
1773
  }
1465
1774
  if (opts.global) {
1466
1775
  const home = os.homedir();
1467
- return platform === "claude-code" ? path12.join(home, ".claude", "commands", "harness") : path12.join(home, ".gemini", "commands", "harness");
1776
+ return platform === "claude-code" ? path14.join(home, ".claude", "commands", "harness") : path14.join(home, ".gemini", "commands", "harness");
1468
1777
  }
1469
- return platform === "claude-code" ? path12.join("agents", "commands", "claude-code", "harness") : path12.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");
1470
1779
  }
1471
1780
  function fileExtension(platform) {
1472
1781
  return platform === "claude-code" ? ".md" : ".toml";
@@ -1481,26 +1790,29 @@ Remove ${files.length} orphaned command(s)? (y/N) `, (answer) => {
1481
1790
  });
1482
1791
  });
1483
1792
  }
1484
- function generateSlashCommands(opts) {
1485
- const skillSources = [];
1793
+ function resolveSkillSources(opts) {
1486
1794
  if (opts.skillsDir) {
1487
- skillSources.push({ dir: opts.skillsDir, source: "project" });
1488
- } else {
1489
- const projectDir = resolveProjectSkillsDir();
1490
- if (projectDir) {
1491
- skillSources.push({ dir: projectDir, source: "project" });
1492
- }
1493
- const communityDir = resolveCommunitySkillsDir();
1494
- if (fs9.existsSync(communityDir)) {
1495
- skillSources.push({ dir: communityDir, source: "community" });
1496
- }
1497
- if (opts.includeGlobal || skillSources.length === 0) {
1498
- const globalDir = resolveGlobalSkillsDir();
1499
- if (!projectDir || path12.resolve(globalDir) !== path12.resolve(projectDir)) {
1500
- skillSources.push({ dir: globalDir, source: "global" });
1501
- }
1795
+ return [{ dir: opts.skillsDir, source: "project" }];
1796
+ }
1797
+ const sources = [];
1798
+ const projectDir = resolveProjectSkillsDir();
1799
+ if (projectDir) {
1800
+ sources.push({ dir: projectDir, source: "project" });
1801
+ }
1802
+ const communityDir = resolveCommunitySkillsDir();
1803
+ if (fs12.existsSync(communityDir)) {
1804
+ sources.push({ dir: communityDir, source: "community" });
1805
+ }
1806
+ if (opts.includeGlobal || sources.length === 0) {
1807
+ const globalDir = resolveGlobalSkillsDir();
1808
+ if (!projectDir || path14.resolve(globalDir) !== path14.resolve(projectDir)) {
1809
+ sources.push({ dir: globalDir, source: "global" });
1502
1810
  }
1503
1811
  }
1812
+ return sources;
1813
+ }
1814
+ function generateSlashCommands(opts) {
1815
+ const skillSources = resolveSkillSources(opts);
1504
1816
  const specs = normalizeSkills(skillSources, opts.platforms);
1505
1817
  const results = [];
1506
1818
  for (const platform of opts.platforms) {
@@ -1518,7 +1830,7 @@ function generateSlashCommands(opts) {
1518
1830
  executionContext: spec.prompt.executionContext.split("\n").map((line) => {
1519
1831
  if (line.startsWith("@")) {
1520
1832
  const relPath = line.slice(1);
1521
- return `@${path12.resolve(relPath)}`;
1833
+ return `@${path14.resolve(relPath)}`;
1522
1834
  }
1523
1835
  return line;
1524
1836
  }).join("\n")
@@ -1526,10 +1838,10 @@ function generateSlashCommands(opts) {
1526
1838
  } : spec;
1527
1839
  rendered.set(filename, renderClaudeCode(renderSpec));
1528
1840
  } else {
1529
- const mdPath = path12.join(spec.skillsBaseDir, spec.sourceDir, "SKILL.md");
1530
- const yamlPath = path12.join(spec.skillsBaseDir, spec.sourceDir, "skill.yaml");
1531
- const mdContent = fs9.existsSync(mdPath) ? fs9.readFileSync(mdPath, "utf-8") : "";
1532
- const yamlContent = fs9.existsSync(yamlPath) ? fs9.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") : "";
1533
1845
  rendered.set(filename, renderGemini(spec, mdContent, yamlContent));
1534
1846
  }
1535
1847
  }
@@ -1555,9 +1867,9 @@ async function handleOrphanDeletion(results, opts) {
1555
1867
  const shouldDelete = opts.yes || await confirmDeletion(result.removed);
1556
1868
  if (shouldDelete) {
1557
1869
  for (const filename of result.removed) {
1558
- const filePath = path12.join(result.outputDir, filename);
1559
- if (fs9.existsSync(filePath)) {
1560
- fs9.unlinkSync(filePath);
1870
+ const filePath = path14.join(result.outputDir, filename);
1871
+ if (fs12.existsSync(filePath)) {
1872
+ fs12.unlinkSync(filePath);
1561
1873
  }
1562
1874
  }
1563
1875
  }
@@ -1683,7 +1995,7 @@ async function handleGenerateSlashCommands(input) {
1683
1995
  // src/mcp/resources/state.ts
1684
1996
  async function getStateResource(projectRoot) {
1685
1997
  try {
1686
- const { loadState, migrateToStreams } = await import("./dist-2B363XUH.js");
1998
+ const { loadState, migrateToStreams } = await import("./dist-ALQDD67R.js");
1687
1999
  await migrateToStreams(projectRoot);
1688
2000
  const result = await loadState(projectRoot);
1689
2001
  if (result.ok) {
@@ -1771,7 +2083,7 @@ async function handleQueryGraph(input) {
1771
2083
  const projectPath = sanitizePath(input.path);
1772
2084
  const store = await loadGraphStore(projectPath);
1773
2085
  if (!store) return graphNotFoundError();
1774
- const { ContextQL } = await import("./dist-HXHWB7SV.js");
2086
+ const { ContextQL } = await import("./dist-B26DFXMP.js");
1775
2087
  const cql = new ContextQL(store);
1776
2088
  const result = cql.execute({
1777
2089
  rootNodeIds: input.rootNodeIds,
@@ -1862,7 +2174,7 @@ async function handleSearchSimilar(input) {
1862
2174
  const projectPath = sanitizePath(input.path);
1863
2175
  const store = await loadGraphStore(projectPath);
1864
2176
  if (!store) return graphNotFoundError();
1865
- const { FusionLayer } = await import("./dist-HXHWB7SV.js");
2177
+ const { FusionLayer } = await import("./dist-B26DFXMP.js");
1866
2178
  const fusion = new FusionLayer(store);
1867
2179
  const results = fusion.search(input.query, input.topK ?? 10);
1868
2180
  if (input.mode === "summary") {
@@ -1917,7 +2229,7 @@ async function handleFindContextFor(input) {
1917
2229
  const projectPath = sanitizePath(input.path);
1918
2230
  const store = await loadGraphStore(projectPath);
1919
2231
  if (!store) return graphNotFoundError();
1920
- const { FusionLayer, ContextQL } = await import("./dist-HXHWB7SV.js");
2232
+ const { FusionLayer, ContextQL } = await import("./dist-B26DFXMP.js");
1921
2233
  const fusion = new FusionLayer(store);
1922
2234
  const cql = new ContextQL(store);
1923
2235
  const tokenBudget = input.tokenBudget ?? 4e3;
@@ -2013,7 +2325,7 @@ async function handleGetRelationships(input) {
2013
2325
  const projectPath = sanitizePath(input.path);
2014
2326
  const store = await loadGraphStore(projectPath);
2015
2327
  if (!store) return graphNotFoundError();
2016
- const { ContextQL } = await import("./dist-HXHWB7SV.js");
2328
+ const { ContextQL } = await import("./dist-B26DFXMP.js");
2017
2329
  const cql = new ContextQL(store);
2018
2330
  const direction = input.direction ?? "both";
2019
2331
  const bidirectional = direction === "both" || direction === "inbound";
@@ -2119,7 +2431,7 @@ async function handleGetImpact(input) {
2119
2431
  const projectPath = sanitizePath(input.path);
2120
2432
  const store = await loadGraphStore(projectPath);
2121
2433
  if (!store) return graphNotFoundError();
2122
- const { ContextQL } = await import("./dist-HXHWB7SV.js");
2434
+ const { ContextQL } = await import("./dist-B26DFXMP.js");
2123
2435
  let targetNodeId = input.nodeId;
2124
2436
  if (!targetNodeId && input.filePath) {
2125
2437
  const fileNodes = store.findNodes({ type: "file" });
@@ -2230,7 +2542,7 @@ async function handleGetImpact(input) {
2230
2542
  }
2231
2543
 
2232
2544
  // src/mcp/tools/graph/ingest-source.ts
2233
- import * as path13 from "path";
2545
+ import * as path15 from "path";
2234
2546
  var ingestSourceDefinition = {
2235
2547
  name: "ingest_source",
2236
2548
  description: "Ingest sources into the project knowledge graph. Supports code analysis, knowledge documents, git history, or all at once.",
@@ -2250,10 +2562,10 @@ var ingestSourceDefinition = {
2250
2562
  async function handleIngestSource(input) {
2251
2563
  try {
2252
2564
  const projectPath = sanitizePath(input.path);
2253
- const graphDir = path13.join(projectPath, ".harness", "graph");
2254
- const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-HXHWB7SV.js");
2255
- const fs12 = await import("fs/promises");
2256
- await fs12.mkdir(graphDir, { recursive: true });
2565
+ const graphDir = path15.join(projectPath, ".harness", "graph");
2566
+ const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-B26DFXMP.js");
2567
+ const fs15 = await import("fs/promises");
2568
+ await fs15.mkdir(graphDir, { recursive: true });
2257
2569
  const store = new GraphStore();
2258
2570
  await store.load(graphDir);
2259
2571
  const results = [];
@@ -2326,7 +2638,7 @@ async function handleDetectAnomalies(input) {
2326
2638
  const projectPath = sanitizePath(input.path);
2327
2639
  const store = await loadGraphStore(projectPath);
2328
2640
  if (!store) return graphNotFoundError();
2329
- const { GraphAnomalyAdapter } = await import("./dist-HXHWB7SV.js");
2641
+ const { GraphAnomalyAdapter } = await import("./dist-B26DFXMP.js");
2330
2642
  const adapter = new GraphAnomalyAdapter(store);
2331
2643
  const report = adapter.detect({
2332
2644
  ...input.threshold !== void 0 && { threshold: input.threshold },
@@ -2366,7 +2678,7 @@ async function handleAskGraph(input) {
2366
2678
  const projectPath = sanitizePath(input.path);
2367
2679
  const store = await loadGraphStore(projectPath);
2368
2680
  if (!store) return graphNotFoundError();
2369
- const { askGraph } = await import("./dist-HXHWB7SV.js");
2681
+ const { askGraph } = await import("./dist-B26DFXMP.js");
2370
2682
  const result = await askGraph(store, input.question);
2371
2683
  return {
2372
2684
  content: [{ type: "text", text: JSON.stringify(result) }]
@@ -2385,8 +2697,8 @@ async function handleAskGraph(input) {
2385
2697
  }
2386
2698
 
2387
2699
  // src/mcp/resources/graph.ts
2388
- import * as fs10 from "fs/promises";
2389
- import * as path14 from "path";
2700
+ import * as fs13 from "fs/promises";
2701
+ import * as path16 from "path";
2390
2702
  var MAX_ITEMS = 5e3;
2391
2703
  function formatStaleness(isoTimestamp) {
2392
2704
  const then = new Date(isoTimestamp).getTime();
@@ -2409,11 +2721,11 @@ async function getGraphResource(projectRoot) {
2409
2721
  message: "No knowledge graph found. Run harness scan to build one."
2410
2722
  });
2411
2723
  }
2412
- const graphDir = path14.join(projectRoot, ".harness", "graph");
2413
- const metadataPath = path14.join(graphDir, "metadata.json");
2724
+ const graphDir = path16.join(projectRoot, ".harness", "graph");
2725
+ const metadataPath = path16.join(graphDir, "metadata.json");
2414
2726
  let lastScanTimestamp = null;
2415
2727
  try {
2416
- const raw = JSON.parse(await fs10.readFile(metadataPath, "utf-8"));
2728
+ const raw = JSON.parse(await fs13.readFile(metadataPath, "utf-8"));
2417
2729
  lastScanTimestamp = raw.lastScanTimestamp ?? null;
2418
2730
  } catch {
2419
2731
  }
@@ -2502,7 +2814,7 @@ var generateAgentDefinitionsDefinition = {
2502
2814
  }
2503
2815
  };
2504
2816
  async function handleGenerateAgentDefinitions(input) {
2505
- const { generateAgentDefinitions } = await import("./generate-agent-definitions-64S3CLEZ.js");
2817
+ const { generateAgentDefinitions } = await import("./generate-agent-definitions-ZAE726AU.js");
2506
2818
  const platforms = input.platform === "all" || !input.platform ? ["claude-code", "gemini-cli"] : [input.platform];
2507
2819
  const results = generateAgentDefinitions({
2508
2820
  platforms: [...platforms],
@@ -2513,8 +2825,8 @@ async function handleGenerateAgentDefinitions(input) {
2513
2825
  }
2514
2826
 
2515
2827
  // src/mcp/tools/roadmap.ts
2516
- import * as fs11 from "fs";
2517
- import * as path15 from "path";
2828
+ import * as fs14 from "fs";
2829
+ import * as path17 from "path";
2518
2830
  var manageRoadmapDefinition = {
2519
2831
  name: "manage_roadmap",
2520
2832
  description: "Manage the project roadmap: show, add, update, remove, sync features, or query by filter. Reads and writes docs/roadmap.md.",
@@ -2569,21 +2881,21 @@ var manageRoadmapDefinition = {
2569
2881
  }
2570
2882
  };
2571
2883
  function roadmapPath(projectRoot) {
2572
- return path15.join(projectRoot, "docs", "roadmap.md");
2884
+ return path17.join(projectRoot, "docs", "roadmap.md");
2573
2885
  }
2574
2886
  function readRoadmapFile(projectRoot) {
2575
2887
  const filePath = roadmapPath(projectRoot);
2576
2888
  try {
2577
- return fs11.readFileSync(filePath, "utf-8");
2889
+ return fs14.readFileSync(filePath, "utf-8");
2578
2890
  } catch {
2579
2891
  return null;
2580
2892
  }
2581
2893
  }
2582
2894
  function writeRoadmapFile(projectRoot, content) {
2583
2895
  const filePath = roadmapPath(projectRoot);
2584
- const dir = path15.dirname(filePath);
2585
- fs11.mkdirSync(dir, { recursive: true });
2586
- fs11.writeFileSync(filePath, content, "utf-8");
2896
+ const dir = path17.dirname(filePath);
2897
+ fs14.mkdirSync(dir, { recursive: true });
2898
+ fs14.writeFileSync(filePath, content, "utf-8");
2587
2899
  }
2588
2900
  function roadmapNotFoundError() {
2589
2901
  return {
@@ -2596,7 +2908,7 @@ function roadmapNotFoundError() {
2596
2908
  isError: true
2597
2909
  };
2598
2910
  }
2599
- function handleShow(projectPath, input, deps) {
2911
+ function handleShow2(projectPath, input, deps) {
2600
2912
  const { parseRoadmap, Ok: Ok2 } = deps;
2601
2913
  const raw = readRoadmapFile(projectPath);
2602
2914
  if (raw === null) return roadmapNotFoundError();
@@ -2788,18 +3100,7 @@ function handleSync(projectPath, input, deps) {
2788
3100
  return resultToMcpResponse(Ok2({ changes: [], message: "Roadmap is up to date." }));
2789
3101
  }
2790
3102
  if (input.apply) {
2791
- for (const change of changes) {
2792
- for (const m of roadmap.milestones) {
2793
- const feature = m.features.find(
2794
- (f) => f.name.toLowerCase() === change.feature.toLowerCase()
2795
- );
2796
- if (feature) {
2797
- feature.status = change.to;
2798
- break;
2799
- }
2800
- }
2801
- }
2802
- roadmap.frontmatter.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
3103
+ deps.applySyncChanges(roadmap, changes);
2803
3104
  writeRoadmapFile(projectPath, serializeRoadmap(roadmap));
2804
3105
  return resultToMcpResponse(Ok2({ changes, applied: true, roadmap }));
2805
3106
  }
@@ -2807,13 +3108,13 @@ function handleSync(projectPath, input, deps) {
2807
3108
  }
2808
3109
  async function handleManageRoadmap(input) {
2809
3110
  try {
2810
- const { parseRoadmap, serializeRoadmap, syncRoadmap } = await import("./dist-2B363XUH.js");
2811
- const { Ok: Ok2 } = await import("./dist-D4RYGUZE.js");
3111
+ const { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges } = await import("./dist-ALQDD67R.js");
3112
+ const { Ok: Ok2 } = await import("./dist-USY2C5JL.js");
2812
3113
  const projectPath = sanitizePath(input.path);
2813
- const deps = { parseRoadmap, serializeRoadmap, syncRoadmap, Ok: Ok2 };
3114
+ const deps = { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges, Ok: Ok2 };
2814
3115
  switch (input.action) {
2815
3116
  case "show":
2816
- return handleShow(projectPath, input, deps);
3117
+ return handleShow2(projectPath, input, deps);
2817
3118
  case "add":
2818
3119
  return handleAdd(projectPath, input, deps);
2819
3120
  case "update":
@@ -3193,226 +3494,119 @@ var emitInteractionDefinition = {
3193
3494
  required: ["path", "type"]
3194
3495
  }
3195
3496
  };
3196
- async function handleEmitInteraction(input) {
3497
+ function formatZodErrors(issues) {
3498
+ return issues.map((i) => i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message).join("; ");
3499
+ }
3500
+ async function handleQuestion(validInput, projectPath, id) {
3501
+ if (!validInput.question)
3502
+ return mcpError("Error: question payload is required when type is question");
3503
+ const questionResult = InteractionQuestionWithOptionsSchema.safeParse(validInput.question);
3504
+ if (!questionResult.success)
3505
+ return mcpError(`Error: ${formatZodErrors(questionResult.error.issues)}`);
3506
+ const prompt = renderQuestion(questionResult.data);
3507
+ await recordInteraction(projectPath, id, "question", questionResult.data.text, validInput.stream);
3508
+ return { content: [{ type: "text", text: JSON.stringify({ id, prompt }) }] };
3509
+ }
3510
+ async function handleConfirmation(validInput, projectPath, id) {
3511
+ if (!validInput.confirmation)
3512
+ return mcpError("Error: confirmation payload is required when type is confirmation");
3513
+ const confirmResult = InteractionConfirmationSchema.safeParse(validInput.confirmation);
3514
+ if (!confirmResult.success)
3515
+ return mcpError(
3516
+ `Error: ${confirmResult.error.issues.map((i) => i.message).join("; ")}`
3517
+ );
3518
+ const prompt = renderConfirmation(confirmResult.data);
3519
+ await recordInteraction(
3520
+ projectPath,
3521
+ id,
3522
+ "confirmation",
3523
+ confirmResult.data.text,
3524
+ validInput.stream
3525
+ );
3526
+ return { content: [{ type: "text", text: JSON.stringify({ id, prompt }) }] };
3527
+ }
3528
+ async function handleTransition(validInput, projectPath, id) {
3529
+ if (!validInput.transition)
3530
+ return mcpError("Error: transition payload is required when type is transition");
3531
+ const transitionResult = InteractionTransitionSchema.safeParse(validInput.transition);
3532
+ if (!transitionResult.success)
3533
+ return mcpError(
3534
+ `Error: ${transitionResult.error.issues.map((i) => i.message).join("; ")}`
3535
+ );
3536
+ const transition = transitionResult.data;
3537
+ const prompt = renderTransition(transition);
3197
3538
  try {
3198
- const parseResult = EmitInteractionInputSchema.safeParse(input);
3199
- if (!parseResult.success) {
3200
- return {
3201
- content: [
3202
- {
3203
- type: "text",
3204
- text: `Error: ${parseResult.error.issues.map((i) => i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message).join("; ")}`
3205
- }
3206
- ],
3207
- isError: true
3208
- };
3209
- }
3539
+ const { saveHandoff } = await import("./dist-ALQDD67R.js");
3540
+ await saveHandoff(
3541
+ projectPath,
3542
+ {
3543
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3544
+ fromSkill: "emit_interaction",
3545
+ phase: transition.completedPhase,
3546
+ summary: transition.reason,
3547
+ completed: [transition.completedPhase],
3548
+ pending: [transition.suggestedNext],
3549
+ concerns: [],
3550
+ decisions: [],
3551
+ blockers: [],
3552
+ contextKeywords: []
3553
+ },
3554
+ validInput.stream,
3555
+ validInput.session
3556
+ );
3557
+ } catch {
3558
+ }
3559
+ await recordInteraction(
3560
+ projectPath,
3561
+ id,
3562
+ "transition",
3563
+ `${transition.completedPhase} -> ${transition.suggestedNext}`,
3564
+ validInput.stream
3565
+ );
3566
+ const responsePayload = { id, prompt, handoffWritten: true };
3567
+ if (!transition.requiresConfirmation) {
3568
+ responsePayload.autoTransition = true;
3569
+ responsePayload.nextAction = `Invoke harness-${transition.suggestedNext} skill now`;
3570
+ }
3571
+ return { content: [{ type: "text", text: JSON.stringify(responsePayload) }] };
3572
+ }
3573
+ async function handleBatch(validInput, projectPath, id) {
3574
+ if (!validInput.batch) return mcpError("Error: batch payload is required when type is batch");
3575
+ const batchResult = InteractionBatchSchema.safeParse(validInput.batch);
3576
+ if (!batchResult.success)
3577
+ return mcpError(
3578
+ `Error: ${batchResult.error.issues.map((i) => i.message).join("; ")}`
3579
+ );
3580
+ const prompt = renderBatch(batchResult.data);
3581
+ await recordInteraction(projectPath, id, "batch", batchResult.data.text, validInput.stream);
3582
+ return {
3583
+ content: [{ type: "text", text: JSON.stringify({ id, prompt, batchMode: true }) }]
3584
+ };
3585
+ }
3586
+ var INTERACTION_HANDLERS = {
3587
+ question: handleQuestion,
3588
+ confirmation: handleConfirmation,
3589
+ transition: handleTransition,
3590
+ batch: handleBatch
3591
+ };
3592
+ async function handleEmitInteraction(input) {
3593
+ try {
3594
+ const parseResult = EmitInteractionInputSchema.safeParse(input);
3595
+ if (!parseResult.success)
3596
+ return mcpError(`Error: ${formatZodErrors(parseResult.error.issues)}`);
3210
3597
  const validInput = parseResult.data;
3211
3598
  const projectPath = sanitizePath(validInput.path);
3212
3599
  const id = randomUUID();
3213
- switch (validInput.type) {
3214
- case "question": {
3215
- if (!validInput.question) {
3216
- return {
3217
- content: [
3218
- {
3219
- type: "text",
3220
- text: "Error: question payload is required when type is question"
3221
- }
3222
- ],
3223
- isError: true
3224
- };
3225
- }
3226
- const questionResult = InteractionQuestionWithOptionsSchema.safeParse(validInput.question);
3227
- if (!questionResult.success) {
3228
- return {
3229
- content: [
3230
- {
3231
- type: "text",
3232
- text: `Error: ${questionResult.error.issues.map((i) => i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message).join("; ")}`
3233
- }
3234
- ],
3235
- isError: true
3236
- };
3237
- }
3238
- const prompt = renderQuestion(questionResult.data);
3239
- await recordInteraction(
3240
- projectPath,
3241
- id,
3242
- "question",
3243
- questionResult.data.text,
3244
- validInput.stream
3245
- );
3246
- return {
3247
- content: [{ type: "text", text: JSON.stringify({ id, prompt }) }]
3248
- };
3249
- }
3250
- case "confirmation": {
3251
- if (!validInput.confirmation) {
3252
- return {
3253
- content: [
3254
- {
3255
- type: "text",
3256
- text: "Error: confirmation payload is required when type is confirmation"
3257
- }
3258
- ],
3259
- isError: true
3260
- };
3261
- }
3262
- const confirmResult = InteractionConfirmationSchema.safeParse(validInput.confirmation);
3263
- if (!confirmResult.success) {
3264
- return {
3265
- content: [
3266
- {
3267
- type: "text",
3268
- text: `Error: ${confirmResult.error.issues.map((i) => i.message).join("; ")}`
3269
- }
3270
- ],
3271
- isError: true
3272
- };
3273
- }
3274
- const prompt = renderConfirmation(confirmResult.data);
3275
- await recordInteraction(
3276
- projectPath,
3277
- id,
3278
- "confirmation",
3279
- confirmResult.data.text,
3280
- validInput.stream
3281
- );
3282
- return {
3283
- content: [{ type: "text", text: JSON.stringify({ id, prompt }) }]
3284
- };
3285
- }
3286
- case "transition": {
3287
- if (!validInput.transition) {
3288
- return {
3289
- content: [
3290
- {
3291
- type: "text",
3292
- text: "Error: transition payload is required when type is transition"
3293
- }
3294
- ],
3295
- isError: true
3296
- };
3297
- }
3298
- const transitionResult = InteractionTransitionSchema.safeParse(validInput.transition);
3299
- if (!transitionResult.success) {
3300
- return {
3301
- content: [
3302
- {
3303
- type: "text",
3304
- text: `Error: ${transitionResult.error.issues.map((i) => i.message).join("; ")}`
3305
- }
3306
- ],
3307
- isError: true
3308
- };
3309
- }
3310
- const transition = transitionResult.data;
3311
- const prompt = renderTransition(transition);
3312
- try {
3313
- const { saveHandoff } = await import("./dist-2B363XUH.js");
3314
- await saveHandoff(
3315
- projectPath,
3316
- {
3317
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
3318
- fromSkill: "emit_interaction",
3319
- phase: transition.completedPhase,
3320
- summary: transition.reason,
3321
- completed: [transition.completedPhase],
3322
- pending: [transition.suggestedNext],
3323
- concerns: [],
3324
- decisions: [],
3325
- blockers: [],
3326
- contextKeywords: []
3327
- },
3328
- validInput.stream,
3329
- validInput.session
3330
- );
3331
- } catch {
3332
- }
3333
- await recordInteraction(
3334
- projectPath,
3335
- id,
3336
- "transition",
3337
- `${transition.completedPhase} -> ${transition.suggestedNext}`,
3338
- validInput.stream
3339
- );
3340
- const responsePayload = { id, prompt, handoffWritten: true };
3341
- if (!transition.requiresConfirmation) {
3342
- responsePayload.autoTransition = true;
3343
- responsePayload.nextAction = `Invoke harness-${transition.suggestedNext} skill now`;
3344
- }
3345
- return {
3346
- content: [
3347
- {
3348
- type: "text",
3349
- text: JSON.stringify(responsePayload)
3350
- }
3351
- ]
3352
- };
3353
- }
3354
- case "batch": {
3355
- if (!validInput.batch) {
3356
- return {
3357
- content: [
3358
- {
3359
- type: "text",
3360
- text: "Error: batch payload is required when type is batch"
3361
- }
3362
- ],
3363
- isError: true
3364
- };
3365
- }
3366
- const batchResult = InteractionBatchSchema.safeParse(validInput.batch);
3367
- if (!batchResult.success) {
3368
- return {
3369
- content: [
3370
- {
3371
- type: "text",
3372
- text: `Error: ${batchResult.error.issues.map((i) => i.message).join("; ")}`
3373
- }
3374
- ],
3375
- isError: true
3376
- };
3377
- }
3378
- const prompt = renderBatch(batchResult.data);
3379
- await recordInteraction(projectPath, id, "batch", batchResult.data.text, validInput.stream);
3380
- return {
3381
- content: [
3382
- {
3383
- type: "text",
3384
- text: JSON.stringify({ id, prompt, batchMode: true })
3385
- }
3386
- ]
3387
- };
3388
- }
3389
- default: {
3390
- return {
3391
- content: [
3392
- {
3393
- type: "text",
3394
- text: `Error: unknown interaction type: ${String(validInput.type)}`
3395
- }
3396
- ],
3397
- isError: true
3398
- };
3399
- }
3400
- }
3600
+ const handler = INTERACTION_HANDLERS[validInput.type];
3601
+ if (!handler) return mcpError(`Error: unknown interaction type: ${String(validInput.type)}`);
3602
+ return await handler(validInput, projectPath, id);
3401
3603
  } catch (error) {
3402
- return {
3403
- content: [
3404
- {
3405
- type: "text",
3406
- text: `Error: ${error instanceof Error ? error.message : String(error)}`
3407
- }
3408
- ],
3409
- isError: true
3410
- };
3604
+ return mcpError(`Error: ${error instanceof Error ? error.message : String(error)}`);
3411
3605
  }
3412
3606
  }
3413
3607
  async function recordInteraction(projectPath, id, type, decision, stream) {
3414
3608
  try {
3415
- const { loadState, saveState } = await import("./dist-2B363XUH.js");
3609
+ const { loadState, saveState } = await import("./dist-ALQDD67R.js");
3416
3610
  const stateResult = await loadState(projectPath, stream);
3417
3611
  if (stateResult.ok) {
3418
3612
  const state = stateResult.value;
@@ -3430,7 +3624,7 @@ async function recordInteraction(projectPath, id, type, decision, stream) {
3430
3624
  // src/mcp/tools/gather-context.ts
3431
3625
  var gatherContextDefinition = {
3432
3626
  name: "gather_context",
3433
- description: "Assemble all working context an agent needs in a single call: state, learnings, handoff, graph context, and project validation. Runs constituents in parallel.",
3627
+ description: "Assemble all working context an agent needs in a single call: state, learnings, handoff, graph context, project validation, and session sections. Runs constituents in parallel.",
3434
3628
  inputSchema: {
3435
3629
  type: "object",
3436
3630
  properties: {
@@ -3451,10 +3645,14 @@ var gatherContextDefinition = {
3451
3645
  type: "array",
3452
3646
  items: {
3453
3647
  type: "string",
3454
- enum: ["state", "learnings", "handoff", "graph", "validation"]
3648
+ enum: ["state", "learnings", "handoff", "graph", "validation", "sessions", "events"]
3455
3649
  },
3456
3650
  description: "Which constituents to include (default: all)"
3457
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
+ },
3458
3656
  mode: {
3459
3657
  type: "string",
3460
3658
  enum: ["summary", "detailed"],
@@ -3467,6 +3665,11 @@ var gatherContextDefinition = {
3467
3665
  session: {
3468
3666
  type: "string",
3469
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.'
3470
3673
  }
3471
3674
  },
3472
3675
  required: ["path", "intent"]
@@ -3492,25 +3695,26 @@ async function handleGatherContext(input) {
3492
3695
  input.include ?? ["state", "learnings", "handoff", "graph", "validation"]
3493
3696
  );
3494
3697
  const errors = [];
3495
- const statePromise = includeSet.has("state") ? import("./dist-2B363XUH.js").then(
3698
+ const statePromise = includeSet.has("state") ? import("./dist-ALQDD67R.js").then(
3496
3699
  (core) => core.loadState(projectPath, void 0, input.session)
3497
3700
  ) : Promise.resolve(null);
3498
- const learningsPromise = includeSet.has("learnings") ? import("./dist-2B363XUH.js").then(
3701
+ const learningsPromise = includeSet.has("learnings") ? import("./dist-ALQDD67R.js").then(
3499
3702
  (core) => core.loadBudgetedLearnings(projectPath, {
3500
3703
  intent: input.intent,
3501
3704
  tokenBudget: input.learningsBudget ?? 1e3,
3502
3705
  ...input.skill !== void 0 && { skill: input.skill },
3503
- ...input.session !== void 0 && { session: input.session }
3706
+ ...input.session !== void 0 && { session: input.session },
3707
+ ...input.depth !== void 0 && { depth: input.depth }
3504
3708
  })
3505
3709
  ) : Promise.resolve(null);
3506
- const handoffPromise = includeSet.has("handoff") ? import("./dist-2B363XUH.js").then(
3710
+ const handoffPromise = includeSet.has("handoff") ? import("./dist-ALQDD67R.js").then(
3507
3711
  (core) => core.loadHandoff(projectPath, void 0, input.session)
3508
3712
  ) : Promise.resolve(null);
3509
3713
  const graphPromise = includeSet.has("graph") ? (async () => {
3510
- const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-GJZ4FN4Y.js");
3714
+ const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-2M2HXDQI.js");
3511
3715
  const store = await loadGraphStore2(projectPath);
3512
3716
  if (!store) return null;
3513
- const { FusionLayer, ContextQL } = await import("./dist-HXHWB7SV.js");
3717
+ const { FusionLayer, ContextQL } = await import("./dist-B26DFXMP.js");
3514
3718
  const fusion = new FusionLayer(store);
3515
3719
  const cql = new ContextQL(store);
3516
3720
  const tokenBudget = input.tokenBudget ?? 4e3;
@@ -3547,18 +3751,39 @@ async function handleGatherContext(input) {
3547
3751
  context: contextBlocks
3548
3752
  };
3549
3753
  })() : Promise.resolve(null);
3754
+ const sessionsPromise = includeSet.has("sessions") && input.session ? import("./dist-ALQDD67R.js").then(
3755
+ (core) => core.readSessionSections(projectPath, input.session)
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);
3550
3765
  const validationPromise = includeSet.has("validation") ? (async () => {
3551
- const { handleValidateProject: handleValidateProject2 } = await import("./validate-GCHZJIL7.js");
3766
+ const { handleValidateProject: handleValidateProject2 } = await import("./validate-KBYQAEWE.js");
3552
3767
  const result = await handleValidateProject2({ path: projectPath });
3553
3768
  const first = result.content[0];
3554
3769
  return first ? JSON.parse(first.text) : null;
3555
3770
  })() : Promise.resolve(null);
3556
- const [stateResult, learningsResult, handoffResult, graphResult, validationResult] = await Promise.allSettled([
3771
+ const [
3772
+ stateResult,
3773
+ learningsResult,
3774
+ handoffResult,
3775
+ graphResult,
3776
+ validationResult,
3777
+ sessionsResult,
3778
+ eventsResult
3779
+ ] = await Promise.allSettled([
3557
3780
  statePromise,
3558
3781
  learningsPromise,
3559
3782
  handoffPromise,
3560
3783
  graphPromise,
3561
- validationPromise
3784
+ validationPromise,
3785
+ sessionsPromise,
3786
+ eventsPromise
3562
3787
  ]);
3563
3788
  function extract(settled, name) {
3564
3789
  if (settled.status === "rejected") {
@@ -3572,6 +3797,8 @@ async function handleGatherContext(input) {
3572
3797
  const handoffRaw = extract(handoffResult, "handoff");
3573
3798
  const graphContextRaw = extract(graphResult, "graph");
3574
3799
  const validationRaw = extract(validationResult, "validation");
3800
+ const sessionsRaw = extract(sessionsResult, "sessions");
3801
+ const eventsTimeline = extract(eventsResult, "events");
3575
3802
  const state = stateRaw && typeof stateRaw === "object" && "ok" in stateRaw ? stateRaw.ok ? stateRaw.value : (() => {
3576
3803
  errors.push(`state: ${stateRaw.error.message}`);
3577
3804
  return null;
@@ -3588,6 +3815,12 @@ async function handleGatherContext(input) {
3588
3815
  })() : handoffRaw;
3589
3816
  const graphContext = graphContextRaw;
3590
3817
  const validation = validationRaw;
3818
+ const sessionSections = sessionsRaw && typeof sessionsRaw === "object" && "ok" in sessionsRaw ? sessionsRaw.ok ? sessionsRaw.value : (() => {
3819
+ errors.push(
3820
+ `sessions: ${sessionsRaw.error.message}`
3821
+ );
3822
+ return null;
3823
+ })() : sessionsRaw;
3591
3824
  const assembledIn = Date.now() - start;
3592
3825
  const mode = input.mode ?? "summary";
3593
3826
  const outputState = state ?? null;
@@ -3612,6 +3845,8 @@ async function handleGatherContext(input) {
3612
3845
  handoff: outputHandoff,
3613
3846
  graphContext: outputGraphContext,
3614
3847
  validation: outputValidation,
3848
+ sessionSections: sessionSections ?? null,
3849
+ events: eventsTimeline || null,
3615
3850
  meta: {
3616
3851
  assembledIn,
3617
3852
  graphAvailable: graphContext !== null,
@@ -3622,7 +3857,7 @@ async function handleGatherContext(input) {
3622
3857
  };
3623
3858
  if (input.session) {
3624
3859
  try {
3625
- const core = await import("./dist-2B363XUH.js");
3860
+ const core = await import("./dist-ALQDD67R.js");
3626
3861
  core.updateSessionIndex(
3627
3862
  projectPath,
3628
3863
  input.session,
@@ -3692,7 +3927,7 @@ async function handleAssessProject(input) {
3692
3927
  let validateResult = null;
3693
3928
  if (checksToRun.has("validate")) {
3694
3929
  try {
3695
- const { handleValidateProject: handleValidateProject2 } = await import("./validate-GCHZJIL7.js");
3930
+ const { handleValidateProject: handleValidateProject2 } = await import("./validate-KBYQAEWE.js");
3696
3931
  const result = await handleValidateProject2({ path: projectPath });
3697
3932
  const first = result.content[0];
3698
3933
  const parsed = first ? JSON.parse(first.text) : {};
@@ -3717,7 +3952,7 @@ async function handleAssessProject(input) {
3717
3952
  parallelChecks.push(
3718
3953
  (async () => {
3719
3954
  try {
3720
- const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-2R5Z4ZAF.js");
3955
+ const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-ZLIH5533.js");
3721
3956
  const result = await handleCheckDependencies2({ path: projectPath });
3722
3957
  const first = result.content[0];
3723
3958
  const parsed = first ? JSON.parse(first.text) : {};
@@ -3744,7 +3979,7 @@ async function handleAssessProject(input) {
3744
3979
  parallelChecks.push(
3745
3980
  (async () => {
3746
3981
  try {
3747
- const { handleCheckDocs: handleCheckDocs2 } = await import("./docs-FZOPM4GK.js");
3982
+ const { handleCheckDocs: handleCheckDocs2 } = await import("./docs-NRMQCOJ6.js");
3748
3983
  const result = await handleCheckDocs2({ path: projectPath, scope: "coverage" });
3749
3984
  const first = result.content[0];
3750
3985
  const parsed = first ? JSON.parse(first.text) : {};
@@ -3771,14 +4006,14 @@ async function handleAssessProject(input) {
3771
4006
  parallelChecks.push(
3772
4007
  (async () => {
3773
4008
  try {
3774
- const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-LVHJMFGH.js");
4009
+ const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-6AGX2ZUN.js");
3775
4010
  const result = await handleDetectEntropy2({ path: projectPath, type: "all" });
3776
4011
  const first = result.content[0];
3777
4012
  const parsed = first ? JSON.parse(first.text) : {};
3778
4013
  const issues = (parsed.drift?.staleReferences?.length ?? 0) + (parsed.drift?.missingTargets?.length ?? 0) + (parsed.deadCode?.unusedImports?.length ?? 0) + (parsed.deadCode?.unusedExports?.length ?? 0) + (parsed.patterns?.violations?.length ?? 0);
3779
4014
  return {
3780
4015
  name: "entropy",
3781
- passed: !result.isError && issues === 0,
4016
+ passed: !("isError" in result && result.isError) && issues === 0,
3782
4017
  issueCount: issues,
3783
4018
  ...issues > 0 ? { topIssue: "Entropy detected -- run detect_entropy for details" } : {},
3784
4019
  ...mode === "detailed" ? { detailed: parsed } : {}
@@ -3798,7 +4033,7 @@ async function handleAssessProject(input) {
3798
4033
  parallelChecks.push(
3799
4034
  (async () => {
3800
4035
  try {
3801
- const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-FWQZF2IZ.js");
4036
+ const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-2RPQEN62.js");
3802
4037
  const result = await handleRunSecurityScan2({ path: projectPath });
3803
4038
  const first = result.content[0];
3804
4039
  const parsed = first ? JSON.parse(first.text) : {};
@@ -3830,14 +4065,14 @@ async function handleAssessProject(input) {
3830
4065
  parallelChecks.push(
3831
4066
  (async () => {
3832
4067
  try {
3833
- const { handleCheckPerformance: handleCheckPerformance2 } = await import("./performance-ZTVSUANN.js");
4068
+ const { handleCheckPerformance: handleCheckPerformance2 } = await import("./performance-2D7G6NMJ.js");
3834
4069
  const result = await handleCheckPerformance2({ path: projectPath });
3835
4070
  const first = result.content[0];
3836
4071
  const parsed = first ? JSON.parse(first.text) : {};
3837
4072
  const issues = parsed.violations?.length ?? parsed.issues?.length ?? 0;
3838
4073
  return {
3839
4074
  name: "perf",
3840
- passed: !result.isError && issues === 0,
4075
+ passed: !("isError" in result && result.isError) && issues === 0,
3841
4076
  issueCount: issues,
3842
4077
  ...issues > 0 ? { topIssue: "Performance issues detected" } : {},
3843
4078
  ...mode === "detailed" ? { detailed: parsed } : {}
@@ -4004,81 +4239,8 @@ async function handleReviewChanges(input) {
4004
4239
  downgraded = true;
4005
4240
  }
4006
4241
  try {
4007
- if (effectiveDepth === "quick") {
4008
- const { handleAnalyzeDiff: handleAnalyzeDiff2 } = await import("./feedback-IHLVLMRD.js");
4009
- const result2 = await handleAnalyzeDiff2({ diff, path: projectPath });
4010
- const firstContent = result2.content[0];
4011
- if (!firstContent) throw new Error("Empty analyze_diff response");
4012
- const parsed2 = JSON.parse(firstContent.text);
4013
- return {
4014
- content: [
4015
- {
4016
- type: "text",
4017
- text: JSON.stringify({
4018
- depth: "quick",
4019
- downgraded,
4020
- findings: parsed2.findings ?? parsed2.warnings ?? [],
4021
- fileCount: parsed2.summary?.filesChanged ?? parsed2.files?.length ?? 0,
4022
- lineCount: diffLines,
4023
- ...result2.isError ? { error: parsed2 } : {}
4024
- })
4025
- }
4026
- ]
4027
- };
4028
- }
4029
- if (effectiveDepth === "standard") {
4030
- const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-IHLVLMRD.js");
4031
- const [diffResult, reviewResult] = await Promise.all([
4032
- handleAnalyzeDiff2({ diff, path: projectPath }),
4033
- handleCreateSelfReview2({ path: projectPath, diff })
4034
- ]);
4035
- const diffContent = diffResult.content[0];
4036
- const reviewContent = reviewResult.content[0];
4037
- if (!diffContent || !reviewContent) throw new Error("Empty review response");
4038
- const diffParsed = JSON.parse(diffContent.text);
4039
- const reviewParsed = JSON.parse(reviewContent.text);
4040
- const findings = [
4041
- ...diffParsed.findings ?? diffParsed.warnings ?? [],
4042
- ...reviewParsed.findings ?? reviewParsed.items ?? []
4043
- ];
4044
- return {
4045
- content: [
4046
- {
4047
- type: "text",
4048
- text: JSON.stringify({
4049
- depth: "standard",
4050
- downgraded,
4051
- findings,
4052
- diffAnalysis: diffParsed,
4053
- selfReview: reviewParsed,
4054
- fileCount: diffParsed.summary?.filesChanged ?? diffParsed.files?.length ?? 0,
4055
- lineCount: diffLines
4056
- })
4057
- }
4058
- ]
4059
- };
4060
- }
4061
- const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-76JHKGSV.js");
4062
- const result = await handleRunCodeReview2({ path: projectPath, diff });
4063
- const deepContent = result.content[0];
4064
- if (!deepContent) throw new Error("Empty code review response");
4065
- const parsed = JSON.parse(deepContent.text);
4066
- return {
4067
- content: [
4068
- {
4069
- type: "text",
4070
- text: JSON.stringify({
4071
- depth: "deep",
4072
- downgraded: false,
4073
- findings: parsed.findings ?? [],
4074
- assessment: parsed.assessment,
4075
- findingCount: parsed.findingCount,
4076
- lineCount: diffLines,
4077
- pipeline: parsed
4078
- })
4079
- }
4080
- ]
4081
- };
4242
+ const reviewFn = DEPTH_HANDLERS[effectiveDepth];
4243
+ return await reviewFn(projectPath, diff, diffLines, downgraded);
4082
4244
  } catch (error) {
4083
4245
  return {
4084
4246
  content: [
@@ -4091,6 +4253,97 @@ async function handleReviewChanges(input) {
4091
4253
  };
4092
4254
  }
4093
4255
  }
4256
+ async function runQuickReview(projectPath, diff, diffLines, downgraded) {
4257
+ const { handleAnalyzeDiff: handleAnalyzeDiff2 } = await import("./feedback-MY4QZIFD.js");
4258
+ const result = await handleAnalyzeDiff2({ diff, path: projectPath });
4259
+ const firstContent = result.content[0];
4260
+ if (!firstContent) throw new Error("Empty analyze_diff response");
4261
+ const parsed = JSON.parse(firstContent.text);
4262
+ return {
4263
+ content: [
4264
+ {
4265
+ type: "text",
4266
+ text: JSON.stringify({
4267
+ depth: "quick",
4268
+ downgraded,
4269
+ findings: parsed.findings ?? parsed.warnings ?? [],
4270
+ fileCount: parsed.summary?.filesChanged ?? parsed.files?.length ?? 0,
4271
+ lineCount: diffLines,
4272
+ ...result.isError ? { error: parsed } : {}
4273
+ })
4274
+ }
4275
+ ]
4276
+ };
4277
+ }
4278
+ function extractFindings(parsed, primaryKey, fallbackKey) {
4279
+ return parsed[primaryKey] ?? parsed[fallbackKey] ?? [];
4280
+ }
4281
+ function extractFileCount(diffParsed) {
4282
+ const summary = diffParsed.summary;
4283
+ if (summary?.filesChanged !== void 0) return summary.filesChanged;
4284
+ const files = diffParsed.files;
4285
+ return files?.length ?? 0;
4286
+ }
4287
+ async function runStandardReview(projectPath, diff, diffLines, downgraded) {
4288
+ const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-MY4QZIFD.js");
4289
+ const [diffResult, reviewResult] = await Promise.all([
4290
+ handleAnalyzeDiff2({ diff, path: projectPath }),
4291
+ handleCreateSelfReview2({ path: projectPath, diff })
4292
+ ]);
4293
+ const diffContent = diffResult.content[0];
4294
+ const reviewContent = reviewResult.content[0];
4295
+ if (!diffContent || !reviewContent) throw new Error("Empty review response");
4296
+ const diffParsed = JSON.parse(diffContent.text);
4297
+ const reviewParsed = JSON.parse(reviewContent.text);
4298
+ const findings = [
4299
+ ...extractFindings(diffParsed, "findings", "warnings"),
4300
+ ...extractFindings(reviewParsed, "findings", "items")
4301
+ ];
4302
+ return {
4303
+ content: [
4304
+ {
4305
+ type: "text",
4306
+ text: JSON.stringify({
4307
+ depth: "standard",
4308
+ downgraded,
4309
+ findings,
4310
+ diffAnalysis: diffParsed,
4311
+ selfReview: reviewParsed,
4312
+ fileCount: extractFileCount(diffParsed),
4313
+ lineCount: diffLines
4314
+ })
4315
+ }
4316
+ ]
4317
+ };
4318
+ }
4319
+ async function runDeepReview(projectPath, diff, diffLines, _downgraded) {
4320
+ const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-RAQ55ISU.js");
4321
+ const result = await handleRunCodeReview2({ path: projectPath, diff });
4322
+ const deepContent = result.content[0];
4323
+ if (!deepContent) throw new Error("Empty code review response");
4324
+ const parsed = JSON.parse(deepContent.text);
4325
+ return {
4326
+ content: [
4327
+ {
4328
+ type: "text",
4329
+ text: JSON.stringify({
4330
+ depth: "deep",
4331
+ downgraded: false,
4332
+ findings: parsed.findings ?? [],
4333
+ assessment: parsed.assessment,
4334
+ findingCount: parsed.findingCount,
4335
+ lineCount: diffLines,
4336
+ pipeline: parsed
4337
+ })
4338
+ }
4339
+ ]
4340
+ };
4341
+ }
4342
+ var DEPTH_HANDLERS = {
4343
+ quick: runQuickReview,
4344
+ standard: runStandardReview,
4345
+ deep: runDeepReview
4346
+ };
4094
4347
 
4095
4348
  // src/mcp/tools/task-independence.ts
4096
4349
  var checkTaskIndependenceDefinition = {
@@ -4135,7 +4388,7 @@ async function handleCheckTaskIndependence(input) {
4135
4388
  try {
4136
4389
  const projectPath = sanitizePath(input.path);
4137
4390
  const store = await loadGraphStore(projectPath);
4138
- const { TaskIndependenceAnalyzer } = await import("./dist-HXHWB7SV.js");
4391
+ const { TaskIndependenceAnalyzer } = await import("./dist-B26DFXMP.js");
4139
4392
  const analyzer = new TaskIndependenceAnalyzer(store ?? void 0);
4140
4393
  const result = analyzer.analyze({
4141
4394
  tasks: input.tasks,
@@ -4223,7 +4476,7 @@ async function handlePredictConflicts(input) {
4223
4476
  try {
4224
4477
  const projectPath = sanitizePath(input.path);
4225
4478
  const store = await loadGraphStore(projectPath);
4226
- const { ConflictPredictor } = await import("./dist-HXHWB7SV.js");
4479
+ const { ConflictPredictor } = await import("./dist-B26DFXMP.js");
4227
4480
  const predictor = new ConflictPredictor(store ?? void 0);
4228
4481
  const result = predictor.predict({
4229
4482
  tasks: input.tasks,
@@ -4329,7 +4582,7 @@ async function handleDetectStaleConstraints(input) {
4329
4582
  isError: true
4330
4583
  };
4331
4584
  }
4332
- const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-GJZ4FN4Y.js");
4585
+ const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-2M2HXDQI.js");
4333
4586
  const store = await loadGraphStore2(projectPath);
4334
4587
  if (!store) {
4335
4588
  return {
@@ -4350,7 +4603,7 @@ async function handleDetectStaleConstraints(input) {
4350
4603
  ]
4351
4604
  };
4352
4605
  }
4353
- const { detectStaleConstraints } = await import("./dist-2B363XUH.js");
4606
+ const { detectStaleConstraints } = await import("./dist-ALQDD67R.js");
4354
4607
  const result = detectStaleConstraints(
4355
4608
  store,
4356
4609
  windowDays,
@@ -4447,6 +4700,226 @@ async function handleSearchSkills(input) {
4447
4700
  };
4448
4701
  }
4449
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
+
4450
4923
  // src/mcp/server.ts
4451
4924
  var TOOL_DEFINITIONS = [
4452
4925
  validateToolDefinition,
@@ -4494,7 +4967,10 @@ var TOOL_DEFINITIONS = [
4494
4967
  checkTaskIndependenceDefinition,
4495
4968
  predictConflictsDefinition,
4496
4969
  detectStaleConstraintsDefinition,
4497
- searchSkillsDefinition
4970
+ searchSkillsDefinition,
4971
+ codeOutlineDefinition,
4972
+ codeSearchDefinition,
4973
+ codeUnfoldDefinition
4498
4974
  ];
4499
4975
  var TOOL_HANDLERS = {
4500
4976
  validate_project: handleValidateProject,
@@ -4542,7 +5018,10 @@ var TOOL_HANDLERS = {
4542
5018
  check_task_independence: handleCheckTaskIndependence,
4543
5019
  predict_conflicts: handlePredictConflicts,
4544
5020
  detect_stale_constraints: handleDetectStaleConstraints,
4545
- search_skills: handleSearchSkills
5021
+ search_skills: handleSearchSkills,
5022
+ code_outline: handleCodeOutline,
5023
+ code_search: handleCodeSearch,
5024
+ code_unfold: handleCodeUnfold
4546
5025
  };
4547
5026
  var RESOURCE_DEFINITIONS = [
4548
5027
  {
@@ -4607,6 +5086,45 @@ var RESOURCE_HANDLERS = {
4607
5086
  function getToolDefinitions() {
4608
5087
  return TOOL_DEFINITIONS;
4609
5088
  }
5089
+ function readConfigInterval(resolvedRoot) {
5090
+ try {
5091
+ const configResult = resolveProjectConfig(resolvedRoot);
5092
+ if (configResult.ok) {
5093
+ const raw = configResult.value.updateCheckInterval;
5094
+ if (typeof raw === "number" && Number.isInteger(raw) && raw >= 0) {
5095
+ return raw;
5096
+ }
5097
+ }
5098
+ } catch {
5099
+ }
5100
+ return void 0;
5101
+ }
5102
+ async function appendUpdateNotification(result, resolvedRoot) {
5103
+ try {
5104
+ const {
5105
+ getUpdateNotification,
5106
+ isUpdateCheckEnabled,
5107
+ shouldRunCheck,
5108
+ readCheckState,
5109
+ spawnBackgroundCheck
5110
+ } = await import("./dist-ALQDD67R.js");
5111
+ const { CLI_VERSION } = await import("./version-KFFPOQAX.js");
5112
+ const configInterval = readConfigInterval(resolvedRoot);
5113
+ const DEFAULT_INTERVAL = 864e5;
5114
+ if (!isUpdateCheckEnabled(configInterval)) return;
5115
+ const state = readCheckState();
5116
+ if (shouldRunCheck(state, configInterval ?? DEFAULT_INTERVAL)) {
5117
+ spawnBackgroundCheck(CLI_VERSION);
5118
+ }
5119
+ const notification = getUpdateNotification(CLI_VERSION);
5120
+ if (notification) {
5121
+ result.content.push({ type: "text", text: `
5122
+ ---
5123
+ ${notification}` });
5124
+ }
5125
+ } catch {
5126
+ }
5127
+ }
4610
5128
  function createHarnessServer(projectRoot) {
4611
5129
  const resolvedRoot = projectRoot ?? process.cwd();
4612
5130
  let sessionChecked = false;
@@ -4626,42 +5144,7 @@ function createHarnessServer(projectRoot) {
4626
5144
  const result = await handler(args ?? {});
4627
5145
  if (!sessionChecked) {
4628
5146
  sessionChecked = true;
4629
- try {
4630
- const {
4631
- getUpdateNotification,
4632
- isUpdateCheckEnabled,
4633
- shouldRunCheck,
4634
- readCheckState,
4635
- spawnBackgroundCheck
4636
- } = await import("./dist-2B363XUH.js");
4637
- const { CLI_VERSION: version } = await import("./version-KFFPOQAX.js");
4638
- let CLI_VERSION = version;
4639
- let configInterval;
4640
- try {
4641
- const configResult = resolveProjectConfig(resolvedRoot);
4642
- if (configResult.ok) {
4643
- const raw = configResult.value.updateCheckInterval;
4644
- if (typeof raw === "number" && Number.isInteger(raw) && raw >= 0) {
4645
- configInterval = raw;
4646
- }
4647
- }
4648
- } catch {
4649
- }
4650
- const DEFAULT_INTERVAL = 864e5;
4651
- if (isUpdateCheckEnabled(configInterval)) {
4652
- const state = readCheckState();
4653
- if (shouldRunCheck(state, configInterval ?? DEFAULT_INTERVAL)) {
4654
- spawnBackgroundCheck(CLI_VERSION);
4655
- }
4656
- const notification = getUpdateNotification(CLI_VERSION);
4657
- if (notification) {
4658
- result.content.push({ type: "text", text: `
4659
- ---
4660
- ${notification}` });
4661
- }
4662
- }
4663
- } catch {
4664
- }
5147
+ await appendUpdateNotification(result, resolvedRoot);
4665
5148
  }
4666
5149
  return result;
4667
5150
  });
@@ -4690,6 +5173,8 @@ async function startServer() {
4690
5173
  }
4691
5174
 
4692
5175
  export {
5176
+ persistToolingConfig,
5177
+ appendFrameworkAgents,
4693
5178
  generateSlashCommands,
4694
5179
  handleOrphanDeletion,
4695
5180
  createGenerateSlashCommandsCommand,