@harness-engineering/cli 1.13.1 → 1.14.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.
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +39 -0
- package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +44 -0
- package/dist/agents/skills/claude-code/harness-execution/SKILL.md +44 -0
- package/dist/agents/skills/claude-code/harness-planning/SKILL.md +39 -0
- package/dist/agents/skills/claude-code/harness-release-readiness/SKILL.md +3 -3
- package/dist/agents/skills/claude-code/harness-verification/SKILL.md +35 -0
- package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +11 -3
- package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +39 -0
- package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +44 -0
- package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +44 -0
- package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +39 -0
- package/dist/agents/skills/gemini-cli/harness-release-readiness/SKILL.md +3 -3
- package/dist/agents/skills/gemini-cli/harness-verification/SKILL.md +35 -0
- package/dist/agents/skills/gemini-cli/initialize-harness-project/SKILL.md +11 -3
- package/dist/agents-md-YTYQDA3P.js +8 -0
- package/dist/{architecture-2R5Z4ZAF.js → architecture-JQZYM4US.js} +4 -4
- package/dist/bin/harness-mcp.js +14 -14
- package/dist/bin/harness.js +24 -24
- package/dist/{check-phase-gate-2OFZ7OWW.js → check-phase-gate-L3RADYWO.js} +4 -4
- package/dist/{chunk-QY4T6YAZ.js → chunk-3C2MLBPJ.js} +4 -4
- package/dist/{chunk-UAX4I5ZE.js → chunk-6KTUUFRN.js} +2 -2
- package/dist/{chunk-ND6PNADU.js → chunk-7IP4JIFL.js} +9 -9
- package/dist/{chunk-C2ERUR3L.js → chunk-7MJAPE3Z.js} +165 -49
- package/dist/{chunk-PQ5YK4AY.js → chunk-ABQHQ6I5.js} +1583 -1169
- package/dist/{chunk-QPEH2QPG.js → chunk-DBSOCI3G.js} +53 -54
- package/dist/{chunk-MHBMTPW7.js → chunk-ERS5EVUZ.js} +9 -0
- package/dist/{chunk-JSTQ3AWB.js → chunk-FIAPHX37.js} +1 -1
- package/dist/{chunk-IMFVFNJE.js → chunk-FTMXDOR6.js} +1 -1
- package/dist/{chunk-72GHBOL2.js → chunk-GZKSBLQL.js} +1 -1
- package/dist/{chunk-K6XAPGML.js → chunk-H7Y5CKTM.js} +1 -1
- package/dist/{chunk-4ZMOCPYO.js → chunk-NLVUVUGD.js} +1 -1
- package/dist/{chunk-Z77YQRQT.js → chunk-O5OJVPL6.js} +16 -5
- package/dist/{chunk-NKDM3FMH.js → chunk-OD3S2NHN.js} +1 -1
- package/dist/{chunk-65FRIL4D.js → chunk-OSXBPAMK.js} +1 -1
- package/dist/{chunk-DZS7CJKL.js → chunk-OXLLOSSR.js} +45 -47
- package/dist/{chunk-TS3XWPW5.js → chunk-RCWZBSK5.js} +1 -1
- package/dist/{chunk-NOPU4RZ4.js → chunk-S2FXOWOR.js} +3 -3
- package/dist/{chunk-VUCPTQ6G.js → chunk-SD3SQOZ2.js} +1 -1
- package/dist/{chunk-IM32EEDM.js → chunk-TPOTOBR7.js} +9 -9
- package/dist/{chunk-SSKDAOX5.js → chunk-XKECDXJS.js} +436 -340
- package/dist/{chunk-TKJZKICB.js → chunk-YPYGXRDR.js} +7 -7
- package/dist/{chunk-Q6AB7W5Z.js → chunk-YQ6KC6TE.js} +1 -1
- package/dist/{chunk-NERR4TAO.js → chunk-YZD2MRNQ.js} +972 -747
- package/dist/ci-workflow-EQZFVX3P.js +8 -0
- package/dist/{dist-HXHWB7SV.js → dist-B26DFXMP.js} +571 -478
- package/dist/{dist-L7LAAQAS.js → dist-DZ63LLUD.js} +1 -1
- package/dist/{dist-2B363XUH.js → dist-HWXF2C3R.js} +18 -2
- package/dist/{dist-D4RYGUZE.js → dist-USY2C5JL.js} +3 -1
- package/dist/{docs-FZOPM4GK.js → docs-7ECGYMAV.js} +4 -4
- package/dist/engine-EG4EH4IX.js +8 -0
- package/dist/{entropy-LVHJMFGH.js → entropy-5USWKLVS.js} +3 -3
- package/dist/{feedback-IHLVLMRD.js → feedback-UTBXZZHF.js} +1 -1
- package/dist/{generate-agent-definitions-64S3CLEZ.js → generate-agent-definitions-3PM5EU7V.js} +4 -4
- package/dist/{graph-loader-GJZ4FN4Y.js → graph-loader-2M2HXDQI.js} +1 -1
- package/dist/index.d.ts +148 -9
- package/dist/index.js +24 -24
- package/dist/loader-ZPALXIVR.js +10 -0
- package/dist/{mcp-JQUI7BVZ.js → mcp-362EZHF4.js} +14 -14
- package/dist/{performance-ZTVSUANN.js → performance-OQAFMJUD.js} +3 -3
- package/dist/{review-pipeline-76JHKGSV.js → review-pipeline-C4GCFVGP.js} +1 -1
- package/dist/runtime-7YLVK453.js +9 -0
- package/dist/{security-FWQZF2IZ.js → security-PZOX7AQS.js} +1 -1
- package/dist/templates/axum/Cargo.toml.hbs +8 -0
- package/dist/templates/axum/src/main.rs +12 -0
- package/dist/templates/axum/template.json +16 -0
- package/dist/templates/django/manage.py.hbs +19 -0
- package/dist/templates/django/requirements.txt.hbs +1 -0
- package/dist/templates/django/src/settings.py.hbs +44 -0
- package/dist/templates/django/src/urls.py +6 -0
- package/dist/templates/django/src/wsgi.py.hbs +9 -0
- package/dist/templates/django/template.json +21 -0
- package/dist/templates/express/package.json.hbs +15 -0
- package/dist/templates/express/src/app.ts +12 -0
- package/dist/templates/express/src/lib/.gitkeep +0 -0
- package/dist/templates/express/template.json +16 -0
- package/dist/templates/fastapi/requirements.txt.hbs +2 -0
- package/dist/templates/fastapi/src/main.py +8 -0
- package/dist/templates/fastapi/template.json +20 -0
- package/dist/templates/gin/go.mod.hbs +5 -0
- package/dist/templates/gin/main.go +15 -0
- package/dist/templates/gin/template.json +19 -0
- package/dist/templates/go-base/.golangci.yml +16 -0
- package/dist/templates/go-base/AGENTS.md.hbs +35 -0
- package/dist/templates/go-base/go.mod.hbs +3 -0
- package/dist/templates/go-base/harness.config.json.hbs +17 -0
- package/dist/templates/go-base/main.go +7 -0
- package/dist/templates/go-base/template.json +14 -0
- package/dist/templates/java-base/AGENTS.md.hbs +35 -0
- package/dist/templates/java-base/checkstyle.xml +20 -0
- package/dist/templates/java-base/harness.config.json.hbs +16 -0
- package/dist/templates/java-base/pom.xml.hbs +39 -0
- package/dist/templates/java-base/src/main/java/App.java.hbs +5 -0
- package/dist/templates/java-base/template.json +13 -0
- package/dist/templates/nestjs/nest-cli.json +5 -0
- package/dist/templates/nestjs/package.json.hbs +18 -0
- package/dist/templates/nestjs/src/app.module.ts +8 -0
- package/dist/templates/nestjs/src/lib/.gitkeep +0 -0
- package/dist/templates/nestjs/src/main.ts +11 -0
- package/dist/templates/nestjs/template.json +16 -0
- package/dist/templates/nextjs/template.json +15 -1
- package/dist/templates/python-base/.python-version +1 -0
- package/dist/templates/python-base/AGENTS.md.hbs +32 -0
- package/dist/templates/python-base/harness.config.json.hbs +16 -0
- package/dist/templates/python-base/pyproject.toml.hbs +18 -0
- package/dist/templates/python-base/ruff.toml +5 -0
- package/dist/templates/python-base/src/__init__.py +0 -0
- package/dist/templates/python-base/template.json +13 -0
- package/dist/templates/react-vite/index.html +12 -0
- package/dist/templates/react-vite/package.json.hbs +18 -0
- package/dist/templates/react-vite/src/App.tsx +7 -0
- package/dist/templates/react-vite/src/lib/.gitkeep +0 -0
- package/dist/templates/react-vite/src/main.tsx +9 -0
- package/dist/templates/react-vite/template.json +19 -0
- package/dist/templates/react-vite/vite.config.ts +6 -0
- package/dist/templates/rust-base/AGENTS.md.hbs +35 -0
- package/dist/templates/rust-base/Cargo.toml.hbs +6 -0
- package/dist/templates/rust-base/clippy.toml +2 -0
- package/dist/templates/rust-base/harness.config.json.hbs +17 -0
- package/dist/templates/rust-base/src/main.rs +3 -0
- package/dist/templates/rust-base/template.json +14 -0
- package/dist/templates/spring-boot/pom.xml.hbs +50 -0
- package/dist/templates/spring-boot/src/main/java/Application.java.hbs +19 -0
- package/dist/templates/spring-boot/template.json +15 -0
- package/dist/templates/vue/index.html +12 -0
- package/dist/templates/vue/package.json.hbs +16 -0
- package/dist/templates/vue/src/App.vue +7 -0
- package/dist/templates/vue/src/lib/.gitkeep +0 -0
- package/dist/templates/vue/src/main.ts +4 -0
- package/dist/templates/vue/template.json +19 -0
- package/dist/templates/vue/vite.config.ts +6 -0
- package/dist/{validate-GCHZJIL7.js → validate-FD3Z6VJD.js} +4 -4
- package/dist/validate-cross-check-WNJM6H2D.js +8 -0
- package/package.json +5 -5
- package/dist/agents-md-XU3BHE22.js +0 -8
- package/dist/ci-workflow-EHV65NQB.js +0 -8
- package/dist/engine-OL4T6NZS.js +0 -8
- package/dist/loader-DPYFB6R6.js +0 -10
- package/dist/runtime-X7U6SC7K.js +0 -9
- 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-
|
|
4
|
+
} from "./chunk-OXLLOSSR.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-
|
|
14
|
+
} from "./chunk-YPYGXRDR.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-
|
|
22
|
+
} from "./chunk-TPOTOBR7.js";
|
|
23
23
|
import {
|
|
24
24
|
handleRunSecurityScan,
|
|
25
25
|
runSecurityScanDefinition
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-OSXBPAMK.js";
|
|
27
27
|
import {
|
|
28
28
|
handleRunCodeReview,
|
|
29
29
|
runCodeReviewDefinition
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-NLVUVUGD.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-
|
|
41
|
+
} from "./chunk-S2FXOWOR.js";
|
|
42
42
|
import {
|
|
43
43
|
loadGraphStore
|
|
44
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-FTMXDOR6.js";
|
|
45
45
|
import {
|
|
46
46
|
checkDependenciesDefinition,
|
|
47
47
|
handleCheckDependencies
|
|
48
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-3C2MLBPJ.js";
|
|
49
49
|
import {
|
|
50
50
|
resolveProjectConfig
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-H7Y5CKTM.js";
|
|
52
52
|
import {
|
|
53
53
|
checkDocsDefinition,
|
|
54
54
|
handleCheckDocs
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-7IP4JIFL.js";
|
|
56
56
|
import {
|
|
57
57
|
resolveConfig
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-O5OJVPL6.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-
|
|
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-
|
|
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-
|
|
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-
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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-EG4EH4IX.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
|
|
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-
|
|
452
|
+
const { listPersonas } = await import("./loader-ZPALXIVR.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-
|
|
250
|
-
const { generateRuntime } = await import("./runtime-
|
|
251
|
-
const { generateAgentsMd } = await import("./agents-md-
|
|
252
|
-
const { generateCIWorkflow } = await import("./ci-workflow-
|
|
476
|
+
const { loadPersona } = await import("./loader-ZPALXIVR.js");
|
|
477
|
+
const { generateRuntime } = await import("./runtime-7YLVK453.js");
|
|
478
|
+
const { generateAgentsMd } = await import("./agents-md-YTYQDA3P.js");
|
|
479
|
+
const { generateCIWorkflow } = await import("./ci-workflow-EQZFVX3P.js");
|
|
253
480
|
const personasDir = resolvePersonasDir();
|
|
254
|
-
const filePath =
|
|
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-
|
|
534
|
+
const { loadPersona } = await import("./loader-ZPALXIVR.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 =
|
|
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
|
|
474
|
-
import * as
|
|
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
|
|
550
|
-
import
|
|
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 (!
|
|
557
|
-
for (const entry of
|
|
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 =
|
|
560
|
-
if (!
|
|
561
|
-
const stat =
|
|
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 =
|
|
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 (!
|
|
588
|
-
for (const entry of
|
|
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 =
|
|
592
|
-
if (!
|
|
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 =
|
|
843
|
+
const indexPath = path4.join(projectRoot, ".harness", "skills-index.json");
|
|
617
844
|
const skillsDirs = resolveAllSkillsDirs(platform);
|
|
618
845
|
const currentHash = computeSkillsDirHash(skillsDirs);
|
|
619
|
-
if (
|
|
846
|
+
if (fs3.existsSync(indexPath)) {
|
|
620
847
|
try {
|
|
621
|
-
const existing = JSON.parse(
|
|
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
|
-
|
|
628
|
-
|
|
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
|
|
634
|
-
import
|
|
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 =
|
|
683
|
-
const exists =
|
|
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 =
|
|
697
|
-
if (
|
|
923
|
+
const profilePath = path5.join(projectRoot, ".harness", "stack-profile.json");
|
|
924
|
+
if (fs4.existsSync(profilePath)) {
|
|
698
925
|
try {
|
|
699
|
-
return JSON.parse(
|
|
926
|
+
return JSON.parse(fs4.readFileSync(profilePath, "utf-8"));
|
|
700
927
|
} catch {
|
|
701
928
|
}
|
|
702
929
|
}
|
|
703
930
|
const profile = generateStackProfile(projectRoot);
|
|
704
|
-
|
|
705
|
-
|
|
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 =
|
|
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 (!
|
|
965
|
+
if (!fs5.existsSync(skillDir)) {
|
|
739
966
|
return resultToMcpResponse(Err(new Error(`Skill not found: ${input.skill}`)));
|
|
740
967
|
}
|
|
741
|
-
const skillMdPath =
|
|
742
|
-
if (!
|
|
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 =
|
|
972
|
+
let content = fs5.readFileSync(skillMdPath, "utf-8");
|
|
746
973
|
if (input.path) {
|
|
747
974
|
const projectPath = sanitizePath(input.path);
|
|
748
|
-
const stateFile =
|
|
749
|
-
if (
|
|
750
|
-
const stateContent =
|
|
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
|
|
830
|
-
import * as
|
|
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 =
|
|
1060
|
+
const skillsDir = path7.join(projectRoot, "agents", "skills", "claude-code");
|
|
834
1061
|
const skills = [];
|
|
835
|
-
if (!
|
|
1062
|
+
if (!fs6.existsSync(skillsDir)) {
|
|
836
1063
|
return JSON.stringify(skills, null, 2);
|
|
837
1064
|
}
|
|
838
|
-
const entries =
|
|
1065
|
+
const entries = fs6.readdirSync(skillsDir, { withFileTypes: true });
|
|
839
1066
|
for (const entry of entries) {
|
|
840
1067
|
if (!entry.isDirectory()) continue;
|
|
841
|
-
const skillYamlPath =
|
|
842
|
-
if (!
|
|
1068
|
+
const skillYamlPath = path7.join(skillsDir, entry.name, "skill.yaml");
|
|
1069
|
+
if (!fs6.existsSync(skillYamlPath)) continue;
|
|
843
1070
|
try {
|
|
844
|
-
const content =
|
|
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
|
|
861
|
-
import * as
|
|
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 =
|
|
865
|
-
if (
|
|
1091
|
+
const configPath = path8.join(projectRoot, "harness.config.json");
|
|
1092
|
+
if (fs7.existsSync(configPath)) {
|
|
866
1093
|
try {
|
|
867
|
-
const config = JSON.parse(
|
|
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 =
|
|
881
|
-
if (
|
|
1107
|
+
const linterPath = path8.join(projectRoot, ".harness", "linter.json");
|
|
1108
|
+
if (fs7.existsSync(linterPath)) {
|
|
882
1109
|
try {
|
|
883
|
-
const linterConfig = JSON.parse(
|
|
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,37 @@ async function getRulesResource(projectRoot) {
|
|
|
889
1116
|
}
|
|
890
1117
|
|
|
891
1118
|
// src/mcp/resources/project.ts
|
|
892
|
-
import * as
|
|
893
|
-
import * as
|
|
1119
|
+
import * as fs8 from "fs";
|
|
1120
|
+
import * as path9 from "path";
|
|
894
1121
|
async function getProjectResource(projectRoot) {
|
|
895
|
-
const agentsPath =
|
|
896
|
-
if (
|
|
897
|
-
return
|
|
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
|
|
904
|
-
import * as
|
|
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 =
|
|
908
|
-
if (
|
|
909
|
-
sections.push("## Review Learnings\n\n" +
|
|
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 =
|
|
912
|
-
if (
|
|
913
|
-
sections.push("## Anti-Pattern Log\n\n" +
|
|
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/utils.ts
|
|
1146
|
+
function mcpError(text) {
|
|
1147
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
1148
|
+
}
|
|
1149
|
+
|
|
918
1150
|
// src/mcp/tools/state.ts
|
|
919
1151
|
var manageStateDefinition = {
|
|
920
1152
|
name: "manage_state",
|
|
@@ -933,7 +1165,12 @@ var manageStateDefinition = {
|
|
|
933
1165
|
"reset",
|
|
934
1166
|
"gate",
|
|
935
1167
|
"save-handoff",
|
|
936
|
-
"load-handoff"
|
|
1168
|
+
"load-handoff",
|
|
1169
|
+
"append_entry",
|
|
1170
|
+
"update_entry_status",
|
|
1171
|
+
"read_section",
|
|
1172
|
+
"read_sections",
|
|
1173
|
+
"archive_session"
|
|
937
1174
|
],
|
|
938
1175
|
description: "Action to perform"
|
|
939
1176
|
},
|
|
@@ -950,138 +1187,175 @@ var manageStateDefinition = {
|
|
|
950
1187
|
session: {
|
|
951
1188
|
type: "string",
|
|
952
1189
|
description: "Session slug for session-scoped state (takes priority over stream when provided)"
|
|
1190
|
+
},
|
|
1191
|
+
section: {
|
|
1192
|
+
type: "string",
|
|
1193
|
+
enum: ["terminology", "decisions", "constraints", "risks", "openQuestions", "evidence"],
|
|
1194
|
+
description: "Session section name (terminology, decisions, constraints, risks, openQuestions, evidence)"
|
|
1195
|
+
},
|
|
1196
|
+
authorSkill: {
|
|
1197
|
+
type: "string",
|
|
1198
|
+
description: "Name of the skill authoring the entry (required for append_entry)"
|
|
1199
|
+
},
|
|
1200
|
+
content: {
|
|
1201
|
+
type: "string",
|
|
1202
|
+
description: "Entry content text (required for append_entry)"
|
|
1203
|
+
},
|
|
1204
|
+
entryId: {
|
|
1205
|
+
type: "string",
|
|
1206
|
+
description: "ID of the entry to update (required for update_entry_status)"
|
|
1207
|
+
},
|
|
1208
|
+
newStatus: {
|
|
1209
|
+
type: "string",
|
|
1210
|
+
enum: ["active", "resolved", "superseded"],
|
|
1211
|
+
description: "New status for the entry: active, resolved, or superseded (required for update_entry_status)"
|
|
953
1212
|
}
|
|
954
1213
|
},
|
|
955
1214
|
required: ["path", "action"]
|
|
956
1215
|
}
|
|
957
1216
|
};
|
|
1217
|
+
async function handleShow(projectPath, input) {
|
|
1218
|
+
const { loadState } = await import("./dist-HWXF2C3R.js");
|
|
1219
|
+
return resultToMcpResponse(await loadState(projectPath, input.stream, input.session));
|
|
1220
|
+
}
|
|
1221
|
+
async function handleLearn(projectPath, input) {
|
|
1222
|
+
if (!input.learning) return mcpError("Error: learning is required for learn action");
|
|
1223
|
+
const { appendLearning } = await import("./dist-HWXF2C3R.js");
|
|
1224
|
+
const result = await appendLearning(
|
|
1225
|
+
projectPath,
|
|
1226
|
+
input.learning,
|
|
1227
|
+
input.skillName,
|
|
1228
|
+
input.outcome,
|
|
1229
|
+
input.stream,
|
|
1230
|
+
input.session
|
|
1231
|
+
);
|
|
1232
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1233
|
+
return resultToMcpResponse(Ok({ recorded: true }));
|
|
1234
|
+
}
|
|
1235
|
+
async function handleFailure(projectPath, input) {
|
|
1236
|
+
if (!input.description) return mcpError("Error: description is required for failure action");
|
|
1237
|
+
if (!input.failureType) return mcpError("Error: failureType is required for failure action");
|
|
1238
|
+
const { appendFailure } = await import("./dist-HWXF2C3R.js");
|
|
1239
|
+
const result = await appendFailure(
|
|
1240
|
+
projectPath,
|
|
1241
|
+
input.description,
|
|
1242
|
+
input.skillName ?? "unknown",
|
|
1243
|
+
input.failureType,
|
|
1244
|
+
input.stream,
|
|
1245
|
+
input.session
|
|
1246
|
+
);
|
|
1247
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1248
|
+
return resultToMcpResponse(Ok({ recorded: true }));
|
|
1249
|
+
}
|
|
1250
|
+
async function handleArchive(projectPath, input) {
|
|
1251
|
+
const { archiveFailures } = await import("./dist-HWXF2C3R.js");
|
|
1252
|
+
const result = await archiveFailures(projectPath, input.stream, input.session);
|
|
1253
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1254
|
+
return resultToMcpResponse(Ok({ archived: true }));
|
|
1255
|
+
}
|
|
1256
|
+
async function handleReset(projectPath, input) {
|
|
1257
|
+
const { saveState, DEFAULT_STATE } = await import("./dist-HWXF2C3R.js");
|
|
1258
|
+
const result = await saveState(projectPath, { ...DEFAULT_STATE }, input.stream, input.session);
|
|
1259
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1260
|
+
return resultToMcpResponse(Ok({ reset: true }));
|
|
1261
|
+
}
|
|
1262
|
+
async function handleGate(projectPath, _input) {
|
|
1263
|
+
const { runMechanicalGate } = await import("./dist-HWXF2C3R.js");
|
|
1264
|
+
return resultToMcpResponse(await runMechanicalGate(projectPath));
|
|
1265
|
+
}
|
|
1266
|
+
async function handleSaveHandoff(projectPath, input) {
|
|
1267
|
+
if (!input.handoff) return mcpError("Error: handoff is required for save-handoff action");
|
|
1268
|
+
const { saveHandoff } = await import("./dist-HWXF2C3R.js");
|
|
1269
|
+
const result = await saveHandoff(
|
|
1270
|
+
projectPath,
|
|
1271
|
+
input.handoff,
|
|
1272
|
+
input.stream,
|
|
1273
|
+
input.session
|
|
1274
|
+
);
|
|
1275
|
+
return resultToMcpResponse(result.ok ? Ok({ saved: true }) : result);
|
|
1276
|
+
}
|
|
1277
|
+
async function handleLoadHandoff(projectPath, input) {
|
|
1278
|
+
const { loadHandoff } = await import("./dist-HWXF2C3R.js");
|
|
1279
|
+
return resultToMcpResponse(await loadHandoff(projectPath, input.stream, input.session));
|
|
1280
|
+
}
|
|
1281
|
+
async function handleAppendEntry(projectPath, input) {
|
|
1282
|
+
if (!input.session) return mcpError("Error: session is required for append_entry action");
|
|
1283
|
+
if (!input.section) return mcpError("Error: section is required for append_entry action");
|
|
1284
|
+
if (!input.authorSkill) return mcpError("Error: authorSkill is required for append_entry action");
|
|
1285
|
+
if (!input.content) return mcpError("Error: content is required for append_entry action");
|
|
1286
|
+
const { appendSessionEntry } = await import("./dist-HWXF2C3R.js");
|
|
1287
|
+
const result = await appendSessionEntry(
|
|
1288
|
+
projectPath,
|
|
1289
|
+
input.session,
|
|
1290
|
+
input.section,
|
|
1291
|
+
input.authorSkill,
|
|
1292
|
+
input.content
|
|
1293
|
+
);
|
|
1294
|
+
return resultToMcpResponse(result);
|
|
1295
|
+
}
|
|
1296
|
+
async function handleUpdateEntryStatus(projectPath, input) {
|
|
1297
|
+
if (!input.session) return mcpError("Error: session is required for update_entry_status action");
|
|
1298
|
+
if (!input.section) return mcpError("Error: section is required for update_entry_status action");
|
|
1299
|
+
if (!input.entryId) return mcpError("Error: entryId is required for update_entry_status action");
|
|
1300
|
+
if (!input.newStatus)
|
|
1301
|
+
return mcpError("Error: newStatus is required for update_entry_status action");
|
|
1302
|
+
const { updateSessionEntryStatus } = await import("./dist-HWXF2C3R.js");
|
|
1303
|
+
const result = await updateSessionEntryStatus(
|
|
1304
|
+
projectPath,
|
|
1305
|
+
input.session,
|
|
1306
|
+
input.section,
|
|
1307
|
+
input.entryId,
|
|
1308
|
+
input.newStatus
|
|
1309
|
+
);
|
|
1310
|
+
return resultToMcpResponse(result);
|
|
1311
|
+
}
|
|
1312
|
+
async function handleReadSection(projectPath, input) {
|
|
1313
|
+
if (!input.session) return mcpError("Error: session is required for read_section action");
|
|
1314
|
+
if (!input.section) return mcpError("Error: section is required for read_section action");
|
|
1315
|
+
const { readSessionSection } = await import("./dist-HWXF2C3R.js");
|
|
1316
|
+
const result = await readSessionSection(
|
|
1317
|
+
projectPath,
|
|
1318
|
+
input.session,
|
|
1319
|
+
input.section
|
|
1320
|
+
);
|
|
1321
|
+
return resultToMcpResponse(result);
|
|
1322
|
+
}
|
|
1323
|
+
async function handleReadSections(projectPath, input) {
|
|
1324
|
+
if (!input.session) return mcpError("Error: session is required for read_sections action");
|
|
1325
|
+
const { readSessionSections } = await import("./dist-HWXF2C3R.js");
|
|
1326
|
+
const result = await readSessionSections(projectPath, input.session);
|
|
1327
|
+
return resultToMcpResponse(result);
|
|
1328
|
+
}
|
|
1329
|
+
async function handleArchiveSession(projectPath, input) {
|
|
1330
|
+
if (!input.session) return mcpError("Error: session is required for archive_session action");
|
|
1331
|
+
const { archiveSession } = await import("./dist-HWXF2C3R.js");
|
|
1332
|
+
const result = await archiveSession(projectPath, input.session);
|
|
1333
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1334
|
+
return resultToMcpResponse(Ok({ archived: true }));
|
|
1335
|
+
}
|
|
1336
|
+
var ACTION_HANDLERS = {
|
|
1337
|
+
show: handleShow,
|
|
1338
|
+
learn: handleLearn,
|
|
1339
|
+
failure: handleFailure,
|
|
1340
|
+
archive: handleArchive,
|
|
1341
|
+
reset: handleReset,
|
|
1342
|
+
gate: handleGate,
|
|
1343
|
+
"save-handoff": handleSaveHandoff,
|
|
1344
|
+
"load-handoff": handleLoadHandoff,
|
|
1345
|
+
append_entry: handleAppendEntry,
|
|
1346
|
+
update_entry_status: handleUpdateEntryStatus,
|
|
1347
|
+
read_section: handleReadSection,
|
|
1348
|
+
read_sections: handleReadSections,
|
|
1349
|
+
archive_session: handleArchiveSession
|
|
1350
|
+
};
|
|
958
1351
|
async function handleManageState(input) {
|
|
959
1352
|
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
1353
|
const projectPath = sanitizePath(input.path);
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
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
|
-
}
|
|
1354
|
+
const handler = ACTION_HANDLERS[input.action];
|
|
1355
|
+
if (!handler) return mcpError("Error: unknown action");
|
|
1356
|
+
return await handler(projectPath, input);
|
|
1075
1357
|
} 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
|
-
};
|
|
1358
|
+
return mcpError(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
1085
1359
|
}
|
|
1086
1360
|
}
|
|
1087
1361
|
var listStreamsDefinition = {
|
|
@@ -1097,7 +1371,7 @@ var listStreamsDefinition = {
|
|
|
1097
1371
|
};
|
|
1098
1372
|
async function handleListStreams(input) {
|
|
1099
1373
|
try {
|
|
1100
|
-
const { listStreams, loadStreamIndex } = await import("./dist-
|
|
1374
|
+
const { listStreams, loadStreamIndex } = await import("./dist-HWXF2C3R.js");
|
|
1101
1375
|
const projectPath = sanitizePath(input.path);
|
|
1102
1376
|
const indexResult = await loadStreamIndex(projectPath);
|
|
1103
1377
|
const streamsResult = await listStreams(projectPath);
|
|
@@ -1135,7 +1409,7 @@ var checkPhaseGateDefinition = {
|
|
|
1135
1409
|
};
|
|
1136
1410
|
async function handleCheckPhaseGate(input) {
|
|
1137
1411
|
try {
|
|
1138
|
-
const { runCheckPhaseGate } = await import("./check-phase-gate-
|
|
1412
|
+
const { runCheckPhaseGate } = await import("./check-phase-gate-L3RADYWO.js");
|
|
1139
1413
|
const result = await runCheckPhaseGate({ cwd: sanitizePath(input.path) });
|
|
1140
1414
|
if (result.ok) {
|
|
1141
1415
|
return { content: [{ type: "text", text: JSON.stringify(result.value) }] };
|
|
@@ -1155,7 +1429,7 @@ async function handleCheckPhaseGate(input) {
|
|
|
1155
1429
|
}
|
|
1156
1430
|
|
|
1157
1431
|
// src/mcp/tools/cross-check.ts
|
|
1158
|
-
import * as
|
|
1432
|
+
import * as path11 from "path";
|
|
1159
1433
|
var validateCrossCheckDefinition = {
|
|
1160
1434
|
name: "validate_cross_check",
|
|
1161
1435
|
description: "Validate plan-to-implementation coverage: checks that specs have plans and plans have implementations, detects staleness",
|
|
@@ -1191,15 +1465,15 @@ async function handleValidateCrossCheck(input) {
|
|
|
1191
1465
|
};
|
|
1192
1466
|
}
|
|
1193
1467
|
try {
|
|
1194
|
-
const { runCrossCheck } = await import("./validate-cross-check-
|
|
1195
|
-
const specsDir =
|
|
1468
|
+
const { runCrossCheck } = await import("./validate-cross-check-WNJM6H2D.js");
|
|
1469
|
+
const specsDir = path11.resolve(projectPath, input.specsDir ?? "docs/specs");
|
|
1196
1470
|
if (!specsDir.startsWith(projectPath)) {
|
|
1197
1471
|
return {
|
|
1198
1472
|
content: [{ type: "text", text: "Error: specsDir escapes project root" }],
|
|
1199
1473
|
isError: true
|
|
1200
1474
|
};
|
|
1201
1475
|
}
|
|
1202
|
-
const plansDir =
|
|
1476
|
+
const plansDir = path11.resolve(projectPath, input.plansDir ?? "docs/plans");
|
|
1203
1477
|
if (!plansDir.startsWith(projectPath)) {
|
|
1204
1478
|
return {
|
|
1205
1479
|
content: [{ type: "text", text: "Error: plansDir escapes project root" }],
|
|
@@ -1230,14 +1504,14 @@ async function handleValidateCrossCheck(input) {
|
|
|
1230
1504
|
|
|
1231
1505
|
// src/commands/generate-slash-commands.ts
|
|
1232
1506
|
import { Command } from "commander";
|
|
1233
|
-
import
|
|
1234
|
-
import
|
|
1507
|
+
import fs11 from "fs";
|
|
1508
|
+
import path13 from "path";
|
|
1235
1509
|
import os from "os";
|
|
1236
1510
|
import readline from "readline";
|
|
1237
1511
|
|
|
1238
1512
|
// src/slash-commands/normalize.ts
|
|
1239
|
-
import
|
|
1240
|
-
import
|
|
1513
|
+
import fs10 from "fs";
|
|
1514
|
+
import path12 from "path";
|
|
1241
1515
|
import { parse as parse3 } from "yaml";
|
|
1242
1516
|
|
|
1243
1517
|
// src/slash-commands/normalize-name.ts
|
|
@@ -1254,113 +1528,125 @@ function normalizeName(skillName) {
|
|
|
1254
1528
|
}
|
|
1255
1529
|
|
|
1256
1530
|
// src/slash-commands/normalize.ts
|
|
1531
|
+
function readSkillYaml(yamlPath) {
|
|
1532
|
+
let raw;
|
|
1533
|
+
try {
|
|
1534
|
+
raw = fs10.readFileSync(yamlPath, "utf-8");
|
|
1535
|
+
} catch {
|
|
1536
|
+
return null;
|
|
1537
|
+
}
|
|
1538
|
+
return SkillMetadataSchema.safeParse(parse3(raw));
|
|
1539
|
+
}
|
|
1540
|
+
function shouldSkipSkill(meta, platforms) {
|
|
1541
|
+
const matchesPlatform = platforms.some((p) => meta.platforms.includes(p));
|
|
1542
|
+
if (!matchesPlatform) return true;
|
|
1543
|
+
if (meta.tier === 3 || meta.internal) return true;
|
|
1544
|
+
return false;
|
|
1545
|
+
}
|
|
1546
|
+
function checkNameCollision(normalized, metaName, source, nameMap) {
|
|
1547
|
+
const existing = nameMap.get(normalized);
|
|
1548
|
+
if (!existing) {
|
|
1549
|
+
nameMap.set(normalized, { skillName: metaName, source });
|
|
1550
|
+
return "ok";
|
|
1551
|
+
}
|
|
1552
|
+
if (existing.source === source) {
|
|
1553
|
+
throw new Error(
|
|
1554
|
+
`Name collision: skills "${existing.skillName}" and "${metaName}" both normalize to "${normalized}"`
|
|
1555
|
+
);
|
|
1556
|
+
}
|
|
1557
|
+
return "skip";
|
|
1558
|
+
}
|
|
1559
|
+
function buildContextLines(meta) {
|
|
1560
|
+
const lines = [];
|
|
1561
|
+
if (meta.cognitive_mode) lines.push(`Cognitive mode: ${meta.cognitive_mode}`);
|
|
1562
|
+
if (meta.type) lines.push(`Type: ${meta.type}`);
|
|
1563
|
+
if (meta.state?.persistent) {
|
|
1564
|
+
const files = meta.state.files?.join(", ") ?? "";
|
|
1565
|
+
lines.push(`State: persistent${files ? ` (files: ${files})` : ""}`);
|
|
1566
|
+
}
|
|
1567
|
+
return lines;
|
|
1568
|
+
}
|
|
1569
|
+
function buildObjectiveLines(meta) {
|
|
1570
|
+
const lines = [meta.description];
|
|
1571
|
+
if (meta.phases && meta.phases.length > 0) {
|
|
1572
|
+
lines.push("", "Phases:");
|
|
1573
|
+
for (const phase of meta.phases) {
|
|
1574
|
+
const req = phase.required !== false ? "" : " (optional)";
|
|
1575
|
+
lines.push(`- ${phase.name}: ${phase.description}${req}`);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return lines;
|
|
1579
|
+
}
|
|
1580
|
+
function buildProcessLines(meta) {
|
|
1581
|
+
if (meta.mcp?.tool) {
|
|
1582
|
+
return [
|
|
1583
|
+
`1. Try: invoke mcp__harness__${meta.mcp.tool} with skill: "${meta.name}"`,
|
|
1584
|
+
`2. If MCP unavailable: read SKILL.md and follow its workflow directly`,
|
|
1585
|
+
`3. Pass through any arguments provided by the user`
|
|
1586
|
+
];
|
|
1587
|
+
}
|
|
1588
|
+
return [
|
|
1589
|
+
`1. Read SKILL.md and follow its workflow directly`,
|
|
1590
|
+
`2. Pass through any arguments provided by the user`
|
|
1591
|
+
];
|
|
1592
|
+
}
|
|
1593
|
+
function buildSpec(meta, normalized, entry, skillsDir, source) {
|
|
1594
|
+
const skillMdPath = path12.join(skillsDir, entry.name, "SKILL.md");
|
|
1595
|
+
const skillMdContent = fs10.existsSync(skillMdPath) ? fs10.readFileSync(skillMdPath, "utf-8") : "";
|
|
1596
|
+
const skillMdRelative = path12.relative(process.cwd(), skillMdPath).replaceAll("\\", "/");
|
|
1597
|
+
const skillYamlRelative = path12.relative(process.cwd(), path12.join(skillsDir, entry.name, "skill.yaml")).replaceAll("\\", "/");
|
|
1598
|
+
const args = (meta.cli?.args ?? []).map((a) => ({
|
|
1599
|
+
name: a.name,
|
|
1600
|
+
description: a.description ?? "",
|
|
1601
|
+
required: a.required ?? false
|
|
1602
|
+
}));
|
|
1603
|
+
const tools = [...meta.tools ?? []];
|
|
1604
|
+
if (!tools.includes("Read")) tools.push("Read");
|
|
1605
|
+
const executionContextLines = [];
|
|
1606
|
+
if (skillMdContent) {
|
|
1607
|
+
executionContextLines.push(`@${skillMdRelative}`, `@${skillYamlRelative}`);
|
|
1608
|
+
}
|
|
1609
|
+
return {
|
|
1610
|
+
name: normalized,
|
|
1611
|
+
namespace: "harness",
|
|
1612
|
+
fullName: `harness:${normalized}`,
|
|
1613
|
+
description: meta.description,
|
|
1614
|
+
version: meta.version,
|
|
1615
|
+
...meta.cognitive_mode ? { cognitiveMode: meta.cognitive_mode } : {},
|
|
1616
|
+
tools,
|
|
1617
|
+
args,
|
|
1618
|
+
skillYamlName: meta.name,
|
|
1619
|
+
sourceDir: entry.name,
|
|
1620
|
+
skillsBaseDir: skillsDir,
|
|
1621
|
+
source,
|
|
1622
|
+
prompt: {
|
|
1623
|
+
context: buildContextLines(meta).join("\n"),
|
|
1624
|
+
objective: buildObjectiveLines(meta).join("\n"),
|
|
1625
|
+
executionContext: executionContextLines.join("\n"),
|
|
1626
|
+
process: buildProcessLines(meta).join("\n")
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1257
1630
|
function normalizeSkills(skillSources, platforms) {
|
|
1258
1631
|
const specs = [];
|
|
1259
1632
|
const nameMap = /* @__PURE__ */ new Map();
|
|
1260
1633
|
for (const { dir: skillsDir, source } of skillSources) {
|
|
1261
|
-
if (!
|
|
1262
|
-
const entries =
|
|
1634
|
+
if (!fs10.existsSync(skillsDir)) continue;
|
|
1635
|
+
const entries = fs10.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
1263
1636
|
for (const entry of entries) {
|
|
1264
|
-
const yamlPath =
|
|
1265
|
-
if (!
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
raw = fs8.readFileSync(yamlPath, "utf-8");
|
|
1269
|
-
} catch {
|
|
1270
|
-
continue;
|
|
1271
|
-
}
|
|
1272
|
-
const parsed = parse3(raw);
|
|
1273
|
-
const result = SkillMetadataSchema.safeParse(parsed);
|
|
1637
|
+
const yamlPath = path12.join(skillsDir, entry.name, "skill.yaml");
|
|
1638
|
+
if (!fs10.existsSync(yamlPath)) continue;
|
|
1639
|
+
const result = readSkillYaml(yamlPath);
|
|
1640
|
+
if (!result) continue;
|
|
1274
1641
|
if (!result.success) {
|
|
1275
1642
|
console.warn(`Skipping ${entry.name}: invalid skill.yaml`);
|
|
1276
1643
|
continue;
|
|
1277
1644
|
}
|
|
1278
1645
|
const meta = result.data;
|
|
1279
|
-
|
|
1280
|
-
if (!matchesPlatform) continue;
|
|
1281
|
-
const tier = meta.tier;
|
|
1282
|
-
const isInternal = meta.internal;
|
|
1283
|
-
if (tier === 3 || isInternal) continue;
|
|
1646
|
+
if (shouldSkipSkill(meta, platforms)) continue;
|
|
1284
1647
|
const normalized = normalizeName(meta.name);
|
|
1285
|
-
|
|
1286
|
-
|
|
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
|
-
});
|
|
1648
|
+
if (checkNameCollision(normalized, meta.name, source, nameMap) === "skip") continue;
|
|
1649
|
+
specs.push(buildSpec(meta, normalized, entry, skillsDir, source));
|
|
1364
1650
|
}
|
|
1365
1651
|
}
|
|
1366
1652
|
return specs;
|
|
@@ -1460,13 +1746,13 @@ function renderGemini(spec, skillMdContent, skillYamlContent) {
|
|
|
1460
1746
|
// src/commands/generate-slash-commands.ts
|
|
1461
1747
|
function resolveOutputDir(platform, opts) {
|
|
1462
1748
|
if (opts.output) {
|
|
1463
|
-
return
|
|
1749
|
+
return path13.join(opts.output, "harness");
|
|
1464
1750
|
}
|
|
1465
1751
|
if (opts.global) {
|
|
1466
1752
|
const home = os.homedir();
|
|
1467
|
-
return platform === "claude-code" ?
|
|
1753
|
+
return platform === "claude-code" ? path13.join(home, ".claude", "commands", "harness") : path13.join(home, ".gemini", "commands", "harness");
|
|
1468
1754
|
}
|
|
1469
|
-
return platform === "claude-code" ?
|
|
1755
|
+
return platform === "claude-code" ? path13.join("agents", "commands", "claude-code", "harness") : path13.join("agents", "commands", "gemini-cli", "harness");
|
|
1470
1756
|
}
|
|
1471
1757
|
function fileExtension(platform) {
|
|
1472
1758
|
return platform === "claude-code" ? ".md" : ".toml";
|
|
@@ -1481,26 +1767,29 @@ Remove ${files.length} orphaned command(s)? (y/N) `, (answer) => {
|
|
|
1481
1767
|
});
|
|
1482
1768
|
});
|
|
1483
1769
|
}
|
|
1484
|
-
function
|
|
1485
|
-
const skillSources = [];
|
|
1770
|
+
function resolveSkillSources(opts) {
|
|
1486
1771
|
if (opts.skillsDir) {
|
|
1487
|
-
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
}
|
|
1772
|
+
return [{ dir: opts.skillsDir, source: "project" }];
|
|
1773
|
+
}
|
|
1774
|
+
const sources = [];
|
|
1775
|
+
const projectDir = resolveProjectSkillsDir();
|
|
1776
|
+
if (projectDir) {
|
|
1777
|
+
sources.push({ dir: projectDir, source: "project" });
|
|
1778
|
+
}
|
|
1779
|
+
const communityDir = resolveCommunitySkillsDir();
|
|
1780
|
+
if (fs11.existsSync(communityDir)) {
|
|
1781
|
+
sources.push({ dir: communityDir, source: "community" });
|
|
1782
|
+
}
|
|
1783
|
+
if (opts.includeGlobal || sources.length === 0) {
|
|
1784
|
+
const globalDir = resolveGlobalSkillsDir();
|
|
1785
|
+
if (!projectDir || path13.resolve(globalDir) !== path13.resolve(projectDir)) {
|
|
1786
|
+
sources.push({ dir: globalDir, source: "global" });
|
|
1502
1787
|
}
|
|
1503
1788
|
}
|
|
1789
|
+
return sources;
|
|
1790
|
+
}
|
|
1791
|
+
function generateSlashCommands(opts) {
|
|
1792
|
+
const skillSources = resolveSkillSources(opts);
|
|
1504
1793
|
const specs = normalizeSkills(skillSources, opts.platforms);
|
|
1505
1794
|
const results = [];
|
|
1506
1795
|
for (const platform of opts.platforms) {
|
|
@@ -1518,7 +1807,7 @@ function generateSlashCommands(opts) {
|
|
|
1518
1807
|
executionContext: spec.prompt.executionContext.split("\n").map((line) => {
|
|
1519
1808
|
if (line.startsWith("@")) {
|
|
1520
1809
|
const relPath = line.slice(1);
|
|
1521
|
-
return `@${
|
|
1810
|
+
return `@${path13.resolve(relPath)}`;
|
|
1522
1811
|
}
|
|
1523
1812
|
return line;
|
|
1524
1813
|
}).join("\n")
|
|
@@ -1526,10 +1815,10 @@ function generateSlashCommands(opts) {
|
|
|
1526
1815
|
} : spec;
|
|
1527
1816
|
rendered.set(filename, renderClaudeCode(renderSpec));
|
|
1528
1817
|
} else {
|
|
1529
|
-
const mdPath =
|
|
1530
|
-
const yamlPath =
|
|
1531
|
-
const mdContent =
|
|
1532
|
-
const yamlContent =
|
|
1818
|
+
const mdPath = path13.join(spec.skillsBaseDir, spec.sourceDir, "SKILL.md");
|
|
1819
|
+
const yamlPath = path13.join(spec.skillsBaseDir, spec.sourceDir, "skill.yaml");
|
|
1820
|
+
const mdContent = fs11.existsSync(mdPath) ? fs11.readFileSync(mdPath, "utf-8") : "";
|
|
1821
|
+
const yamlContent = fs11.existsSync(yamlPath) ? fs11.readFileSync(yamlPath, "utf-8") : "";
|
|
1533
1822
|
rendered.set(filename, renderGemini(spec, mdContent, yamlContent));
|
|
1534
1823
|
}
|
|
1535
1824
|
}
|
|
@@ -1555,9 +1844,9 @@ async function handleOrphanDeletion(results, opts) {
|
|
|
1555
1844
|
const shouldDelete = opts.yes || await confirmDeletion(result.removed);
|
|
1556
1845
|
if (shouldDelete) {
|
|
1557
1846
|
for (const filename of result.removed) {
|
|
1558
|
-
const filePath =
|
|
1559
|
-
if (
|
|
1560
|
-
|
|
1847
|
+
const filePath = path13.join(result.outputDir, filename);
|
|
1848
|
+
if (fs11.existsSync(filePath)) {
|
|
1849
|
+
fs11.unlinkSync(filePath);
|
|
1561
1850
|
}
|
|
1562
1851
|
}
|
|
1563
1852
|
}
|
|
@@ -1683,7 +1972,7 @@ async function handleGenerateSlashCommands(input) {
|
|
|
1683
1972
|
// src/mcp/resources/state.ts
|
|
1684
1973
|
async function getStateResource(projectRoot) {
|
|
1685
1974
|
try {
|
|
1686
|
-
const { loadState, migrateToStreams } = await import("./dist-
|
|
1975
|
+
const { loadState, migrateToStreams } = await import("./dist-HWXF2C3R.js");
|
|
1687
1976
|
await migrateToStreams(projectRoot);
|
|
1688
1977
|
const result = await loadState(projectRoot);
|
|
1689
1978
|
if (result.ok) {
|
|
@@ -1771,7 +2060,7 @@ async function handleQueryGraph(input) {
|
|
|
1771
2060
|
const projectPath = sanitizePath(input.path);
|
|
1772
2061
|
const store = await loadGraphStore(projectPath);
|
|
1773
2062
|
if (!store) return graphNotFoundError();
|
|
1774
|
-
const { ContextQL } = await import("./dist-
|
|
2063
|
+
const { ContextQL } = await import("./dist-B26DFXMP.js");
|
|
1775
2064
|
const cql = new ContextQL(store);
|
|
1776
2065
|
const result = cql.execute({
|
|
1777
2066
|
rootNodeIds: input.rootNodeIds,
|
|
@@ -1862,7 +2151,7 @@ async function handleSearchSimilar(input) {
|
|
|
1862
2151
|
const projectPath = sanitizePath(input.path);
|
|
1863
2152
|
const store = await loadGraphStore(projectPath);
|
|
1864
2153
|
if (!store) return graphNotFoundError();
|
|
1865
|
-
const { FusionLayer } = await import("./dist-
|
|
2154
|
+
const { FusionLayer } = await import("./dist-B26DFXMP.js");
|
|
1866
2155
|
const fusion = new FusionLayer(store);
|
|
1867
2156
|
const results = fusion.search(input.query, input.topK ?? 10);
|
|
1868
2157
|
if (input.mode === "summary") {
|
|
@@ -1917,7 +2206,7 @@ async function handleFindContextFor(input) {
|
|
|
1917
2206
|
const projectPath = sanitizePath(input.path);
|
|
1918
2207
|
const store = await loadGraphStore(projectPath);
|
|
1919
2208
|
if (!store) return graphNotFoundError();
|
|
1920
|
-
const { FusionLayer, ContextQL } = await import("./dist-
|
|
2209
|
+
const { FusionLayer, ContextQL } = await import("./dist-B26DFXMP.js");
|
|
1921
2210
|
const fusion = new FusionLayer(store);
|
|
1922
2211
|
const cql = new ContextQL(store);
|
|
1923
2212
|
const tokenBudget = input.tokenBudget ?? 4e3;
|
|
@@ -2013,7 +2302,7 @@ async function handleGetRelationships(input) {
|
|
|
2013
2302
|
const projectPath = sanitizePath(input.path);
|
|
2014
2303
|
const store = await loadGraphStore(projectPath);
|
|
2015
2304
|
if (!store) return graphNotFoundError();
|
|
2016
|
-
const { ContextQL } = await import("./dist-
|
|
2305
|
+
const { ContextQL } = await import("./dist-B26DFXMP.js");
|
|
2017
2306
|
const cql = new ContextQL(store);
|
|
2018
2307
|
const direction = input.direction ?? "both";
|
|
2019
2308
|
const bidirectional = direction === "both" || direction === "inbound";
|
|
@@ -2119,7 +2408,7 @@ async function handleGetImpact(input) {
|
|
|
2119
2408
|
const projectPath = sanitizePath(input.path);
|
|
2120
2409
|
const store = await loadGraphStore(projectPath);
|
|
2121
2410
|
if (!store) return graphNotFoundError();
|
|
2122
|
-
const { ContextQL } = await import("./dist-
|
|
2411
|
+
const { ContextQL } = await import("./dist-B26DFXMP.js");
|
|
2123
2412
|
let targetNodeId = input.nodeId;
|
|
2124
2413
|
if (!targetNodeId && input.filePath) {
|
|
2125
2414
|
const fileNodes = store.findNodes({ type: "file" });
|
|
@@ -2230,7 +2519,7 @@ async function handleGetImpact(input) {
|
|
|
2230
2519
|
}
|
|
2231
2520
|
|
|
2232
2521
|
// src/mcp/tools/graph/ingest-source.ts
|
|
2233
|
-
import * as
|
|
2522
|
+
import * as path14 from "path";
|
|
2234
2523
|
var ingestSourceDefinition = {
|
|
2235
2524
|
name: "ingest_source",
|
|
2236
2525
|
description: "Ingest sources into the project knowledge graph. Supports code analysis, knowledge documents, git history, or all at once.",
|
|
@@ -2250,10 +2539,10 @@ var ingestSourceDefinition = {
|
|
|
2250
2539
|
async function handleIngestSource(input) {
|
|
2251
2540
|
try {
|
|
2252
2541
|
const projectPath = sanitizePath(input.path);
|
|
2253
|
-
const graphDir =
|
|
2254
|
-
const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-
|
|
2255
|
-
const
|
|
2256
|
-
await
|
|
2542
|
+
const graphDir = path14.join(projectPath, ".harness", "graph");
|
|
2543
|
+
const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-B26DFXMP.js");
|
|
2544
|
+
const fs14 = await import("fs/promises");
|
|
2545
|
+
await fs14.mkdir(graphDir, { recursive: true });
|
|
2257
2546
|
const store = new GraphStore();
|
|
2258
2547
|
await store.load(graphDir);
|
|
2259
2548
|
const results = [];
|
|
@@ -2326,7 +2615,7 @@ async function handleDetectAnomalies(input) {
|
|
|
2326
2615
|
const projectPath = sanitizePath(input.path);
|
|
2327
2616
|
const store = await loadGraphStore(projectPath);
|
|
2328
2617
|
if (!store) return graphNotFoundError();
|
|
2329
|
-
const { GraphAnomalyAdapter } = await import("./dist-
|
|
2618
|
+
const { GraphAnomalyAdapter } = await import("./dist-B26DFXMP.js");
|
|
2330
2619
|
const adapter = new GraphAnomalyAdapter(store);
|
|
2331
2620
|
const report = adapter.detect({
|
|
2332
2621
|
...input.threshold !== void 0 && { threshold: input.threshold },
|
|
@@ -2366,7 +2655,7 @@ async function handleAskGraph(input) {
|
|
|
2366
2655
|
const projectPath = sanitizePath(input.path);
|
|
2367
2656
|
const store = await loadGraphStore(projectPath);
|
|
2368
2657
|
if (!store) return graphNotFoundError();
|
|
2369
|
-
const { askGraph } = await import("./dist-
|
|
2658
|
+
const { askGraph } = await import("./dist-B26DFXMP.js");
|
|
2370
2659
|
const result = await askGraph(store, input.question);
|
|
2371
2660
|
return {
|
|
2372
2661
|
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
@@ -2385,8 +2674,8 @@ async function handleAskGraph(input) {
|
|
|
2385
2674
|
}
|
|
2386
2675
|
|
|
2387
2676
|
// src/mcp/resources/graph.ts
|
|
2388
|
-
import * as
|
|
2389
|
-
import * as
|
|
2677
|
+
import * as fs12 from "fs/promises";
|
|
2678
|
+
import * as path15 from "path";
|
|
2390
2679
|
var MAX_ITEMS = 5e3;
|
|
2391
2680
|
function formatStaleness(isoTimestamp) {
|
|
2392
2681
|
const then = new Date(isoTimestamp).getTime();
|
|
@@ -2409,11 +2698,11 @@ async function getGraphResource(projectRoot) {
|
|
|
2409
2698
|
message: "No knowledge graph found. Run harness scan to build one."
|
|
2410
2699
|
});
|
|
2411
2700
|
}
|
|
2412
|
-
const graphDir =
|
|
2413
|
-
const metadataPath =
|
|
2701
|
+
const graphDir = path15.join(projectRoot, ".harness", "graph");
|
|
2702
|
+
const metadataPath = path15.join(graphDir, "metadata.json");
|
|
2414
2703
|
let lastScanTimestamp = null;
|
|
2415
2704
|
try {
|
|
2416
|
-
const raw = JSON.parse(await
|
|
2705
|
+
const raw = JSON.parse(await fs12.readFile(metadataPath, "utf-8"));
|
|
2417
2706
|
lastScanTimestamp = raw.lastScanTimestamp ?? null;
|
|
2418
2707
|
} catch {
|
|
2419
2708
|
}
|
|
@@ -2502,7 +2791,7 @@ var generateAgentDefinitionsDefinition = {
|
|
|
2502
2791
|
}
|
|
2503
2792
|
};
|
|
2504
2793
|
async function handleGenerateAgentDefinitions(input) {
|
|
2505
|
-
const { generateAgentDefinitions } = await import("./generate-agent-definitions-
|
|
2794
|
+
const { generateAgentDefinitions } = await import("./generate-agent-definitions-3PM5EU7V.js");
|
|
2506
2795
|
const platforms = input.platform === "all" || !input.platform ? ["claude-code", "gemini-cli"] : [input.platform];
|
|
2507
2796
|
const results = generateAgentDefinitions({
|
|
2508
2797
|
platforms: [...platforms],
|
|
@@ -2513,8 +2802,8 @@ async function handleGenerateAgentDefinitions(input) {
|
|
|
2513
2802
|
}
|
|
2514
2803
|
|
|
2515
2804
|
// src/mcp/tools/roadmap.ts
|
|
2516
|
-
import * as
|
|
2517
|
-
import * as
|
|
2805
|
+
import * as fs13 from "fs";
|
|
2806
|
+
import * as path16 from "path";
|
|
2518
2807
|
var manageRoadmapDefinition = {
|
|
2519
2808
|
name: "manage_roadmap",
|
|
2520
2809
|
description: "Manage the project roadmap: show, add, update, remove, sync features, or query by filter. Reads and writes docs/roadmap.md.",
|
|
@@ -2569,21 +2858,21 @@ var manageRoadmapDefinition = {
|
|
|
2569
2858
|
}
|
|
2570
2859
|
};
|
|
2571
2860
|
function roadmapPath(projectRoot) {
|
|
2572
|
-
return
|
|
2861
|
+
return path16.join(projectRoot, "docs", "roadmap.md");
|
|
2573
2862
|
}
|
|
2574
2863
|
function readRoadmapFile(projectRoot) {
|
|
2575
2864
|
const filePath = roadmapPath(projectRoot);
|
|
2576
2865
|
try {
|
|
2577
|
-
return
|
|
2866
|
+
return fs13.readFileSync(filePath, "utf-8");
|
|
2578
2867
|
} catch {
|
|
2579
2868
|
return null;
|
|
2580
2869
|
}
|
|
2581
2870
|
}
|
|
2582
2871
|
function writeRoadmapFile(projectRoot, content) {
|
|
2583
2872
|
const filePath = roadmapPath(projectRoot);
|
|
2584
|
-
const dir =
|
|
2585
|
-
|
|
2586
|
-
|
|
2873
|
+
const dir = path16.dirname(filePath);
|
|
2874
|
+
fs13.mkdirSync(dir, { recursive: true });
|
|
2875
|
+
fs13.writeFileSync(filePath, content, "utf-8");
|
|
2587
2876
|
}
|
|
2588
2877
|
function roadmapNotFoundError() {
|
|
2589
2878
|
return {
|
|
@@ -2596,7 +2885,7 @@ function roadmapNotFoundError() {
|
|
|
2596
2885
|
isError: true
|
|
2597
2886
|
};
|
|
2598
2887
|
}
|
|
2599
|
-
function
|
|
2888
|
+
function handleShow2(projectPath, input, deps) {
|
|
2600
2889
|
const { parseRoadmap, Ok: Ok2 } = deps;
|
|
2601
2890
|
const raw = readRoadmapFile(projectPath);
|
|
2602
2891
|
if (raw === null) return roadmapNotFoundError();
|
|
@@ -2807,13 +3096,13 @@ function handleSync(projectPath, input, deps) {
|
|
|
2807
3096
|
}
|
|
2808
3097
|
async function handleManageRoadmap(input) {
|
|
2809
3098
|
try {
|
|
2810
|
-
const { parseRoadmap, serializeRoadmap, syncRoadmap } = await import("./dist-
|
|
2811
|
-
const { Ok: Ok2 } = await import("./dist-
|
|
3099
|
+
const { parseRoadmap, serializeRoadmap, syncRoadmap } = await import("./dist-HWXF2C3R.js");
|
|
3100
|
+
const { Ok: Ok2 } = await import("./dist-USY2C5JL.js");
|
|
2812
3101
|
const projectPath = sanitizePath(input.path);
|
|
2813
3102
|
const deps = { parseRoadmap, serializeRoadmap, syncRoadmap, Ok: Ok2 };
|
|
2814
3103
|
switch (input.action) {
|
|
2815
3104
|
case "show":
|
|
2816
|
-
return
|
|
3105
|
+
return handleShow2(projectPath, input, deps);
|
|
2817
3106
|
case "add":
|
|
2818
3107
|
return handleAdd(projectPath, input, deps);
|
|
2819
3108
|
case "update":
|
|
@@ -3193,226 +3482,119 @@ var emitInteractionDefinition = {
|
|
|
3193
3482
|
required: ["path", "type"]
|
|
3194
3483
|
}
|
|
3195
3484
|
};
|
|
3485
|
+
function formatZodErrors(issues) {
|
|
3486
|
+
return issues.map((i) => i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message).join("; ");
|
|
3487
|
+
}
|
|
3488
|
+
async function handleQuestion(validInput, projectPath, id) {
|
|
3489
|
+
if (!validInput.question)
|
|
3490
|
+
return mcpError("Error: question payload is required when type is question");
|
|
3491
|
+
const questionResult = InteractionQuestionWithOptionsSchema.safeParse(validInput.question);
|
|
3492
|
+
if (!questionResult.success)
|
|
3493
|
+
return mcpError(`Error: ${formatZodErrors(questionResult.error.issues)}`);
|
|
3494
|
+
const prompt = renderQuestion(questionResult.data);
|
|
3495
|
+
await recordInteraction(projectPath, id, "question", questionResult.data.text, validInput.stream);
|
|
3496
|
+
return { content: [{ type: "text", text: JSON.stringify({ id, prompt }) }] };
|
|
3497
|
+
}
|
|
3498
|
+
async function handleConfirmation(validInput, projectPath, id) {
|
|
3499
|
+
if (!validInput.confirmation)
|
|
3500
|
+
return mcpError("Error: confirmation payload is required when type is confirmation");
|
|
3501
|
+
const confirmResult = InteractionConfirmationSchema.safeParse(validInput.confirmation);
|
|
3502
|
+
if (!confirmResult.success)
|
|
3503
|
+
return mcpError(
|
|
3504
|
+
`Error: ${confirmResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3505
|
+
);
|
|
3506
|
+
const prompt = renderConfirmation(confirmResult.data);
|
|
3507
|
+
await recordInteraction(
|
|
3508
|
+
projectPath,
|
|
3509
|
+
id,
|
|
3510
|
+
"confirmation",
|
|
3511
|
+
confirmResult.data.text,
|
|
3512
|
+
validInput.stream
|
|
3513
|
+
);
|
|
3514
|
+
return { content: [{ type: "text", text: JSON.stringify({ id, prompt }) }] };
|
|
3515
|
+
}
|
|
3516
|
+
async function handleTransition(validInput, projectPath, id) {
|
|
3517
|
+
if (!validInput.transition)
|
|
3518
|
+
return mcpError("Error: transition payload is required when type is transition");
|
|
3519
|
+
const transitionResult = InteractionTransitionSchema.safeParse(validInput.transition);
|
|
3520
|
+
if (!transitionResult.success)
|
|
3521
|
+
return mcpError(
|
|
3522
|
+
`Error: ${transitionResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3523
|
+
);
|
|
3524
|
+
const transition = transitionResult.data;
|
|
3525
|
+
const prompt = renderTransition(transition);
|
|
3526
|
+
try {
|
|
3527
|
+
const { saveHandoff } = await import("./dist-HWXF2C3R.js");
|
|
3528
|
+
await saveHandoff(
|
|
3529
|
+
projectPath,
|
|
3530
|
+
{
|
|
3531
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3532
|
+
fromSkill: "emit_interaction",
|
|
3533
|
+
phase: transition.completedPhase,
|
|
3534
|
+
summary: transition.reason,
|
|
3535
|
+
completed: [transition.completedPhase],
|
|
3536
|
+
pending: [transition.suggestedNext],
|
|
3537
|
+
concerns: [],
|
|
3538
|
+
decisions: [],
|
|
3539
|
+
blockers: [],
|
|
3540
|
+
contextKeywords: []
|
|
3541
|
+
},
|
|
3542
|
+
validInput.stream,
|
|
3543
|
+
validInput.session
|
|
3544
|
+
);
|
|
3545
|
+
} catch {
|
|
3546
|
+
}
|
|
3547
|
+
await recordInteraction(
|
|
3548
|
+
projectPath,
|
|
3549
|
+
id,
|
|
3550
|
+
"transition",
|
|
3551
|
+
`${transition.completedPhase} -> ${transition.suggestedNext}`,
|
|
3552
|
+
validInput.stream
|
|
3553
|
+
);
|
|
3554
|
+
const responsePayload = { id, prompt, handoffWritten: true };
|
|
3555
|
+
if (!transition.requiresConfirmation) {
|
|
3556
|
+
responsePayload.autoTransition = true;
|
|
3557
|
+
responsePayload.nextAction = `Invoke harness-${transition.suggestedNext} skill now`;
|
|
3558
|
+
}
|
|
3559
|
+
return { content: [{ type: "text", text: JSON.stringify(responsePayload) }] };
|
|
3560
|
+
}
|
|
3561
|
+
async function handleBatch(validInput, projectPath, id) {
|
|
3562
|
+
if (!validInput.batch) return mcpError("Error: batch payload is required when type is batch");
|
|
3563
|
+
const batchResult = InteractionBatchSchema.safeParse(validInput.batch);
|
|
3564
|
+
if (!batchResult.success)
|
|
3565
|
+
return mcpError(
|
|
3566
|
+
`Error: ${batchResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3567
|
+
);
|
|
3568
|
+
const prompt = renderBatch(batchResult.data);
|
|
3569
|
+
await recordInteraction(projectPath, id, "batch", batchResult.data.text, validInput.stream);
|
|
3570
|
+
return {
|
|
3571
|
+
content: [{ type: "text", text: JSON.stringify({ id, prompt, batchMode: true }) }]
|
|
3572
|
+
};
|
|
3573
|
+
}
|
|
3574
|
+
var INTERACTION_HANDLERS = {
|
|
3575
|
+
question: handleQuestion,
|
|
3576
|
+
confirmation: handleConfirmation,
|
|
3577
|
+
transition: handleTransition,
|
|
3578
|
+
batch: handleBatch
|
|
3579
|
+
};
|
|
3196
3580
|
async function handleEmitInteraction(input) {
|
|
3197
3581
|
try {
|
|
3198
3582
|
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
|
-
}
|
|
3583
|
+
if (!parseResult.success)
|
|
3584
|
+
return mcpError(`Error: ${formatZodErrors(parseResult.error.issues)}`);
|
|
3210
3585
|
const validInput = parseResult.data;
|
|
3211
3586
|
const projectPath = sanitizePath(validInput.path);
|
|
3212
3587
|
const id = randomUUID();
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
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
|
-
}
|
|
3588
|
+
const handler = INTERACTION_HANDLERS[validInput.type];
|
|
3589
|
+
if (!handler) return mcpError(`Error: unknown interaction type: ${String(validInput.type)}`);
|
|
3590
|
+
return await handler(validInput, projectPath, id);
|
|
3401
3591
|
} 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
|
-
};
|
|
3592
|
+
return mcpError(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
3411
3593
|
}
|
|
3412
3594
|
}
|
|
3413
3595
|
async function recordInteraction(projectPath, id, type, decision, stream) {
|
|
3414
3596
|
try {
|
|
3415
|
-
const { loadState, saveState } = await import("./dist-
|
|
3597
|
+
const { loadState, saveState } = await import("./dist-HWXF2C3R.js");
|
|
3416
3598
|
const stateResult = await loadState(projectPath, stream);
|
|
3417
3599
|
if (stateResult.ok) {
|
|
3418
3600
|
const state = stateResult.value;
|
|
@@ -3430,7 +3612,7 @@ async function recordInteraction(projectPath, id, type, decision, stream) {
|
|
|
3430
3612
|
// src/mcp/tools/gather-context.ts
|
|
3431
3613
|
var gatherContextDefinition = {
|
|
3432
3614
|
name: "gather_context",
|
|
3433
|
-
description: "Assemble all working context an agent needs in a single call: state, learnings, handoff, graph context, and
|
|
3615
|
+
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
3616
|
inputSchema: {
|
|
3435
3617
|
type: "object",
|
|
3436
3618
|
properties: {
|
|
@@ -3451,7 +3633,7 @@ var gatherContextDefinition = {
|
|
|
3451
3633
|
type: "array",
|
|
3452
3634
|
items: {
|
|
3453
3635
|
type: "string",
|
|
3454
|
-
enum: ["state", "learnings", "handoff", "graph", "validation"]
|
|
3636
|
+
enum: ["state", "learnings", "handoff", "graph", "validation", "sessions"]
|
|
3455
3637
|
},
|
|
3456
3638
|
description: "Which constituents to include (default: all)"
|
|
3457
3639
|
},
|
|
@@ -3492,10 +3674,10 @@ async function handleGatherContext(input) {
|
|
|
3492
3674
|
input.include ?? ["state", "learnings", "handoff", "graph", "validation"]
|
|
3493
3675
|
);
|
|
3494
3676
|
const errors = [];
|
|
3495
|
-
const statePromise = includeSet.has("state") ? import("./dist-
|
|
3677
|
+
const statePromise = includeSet.has("state") ? import("./dist-HWXF2C3R.js").then(
|
|
3496
3678
|
(core) => core.loadState(projectPath, void 0, input.session)
|
|
3497
3679
|
) : Promise.resolve(null);
|
|
3498
|
-
const learningsPromise = includeSet.has("learnings") ? import("./dist-
|
|
3680
|
+
const learningsPromise = includeSet.has("learnings") ? import("./dist-HWXF2C3R.js").then(
|
|
3499
3681
|
(core) => core.loadBudgetedLearnings(projectPath, {
|
|
3500
3682
|
intent: input.intent,
|
|
3501
3683
|
tokenBudget: input.learningsBudget ?? 1e3,
|
|
@@ -3503,14 +3685,14 @@ async function handleGatherContext(input) {
|
|
|
3503
3685
|
...input.session !== void 0 && { session: input.session }
|
|
3504
3686
|
})
|
|
3505
3687
|
) : Promise.resolve(null);
|
|
3506
|
-
const handoffPromise = includeSet.has("handoff") ? import("./dist-
|
|
3688
|
+
const handoffPromise = includeSet.has("handoff") ? import("./dist-HWXF2C3R.js").then(
|
|
3507
3689
|
(core) => core.loadHandoff(projectPath, void 0, input.session)
|
|
3508
3690
|
) : Promise.resolve(null);
|
|
3509
3691
|
const graphPromise = includeSet.has("graph") ? (async () => {
|
|
3510
|
-
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-
|
|
3692
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-2M2HXDQI.js");
|
|
3511
3693
|
const store = await loadGraphStore2(projectPath);
|
|
3512
3694
|
if (!store) return null;
|
|
3513
|
-
const { FusionLayer, ContextQL } = await import("./dist-
|
|
3695
|
+
const { FusionLayer, ContextQL } = await import("./dist-B26DFXMP.js");
|
|
3514
3696
|
const fusion = new FusionLayer(store);
|
|
3515
3697
|
const cql = new ContextQL(store);
|
|
3516
3698
|
const tokenBudget = input.tokenBudget ?? 4e3;
|
|
@@ -3547,18 +3729,29 @@ async function handleGatherContext(input) {
|
|
|
3547
3729
|
context: contextBlocks
|
|
3548
3730
|
};
|
|
3549
3731
|
})() : Promise.resolve(null);
|
|
3732
|
+
const sessionsPromise = includeSet.has("sessions") && input.session ? import("./dist-HWXF2C3R.js").then(
|
|
3733
|
+
(core) => core.readSessionSections(projectPath, input.session)
|
|
3734
|
+
) : Promise.resolve(null);
|
|
3550
3735
|
const validationPromise = includeSet.has("validation") ? (async () => {
|
|
3551
|
-
const { handleValidateProject: handleValidateProject2 } = await import("./validate-
|
|
3736
|
+
const { handleValidateProject: handleValidateProject2 } = await import("./validate-FD3Z6VJD.js");
|
|
3552
3737
|
const result = await handleValidateProject2({ path: projectPath });
|
|
3553
3738
|
const first = result.content[0];
|
|
3554
3739
|
return first ? JSON.parse(first.text) : null;
|
|
3555
3740
|
})() : Promise.resolve(null);
|
|
3556
|
-
const [
|
|
3741
|
+
const [
|
|
3742
|
+
stateResult,
|
|
3743
|
+
learningsResult,
|
|
3744
|
+
handoffResult,
|
|
3745
|
+
graphResult,
|
|
3746
|
+
validationResult,
|
|
3747
|
+
sessionsResult
|
|
3748
|
+
] = await Promise.allSettled([
|
|
3557
3749
|
statePromise,
|
|
3558
3750
|
learningsPromise,
|
|
3559
3751
|
handoffPromise,
|
|
3560
3752
|
graphPromise,
|
|
3561
|
-
validationPromise
|
|
3753
|
+
validationPromise,
|
|
3754
|
+
sessionsPromise
|
|
3562
3755
|
]);
|
|
3563
3756
|
function extract(settled, name) {
|
|
3564
3757
|
if (settled.status === "rejected") {
|
|
@@ -3572,6 +3765,7 @@ async function handleGatherContext(input) {
|
|
|
3572
3765
|
const handoffRaw = extract(handoffResult, "handoff");
|
|
3573
3766
|
const graphContextRaw = extract(graphResult, "graph");
|
|
3574
3767
|
const validationRaw = extract(validationResult, "validation");
|
|
3768
|
+
const sessionsRaw = extract(sessionsResult, "sessions");
|
|
3575
3769
|
const state = stateRaw && typeof stateRaw === "object" && "ok" in stateRaw ? stateRaw.ok ? stateRaw.value : (() => {
|
|
3576
3770
|
errors.push(`state: ${stateRaw.error.message}`);
|
|
3577
3771
|
return null;
|
|
@@ -3588,6 +3782,12 @@ async function handleGatherContext(input) {
|
|
|
3588
3782
|
})() : handoffRaw;
|
|
3589
3783
|
const graphContext = graphContextRaw;
|
|
3590
3784
|
const validation = validationRaw;
|
|
3785
|
+
const sessionSections = sessionsRaw && typeof sessionsRaw === "object" && "ok" in sessionsRaw ? sessionsRaw.ok ? sessionsRaw.value : (() => {
|
|
3786
|
+
errors.push(
|
|
3787
|
+
`sessions: ${sessionsRaw.error.message}`
|
|
3788
|
+
);
|
|
3789
|
+
return null;
|
|
3790
|
+
})() : sessionsRaw;
|
|
3591
3791
|
const assembledIn = Date.now() - start;
|
|
3592
3792
|
const mode = input.mode ?? "summary";
|
|
3593
3793
|
const outputState = state ?? null;
|
|
@@ -3612,6 +3812,7 @@ async function handleGatherContext(input) {
|
|
|
3612
3812
|
handoff: outputHandoff,
|
|
3613
3813
|
graphContext: outputGraphContext,
|
|
3614
3814
|
validation: outputValidation,
|
|
3815
|
+
sessionSections: sessionSections ?? null,
|
|
3615
3816
|
meta: {
|
|
3616
3817
|
assembledIn,
|
|
3617
3818
|
graphAvailable: graphContext !== null,
|
|
@@ -3622,7 +3823,7 @@ async function handleGatherContext(input) {
|
|
|
3622
3823
|
};
|
|
3623
3824
|
if (input.session) {
|
|
3624
3825
|
try {
|
|
3625
|
-
const core = await import("./dist-
|
|
3826
|
+
const core = await import("./dist-HWXF2C3R.js");
|
|
3626
3827
|
core.updateSessionIndex(
|
|
3627
3828
|
projectPath,
|
|
3628
3829
|
input.session,
|
|
@@ -3692,7 +3893,7 @@ async function handleAssessProject(input) {
|
|
|
3692
3893
|
let validateResult = null;
|
|
3693
3894
|
if (checksToRun.has("validate")) {
|
|
3694
3895
|
try {
|
|
3695
|
-
const { handleValidateProject: handleValidateProject2 } = await import("./validate-
|
|
3896
|
+
const { handleValidateProject: handleValidateProject2 } = await import("./validate-FD3Z6VJD.js");
|
|
3696
3897
|
const result = await handleValidateProject2({ path: projectPath });
|
|
3697
3898
|
const first = result.content[0];
|
|
3698
3899
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3717,7 +3918,7 @@ async function handleAssessProject(input) {
|
|
|
3717
3918
|
parallelChecks.push(
|
|
3718
3919
|
(async () => {
|
|
3719
3920
|
try {
|
|
3720
|
-
const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-
|
|
3921
|
+
const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-JQZYM4US.js");
|
|
3721
3922
|
const result = await handleCheckDependencies2({ path: projectPath });
|
|
3722
3923
|
const first = result.content[0];
|
|
3723
3924
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3744,7 +3945,7 @@ async function handleAssessProject(input) {
|
|
|
3744
3945
|
parallelChecks.push(
|
|
3745
3946
|
(async () => {
|
|
3746
3947
|
try {
|
|
3747
|
-
const { handleCheckDocs: handleCheckDocs2 } = await import("./docs-
|
|
3948
|
+
const { handleCheckDocs: handleCheckDocs2 } = await import("./docs-7ECGYMAV.js");
|
|
3748
3949
|
const result = await handleCheckDocs2({ path: projectPath, scope: "coverage" });
|
|
3749
3950
|
const first = result.content[0];
|
|
3750
3951
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3771,14 +3972,14 @@ async function handleAssessProject(input) {
|
|
|
3771
3972
|
parallelChecks.push(
|
|
3772
3973
|
(async () => {
|
|
3773
3974
|
try {
|
|
3774
|
-
const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-
|
|
3975
|
+
const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-5USWKLVS.js");
|
|
3775
3976
|
const result = await handleDetectEntropy2({ path: projectPath, type: "all" });
|
|
3776
3977
|
const first = result.content[0];
|
|
3777
3978
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
3778
3979
|
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
3980
|
return {
|
|
3780
3981
|
name: "entropy",
|
|
3781
|
-
passed: !result.isError && issues === 0,
|
|
3982
|
+
passed: !("isError" in result && result.isError) && issues === 0,
|
|
3782
3983
|
issueCount: issues,
|
|
3783
3984
|
...issues > 0 ? { topIssue: "Entropy detected -- run detect_entropy for details" } : {},
|
|
3784
3985
|
...mode === "detailed" ? { detailed: parsed } : {}
|
|
@@ -3798,7 +3999,7 @@ async function handleAssessProject(input) {
|
|
|
3798
3999
|
parallelChecks.push(
|
|
3799
4000
|
(async () => {
|
|
3800
4001
|
try {
|
|
3801
|
-
const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-
|
|
4002
|
+
const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-PZOX7AQS.js");
|
|
3802
4003
|
const result = await handleRunSecurityScan2({ path: projectPath });
|
|
3803
4004
|
const first = result.content[0];
|
|
3804
4005
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3830,14 +4031,14 @@ async function handleAssessProject(input) {
|
|
|
3830
4031
|
parallelChecks.push(
|
|
3831
4032
|
(async () => {
|
|
3832
4033
|
try {
|
|
3833
|
-
const { handleCheckPerformance: handleCheckPerformance2 } = await import("./performance-
|
|
4034
|
+
const { handleCheckPerformance: handleCheckPerformance2 } = await import("./performance-OQAFMJUD.js");
|
|
3834
4035
|
const result = await handleCheckPerformance2({ path: projectPath });
|
|
3835
4036
|
const first = result.content[0];
|
|
3836
4037
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
3837
4038
|
const issues = parsed.violations?.length ?? parsed.issues?.length ?? 0;
|
|
3838
4039
|
return {
|
|
3839
4040
|
name: "perf",
|
|
3840
|
-
passed: !result.isError && issues === 0,
|
|
4041
|
+
passed: !("isError" in result && result.isError) && issues === 0,
|
|
3841
4042
|
issueCount: issues,
|
|
3842
4043
|
...issues > 0 ? { topIssue: "Performance issues detected" } : {},
|
|
3843
4044
|
...mode === "detailed" ? { detailed: parsed } : {}
|
|
@@ -4004,81 +4205,8 @@ async function handleReviewChanges(input) {
|
|
|
4004
4205
|
downgraded = true;
|
|
4005
4206
|
}
|
|
4006
4207
|
try {
|
|
4007
|
-
|
|
4008
|
-
|
|
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
|
-
};
|
|
4208
|
+
const reviewFn = DEPTH_HANDLERS[effectiveDepth];
|
|
4209
|
+
return await reviewFn(projectPath, diff, diffLines, downgraded);
|
|
4082
4210
|
} catch (error) {
|
|
4083
4211
|
return {
|
|
4084
4212
|
content: [
|
|
@@ -4091,6 +4219,97 @@ async function handleReviewChanges(input) {
|
|
|
4091
4219
|
};
|
|
4092
4220
|
}
|
|
4093
4221
|
}
|
|
4222
|
+
async function runQuickReview(projectPath, diff, diffLines, downgraded) {
|
|
4223
|
+
const { handleAnalyzeDiff: handleAnalyzeDiff2 } = await import("./feedback-UTBXZZHF.js");
|
|
4224
|
+
const result = await handleAnalyzeDiff2({ diff, path: projectPath });
|
|
4225
|
+
const firstContent = result.content[0];
|
|
4226
|
+
if (!firstContent) throw new Error("Empty analyze_diff response");
|
|
4227
|
+
const parsed = JSON.parse(firstContent.text);
|
|
4228
|
+
return {
|
|
4229
|
+
content: [
|
|
4230
|
+
{
|
|
4231
|
+
type: "text",
|
|
4232
|
+
text: JSON.stringify({
|
|
4233
|
+
depth: "quick",
|
|
4234
|
+
downgraded,
|
|
4235
|
+
findings: parsed.findings ?? parsed.warnings ?? [],
|
|
4236
|
+
fileCount: parsed.summary?.filesChanged ?? parsed.files?.length ?? 0,
|
|
4237
|
+
lineCount: diffLines,
|
|
4238
|
+
...result.isError ? { error: parsed } : {}
|
|
4239
|
+
})
|
|
4240
|
+
}
|
|
4241
|
+
]
|
|
4242
|
+
};
|
|
4243
|
+
}
|
|
4244
|
+
function extractFindings(parsed, primaryKey, fallbackKey) {
|
|
4245
|
+
return parsed[primaryKey] ?? parsed[fallbackKey] ?? [];
|
|
4246
|
+
}
|
|
4247
|
+
function extractFileCount(diffParsed) {
|
|
4248
|
+
const summary = diffParsed.summary;
|
|
4249
|
+
if (summary?.filesChanged !== void 0) return summary.filesChanged;
|
|
4250
|
+
const files = diffParsed.files;
|
|
4251
|
+
return files?.length ?? 0;
|
|
4252
|
+
}
|
|
4253
|
+
async function runStandardReview(projectPath, diff, diffLines, downgraded) {
|
|
4254
|
+
const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-UTBXZZHF.js");
|
|
4255
|
+
const [diffResult, reviewResult] = await Promise.all([
|
|
4256
|
+
handleAnalyzeDiff2({ diff, path: projectPath }),
|
|
4257
|
+
handleCreateSelfReview2({ path: projectPath, diff })
|
|
4258
|
+
]);
|
|
4259
|
+
const diffContent = diffResult.content[0];
|
|
4260
|
+
const reviewContent = reviewResult.content[0];
|
|
4261
|
+
if (!diffContent || !reviewContent) throw new Error("Empty review response");
|
|
4262
|
+
const diffParsed = JSON.parse(diffContent.text);
|
|
4263
|
+
const reviewParsed = JSON.parse(reviewContent.text);
|
|
4264
|
+
const findings = [
|
|
4265
|
+
...extractFindings(diffParsed, "findings", "warnings"),
|
|
4266
|
+
...extractFindings(reviewParsed, "findings", "items")
|
|
4267
|
+
];
|
|
4268
|
+
return {
|
|
4269
|
+
content: [
|
|
4270
|
+
{
|
|
4271
|
+
type: "text",
|
|
4272
|
+
text: JSON.stringify({
|
|
4273
|
+
depth: "standard",
|
|
4274
|
+
downgraded,
|
|
4275
|
+
findings,
|
|
4276
|
+
diffAnalysis: diffParsed,
|
|
4277
|
+
selfReview: reviewParsed,
|
|
4278
|
+
fileCount: extractFileCount(diffParsed),
|
|
4279
|
+
lineCount: diffLines
|
|
4280
|
+
})
|
|
4281
|
+
}
|
|
4282
|
+
]
|
|
4283
|
+
};
|
|
4284
|
+
}
|
|
4285
|
+
async function runDeepReview(projectPath, diff, diffLines, _downgraded) {
|
|
4286
|
+
const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-C4GCFVGP.js");
|
|
4287
|
+
const result = await handleRunCodeReview2({ path: projectPath, diff });
|
|
4288
|
+
const deepContent = result.content[0];
|
|
4289
|
+
if (!deepContent) throw new Error("Empty code review response");
|
|
4290
|
+
const parsed = JSON.parse(deepContent.text);
|
|
4291
|
+
return {
|
|
4292
|
+
content: [
|
|
4293
|
+
{
|
|
4294
|
+
type: "text",
|
|
4295
|
+
text: JSON.stringify({
|
|
4296
|
+
depth: "deep",
|
|
4297
|
+
downgraded: false,
|
|
4298
|
+
findings: parsed.findings ?? [],
|
|
4299
|
+
assessment: parsed.assessment,
|
|
4300
|
+
findingCount: parsed.findingCount,
|
|
4301
|
+
lineCount: diffLines,
|
|
4302
|
+
pipeline: parsed
|
|
4303
|
+
})
|
|
4304
|
+
}
|
|
4305
|
+
]
|
|
4306
|
+
};
|
|
4307
|
+
}
|
|
4308
|
+
var DEPTH_HANDLERS = {
|
|
4309
|
+
quick: runQuickReview,
|
|
4310
|
+
standard: runStandardReview,
|
|
4311
|
+
deep: runDeepReview
|
|
4312
|
+
};
|
|
4094
4313
|
|
|
4095
4314
|
// src/mcp/tools/task-independence.ts
|
|
4096
4315
|
var checkTaskIndependenceDefinition = {
|
|
@@ -4135,7 +4354,7 @@ async function handleCheckTaskIndependence(input) {
|
|
|
4135
4354
|
try {
|
|
4136
4355
|
const projectPath = sanitizePath(input.path);
|
|
4137
4356
|
const store = await loadGraphStore(projectPath);
|
|
4138
|
-
const { TaskIndependenceAnalyzer } = await import("./dist-
|
|
4357
|
+
const { TaskIndependenceAnalyzer } = await import("./dist-B26DFXMP.js");
|
|
4139
4358
|
const analyzer = new TaskIndependenceAnalyzer(store ?? void 0);
|
|
4140
4359
|
const result = analyzer.analyze({
|
|
4141
4360
|
tasks: input.tasks,
|
|
@@ -4223,7 +4442,7 @@ async function handlePredictConflicts(input) {
|
|
|
4223
4442
|
try {
|
|
4224
4443
|
const projectPath = sanitizePath(input.path);
|
|
4225
4444
|
const store = await loadGraphStore(projectPath);
|
|
4226
|
-
const { ConflictPredictor } = await import("./dist-
|
|
4445
|
+
const { ConflictPredictor } = await import("./dist-B26DFXMP.js");
|
|
4227
4446
|
const predictor = new ConflictPredictor(store ?? void 0);
|
|
4228
4447
|
const result = predictor.predict({
|
|
4229
4448
|
tasks: input.tasks,
|
|
@@ -4329,7 +4548,7 @@ async function handleDetectStaleConstraints(input) {
|
|
|
4329
4548
|
isError: true
|
|
4330
4549
|
};
|
|
4331
4550
|
}
|
|
4332
|
-
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-
|
|
4551
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-2M2HXDQI.js");
|
|
4333
4552
|
const store = await loadGraphStore2(projectPath);
|
|
4334
4553
|
if (!store) {
|
|
4335
4554
|
return {
|
|
@@ -4350,7 +4569,7 @@ async function handleDetectStaleConstraints(input) {
|
|
|
4350
4569
|
]
|
|
4351
4570
|
};
|
|
4352
4571
|
}
|
|
4353
|
-
const { detectStaleConstraints } = await import("./dist-
|
|
4572
|
+
const { detectStaleConstraints } = await import("./dist-HWXF2C3R.js");
|
|
4354
4573
|
const result = detectStaleConstraints(
|
|
4355
4574
|
store,
|
|
4356
4575
|
windowDays,
|
|
@@ -4607,6 +4826,45 @@ var RESOURCE_HANDLERS = {
|
|
|
4607
4826
|
function getToolDefinitions() {
|
|
4608
4827
|
return TOOL_DEFINITIONS;
|
|
4609
4828
|
}
|
|
4829
|
+
function readConfigInterval(resolvedRoot) {
|
|
4830
|
+
try {
|
|
4831
|
+
const configResult = resolveProjectConfig(resolvedRoot);
|
|
4832
|
+
if (configResult.ok) {
|
|
4833
|
+
const raw = configResult.value.updateCheckInterval;
|
|
4834
|
+
if (typeof raw === "number" && Number.isInteger(raw) && raw >= 0) {
|
|
4835
|
+
return raw;
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
} catch {
|
|
4839
|
+
}
|
|
4840
|
+
return void 0;
|
|
4841
|
+
}
|
|
4842
|
+
async function appendUpdateNotification(result, resolvedRoot) {
|
|
4843
|
+
try {
|
|
4844
|
+
const {
|
|
4845
|
+
getUpdateNotification,
|
|
4846
|
+
isUpdateCheckEnabled,
|
|
4847
|
+
shouldRunCheck,
|
|
4848
|
+
readCheckState,
|
|
4849
|
+
spawnBackgroundCheck
|
|
4850
|
+
} = await import("./dist-HWXF2C3R.js");
|
|
4851
|
+
const { CLI_VERSION } = await import("./version-KFFPOQAX.js");
|
|
4852
|
+
const configInterval = readConfigInterval(resolvedRoot);
|
|
4853
|
+
const DEFAULT_INTERVAL = 864e5;
|
|
4854
|
+
if (!isUpdateCheckEnabled(configInterval)) return;
|
|
4855
|
+
const state = readCheckState();
|
|
4856
|
+
if (shouldRunCheck(state, configInterval ?? DEFAULT_INTERVAL)) {
|
|
4857
|
+
spawnBackgroundCheck(CLI_VERSION);
|
|
4858
|
+
}
|
|
4859
|
+
const notification = getUpdateNotification(CLI_VERSION);
|
|
4860
|
+
if (notification) {
|
|
4861
|
+
result.content.push({ type: "text", text: `
|
|
4862
|
+
---
|
|
4863
|
+
${notification}` });
|
|
4864
|
+
}
|
|
4865
|
+
} catch {
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4610
4868
|
function createHarnessServer(projectRoot) {
|
|
4611
4869
|
const resolvedRoot = projectRoot ?? process.cwd();
|
|
4612
4870
|
let sessionChecked = false;
|
|
@@ -4626,42 +4884,7 @@ function createHarnessServer(projectRoot) {
|
|
|
4626
4884
|
const result = await handler(args ?? {});
|
|
4627
4885
|
if (!sessionChecked) {
|
|
4628
4886
|
sessionChecked = true;
|
|
4629
|
-
|
|
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
|
-
}
|
|
4887
|
+
await appendUpdateNotification(result, resolvedRoot);
|
|
4665
4888
|
}
|
|
4666
4889
|
return result;
|
|
4667
4890
|
});
|
|
@@ -4690,6 +4913,8 @@ async function startServer() {
|
|
|
4690
4913
|
}
|
|
4691
4914
|
|
|
4692
4915
|
export {
|
|
4916
|
+
persistToolingConfig,
|
|
4917
|
+
appendFrameworkAgents,
|
|
4693
4918
|
generateSlashCommands,
|
|
4694
4919
|
handleOrphanDeletion,
|
|
4695
4920
|
createGenerateSlashCommandsCommand,
|