@harness-engineering/cli 1.13.1 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +240 -39
- package/dist/agents/skills/claude-code/harness-autopilot/skill.yaml +6 -0
- 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-product-spec/SKILL.md +5 -5
- 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-autopilot/SKILL.md +240 -39
- package/dist/agents/skills/gemini-cli/harness-autopilot/skill.yaml +6 -0
- 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-product-spec/SKILL.md +5 -5
- 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/skills/package.json +1 -0
- package/dist/agents/skills/vitest.config.mts +5 -0
- package/dist/agents-md-ZGNIDWAF.js +8 -0
- package/dist/{architecture-2R5Z4ZAF.js → architecture-ZLIH5533.js} +4 -4
- package/dist/bin/harness-mcp.js +14 -14
- package/dist/bin/harness.js +27 -25
- package/dist/{check-phase-gate-2OFZ7OWW.js → check-phase-gate-ZOXVBDCN.js} +4 -4
- package/dist/{chunk-ND6PNADU.js → chunk-2BKLWLY6.js} +9 -9
- package/dist/{chunk-65FRIL4D.js → chunk-3ZZKVN62.js} +1 -1
- package/dist/{chunk-C2ERUR3L.js → chunk-7MJAPE3Z.js} +165 -49
- package/dist/{chunk-Z77YQRQT.js → chunk-B2HKP423.js} +16 -5
- package/dist/{chunk-QPEH2QPG.js → chunk-DBSOCI3G.js} +53 -54
- package/dist/{chunk-TKJZKICB.js → chunk-EDXIVMAP.js} +7 -7
- 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-SSKDAOX5.js → chunk-J4RAX7YB.js} +1164 -516
- package/dist/{chunk-UAX4I5ZE.js → chunk-LGYBN7Y6.js} +2 -2
- package/dist/{chunk-QY4T6YAZ.js → chunk-N25INEIX.js} +4 -4
- package/dist/{chunk-4ZMOCPYO.js → chunk-ND2ENWDM.js} +1 -1
- package/dist/{chunk-NERR4TAO.js → chunk-NNHDDXYT.js} +1250 -765
- package/dist/{chunk-NKDM3FMH.js → chunk-OD3S2NHN.js} +1 -1
- package/dist/{chunk-NOPU4RZ4.js → chunk-OFXQSFOW.js} +3 -3
- package/dist/{chunk-TS3XWPW5.js → chunk-RCWZBSK5.js} +1 -1
- package/dist/{chunk-VUCPTQ6G.js → chunk-SD3SQOZ2.js} +1 -1
- package/dist/{chunk-DZS7CJKL.js → chunk-VEPAJXBW.js} +45 -47
- package/dist/{chunk-IM32EEDM.js → chunk-YLXFKVJE.js} +9 -9
- package/dist/{chunk-Q6AB7W5Z.js → chunk-YQ6KC6TE.js} +1 -1
- package/dist/{chunk-PQ5YK4AY.js → chunk-Z2OOPXJO.js} +2740 -1221
- package/dist/ci-workflow-765LSHRD.js +8 -0
- package/dist/{dist-2B363XUH.js → dist-ALQDD67R.js} +64 -2
- package/dist/{dist-HXHWB7SV.js → dist-B26DFXMP.js} +571 -478
- package/dist/{dist-L7LAAQAS.js → dist-DZ63LLUD.js} +1 -1
- package/dist/{dist-D4RYGUZE.js → dist-USY2C5JL.js} +3 -1
- package/dist/{docs-FZOPM4GK.js → docs-NRMQCOJ6.js} +4 -4
- package/dist/engine-3RB7MXPP.js +8 -0
- package/dist/{entropy-LVHJMFGH.js → entropy-6AGX2ZUN.js} +3 -3
- package/dist/{feedback-IHLVLMRD.js → feedback-MY4QZIFD.js} +1 -1
- package/dist/{generate-agent-definitions-64S3CLEZ.js → generate-agent-definitions-ZAE726AU.js} +4 -4
- package/dist/{graph-loader-GJZ4FN4Y.js → graph-loader-2M2HXDQI.js} +1 -1
- package/dist/index.d.ts +156 -17
- package/dist/index.js +24 -24
- package/dist/loader-UUTVMQCC.js +10 -0
- package/dist/{mcp-JQUI7BVZ.js → mcp-VU5FMO52.js} +14 -14
- package/dist/{performance-ZTVSUANN.js → performance-2D7G6NMJ.js} +3 -3
- package/dist/{review-pipeline-76JHKGSV.js → review-pipeline-RAQ55ISU.js} +1 -1
- package/dist/runtime-BCK5RRZQ.js +9 -0
- package/dist/{security-FWQZF2IZ.js → security-2RPQEN62.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-KBYQAEWE.js} +4 -4
- package/dist/validate-cross-check-OABMREW4.js +8 -0
- package/package.json +7 -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-VEPAJXBW.js";
|
|
5
5
|
import {
|
|
6
6
|
checkPerformanceDefinition,
|
|
7
7
|
getCriticalPathsDefinition,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
handleGetPerfBaselines,
|
|
12
12
|
handleUpdatePerfBaselines,
|
|
13
13
|
updatePerfBaselinesDefinition
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-EDXIVMAP.js";
|
|
15
15
|
import {
|
|
16
16
|
analyzeDiffDefinition,
|
|
17
17
|
createSelfReviewDefinition,
|
|
@@ -19,15 +19,15 @@ import {
|
|
|
19
19
|
handleCreateSelfReview,
|
|
20
20
|
handleRequestPeerReview,
|
|
21
21
|
requestPeerReviewDefinition
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-YLXFKVJE.js";
|
|
23
23
|
import {
|
|
24
24
|
handleRunSecurityScan,
|
|
25
25
|
runSecurityScanDefinition
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-3ZZKVN62.js";
|
|
27
27
|
import {
|
|
28
28
|
handleRunCodeReview,
|
|
29
29
|
runCodeReviewDefinition
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-ND2ENWDM.js";
|
|
31
31
|
import {
|
|
32
32
|
GENERATED_HEADER_CLAUDE,
|
|
33
33
|
GENERATED_HEADER_GEMINI,
|
|
@@ -38,24 +38,24 @@ import {
|
|
|
38
38
|
import {
|
|
39
39
|
handleValidateProject,
|
|
40
40
|
validateToolDefinition
|
|
41
|
-
} from "./chunk-
|
|
41
|
+
} from "./chunk-OFXQSFOW.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-N25INEIX.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-2BKLWLY6.js";
|
|
56
56
|
import {
|
|
57
57
|
resolveConfig
|
|
58
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-B2HKP423.js";
|
|
59
59
|
import {
|
|
60
60
|
resultToMcpResponse
|
|
61
61
|
} from "./chunk-IDZNPTYD.js";
|
|
@@ -82,7 +82,7 @@ import {
|
|
|
82
82
|
import {
|
|
83
83
|
Err,
|
|
84
84
|
Ok
|
|
85
|
-
} from "./chunk-
|
|
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-3RB7MXPP.js");
|
|
432
|
+
const engine = new TemplateEngine(resolveTemplatesDir());
|
|
433
|
+
const safePath = sanitizePath(i.path);
|
|
434
|
+
const detected = tryDetectFramework(engine, safePath, i);
|
|
435
|
+
if (detected) return detected;
|
|
436
|
+
const conflict = checkFrameworkLanguageConflict(engine, i);
|
|
437
|
+
if (conflict) return conflict;
|
|
438
|
+
return scaffoldMcp(engine, safePath, i, inferLanguage(engine, i));
|
|
204
439
|
} catch (error) {
|
|
205
|
-
return {
|
|
206
|
-
content: [
|
|
207
|
-
{
|
|
208
|
-
type: "text",
|
|
209
|
-
text: `Init failed: ${error instanceof Error ? error.message : String(error)}`
|
|
210
|
-
}
|
|
211
|
-
],
|
|
212
|
-
isError: true
|
|
213
|
-
};
|
|
440
|
+
return mcpText(`Init failed: ${error instanceof Error ? error.message : String(error)}`, true);
|
|
214
441
|
}
|
|
215
442
|
}
|
|
216
443
|
|
|
217
444
|
// src/mcp/tools/persona.ts
|
|
218
|
-
import * as
|
|
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-UUTVMQCC.js");
|
|
226
453
|
const result = listPersonas(resolvePersonasDir());
|
|
227
454
|
return resultToMcpResponse(result);
|
|
228
455
|
}
|
|
@@ -246,12 +473,12 @@ async function handleGeneratePersonaArtifacts(input) {
|
|
|
246
473
|
if (!/^[a-z0-9][a-z0-9._-]*$/i.test(input.name)) {
|
|
247
474
|
return resultToMcpResponse(Err(new Error(`Invalid persona name: ${input.name}`)));
|
|
248
475
|
}
|
|
249
|
-
const { loadPersona } = await import("./loader-
|
|
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-UUTVMQCC.js");
|
|
477
|
+
const { generateRuntime } = await import("./runtime-BCK5RRZQ.js");
|
|
478
|
+
const { generateAgentsMd } = await import("./agents-md-ZGNIDWAF.js");
|
|
479
|
+
const { generateCIWorkflow } = await import("./ci-workflow-765LSHRD.js");
|
|
253
480
|
const personasDir = resolvePersonasDir();
|
|
254
|
-
const filePath =
|
|
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-UUTVMQCC.js");
|
|
308
535
|
const { runPersona } = await import("./runner-VMYLHWOC.js");
|
|
309
536
|
const { executeSkill } = await import("./skill-executor-XZLYZYAK.js");
|
|
310
537
|
const personasDir = resolvePersonasDir();
|
|
311
|
-
const filePath =
|
|
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,57 @@ 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/tools/roadmap-auto-sync.ts
|
|
1146
|
+
import * as fs10 from "fs";
|
|
1147
|
+
import * as path11 from "path";
|
|
1148
|
+
async function autoSyncRoadmap(projectPath) {
|
|
1149
|
+
try {
|
|
1150
|
+
const roadmapFile = path11.join(projectPath, "docs", "roadmap.md");
|
|
1151
|
+
if (!fs10.existsSync(roadmapFile)) return;
|
|
1152
|
+
const { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges } = await import("./dist-ALQDD67R.js");
|
|
1153
|
+
const raw = fs10.readFileSync(roadmapFile, "utf-8");
|
|
1154
|
+
const parseResult = parseRoadmap(raw);
|
|
1155
|
+
if (!parseResult.ok) return;
|
|
1156
|
+
const roadmap = parseResult.value;
|
|
1157
|
+
const syncResult = syncRoadmap({ projectPath, roadmap });
|
|
1158
|
+
if (!syncResult.ok || syncResult.value.length === 0) return;
|
|
1159
|
+
applySyncChanges(roadmap, syncResult.value);
|
|
1160
|
+
fs10.writeFileSync(roadmapFile, serializeRoadmap(roadmap), "utf-8");
|
|
1161
|
+
} catch {
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// src/mcp/utils.ts
|
|
1166
|
+
function mcpError(text) {
|
|
1167
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
1168
|
+
}
|
|
1169
|
+
|
|
918
1170
|
// src/mcp/tools/state.ts
|
|
919
1171
|
var manageStateDefinition = {
|
|
920
1172
|
name: "manage_state",
|
|
@@ -933,7 +1185,12 @@ var manageStateDefinition = {
|
|
|
933
1185
|
"reset",
|
|
934
1186
|
"gate",
|
|
935
1187
|
"save-handoff",
|
|
936
|
-
"load-handoff"
|
|
1188
|
+
"load-handoff",
|
|
1189
|
+
"append_entry",
|
|
1190
|
+
"update_entry_status",
|
|
1191
|
+
"read_section",
|
|
1192
|
+
"read_sections",
|
|
1193
|
+
"archive_session"
|
|
937
1194
|
],
|
|
938
1195
|
description: "Action to perform"
|
|
939
1196
|
},
|
|
@@ -950,138 +1207,178 @@ var manageStateDefinition = {
|
|
|
950
1207
|
session: {
|
|
951
1208
|
type: "string",
|
|
952
1209
|
description: "Session slug for session-scoped state (takes priority over stream when provided)"
|
|
1210
|
+
},
|
|
1211
|
+
section: {
|
|
1212
|
+
type: "string",
|
|
1213
|
+
enum: ["terminology", "decisions", "constraints", "risks", "openQuestions", "evidence"],
|
|
1214
|
+
description: "Session section name (terminology, decisions, constraints, risks, openQuestions, evidence)"
|
|
1215
|
+
},
|
|
1216
|
+
authorSkill: {
|
|
1217
|
+
type: "string",
|
|
1218
|
+
description: "Name of the skill authoring the entry (required for append_entry)"
|
|
1219
|
+
},
|
|
1220
|
+
content: {
|
|
1221
|
+
type: "string",
|
|
1222
|
+
description: "Entry content text (required for append_entry)"
|
|
1223
|
+
},
|
|
1224
|
+
entryId: {
|
|
1225
|
+
type: "string",
|
|
1226
|
+
description: "ID of the entry to update (required for update_entry_status)"
|
|
1227
|
+
},
|
|
1228
|
+
newStatus: {
|
|
1229
|
+
type: "string",
|
|
1230
|
+
enum: ["active", "resolved", "superseded"],
|
|
1231
|
+
description: "New status for the entry: active, resolved, or superseded (required for update_entry_status)"
|
|
953
1232
|
}
|
|
954
1233
|
},
|
|
955
1234
|
required: ["path", "action"]
|
|
956
1235
|
}
|
|
957
1236
|
};
|
|
1237
|
+
async function handleShow(projectPath, input) {
|
|
1238
|
+
const { loadState } = await import("./dist-ALQDD67R.js");
|
|
1239
|
+
return resultToMcpResponse(await loadState(projectPath, input.stream, input.session));
|
|
1240
|
+
}
|
|
1241
|
+
async function handleLearn(projectPath, input) {
|
|
1242
|
+
if (!input.learning) return mcpError("Error: learning is required for learn action");
|
|
1243
|
+
const { appendLearning } = await import("./dist-ALQDD67R.js");
|
|
1244
|
+
const result = await appendLearning(
|
|
1245
|
+
projectPath,
|
|
1246
|
+
input.learning,
|
|
1247
|
+
input.skillName,
|
|
1248
|
+
input.outcome,
|
|
1249
|
+
input.stream,
|
|
1250
|
+
input.session
|
|
1251
|
+
);
|
|
1252
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1253
|
+
return resultToMcpResponse(Ok({ recorded: true }));
|
|
1254
|
+
}
|
|
1255
|
+
async function handleFailure(projectPath, input) {
|
|
1256
|
+
if (!input.description) return mcpError("Error: description is required for failure action");
|
|
1257
|
+
if (!input.failureType) return mcpError("Error: failureType is required for failure action");
|
|
1258
|
+
const { appendFailure } = await import("./dist-ALQDD67R.js");
|
|
1259
|
+
const result = await appendFailure(
|
|
1260
|
+
projectPath,
|
|
1261
|
+
input.description,
|
|
1262
|
+
input.skillName ?? "unknown",
|
|
1263
|
+
input.failureType,
|
|
1264
|
+
input.stream,
|
|
1265
|
+
input.session
|
|
1266
|
+
);
|
|
1267
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1268
|
+
return resultToMcpResponse(Ok({ recorded: true }));
|
|
1269
|
+
}
|
|
1270
|
+
async function handleArchive(projectPath, input) {
|
|
1271
|
+
const { archiveFailures } = await import("./dist-ALQDD67R.js");
|
|
1272
|
+
const result = await archiveFailures(projectPath, input.stream, input.session);
|
|
1273
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1274
|
+
return resultToMcpResponse(Ok({ archived: true }));
|
|
1275
|
+
}
|
|
1276
|
+
async function handleReset(projectPath, input) {
|
|
1277
|
+
const { saveState, DEFAULT_STATE } = await import("./dist-ALQDD67R.js");
|
|
1278
|
+
const result = await saveState(projectPath, { ...DEFAULT_STATE }, input.stream, input.session);
|
|
1279
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1280
|
+
return resultToMcpResponse(Ok({ reset: true }));
|
|
1281
|
+
}
|
|
1282
|
+
async function handleGate(projectPath, _input) {
|
|
1283
|
+
const { runMechanicalGate } = await import("./dist-ALQDD67R.js");
|
|
1284
|
+
return resultToMcpResponse(await runMechanicalGate(projectPath));
|
|
1285
|
+
}
|
|
1286
|
+
async function handleSaveHandoff(projectPath, input) {
|
|
1287
|
+
if (!input.handoff) return mcpError("Error: handoff is required for save-handoff action");
|
|
1288
|
+
const { saveHandoff } = await import("./dist-ALQDD67R.js");
|
|
1289
|
+
const result = await saveHandoff(
|
|
1290
|
+
projectPath,
|
|
1291
|
+
input.handoff,
|
|
1292
|
+
input.stream,
|
|
1293
|
+
input.session
|
|
1294
|
+
);
|
|
1295
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1296
|
+
await autoSyncRoadmap(projectPath);
|
|
1297
|
+
return resultToMcpResponse(Ok({ saved: true }));
|
|
1298
|
+
}
|
|
1299
|
+
async function handleLoadHandoff(projectPath, input) {
|
|
1300
|
+
const { loadHandoff } = await import("./dist-ALQDD67R.js");
|
|
1301
|
+
return resultToMcpResponse(await loadHandoff(projectPath, input.stream, input.session));
|
|
1302
|
+
}
|
|
1303
|
+
async function handleAppendEntry(projectPath, input) {
|
|
1304
|
+
if (!input.session) return mcpError("Error: session is required for append_entry action");
|
|
1305
|
+
if (!input.section) return mcpError("Error: section is required for append_entry action");
|
|
1306
|
+
if (!input.authorSkill) return mcpError("Error: authorSkill is required for append_entry action");
|
|
1307
|
+
if (!input.content) return mcpError("Error: content is required for append_entry action");
|
|
1308
|
+
const { appendSessionEntry } = await import("./dist-ALQDD67R.js");
|
|
1309
|
+
const result = await appendSessionEntry(
|
|
1310
|
+
projectPath,
|
|
1311
|
+
input.session,
|
|
1312
|
+
input.section,
|
|
1313
|
+
input.authorSkill,
|
|
1314
|
+
input.content
|
|
1315
|
+
);
|
|
1316
|
+
return resultToMcpResponse(result);
|
|
1317
|
+
}
|
|
1318
|
+
async function handleUpdateEntryStatus(projectPath, input) {
|
|
1319
|
+
if (!input.session) return mcpError("Error: session is required for update_entry_status action");
|
|
1320
|
+
if (!input.section) return mcpError("Error: section is required for update_entry_status action");
|
|
1321
|
+
if (!input.entryId) return mcpError("Error: entryId is required for update_entry_status action");
|
|
1322
|
+
if (!input.newStatus)
|
|
1323
|
+
return mcpError("Error: newStatus is required for update_entry_status action");
|
|
1324
|
+
const { updateSessionEntryStatus } = await import("./dist-ALQDD67R.js");
|
|
1325
|
+
const result = await updateSessionEntryStatus(
|
|
1326
|
+
projectPath,
|
|
1327
|
+
input.session,
|
|
1328
|
+
input.section,
|
|
1329
|
+
input.entryId,
|
|
1330
|
+
input.newStatus
|
|
1331
|
+
);
|
|
1332
|
+
return resultToMcpResponse(result);
|
|
1333
|
+
}
|
|
1334
|
+
async function handleReadSection(projectPath, input) {
|
|
1335
|
+
if (!input.session) return mcpError("Error: session is required for read_section action");
|
|
1336
|
+
if (!input.section) return mcpError("Error: section is required for read_section action");
|
|
1337
|
+
const { readSessionSection } = await import("./dist-ALQDD67R.js");
|
|
1338
|
+
const result = await readSessionSection(
|
|
1339
|
+
projectPath,
|
|
1340
|
+
input.session,
|
|
1341
|
+
input.section
|
|
1342
|
+
);
|
|
1343
|
+
return resultToMcpResponse(result);
|
|
1344
|
+
}
|
|
1345
|
+
async function handleReadSections(projectPath, input) {
|
|
1346
|
+
if (!input.session) return mcpError("Error: session is required for read_sections action");
|
|
1347
|
+
const { readSessionSections } = await import("./dist-ALQDD67R.js");
|
|
1348
|
+
const result = await readSessionSections(projectPath, input.session);
|
|
1349
|
+
return resultToMcpResponse(result);
|
|
1350
|
+
}
|
|
1351
|
+
async function handleArchiveSession(projectPath, input) {
|
|
1352
|
+
if (!input.session) return mcpError("Error: session is required for archive_session action");
|
|
1353
|
+
const { archiveSession } = await import("./dist-ALQDD67R.js");
|
|
1354
|
+
const result = await archiveSession(projectPath, input.session);
|
|
1355
|
+
if (!result.ok) return resultToMcpResponse(result);
|
|
1356
|
+
await autoSyncRoadmap(projectPath);
|
|
1357
|
+
return resultToMcpResponse(Ok({ archived: true }));
|
|
1358
|
+
}
|
|
1359
|
+
var ACTION_HANDLERS = {
|
|
1360
|
+
show: handleShow,
|
|
1361
|
+
learn: handleLearn,
|
|
1362
|
+
failure: handleFailure,
|
|
1363
|
+
archive: handleArchive,
|
|
1364
|
+
reset: handleReset,
|
|
1365
|
+
gate: handleGate,
|
|
1366
|
+
"save-handoff": handleSaveHandoff,
|
|
1367
|
+
"load-handoff": handleLoadHandoff,
|
|
1368
|
+
append_entry: handleAppendEntry,
|
|
1369
|
+
update_entry_status: handleUpdateEntryStatus,
|
|
1370
|
+
read_section: handleReadSection,
|
|
1371
|
+
read_sections: handleReadSections,
|
|
1372
|
+
archive_session: handleArchiveSession
|
|
1373
|
+
};
|
|
958
1374
|
async function handleManageState(input) {
|
|
959
1375
|
try {
|
|
960
|
-
const {
|
|
961
|
-
loadState,
|
|
962
|
-
saveState,
|
|
963
|
-
appendLearning,
|
|
964
|
-
appendFailure,
|
|
965
|
-
archiveFailures,
|
|
966
|
-
runMechanicalGate,
|
|
967
|
-
DEFAULT_STATE
|
|
968
|
-
} = await import("./dist-2B363XUH.js");
|
|
969
1376
|
const projectPath = sanitizePath(input.path);
|
|
970
|
-
|
|
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
|
-
}
|
|
1377
|
+
const handler = ACTION_HANDLERS[input.action];
|
|
1378
|
+
if (!handler) return mcpError("Error: unknown action");
|
|
1379
|
+
return await handler(projectPath, input);
|
|
1075
1380
|
} catch (error) {
|
|
1076
|
-
return {
|
|
1077
|
-
content: [
|
|
1078
|
-
{
|
|
1079
|
-
type: "text",
|
|
1080
|
-
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
1081
|
-
}
|
|
1082
|
-
],
|
|
1083
|
-
isError: true
|
|
1084
|
-
};
|
|
1381
|
+
return mcpError(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
1085
1382
|
}
|
|
1086
1383
|
}
|
|
1087
1384
|
var listStreamsDefinition = {
|
|
@@ -1097,7 +1394,7 @@ var listStreamsDefinition = {
|
|
|
1097
1394
|
};
|
|
1098
1395
|
async function handleListStreams(input) {
|
|
1099
1396
|
try {
|
|
1100
|
-
const { listStreams, loadStreamIndex } = await import("./dist-
|
|
1397
|
+
const { listStreams, loadStreamIndex } = await import("./dist-ALQDD67R.js");
|
|
1101
1398
|
const projectPath = sanitizePath(input.path);
|
|
1102
1399
|
const indexResult = await loadStreamIndex(projectPath);
|
|
1103
1400
|
const streamsResult = await listStreams(projectPath);
|
|
@@ -1135,7 +1432,7 @@ var checkPhaseGateDefinition = {
|
|
|
1135
1432
|
};
|
|
1136
1433
|
async function handleCheckPhaseGate(input) {
|
|
1137
1434
|
try {
|
|
1138
|
-
const { runCheckPhaseGate } = await import("./check-phase-gate-
|
|
1435
|
+
const { runCheckPhaseGate } = await import("./check-phase-gate-ZOXVBDCN.js");
|
|
1139
1436
|
const result = await runCheckPhaseGate({ cwd: sanitizePath(input.path) });
|
|
1140
1437
|
if (result.ok) {
|
|
1141
1438
|
return { content: [{ type: "text", text: JSON.stringify(result.value) }] };
|
|
@@ -1155,7 +1452,7 @@ async function handleCheckPhaseGate(input) {
|
|
|
1155
1452
|
}
|
|
1156
1453
|
|
|
1157
1454
|
// src/mcp/tools/cross-check.ts
|
|
1158
|
-
import * as
|
|
1455
|
+
import * as path12 from "path";
|
|
1159
1456
|
var validateCrossCheckDefinition = {
|
|
1160
1457
|
name: "validate_cross_check",
|
|
1161
1458
|
description: "Validate plan-to-implementation coverage: checks that specs have plans and plans have implementations, detects staleness",
|
|
@@ -1191,15 +1488,15 @@ async function handleValidateCrossCheck(input) {
|
|
|
1191
1488
|
};
|
|
1192
1489
|
}
|
|
1193
1490
|
try {
|
|
1194
|
-
const { runCrossCheck } = await import("./validate-cross-check-
|
|
1195
|
-
const specsDir =
|
|
1491
|
+
const { runCrossCheck } = await import("./validate-cross-check-OABMREW4.js");
|
|
1492
|
+
const specsDir = path12.resolve(projectPath, input.specsDir ?? "docs/specs");
|
|
1196
1493
|
if (!specsDir.startsWith(projectPath)) {
|
|
1197
1494
|
return {
|
|
1198
1495
|
content: [{ type: "text", text: "Error: specsDir escapes project root" }],
|
|
1199
1496
|
isError: true
|
|
1200
1497
|
};
|
|
1201
1498
|
}
|
|
1202
|
-
const plansDir =
|
|
1499
|
+
const plansDir = path12.resolve(projectPath, input.plansDir ?? "docs/plans");
|
|
1203
1500
|
if (!plansDir.startsWith(projectPath)) {
|
|
1204
1501
|
return {
|
|
1205
1502
|
content: [{ type: "text", text: "Error: plansDir escapes project root" }],
|
|
@@ -1230,14 +1527,14 @@ async function handleValidateCrossCheck(input) {
|
|
|
1230
1527
|
|
|
1231
1528
|
// src/commands/generate-slash-commands.ts
|
|
1232
1529
|
import { Command } from "commander";
|
|
1233
|
-
import
|
|
1234
|
-
import
|
|
1530
|
+
import fs12 from "fs";
|
|
1531
|
+
import path14 from "path";
|
|
1235
1532
|
import os from "os";
|
|
1236
1533
|
import readline from "readline";
|
|
1237
1534
|
|
|
1238
1535
|
// src/slash-commands/normalize.ts
|
|
1239
|
-
import
|
|
1240
|
-
import
|
|
1536
|
+
import fs11 from "fs";
|
|
1537
|
+
import path13 from "path";
|
|
1241
1538
|
import { parse as parse3 } from "yaml";
|
|
1242
1539
|
|
|
1243
1540
|
// src/slash-commands/normalize-name.ts
|
|
@@ -1254,113 +1551,125 @@ function normalizeName(skillName) {
|
|
|
1254
1551
|
}
|
|
1255
1552
|
|
|
1256
1553
|
// src/slash-commands/normalize.ts
|
|
1554
|
+
function readSkillYaml(yamlPath) {
|
|
1555
|
+
let raw;
|
|
1556
|
+
try {
|
|
1557
|
+
raw = fs11.readFileSync(yamlPath, "utf-8");
|
|
1558
|
+
} catch {
|
|
1559
|
+
return null;
|
|
1560
|
+
}
|
|
1561
|
+
return SkillMetadataSchema.safeParse(parse3(raw));
|
|
1562
|
+
}
|
|
1563
|
+
function shouldSkipSkill(meta, platforms) {
|
|
1564
|
+
const matchesPlatform = platforms.some((p) => meta.platforms.includes(p));
|
|
1565
|
+
if (!matchesPlatform) return true;
|
|
1566
|
+
if (meta.tier === 3 || meta.internal) return true;
|
|
1567
|
+
return false;
|
|
1568
|
+
}
|
|
1569
|
+
function checkNameCollision(normalized, metaName, source, nameMap) {
|
|
1570
|
+
const existing = nameMap.get(normalized);
|
|
1571
|
+
if (!existing) {
|
|
1572
|
+
nameMap.set(normalized, { skillName: metaName, source });
|
|
1573
|
+
return "ok";
|
|
1574
|
+
}
|
|
1575
|
+
if (existing.source === source) {
|
|
1576
|
+
throw new Error(
|
|
1577
|
+
`Name collision: skills "${existing.skillName}" and "${metaName}" both normalize to "${normalized}"`
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
return "skip";
|
|
1581
|
+
}
|
|
1582
|
+
function buildContextLines(meta) {
|
|
1583
|
+
const lines = [];
|
|
1584
|
+
if (meta.cognitive_mode) lines.push(`Cognitive mode: ${meta.cognitive_mode}`);
|
|
1585
|
+
if (meta.type) lines.push(`Type: ${meta.type}`);
|
|
1586
|
+
if (meta.state?.persistent) {
|
|
1587
|
+
const files = meta.state.files?.join(", ") ?? "";
|
|
1588
|
+
lines.push(`State: persistent${files ? ` (files: ${files})` : ""}`);
|
|
1589
|
+
}
|
|
1590
|
+
return lines;
|
|
1591
|
+
}
|
|
1592
|
+
function buildObjectiveLines(meta) {
|
|
1593
|
+
const lines = [meta.description];
|
|
1594
|
+
if (meta.phases && meta.phases.length > 0) {
|
|
1595
|
+
lines.push("", "Phases:");
|
|
1596
|
+
for (const phase of meta.phases) {
|
|
1597
|
+
const req = phase.required !== false ? "" : " (optional)";
|
|
1598
|
+
lines.push(`- ${phase.name}: ${phase.description}${req}`);
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
return lines;
|
|
1602
|
+
}
|
|
1603
|
+
function buildProcessLines(meta) {
|
|
1604
|
+
if (meta.mcp?.tool) {
|
|
1605
|
+
return [
|
|
1606
|
+
`1. Try: invoke mcp__harness__${meta.mcp.tool} with skill: "${meta.name}"`,
|
|
1607
|
+
`2. If MCP unavailable: read SKILL.md and follow its workflow directly`,
|
|
1608
|
+
`3. Pass through any arguments provided by the user`
|
|
1609
|
+
];
|
|
1610
|
+
}
|
|
1611
|
+
return [
|
|
1612
|
+
`1. Read SKILL.md and follow its workflow directly`,
|
|
1613
|
+
`2. Pass through any arguments provided by the user`
|
|
1614
|
+
];
|
|
1615
|
+
}
|
|
1616
|
+
function buildSpec(meta, normalized, entry, skillsDir, source) {
|
|
1617
|
+
const skillMdPath = path13.join(skillsDir, entry.name, "SKILL.md");
|
|
1618
|
+
const skillMdContent = fs11.existsSync(skillMdPath) ? fs11.readFileSync(skillMdPath, "utf-8") : "";
|
|
1619
|
+
const skillMdRelative = path13.relative(process.cwd(), skillMdPath).replaceAll("\\", "/");
|
|
1620
|
+
const skillYamlRelative = path13.relative(process.cwd(), path13.join(skillsDir, entry.name, "skill.yaml")).replaceAll("\\", "/");
|
|
1621
|
+
const args = (meta.cli?.args ?? []).map((a) => ({
|
|
1622
|
+
name: a.name,
|
|
1623
|
+
description: a.description ?? "",
|
|
1624
|
+
required: a.required ?? false
|
|
1625
|
+
}));
|
|
1626
|
+
const tools = [...meta.tools ?? []];
|
|
1627
|
+
if (!tools.includes("Read")) tools.push("Read");
|
|
1628
|
+
const executionContextLines = [];
|
|
1629
|
+
if (skillMdContent) {
|
|
1630
|
+
executionContextLines.push(`@${skillMdRelative}`, `@${skillYamlRelative}`);
|
|
1631
|
+
}
|
|
1632
|
+
return {
|
|
1633
|
+
name: normalized,
|
|
1634
|
+
namespace: "harness",
|
|
1635
|
+
fullName: `harness:${normalized}`,
|
|
1636
|
+
description: meta.description,
|
|
1637
|
+
version: meta.version,
|
|
1638
|
+
...meta.cognitive_mode ? { cognitiveMode: meta.cognitive_mode } : {},
|
|
1639
|
+
tools,
|
|
1640
|
+
args,
|
|
1641
|
+
skillYamlName: meta.name,
|
|
1642
|
+
sourceDir: entry.name,
|
|
1643
|
+
skillsBaseDir: skillsDir,
|
|
1644
|
+
source,
|
|
1645
|
+
prompt: {
|
|
1646
|
+
context: buildContextLines(meta).join("\n"),
|
|
1647
|
+
objective: buildObjectiveLines(meta).join("\n"),
|
|
1648
|
+
executionContext: executionContextLines.join("\n"),
|
|
1649
|
+
process: buildProcessLines(meta).join("\n")
|
|
1650
|
+
}
|
|
1651
|
+
};
|
|
1652
|
+
}
|
|
1257
1653
|
function normalizeSkills(skillSources, platforms) {
|
|
1258
1654
|
const specs = [];
|
|
1259
1655
|
const nameMap = /* @__PURE__ */ new Map();
|
|
1260
1656
|
for (const { dir: skillsDir, source } of skillSources) {
|
|
1261
|
-
if (!
|
|
1262
|
-
const entries =
|
|
1657
|
+
if (!fs11.existsSync(skillsDir)) continue;
|
|
1658
|
+
const entries = fs11.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
1263
1659
|
for (const entry of entries) {
|
|
1264
|
-
const yamlPath =
|
|
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);
|
|
1660
|
+
const yamlPath = path13.join(skillsDir, entry.name, "skill.yaml");
|
|
1661
|
+
if (!fs11.existsSync(yamlPath)) continue;
|
|
1662
|
+
const result = readSkillYaml(yamlPath);
|
|
1663
|
+
if (!result) continue;
|
|
1274
1664
|
if (!result.success) {
|
|
1275
1665
|
console.warn(`Skipping ${entry.name}: invalid skill.yaml`);
|
|
1276
1666
|
continue;
|
|
1277
1667
|
}
|
|
1278
1668
|
const meta = result.data;
|
|
1279
|
-
|
|
1280
|
-
if (!matchesPlatform) continue;
|
|
1281
|
-
const tier = meta.tier;
|
|
1282
|
-
const isInternal = meta.internal;
|
|
1283
|
-
if (tier === 3 || isInternal) continue;
|
|
1669
|
+
if (shouldSkipSkill(meta, platforms)) continue;
|
|
1284
1670
|
const normalized = normalizeName(meta.name);
|
|
1285
|
-
|
|
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
|
-
});
|
|
1671
|
+
if (checkNameCollision(normalized, meta.name, source, nameMap) === "skip") continue;
|
|
1672
|
+
specs.push(buildSpec(meta, normalized, entry, skillsDir, source));
|
|
1364
1673
|
}
|
|
1365
1674
|
}
|
|
1366
1675
|
return specs;
|
|
@@ -1460,13 +1769,13 @@ function renderGemini(spec, skillMdContent, skillYamlContent) {
|
|
|
1460
1769
|
// src/commands/generate-slash-commands.ts
|
|
1461
1770
|
function resolveOutputDir(platform, opts) {
|
|
1462
1771
|
if (opts.output) {
|
|
1463
|
-
return
|
|
1772
|
+
return path14.join(opts.output, "harness");
|
|
1464
1773
|
}
|
|
1465
1774
|
if (opts.global) {
|
|
1466
1775
|
const home = os.homedir();
|
|
1467
|
-
return platform === "claude-code" ?
|
|
1776
|
+
return platform === "claude-code" ? path14.join(home, ".claude", "commands", "harness") : path14.join(home, ".gemini", "commands", "harness");
|
|
1468
1777
|
}
|
|
1469
|
-
return platform === "claude-code" ?
|
|
1778
|
+
return platform === "claude-code" ? path14.join("agents", "commands", "claude-code", "harness") : path14.join("agents", "commands", "gemini-cli", "harness");
|
|
1470
1779
|
}
|
|
1471
1780
|
function fileExtension(platform) {
|
|
1472
1781
|
return platform === "claude-code" ? ".md" : ".toml";
|
|
@@ -1481,26 +1790,29 @@ Remove ${files.length} orphaned command(s)? (y/N) `, (answer) => {
|
|
|
1481
1790
|
});
|
|
1482
1791
|
});
|
|
1483
1792
|
}
|
|
1484
|
-
function
|
|
1485
|
-
const skillSources = [];
|
|
1793
|
+
function resolveSkillSources(opts) {
|
|
1486
1794
|
if (opts.skillsDir) {
|
|
1487
|
-
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
}
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
}
|
|
1795
|
+
return [{ dir: opts.skillsDir, source: "project" }];
|
|
1796
|
+
}
|
|
1797
|
+
const sources = [];
|
|
1798
|
+
const projectDir = resolveProjectSkillsDir();
|
|
1799
|
+
if (projectDir) {
|
|
1800
|
+
sources.push({ dir: projectDir, source: "project" });
|
|
1801
|
+
}
|
|
1802
|
+
const communityDir = resolveCommunitySkillsDir();
|
|
1803
|
+
if (fs12.existsSync(communityDir)) {
|
|
1804
|
+
sources.push({ dir: communityDir, source: "community" });
|
|
1805
|
+
}
|
|
1806
|
+
if (opts.includeGlobal || sources.length === 0) {
|
|
1807
|
+
const globalDir = resolveGlobalSkillsDir();
|
|
1808
|
+
if (!projectDir || path14.resolve(globalDir) !== path14.resolve(projectDir)) {
|
|
1809
|
+
sources.push({ dir: globalDir, source: "global" });
|
|
1502
1810
|
}
|
|
1503
1811
|
}
|
|
1812
|
+
return sources;
|
|
1813
|
+
}
|
|
1814
|
+
function generateSlashCommands(opts) {
|
|
1815
|
+
const skillSources = resolveSkillSources(opts);
|
|
1504
1816
|
const specs = normalizeSkills(skillSources, opts.platforms);
|
|
1505
1817
|
const results = [];
|
|
1506
1818
|
for (const platform of opts.platforms) {
|
|
@@ -1518,7 +1830,7 @@ function generateSlashCommands(opts) {
|
|
|
1518
1830
|
executionContext: spec.prompt.executionContext.split("\n").map((line) => {
|
|
1519
1831
|
if (line.startsWith("@")) {
|
|
1520
1832
|
const relPath = line.slice(1);
|
|
1521
|
-
return `@${
|
|
1833
|
+
return `@${path14.resolve(relPath)}`;
|
|
1522
1834
|
}
|
|
1523
1835
|
return line;
|
|
1524
1836
|
}).join("\n")
|
|
@@ -1526,10 +1838,10 @@ function generateSlashCommands(opts) {
|
|
|
1526
1838
|
} : spec;
|
|
1527
1839
|
rendered.set(filename, renderClaudeCode(renderSpec));
|
|
1528
1840
|
} else {
|
|
1529
|
-
const mdPath =
|
|
1530
|
-
const yamlPath =
|
|
1531
|
-
const mdContent =
|
|
1532
|
-
const yamlContent =
|
|
1841
|
+
const mdPath = path14.join(spec.skillsBaseDir, spec.sourceDir, "SKILL.md");
|
|
1842
|
+
const yamlPath = path14.join(spec.skillsBaseDir, spec.sourceDir, "skill.yaml");
|
|
1843
|
+
const mdContent = fs12.existsSync(mdPath) ? fs12.readFileSync(mdPath, "utf-8") : "";
|
|
1844
|
+
const yamlContent = fs12.existsSync(yamlPath) ? fs12.readFileSync(yamlPath, "utf-8") : "";
|
|
1533
1845
|
rendered.set(filename, renderGemini(spec, mdContent, yamlContent));
|
|
1534
1846
|
}
|
|
1535
1847
|
}
|
|
@@ -1555,9 +1867,9 @@ async function handleOrphanDeletion(results, opts) {
|
|
|
1555
1867
|
const shouldDelete = opts.yes || await confirmDeletion(result.removed);
|
|
1556
1868
|
if (shouldDelete) {
|
|
1557
1869
|
for (const filename of result.removed) {
|
|
1558
|
-
const filePath =
|
|
1559
|
-
if (
|
|
1560
|
-
|
|
1870
|
+
const filePath = path14.join(result.outputDir, filename);
|
|
1871
|
+
if (fs12.existsSync(filePath)) {
|
|
1872
|
+
fs12.unlinkSync(filePath);
|
|
1561
1873
|
}
|
|
1562
1874
|
}
|
|
1563
1875
|
}
|
|
@@ -1683,7 +1995,7 @@ async function handleGenerateSlashCommands(input) {
|
|
|
1683
1995
|
// src/mcp/resources/state.ts
|
|
1684
1996
|
async function getStateResource(projectRoot) {
|
|
1685
1997
|
try {
|
|
1686
|
-
const { loadState, migrateToStreams } = await import("./dist-
|
|
1998
|
+
const { loadState, migrateToStreams } = await import("./dist-ALQDD67R.js");
|
|
1687
1999
|
await migrateToStreams(projectRoot);
|
|
1688
2000
|
const result = await loadState(projectRoot);
|
|
1689
2001
|
if (result.ok) {
|
|
@@ -1771,7 +2083,7 @@ async function handleQueryGraph(input) {
|
|
|
1771
2083
|
const projectPath = sanitizePath(input.path);
|
|
1772
2084
|
const store = await loadGraphStore(projectPath);
|
|
1773
2085
|
if (!store) return graphNotFoundError();
|
|
1774
|
-
const { ContextQL } = await import("./dist-
|
|
2086
|
+
const { ContextQL } = await import("./dist-B26DFXMP.js");
|
|
1775
2087
|
const cql = new ContextQL(store);
|
|
1776
2088
|
const result = cql.execute({
|
|
1777
2089
|
rootNodeIds: input.rootNodeIds,
|
|
@@ -1862,7 +2174,7 @@ async function handleSearchSimilar(input) {
|
|
|
1862
2174
|
const projectPath = sanitizePath(input.path);
|
|
1863
2175
|
const store = await loadGraphStore(projectPath);
|
|
1864
2176
|
if (!store) return graphNotFoundError();
|
|
1865
|
-
const { FusionLayer } = await import("./dist-
|
|
2177
|
+
const { FusionLayer } = await import("./dist-B26DFXMP.js");
|
|
1866
2178
|
const fusion = new FusionLayer(store);
|
|
1867
2179
|
const results = fusion.search(input.query, input.topK ?? 10);
|
|
1868
2180
|
if (input.mode === "summary") {
|
|
@@ -1917,7 +2229,7 @@ async function handleFindContextFor(input) {
|
|
|
1917
2229
|
const projectPath = sanitizePath(input.path);
|
|
1918
2230
|
const store = await loadGraphStore(projectPath);
|
|
1919
2231
|
if (!store) return graphNotFoundError();
|
|
1920
|
-
const { FusionLayer, ContextQL } = await import("./dist-
|
|
2232
|
+
const { FusionLayer, ContextQL } = await import("./dist-B26DFXMP.js");
|
|
1921
2233
|
const fusion = new FusionLayer(store);
|
|
1922
2234
|
const cql = new ContextQL(store);
|
|
1923
2235
|
const tokenBudget = input.tokenBudget ?? 4e3;
|
|
@@ -2013,7 +2325,7 @@ async function handleGetRelationships(input) {
|
|
|
2013
2325
|
const projectPath = sanitizePath(input.path);
|
|
2014
2326
|
const store = await loadGraphStore(projectPath);
|
|
2015
2327
|
if (!store) return graphNotFoundError();
|
|
2016
|
-
const { ContextQL } = await import("./dist-
|
|
2328
|
+
const { ContextQL } = await import("./dist-B26DFXMP.js");
|
|
2017
2329
|
const cql = new ContextQL(store);
|
|
2018
2330
|
const direction = input.direction ?? "both";
|
|
2019
2331
|
const bidirectional = direction === "both" || direction === "inbound";
|
|
@@ -2119,7 +2431,7 @@ async function handleGetImpact(input) {
|
|
|
2119
2431
|
const projectPath = sanitizePath(input.path);
|
|
2120
2432
|
const store = await loadGraphStore(projectPath);
|
|
2121
2433
|
if (!store) return graphNotFoundError();
|
|
2122
|
-
const { ContextQL } = await import("./dist-
|
|
2434
|
+
const { ContextQL } = await import("./dist-B26DFXMP.js");
|
|
2123
2435
|
let targetNodeId = input.nodeId;
|
|
2124
2436
|
if (!targetNodeId && input.filePath) {
|
|
2125
2437
|
const fileNodes = store.findNodes({ type: "file" });
|
|
@@ -2230,7 +2542,7 @@ async function handleGetImpact(input) {
|
|
|
2230
2542
|
}
|
|
2231
2543
|
|
|
2232
2544
|
// src/mcp/tools/graph/ingest-source.ts
|
|
2233
|
-
import * as
|
|
2545
|
+
import * as path15 from "path";
|
|
2234
2546
|
var ingestSourceDefinition = {
|
|
2235
2547
|
name: "ingest_source",
|
|
2236
2548
|
description: "Ingest sources into the project knowledge graph. Supports code analysis, knowledge documents, git history, or all at once.",
|
|
@@ -2250,10 +2562,10 @@ var ingestSourceDefinition = {
|
|
|
2250
2562
|
async function handleIngestSource(input) {
|
|
2251
2563
|
try {
|
|
2252
2564
|
const projectPath = sanitizePath(input.path);
|
|
2253
|
-
const graphDir =
|
|
2254
|
-
const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-
|
|
2255
|
-
const
|
|
2256
|
-
await
|
|
2565
|
+
const graphDir = path15.join(projectPath, ".harness", "graph");
|
|
2566
|
+
const { GraphStore, CodeIngestor, TopologicalLinker, KnowledgeIngestor, GitIngestor } = await import("./dist-B26DFXMP.js");
|
|
2567
|
+
const fs15 = await import("fs/promises");
|
|
2568
|
+
await fs15.mkdir(graphDir, { recursive: true });
|
|
2257
2569
|
const store = new GraphStore();
|
|
2258
2570
|
await store.load(graphDir);
|
|
2259
2571
|
const results = [];
|
|
@@ -2326,7 +2638,7 @@ async function handleDetectAnomalies(input) {
|
|
|
2326
2638
|
const projectPath = sanitizePath(input.path);
|
|
2327
2639
|
const store = await loadGraphStore(projectPath);
|
|
2328
2640
|
if (!store) return graphNotFoundError();
|
|
2329
|
-
const { GraphAnomalyAdapter } = await import("./dist-
|
|
2641
|
+
const { GraphAnomalyAdapter } = await import("./dist-B26DFXMP.js");
|
|
2330
2642
|
const adapter = new GraphAnomalyAdapter(store);
|
|
2331
2643
|
const report = adapter.detect({
|
|
2332
2644
|
...input.threshold !== void 0 && { threshold: input.threshold },
|
|
@@ -2366,7 +2678,7 @@ async function handleAskGraph(input) {
|
|
|
2366
2678
|
const projectPath = sanitizePath(input.path);
|
|
2367
2679
|
const store = await loadGraphStore(projectPath);
|
|
2368
2680
|
if (!store) return graphNotFoundError();
|
|
2369
|
-
const { askGraph } = await import("./dist-
|
|
2681
|
+
const { askGraph } = await import("./dist-B26DFXMP.js");
|
|
2370
2682
|
const result = await askGraph(store, input.question);
|
|
2371
2683
|
return {
|
|
2372
2684
|
content: [{ type: "text", text: JSON.stringify(result) }]
|
|
@@ -2385,8 +2697,8 @@ async function handleAskGraph(input) {
|
|
|
2385
2697
|
}
|
|
2386
2698
|
|
|
2387
2699
|
// src/mcp/resources/graph.ts
|
|
2388
|
-
import * as
|
|
2389
|
-
import * as
|
|
2700
|
+
import * as fs13 from "fs/promises";
|
|
2701
|
+
import * as path16 from "path";
|
|
2390
2702
|
var MAX_ITEMS = 5e3;
|
|
2391
2703
|
function formatStaleness(isoTimestamp) {
|
|
2392
2704
|
const then = new Date(isoTimestamp).getTime();
|
|
@@ -2409,11 +2721,11 @@ async function getGraphResource(projectRoot) {
|
|
|
2409
2721
|
message: "No knowledge graph found. Run harness scan to build one."
|
|
2410
2722
|
});
|
|
2411
2723
|
}
|
|
2412
|
-
const graphDir =
|
|
2413
|
-
const metadataPath =
|
|
2724
|
+
const graphDir = path16.join(projectRoot, ".harness", "graph");
|
|
2725
|
+
const metadataPath = path16.join(graphDir, "metadata.json");
|
|
2414
2726
|
let lastScanTimestamp = null;
|
|
2415
2727
|
try {
|
|
2416
|
-
const raw = JSON.parse(await
|
|
2728
|
+
const raw = JSON.parse(await fs13.readFile(metadataPath, "utf-8"));
|
|
2417
2729
|
lastScanTimestamp = raw.lastScanTimestamp ?? null;
|
|
2418
2730
|
} catch {
|
|
2419
2731
|
}
|
|
@@ -2502,7 +2814,7 @@ var generateAgentDefinitionsDefinition = {
|
|
|
2502
2814
|
}
|
|
2503
2815
|
};
|
|
2504
2816
|
async function handleGenerateAgentDefinitions(input) {
|
|
2505
|
-
const { generateAgentDefinitions } = await import("./generate-agent-definitions-
|
|
2817
|
+
const { generateAgentDefinitions } = await import("./generate-agent-definitions-ZAE726AU.js");
|
|
2506
2818
|
const platforms = input.platform === "all" || !input.platform ? ["claude-code", "gemini-cli"] : [input.platform];
|
|
2507
2819
|
const results = generateAgentDefinitions({
|
|
2508
2820
|
platforms: [...platforms],
|
|
@@ -2513,8 +2825,8 @@ async function handleGenerateAgentDefinitions(input) {
|
|
|
2513
2825
|
}
|
|
2514
2826
|
|
|
2515
2827
|
// src/mcp/tools/roadmap.ts
|
|
2516
|
-
import * as
|
|
2517
|
-
import * as
|
|
2828
|
+
import * as fs14 from "fs";
|
|
2829
|
+
import * as path17 from "path";
|
|
2518
2830
|
var manageRoadmapDefinition = {
|
|
2519
2831
|
name: "manage_roadmap",
|
|
2520
2832
|
description: "Manage the project roadmap: show, add, update, remove, sync features, or query by filter. Reads and writes docs/roadmap.md.",
|
|
@@ -2569,21 +2881,21 @@ var manageRoadmapDefinition = {
|
|
|
2569
2881
|
}
|
|
2570
2882
|
};
|
|
2571
2883
|
function roadmapPath(projectRoot) {
|
|
2572
|
-
return
|
|
2884
|
+
return path17.join(projectRoot, "docs", "roadmap.md");
|
|
2573
2885
|
}
|
|
2574
2886
|
function readRoadmapFile(projectRoot) {
|
|
2575
2887
|
const filePath = roadmapPath(projectRoot);
|
|
2576
2888
|
try {
|
|
2577
|
-
return
|
|
2889
|
+
return fs14.readFileSync(filePath, "utf-8");
|
|
2578
2890
|
} catch {
|
|
2579
2891
|
return null;
|
|
2580
2892
|
}
|
|
2581
2893
|
}
|
|
2582
2894
|
function writeRoadmapFile(projectRoot, content) {
|
|
2583
2895
|
const filePath = roadmapPath(projectRoot);
|
|
2584
|
-
const dir =
|
|
2585
|
-
|
|
2586
|
-
|
|
2896
|
+
const dir = path17.dirname(filePath);
|
|
2897
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
2898
|
+
fs14.writeFileSync(filePath, content, "utf-8");
|
|
2587
2899
|
}
|
|
2588
2900
|
function roadmapNotFoundError() {
|
|
2589
2901
|
return {
|
|
@@ -2596,7 +2908,7 @@ function roadmapNotFoundError() {
|
|
|
2596
2908
|
isError: true
|
|
2597
2909
|
};
|
|
2598
2910
|
}
|
|
2599
|
-
function
|
|
2911
|
+
function handleShow2(projectPath, input, deps) {
|
|
2600
2912
|
const { parseRoadmap, Ok: Ok2 } = deps;
|
|
2601
2913
|
const raw = readRoadmapFile(projectPath);
|
|
2602
2914
|
if (raw === null) return roadmapNotFoundError();
|
|
@@ -2788,18 +3100,7 @@ function handleSync(projectPath, input, deps) {
|
|
|
2788
3100
|
return resultToMcpResponse(Ok2({ changes: [], message: "Roadmap is up to date." }));
|
|
2789
3101
|
}
|
|
2790
3102
|
if (input.apply) {
|
|
2791
|
-
|
|
2792
|
-
for (const m of roadmap.milestones) {
|
|
2793
|
-
const feature = m.features.find(
|
|
2794
|
-
(f) => f.name.toLowerCase() === change.feature.toLowerCase()
|
|
2795
|
-
);
|
|
2796
|
-
if (feature) {
|
|
2797
|
-
feature.status = change.to;
|
|
2798
|
-
break;
|
|
2799
|
-
}
|
|
2800
|
-
}
|
|
2801
|
-
}
|
|
2802
|
-
roadmap.frontmatter.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
|
|
3103
|
+
deps.applySyncChanges(roadmap, changes);
|
|
2803
3104
|
writeRoadmapFile(projectPath, serializeRoadmap(roadmap));
|
|
2804
3105
|
return resultToMcpResponse(Ok2({ changes, applied: true, roadmap }));
|
|
2805
3106
|
}
|
|
@@ -2807,13 +3108,13 @@ function handleSync(projectPath, input, deps) {
|
|
|
2807
3108
|
}
|
|
2808
3109
|
async function handleManageRoadmap(input) {
|
|
2809
3110
|
try {
|
|
2810
|
-
const { parseRoadmap, serializeRoadmap, syncRoadmap } = await import("./dist-
|
|
2811
|
-
const { Ok: Ok2 } = await import("./dist-
|
|
3111
|
+
const { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges } = await import("./dist-ALQDD67R.js");
|
|
3112
|
+
const { Ok: Ok2 } = await import("./dist-USY2C5JL.js");
|
|
2812
3113
|
const projectPath = sanitizePath(input.path);
|
|
2813
|
-
const deps = { parseRoadmap, serializeRoadmap, syncRoadmap, Ok: Ok2 };
|
|
3114
|
+
const deps = { parseRoadmap, serializeRoadmap, syncRoadmap, applySyncChanges, Ok: Ok2 };
|
|
2814
3115
|
switch (input.action) {
|
|
2815
3116
|
case "show":
|
|
2816
|
-
return
|
|
3117
|
+
return handleShow2(projectPath, input, deps);
|
|
2817
3118
|
case "add":
|
|
2818
3119
|
return handleAdd(projectPath, input, deps);
|
|
2819
3120
|
case "update":
|
|
@@ -3193,226 +3494,119 @@ var emitInteractionDefinition = {
|
|
|
3193
3494
|
required: ["path", "type"]
|
|
3194
3495
|
}
|
|
3195
3496
|
};
|
|
3196
|
-
|
|
3497
|
+
function formatZodErrors(issues) {
|
|
3498
|
+
return issues.map((i) => i.path.length > 0 ? `${i.path.join(".")}: ${i.message}` : i.message).join("; ");
|
|
3499
|
+
}
|
|
3500
|
+
async function handleQuestion(validInput, projectPath, id) {
|
|
3501
|
+
if (!validInput.question)
|
|
3502
|
+
return mcpError("Error: question payload is required when type is question");
|
|
3503
|
+
const questionResult = InteractionQuestionWithOptionsSchema.safeParse(validInput.question);
|
|
3504
|
+
if (!questionResult.success)
|
|
3505
|
+
return mcpError(`Error: ${formatZodErrors(questionResult.error.issues)}`);
|
|
3506
|
+
const prompt = renderQuestion(questionResult.data);
|
|
3507
|
+
await recordInteraction(projectPath, id, "question", questionResult.data.text, validInput.stream);
|
|
3508
|
+
return { content: [{ type: "text", text: JSON.stringify({ id, prompt }) }] };
|
|
3509
|
+
}
|
|
3510
|
+
async function handleConfirmation(validInput, projectPath, id) {
|
|
3511
|
+
if (!validInput.confirmation)
|
|
3512
|
+
return mcpError("Error: confirmation payload is required when type is confirmation");
|
|
3513
|
+
const confirmResult = InteractionConfirmationSchema.safeParse(validInput.confirmation);
|
|
3514
|
+
if (!confirmResult.success)
|
|
3515
|
+
return mcpError(
|
|
3516
|
+
`Error: ${confirmResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3517
|
+
);
|
|
3518
|
+
const prompt = renderConfirmation(confirmResult.data);
|
|
3519
|
+
await recordInteraction(
|
|
3520
|
+
projectPath,
|
|
3521
|
+
id,
|
|
3522
|
+
"confirmation",
|
|
3523
|
+
confirmResult.data.text,
|
|
3524
|
+
validInput.stream
|
|
3525
|
+
);
|
|
3526
|
+
return { content: [{ type: "text", text: JSON.stringify({ id, prompt }) }] };
|
|
3527
|
+
}
|
|
3528
|
+
async function handleTransition(validInput, projectPath, id) {
|
|
3529
|
+
if (!validInput.transition)
|
|
3530
|
+
return mcpError("Error: transition payload is required when type is transition");
|
|
3531
|
+
const transitionResult = InteractionTransitionSchema.safeParse(validInput.transition);
|
|
3532
|
+
if (!transitionResult.success)
|
|
3533
|
+
return mcpError(
|
|
3534
|
+
`Error: ${transitionResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3535
|
+
);
|
|
3536
|
+
const transition = transitionResult.data;
|
|
3537
|
+
const prompt = renderTransition(transition);
|
|
3197
3538
|
try {
|
|
3198
|
-
const
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
],
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3539
|
+
const { saveHandoff } = await import("./dist-ALQDD67R.js");
|
|
3540
|
+
await saveHandoff(
|
|
3541
|
+
projectPath,
|
|
3542
|
+
{
|
|
3543
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3544
|
+
fromSkill: "emit_interaction",
|
|
3545
|
+
phase: transition.completedPhase,
|
|
3546
|
+
summary: transition.reason,
|
|
3547
|
+
completed: [transition.completedPhase],
|
|
3548
|
+
pending: [transition.suggestedNext],
|
|
3549
|
+
concerns: [],
|
|
3550
|
+
decisions: [],
|
|
3551
|
+
blockers: [],
|
|
3552
|
+
contextKeywords: []
|
|
3553
|
+
},
|
|
3554
|
+
validInput.stream,
|
|
3555
|
+
validInput.session
|
|
3556
|
+
);
|
|
3557
|
+
} catch {
|
|
3558
|
+
}
|
|
3559
|
+
await recordInteraction(
|
|
3560
|
+
projectPath,
|
|
3561
|
+
id,
|
|
3562
|
+
"transition",
|
|
3563
|
+
`${transition.completedPhase} -> ${transition.suggestedNext}`,
|
|
3564
|
+
validInput.stream
|
|
3565
|
+
);
|
|
3566
|
+
const responsePayload = { id, prompt, handoffWritten: true };
|
|
3567
|
+
if (!transition.requiresConfirmation) {
|
|
3568
|
+
responsePayload.autoTransition = true;
|
|
3569
|
+
responsePayload.nextAction = `Invoke harness-${transition.suggestedNext} skill now`;
|
|
3570
|
+
}
|
|
3571
|
+
return { content: [{ type: "text", text: JSON.stringify(responsePayload) }] };
|
|
3572
|
+
}
|
|
3573
|
+
async function handleBatch(validInput, projectPath, id) {
|
|
3574
|
+
if (!validInput.batch) return mcpError("Error: batch payload is required when type is batch");
|
|
3575
|
+
const batchResult = InteractionBatchSchema.safeParse(validInput.batch);
|
|
3576
|
+
if (!batchResult.success)
|
|
3577
|
+
return mcpError(
|
|
3578
|
+
`Error: ${batchResult.error.issues.map((i) => i.message).join("; ")}`
|
|
3579
|
+
);
|
|
3580
|
+
const prompt = renderBatch(batchResult.data);
|
|
3581
|
+
await recordInteraction(projectPath, id, "batch", batchResult.data.text, validInput.stream);
|
|
3582
|
+
return {
|
|
3583
|
+
content: [{ type: "text", text: JSON.stringify({ id, prompt, batchMode: true }) }]
|
|
3584
|
+
};
|
|
3585
|
+
}
|
|
3586
|
+
var INTERACTION_HANDLERS = {
|
|
3587
|
+
question: handleQuestion,
|
|
3588
|
+
confirmation: handleConfirmation,
|
|
3589
|
+
transition: handleTransition,
|
|
3590
|
+
batch: handleBatch
|
|
3591
|
+
};
|
|
3592
|
+
async function handleEmitInteraction(input) {
|
|
3593
|
+
try {
|
|
3594
|
+
const parseResult = EmitInteractionInputSchema.safeParse(input);
|
|
3595
|
+
if (!parseResult.success)
|
|
3596
|
+
return mcpError(`Error: ${formatZodErrors(parseResult.error.issues)}`);
|
|
3210
3597
|
const validInput = parseResult.data;
|
|
3211
3598
|
const projectPath = sanitizePath(validInput.path);
|
|
3212
3599
|
const id = randomUUID();
|
|
3213
|
-
|
|
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
|
-
}
|
|
3600
|
+
const handler = INTERACTION_HANDLERS[validInput.type];
|
|
3601
|
+
if (!handler) return mcpError(`Error: unknown interaction type: ${String(validInput.type)}`);
|
|
3602
|
+
return await handler(validInput, projectPath, id);
|
|
3401
3603
|
} catch (error) {
|
|
3402
|
-
return {
|
|
3403
|
-
content: [
|
|
3404
|
-
{
|
|
3405
|
-
type: "text",
|
|
3406
|
-
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
3407
|
-
}
|
|
3408
|
-
],
|
|
3409
|
-
isError: true
|
|
3410
|
-
};
|
|
3604
|
+
return mcpError(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
3411
3605
|
}
|
|
3412
3606
|
}
|
|
3413
3607
|
async function recordInteraction(projectPath, id, type, decision, stream) {
|
|
3414
3608
|
try {
|
|
3415
|
-
const { loadState, saveState } = await import("./dist-
|
|
3609
|
+
const { loadState, saveState } = await import("./dist-ALQDD67R.js");
|
|
3416
3610
|
const stateResult = await loadState(projectPath, stream);
|
|
3417
3611
|
if (stateResult.ok) {
|
|
3418
3612
|
const state = stateResult.value;
|
|
@@ -3430,7 +3624,7 @@ async function recordInteraction(projectPath, id, type, decision, stream) {
|
|
|
3430
3624
|
// src/mcp/tools/gather-context.ts
|
|
3431
3625
|
var gatherContextDefinition = {
|
|
3432
3626
|
name: "gather_context",
|
|
3433
|
-
description: "Assemble all working context an agent needs in a single call: state, learnings, handoff, graph context, and
|
|
3627
|
+
description: "Assemble all working context an agent needs in a single call: state, learnings, handoff, graph context, project validation, and session sections. Runs constituents in parallel.",
|
|
3434
3628
|
inputSchema: {
|
|
3435
3629
|
type: "object",
|
|
3436
3630
|
properties: {
|
|
@@ -3451,10 +3645,14 @@ var gatherContextDefinition = {
|
|
|
3451
3645
|
type: "array",
|
|
3452
3646
|
items: {
|
|
3453
3647
|
type: "string",
|
|
3454
|
-
enum: ["state", "learnings", "handoff", "graph", "validation"]
|
|
3648
|
+
enum: ["state", "learnings", "handoff", "graph", "validation", "sessions", "events"]
|
|
3455
3649
|
},
|
|
3456
3650
|
description: "Which constituents to include (default: all)"
|
|
3457
3651
|
},
|
|
3652
|
+
includeEvents: {
|
|
3653
|
+
type: "boolean",
|
|
3654
|
+
description: "Include recent events timeline. Default: true when session is provided, false otherwise. Can also be controlled via include array."
|
|
3655
|
+
},
|
|
3458
3656
|
mode: {
|
|
3459
3657
|
type: "string",
|
|
3460
3658
|
enum: ["summary", "detailed"],
|
|
@@ -3467,6 +3665,11 @@ var gatherContextDefinition = {
|
|
|
3467
3665
|
session: {
|
|
3468
3666
|
type: "string",
|
|
3469
3667
|
description: "Session slug for session-scoped state. When provided, state/learnings/handoff/failures are read from .harness/sessions/<session>/ instead of .harness/. Omit for global fallback."
|
|
3668
|
+
},
|
|
3669
|
+
depth: {
|
|
3670
|
+
type: "string",
|
|
3671
|
+
enum: ["index", "summary", "full"],
|
|
3672
|
+
description: 'Retrieval depth for learnings. "index" returns one-line summaries, "summary" (default) returns full entries, "full" returns entries with linked context.'
|
|
3470
3673
|
}
|
|
3471
3674
|
},
|
|
3472
3675
|
required: ["path", "intent"]
|
|
@@ -3492,25 +3695,26 @@ async function handleGatherContext(input) {
|
|
|
3492
3695
|
input.include ?? ["state", "learnings", "handoff", "graph", "validation"]
|
|
3493
3696
|
);
|
|
3494
3697
|
const errors = [];
|
|
3495
|
-
const statePromise = includeSet.has("state") ? import("./dist-
|
|
3698
|
+
const statePromise = includeSet.has("state") ? import("./dist-ALQDD67R.js").then(
|
|
3496
3699
|
(core) => core.loadState(projectPath, void 0, input.session)
|
|
3497
3700
|
) : Promise.resolve(null);
|
|
3498
|
-
const learningsPromise = includeSet.has("learnings") ? import("./dist-
|
|
3701
|
+
const learningsPromise = includeSet.has("learnings") ? import("./dist-ALQDD67R.js").then(
|
|
3499
3702
|
(core) => core.loadBudgetedLearnings(projectPath, {
|
|
3500
3703
|
intent: input.intent,
|
|
3501
3704
|
tokenBudget: input.learningsBudget ?? 1e3,
|
|
3502
3705
|
...input.skill !== void 0 && { skill: input.skill },
|
|
3503
|
-
...input.session !== void 0 && { session: input.session }
|
|
3706
|
+
...input.session !== void 0 && { session: input.session },
|
|
3707
|
+
...input.depth !== void 0 && { depth: input.depth }
|
|
3504
3708
|
})
|
|
3505
3709
|
) : Promise.resolve(null);
|
|
3506
|
-
const handoffPromise = includeSet.has("handoff") ? import("./dist-
|
|
3710
|
+
const handoffPromise = includeSet.has("handoff") ? import("./dist-ALQDD67R.js").then(
|
|
3507
3711
|
(core) => core.loadHandoff(projectPath, void 0, input.session)
|
|
3508
3712
|
) : Promise.resolve(null);
|
|
3509
3713
|
const graphPromise = includeSet.has("graph") ? (async () => {
|
|
3510
|
-
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-
|
|
3714
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-2M2HXDQI.js");
|
|
3511
3715
|
const store = await loadGraphStore2(projectPath);
|
|
3512
3716
|
if (!store) return null;
|
|
3513
|
-
const { FusionLayer, ContextQL } = await import("./dist-
|
|
3717
|
+
const { FusionLayer, ContextQL } = await import("./dist-B26DFXMP.js");
|
|
3514
3718
|
const fusion = new FusionLayer(store);
|
|
3515
3719
|
const cql = new ContextQL(store);
|
|
3516
3720
|
const tokenBudget = input.tokenBudget ?? 4e3;
|
|
@@ -3547,18 +3751,39 @@ async function handleGatherContext(input) {
|
|
|
3547
3751
|
context: contextBlocks
|
|
3548
3752
|
};
|
|
3549
3753
|
})() : Promise.resolve(null);
|
|
3754
|
+
const sessionsPromise = includeSet.has("sessions") && input.session ? import("./dist-ALQDD67R.js").then(
|
|
3755
|
+
(core) => core.readSessionSections(projectPath, input.session)
|
|
3756
|
+
) : Promise.resolve(null);
|
|
3757
|
+
const shouldIncludeEvents = input.includeEvents !== void 0 ? input.includeEvents : includeSet.has("events") || !!input.session && !input.include;
|
|
3758
|
+
const eventsPromise = shouldIncludeEvents ? import("./dist-ALQDD67R.js").then(async (core) => {
|
|
3759
|
+
const result = await core.loadEvents(projectPath, {
|
|
3760
|
+
session: input.session
|
|
3761
|
+
});
|
|
3762
|
+
if (!result.ok) return null;
|
|
3763
|
+
return core.formatEventTimeline(result.value);
|
|
3764
|
+
}) : Promise.resolve(null);
|
|
3550
3765
|
const validationPromise = includeSet.has("validation") ? (async () => {
|
|
3551
|
-
const { handleValidateProject: handleValidateProject2 } = await import("./validate-
|
|
3766
|
+
const { handleValidateProject: handleValidateProject2 } = await import("./validate-KBYQAEWE.js");
|
|
3552
3767
|
const result = await handleValidateProject2({ path: projectPath });
|
|
3553
3768
|
const first = result.content[0];
|
|
3554
3769
|
return first ? JSON.parse(first.text) : null;
|
|
3555
3770
|
})() : Promise.resolve(null);
|
|
3556
|
-
const [
|
|
3771
|
+
const [
|
|
3772
|
+
stateResult,
|
|
3773
|
+
learningsResult,
|
|
3774
|
+
handoffResult,
|
|
3775
|
+
graphResult,
|
|
3776
|
+
validationResult,
|
|
3777
|
+
sessionsResult,
|
|
3778
|
+
eventsResult
|
|
3779
|
+
] = await Promise.allSettled([
|
|
3557
3780
|
statePromise,
|
|
3558
3781
|
learningsPromise,
|
|
3559
3782
|
handoffPromise,
|
|
3560
3783
|
graphPromise,
|
|
3561
|
-
validationPromise
|
|
3784
|
+
validationPromise,
|
|
3785
|
+
sessionsPromise,
|
|
3786
|
+
eventsPromise
|
|
3562
3787
|
]);
|
|
3563
3788
|
function extract(settled, name) {
|
|
3564
3789
|
if (settled.status === "rejected") {
|
|
@@ -3572,6 +3797,8 @@ async function handleGatherContext(input) {
|
|
|
3572
3797
|
const handoffRaw = extract(handoffResult, "handoff");
|
|
3573
3798
|
const graphContextRaw = extract(graphResult, "graph");
|
|
3574
3799
|
const validationRaw = extract(validationResult, "validation");
|
|
3800
|
+
const sessionsRaw = extract(sessionsResult, "sessions");
|
|
3801
|
+
const eventsTimeline = extract(eventsResult, "events");
|
|
3575
3802
|
const state = stateRaw && typeof stateRaw === "object" && "ok" in stateRaw ? stateRaw.ok ? stateRaw.value : (() => {
|
|
3576
3803
|
errors.push(`state: ${stateRaw.error.message}`);
|
|
3577
3804
|
return null;
|
|
@@ -3588,6 +3815,12 @@ async function handleGatherContext(input) {
|
|
|
3588
3815
|
})() : handoffRaw;
|
|
3589
3816
|
const graphContext = graphContextRaw;
|
|
3590
3817
|
const validation = validationRaw;
|
|
3818
|
+
const sessionSections = sessionsRaw && typeof sessionsRaw === "object" && "ok" in sessionsRaw ? sessionsRaw.ok ? sessionsRaw.value : (() => {
|
|
3819
|
+
errors.push(
|
|
3820
|
+
`sessions: ${sessionsRaw.error.message}`
|
|
3821
|
+
);
|
|
3822
|
+
return null;
|
|
3823
|
+
})() : sessionsRaw;
|
|
3591
3824
|
const assembledIn = Date.now() - start;
|
|
3592
3825
|
const mode = input.mode ?? "summary";
|
|
3593
3826
|
const outputState = state ?? null;
|
|
@@ -3612,6 +3845,8 @@ async function handleGatherContext(input) {
|
|
|
3612
3845
|
handoff: outputHandoff,
|
|
3613
3846
|
graphContext: outputGraphContext,
|
|
3614
3847
|
validation: outputValidation,
|
|
3848
|
+
sessionSections: sessionSections ?? null,
|
|
3849
|
+
events: eventsTimeline || null,
|
|
3615
3850
|
meta: {
|
|
3616
3851
|
assembledIn,
|
|
3617
3852
|
graphAvailable: graphContext !== null,
|
|
@@ -3622,7 +3857,7 @@ async function handleGatherContext(input) {
|
|
|
3622
3857
|
};
|
|
3623
3858
|
if (input.session) {
|
|
3624
3859
|
try {
|
|
3625
|
-
const core = await import("./dist-
|
|
3860
|
+
const core = await import("./dist-ALQDD67R.js");
|
|
3626
3861
|
core.updateSessionIndex(
|
|
3627
3862
|
projectPath,
|
|
3628
3863
|
input.session,
|
|
@@ -3692,7 +3927,7 @@ async function handleAssessProject(input) {
|
|
|
3692
3927
|
let validateResult = null;
|
|
3693
3928
|
if (checksToRun.has("validate")) {
|
|
3694
3929
|
try {
|
|
3695
|
-
const { handleValidateProject: handleValidateProject2 } = await import("./validate-
|
|
3930
|
+
const { handleValidateProject: handleValidateProject2 } = await import("./validate-KBYQAEWE.js");
|
|
3696
3931
|
const result = await handleValidateProject2({ path: projectPath });
|
|
3697
3932
|
const first = result.content[0];
|
|
3698
3933
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3717,7 +3952,7 @@ async function handleAssessProject(input) {
|
|
|
3717
3952
|
parallelChecks.push(
|
|
3718
3953
|
(async () => {
|
|
3719
3954
|
try {
|
|
3720
|
-
const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-
|
|
3955
|
+
const { handleCheckDependencies: handleCheckDependencies2 } = await import("./architecture-ZLIH5533.js");
|
|
3721
3956
|
const result = await handleCheckDependencies2({ path: projectPath });
|
|
3722
3957
|
const first = result.content[0];
|
|
3723
3958
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3744,7 +3979,7 @@ async function handleAssessProject(input) {
|
|
|
3744
3979
|
parallelChecks.push(
|
|
3745
3980
|
(async () => {
|
|
3746
3981
|
try {
|
|
3747
|
-
const { handleCheckDocs: handleCheckDocs2 } = await import("./docs-
|
|
3982
|
+
const { handleCheckDocs: handleCheckDocs2 } = await import("./docs-NRMQCOJ6.js");
|
|
3748
3983
|
const result = await handleCheckDocs2({ path: projectPath, scope: "coverage" });
|
|
3749
3984
|
const first = result.content[0];
|
|
3750
3985
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3771,14 +4006,14 @@ async function handleAssessProject(input) {
|
|
|
3771
4006
|
parallelChecks.push(
|
|
3772
4007
|
(async () => {
|
|
3773
4008
|
try {
|
|
3774
|
-
const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-
|
|
4009
|
+
const { handleDetectEntropy: handleDetectEntropy2 } = await import("./entropy-6AGX2ZUN.js");
|
|
3775
4010
|
const result = await handleDetectEntropy2({ path: projectPath, type: "all" });
|
|
3776
4011
|
const first = result.content[0];
|
|
3777
4012
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
3778
4013
|
const issues = (parsed.drift?.staleReferences?.length ?? 0) + (parsed.drift?.missingTargets?.length ?? 0) + (parsed.deadCode?.unusedImports?.length ?? 0) + (parsed.deadCode?.unusedExports?.length ?? 0) + (parsed.patterns?.violations?.length ?? 0);
|
|
3779
4014
|
return {
|
|
3780
4015
|
name: "entropy",
|
|
3781
|
-
passed: !result.isError && issues === 0,
|
|
4016
|
+
passed: !("isError" in result && result.isError) && issues === 0,
|
|
3782
4017
|
issueCount: issues,
|
|
3783
4018
|
...issues > 0 ? { topIssue: "Entropy detected -- run detect_entropy for details" } : {},
|
|
3784
4019
|
...mode === "detailed" ? { detailed: parsed } : {}
|
|
@@ -3798,7 +4033,7 @@ async function handleAssessProject(input) {
|
|
|
3798
4033
|
parallelChecks.push(
|
|
3799
4034
|
(async () => {
|
|
3800
4035
|
try {
|
|
3801
|
-
const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-
|
|
4036
|
+
const { handleRunSecurityScan: handleRunSecurityScan2 } = await import("./security-2RPQEN62.js");
|
|
3802
4037
|
const result = await handleRunSecurityScan2({ path: projectPath });
|
|
3803
4038
|
const first = result.content[0];
|
|
3804
4039
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
@@ -3830,14 +4065,14 @@ async function handleAssessProject(input) {
|
|
|
3830
4065
|
parallelChecks.push(
|
|
3831
4066
|
(async () => {
|
|
3832
4067
|
try {
|
|
3833
|
-
const { handleCheckPerformance: handleCheckPerformance2 } = await import("./performance-
|
|
4068
|
+
const { handleCheckPerformance: handleCheckPerformance2 } = await import("./performance-2D7G6NMJ.js");
|
|
3834
4069
|
const result = await handleCheckPerformance2({ path: projectPath });
|
|
3835
4070
|
const first = result.content[0];
|
|
3836
4071
|
const parsed = first ? JSON.parse(first.text) : {};
|
|
3837
4072
|
const issues = parsed.violations?.length ?? parsed.issues?.length ?? 0;
|
|
3838
4073
|
return {
|
|
3839
4074
|
name: "perf",
|
|
3840
|
-
passed: !result.isError && issues === 0,
|
|
4075
|
+
passed: !("isError" in result && result.isError) && issues === 0,
|
|
3841
4076
|
issueCount: issues,
|
|
3842
4077
|
...issues > 0 ? { topIssue: "Performance issues detected" } : {},
|
|
3843
4078
|
...mode === "detailed" ? { detailed: parsed } : {}
|
|
@@ -4004,81 +4239,8 @@ async function handleReviewChanges(input) {
|
|
|
4004
4239
|
downgraded = true;
|
|
4005
4240
|
}
|
|
4006
4241
|
try {
|
|
4007
|
-
|
|
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
|
-
};
|
|
4242
|
+
const reviewFn = DEPTH_HANDLERS[effectiveDepth];
|
|
4243
|
+
return await reviewFn(projectPath, diff, diffLines, downgraded);
|
|
4082
4244
|
} catch (error) {
|
|
4083
4245
|
return {
|
|
4084
4246
|
content: [
|
|
@@ -4091,6 +4253,97 @@ async function handleReviewChanges(input) {
|
|
|
4091
4253
|
};
|
|
4092
4254
|
}
|
|
4093
4255
|
}
|
|
4256
|
+
async function runQuickReview(projectPath, diff, diffLines, downgraded) {
|
|
4257
|
+
const { handleAnalyzeDiff: handleAnalyzeDiff2 } = await import("./feedback-MY4QZIFD.js");
|
|
4258
|
+
const result = await handleAnalyzeDiff2({ diff, path: projectPath });
|
|
4259
|
+
const firstContent = result.content[0];
|
|
4260
|
+
if (!firstContent) throw new Error("Empty analyze_diff response");
|
|
4261
|
+
const parsed = JSON.parse(firstContent.text);
|
|
4262
|
+
return {
|
|
4263
|
+
content: [
|
|
4264
|
+
{
|
|
4265
|
+
type: "text",
|
|
4266
|
+
text: JSON.stringify({
|
|
4267
|
+
depth: "quick",
|
|
4268
|
+
downgraded,
|
|
4269
|
+
findings: parsed.findings ?? parsed.warnings ?? [],
|
|
4270
|
+
fileCount: parsed.summary?.filesChanged ?? parsed.files?.length ?? 0,
|
|
4271
|
+
lineCount: diffLines,
|
|
4272
|
+
...result.isError ? { error: parsed } : {}
|
|
4273
|
+
})
|
|
4274
|
+
}
|
|
4275
|
+
]
|
|
4276
|
+
};
|
|
4277
|
+
}
|
|
4278
|
+
function extractFindings(parsed, primaryKey, fallbackKey) {
|
|
4279
|
+
return parsed[primaryKey] ?? parsed[fallbackKey] ?? [];
|
|
4280
|
+
}
|
|
4281
|
+
function extractFileCount(diffParsed) {
|
|
4282
|
+
const summary = diffParsed.summary;
|
|
4283
|
+
if (summary?.filesChanged !== void 0) return summary.filesChanged;
|
|
4284
|
+
const files = diffParsed.files;
|
|
4285
|
+
return files?.length ?? 0;
|
|
4286
|
+
}
|
|
4287
|
+
async function runStandardReview(projectPath, diff, diffLines, downgraded) {
|
|
4288
|
+
const { handleAnalyzeDiff: handleAnalyzeDiff2, handleCreateSelfReview: handleCreateSelfReview2 } = await import("./feedback-MY4QZIFD.js");
|
|
4289
|
+
const [diffResult, reviewResult] = await Promise.all([
|
|
4290
|
+
handleAnalyzeDiff2({ diff, path: projectPath }),
|
|
4291
|
+
handleCreateSelfReview2({ path: projectPath, diff })
|
|
4292
|
+
]);
|
|
4293
|
+
const diffContent = diffResult.content[0];
|
|
4294
|
+
const reviewContent = reviewResult.content[0];
|
|
4295
|
+
if (!diffContent || !reviewContent) throw new Error("Empty review response");
|
|
4296
|
+
const diffParsed = JSON.parse(diffContent.text);
|
|
4297
|
+
const reviewParsed = JSON.parse(reviewContent.text);
|
|
4298
|
+
const findings = [
|
|
4299
|
+
...extractFindings(diffParsed, "findings", "warnings"),
|
|
4300
|
+
...extractFindings(reviewParsed, "findings", "items")
|
|
4301
|
+
];
|
|
4302
|
+
return {
|
|
4303
|
+
content: [
|
|
4304
|
+
{
|
|
4305
|
+
type: "text",
|
|
4306
|
+
text: JSON.stringify({
|
|
4307
|
+
depth: "standard",
|
|
4308
|
+
downgraded,
|
|
4309
|
+
findings,
|
|
4310
|
+
diffAnalysis: diffParsed,
|
|
4311
|
+
selfReview: reviewParsed,
|
|
4312
|
+
fileCount: extractFileCount(diffParsed),
|
|
4313
|
+
lineCount: diffLines
|
|
4314
|
+
})
|
|
4315
|
+
}
|
|
4316
|
+
]
|
|
4317
|
+
};
|
|
4318
|
+
}
|
|
4319
|
+
async function runDeepReview(projectPath, diff, diffLines, _downgraded) {
|
|
4320
|
+
const { handleRunCodeReview: handleRunCodeReview2 } = await import("./review-pipeline-RAQ55ISU.js");
|
|
4321
|
+
const result = await handleRunCodeReview2({ path: projectPath, diff });
|
|
4322
|
+
const deepContent = result.content[0];
|
|
4323
|
+
if (!deepContent) throw new Error("Empty code review response");
|
|
4324
|
+
const parsed = JSON.parse(deepContent.text);
|
|
4325
|
+
return {
|
|
4326
|
+
content: [
|
|
4327
|
+
{
|
|
4328
|
+
type: "text",
|
|
4329
|
+
text: JSON.stringify({
|
|
4330
|
+
depth: "deep",
|
|
4331
|
+
downgraded: false,
|
|
4332
|
+
findings: parsed.findings ?? [],
|
|
4333
|
+
assessment: parsed.assessment,
|
|
4334
|
+
findingCount: parsed.findingCount,
|
|
4335
|
+
lineCount: diffLines,
|
|
4336
|
+
pipeline: parsed
|
|
4337
|
+
})
|
|
4338
|
+
}
|
|
4339
|
+
]
|
|
4340
|
+
};
|
|
4341
|
+
}
|
|
4342
|
+
var DEPTH_HANDLERS = {
|
|
4343
|
+
quick: runQuickReview,
|
|
4344
|
+
standard: runStandardReview,
|
|
4345
|
+
deep: runDeepReview
|
|
4346
|
+
};
|
|
4094
4347
|
|
|
4095
4348
|
// src/mcp/tools/task-independence.ts
|
|
4096
4349
|
var checkTaskIndependenceDefinition = {
|
|
@@ -4135,7 +4388,7 @@ async function handleCheckTaskIndependence(input) {
|
|
|
4135
4388
|
try {
|
|
4136
4389
|
const projectPath = sanitizePath(input.path);
|
|
4137
4390
|
const store = await loadGraphStore(projectPath);
|
|
4138
|
-
const { TaskIndependenceAnalyzer } = await import("./dist-
|
|
4391
|
+
const { TaskIndependenceAnalyzer } = await import("./dist-B26DFXMP.js");
|
|
4139
4392
|
const analyzer = new TaskIndependenceAnalyzer(store ?? void 0);
|
|
4140
4393
|
const result = analyzer.analyze({
|
|
4141
4394
|
tasks: input.tasks,
|
|
@@ -4223,7 +4476,7 @@ async function handlePredictConflicts(input) {
|
|
|
4223
4476
|
try {
|
|
4224
4477
|
const projectPath = sanitizePath(input.path);
|
|
4225
4478
|
const store = await loadGraphStore(projectPath);
|
|
4226
|
-
const { ConflictPredictor } = await import("./dist-
|
|
4479
|
+
const { ConflictPredictor } = await import("./dist-B26DFXMP.js");
|
|
4227
4480
|
const predictor = new ConflictPredictor(store ?? void 0);
|
|
4228
4481
|
const result = predictor.predict({
|
|
4229
4482
|
tasks: input.tasks,
|
|
@@ -4329,7 +4582,7 @@ async function handleDetectStaleConstraints(input) {
|
|
|
4329
4582
|
isError: true
|
|
4330
4583
|
};
|
|
4331
4584
|
}
|
|
4332
|
-
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-
|
|
4585
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-2M2HXDQI.js");
|
|
4333
4586
|
const store = await loadGraphStore2(projectPath);
|
|
4334
4587
|
if (!store) {
|
|
4335
4588
|
return {
|
|
@@ -4350,7 +4603,7 @@ async function handleDetectStaleConstraints(input) {
|
|
|
4350
4603
|
]
|
|
4351
4604
|
};
|
|
4352
4605
|
}
|
|
4353
|
-
const { detectStaleConstraints } = await import("./dist-
|
|
4606
|
+
const { detectStaleConstraints } = await import("./dist-ALQDD67R.js");
|
|
4354
4607
|
const result = detectStaleConstraints(
|
|
4355
4608
|
store,
|
|
4356
4609
|
windowDays,
|
|
@@ -4447,6 +4700,226 @@ async function handleSearchSkills(input) {
|
|
|
4447
4700
|
};
|
|
4448
4701
|
}
|
|
4449
4702
|
|
|
4703
|
+
// src/mcp/tools/code-nav.ts
|
|
4704
|
+
var codeOutlineDefinition = {
|
|
4705
|
+
name: "code_outline",
|
|
4706
|
+
description: "Get a structural skeleton of a file or files matching a glob: exports, classes, functions, types with signatures and line numbers. No implementation bodies. 4-8x token savings vs full file read.",
|
|
4707
|
+
inputSchema: {
|
|
4708
|
+
type: "object",
|
|
4709
|
+
properties: {
|
|
4710
|
+
path: {
|
|
4711
|
+
type: "string",
|
|
4712
|
+
description: "Absolute file path or directory path. When a directory, outlines all supported files within it."
|
|
4713
|
+
},
|
|
4714
|
+
glob: {
|
|
4715
|
+
type: "string",
|
|
4716
|
+
description: 'Optional glob pattern to filter files (e.g. "*.ts", "src/**/*.py"). Only used when path is a directory.'
|
|
4717
|
+
}
|
|
4718
|
+
},
|
|
4719
|
+
required: ["path"]
|
|
4720
|
+
}
|
|
4721
|
+
};
|
|
4722
|
+
async function handleCodeOutline(input) {
|
|
4723
|
+
let targetPath;
|
|
4724
|
+
try {
|
|
4725
|
+
targetPath = sanitizePath(input.path);
|
|
4726
|
+
} catch (error) {
|
|
4727
|
+
return {
|
|
4728
|
+
content: [
|
|
4729
|
+
{
|
|
4730
|
+
type: "text",
|
|
4731
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
4732
|
+
}
|
|
4733
|
+
],
|
|
4734
|
+
isError: true
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
try {
|
|
4738
|
+
const { getOutline, formatOutline, EXTENSION_MAP } = await import("./dist-ALQDD67R.js");
|
|
4739
|
+
const { stat } = await import("fs/promises");
|
|
4740
|
+
const stats = await stat(targetPath).catch(() => null);
|
|
4741
|
+
if (stats?.isFile()) {
|
|
4742
|
+
const outline = await getOutline(targetPath);
|
|
4743
|
+
return { content: [{ type: "text", text: formatOutline(outline) }] };
|
|
4744
|
+
}
|
|
4745
|
+
if (stats?.isDirectory()) {
|
|
4746
|
+
const { glob } = await import("glob");
|
|
4747
|
+
const exts = Object.keys(EXTENSION_MAP).map((e) => e.slice(1));
|
|
4748
|
+
const pattern = input.glob ?? `**/*.{${exts.join(",")}}`;
|
|
4749
|
+
const files = await glob(pattern, { cwd: targetPath, absolute: true });
|
|
4750
|
+
const results = [];
|
|
4751
|
+
const MAX_FILES = 50;
|
|
4752
|
+
for (const file of files.slice(0, MAX_FILES)) {
|
|
4753
|
+
const outline = await getOutline(file);
|
|
4754
|
+
results.push(formatOutline(outline));
|
|
4755
|
+
}
|
|
4756
|
+
if (files.length > MAX_FILES) {
|
|
4757
|
+
results.push(
|
|
4758
|
+
`
|
|
4759
|
+
... and ${files.length - MAX_FILES} more files (use a narrower glob to see them)`
|
|
4760
|
+
);
|
|
4761
|
+
}
|
|
4762
|
+
return { content: [{ type: "text", text: results.join("\n\n") }] };
|
|
4763
|
+
}
|
|
4764
|
+
return {
|
|
4765
|
+
content: [{ type: "text", text: `Error: Path not found: ${targetPath}` }],
|
|
4766
|
+
isError: true
|
|
4767
|
+
};
|
|
4768
|
+
} catch (error) {
|
|
4769
|
+
return {
|
|
4770
|
+
content: [
|
|
4771
|
+
{
|
|
4772
|
+
type: "text",
|
|
4773
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
4774
|
+
}
|
|
4775
|
+
],
|
|
4776
|
+
isError: true
|
|
4777
|
+
};
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
var codeSearchDefinition = {
|
|
4781
|
+
name: "code_search",
|
|
4782
|
+
description: "Search for symbols (functions, classes, types, variables) by name or pattern across a directory. Returns matching locations with file, line, kind, and one-line context. 6-12x token savings vs grep + read.",
|
|
4783
|
+
inputSchema: {
|
|
4784
|
+
type: "object",
|
|
4785
|
+
properties: {
|
|
4786
|
+
query: {
|
|
4787
|
+
type: "string",
|
|
4788
|
+
description: "Symbol name or substring to search for (case-insensitive)."
|
|
4789
|
+
},
|
|
4790
|
+
directory: {
|
|
4791
|
+
type: "string",
|
|
4792
|
+
description: "Absolute path to directory to search in."
|
|
4793
|
+
},
|
|
4794
|
+
glob: {
|
|
4795
|
+
type: "string",
|
|
4796
|
+
description: 'Optional glob pattern to filter files (e.g. "*.ts").'
|
|
4797
|
+
}
|
|
4798
|
+
},
|
|
4799
|
+
required: ["query", "directory"]
|
|
4800
|
+
}
|
|
4801
|
+
};
|
|
4802
|
+
async function handleCodeSearch(input) {
|
|
4803
|
+
let directory;
|
|
4804
|
+
try {
|
|
4805
|
+
directory = sanitizePath(input.directory);
|
|
4806
|
+
} catch (error) {
|
|
4807
|
+
return {
|
|
4808
|
+
content: [
|
|
4809
|
+
{
|
|
4810
|
+
type: "text",
|
|
4811
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
4812
|
+
}
|
|
4813
|
+
],
|
|
4814
|
+
isError: true
|
|
4815
|
+
};
|
|
4816
|
+
}
|
|
4817
|
+
try {
|
|
4818
|
+
const { searchSymbols } = await import("./dist-ALQDD67R.js");
|
|
4819
|
+
const result = await searchSymbols(input.query, directory, input.glob);
|
|
4820
|
+
const lines = [`Search: "${result.query}" \u2014 ${result.matches.length} matches`];
|
|
4821
|
+
for (const match of result.matches) {
|
|
4822
|
+
const { symbol } = match;
|
|
4823
|
+
lines.push(
|
|
4824
|
+
` ${symbol.file}:${symbol.line} [${symbol.kind}] ${symbol.name} \u2014 ${match.context}`
|
|
4825
|
+
);
|
|
4826
|
+
}
|
|
4827
|
+
if (result.skipped.length > 0) {
|
|
4828
|
+
lines.push(`
|
|
4829
|
+
Skipped ${result.skipped.length} files (unsupported or parse failed)`);
|
|
4830
|
+
}
|
|
4831
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
4832
|
+
} catch (error) {
|
|
4833
|
+
return {
|
|
4834
|
+
content: [
|
|
4835
|
+
{
|
|
4836
|
+
type: "text",
|
|
4837
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
4838
|
+
}
|
|
4839
|
+
],
|
|
4840
|
+
isError: true
|
|
4841
|
+
};
|
|
4842
|
+
}
|
|
4843
|
+
}
|
|
4844
|
+
var codeUnfoldDefinition = {
|
|
4845
|
+
name: "code_unfold",
|
|
4846
|
+
description: "Extract the complete implementation of a specific symbol (function, class, type) or a line range from a file. Uses AST boundaries for precise extraction. 2-4x token savings vs full file read.",
|
|
4847
|
+
inputSchema: {
|
|
4848
|
+
type: "object",
|
|
4849
|
+
properties: {
|
|
4850
|
+
path: {
|
|
4851
|
+
type: "string",
|
|
4852
|
+
description: "Absolute path to the file."
|
|
4853
|
+
},
|
|
4854
|
+
symbol: {
|
|
4855
|
+
type: "string",
|
|
4856
|
+
description: "Name of the symbol to extract (function, class, type, etc.). Mutually exclusive with startLine/endLine."
|
|
4857
|
+
},
|
|
4858
|
+
startLine: {
|
|
4859
|
+
type: "number",
|
|
4860
|
+
description: "Start line number (1-indexed). Used with endLine for range extraction. Mutually exclusive with symbol."
|
|
4861
|
+
},
|
|
4862
|
+
endLine: {
|
|
4863
|
+
type: "number",
|
|
4864
|
+
description: "End line number (1-indexed, inclusive). Used with startLine for range extraction."
|
|
4865
|
+
}
|
|
4866
|
+
},
|
|
4867
|
+
required: ["path"]
|
|
4868
|
+
}
|
|
4869
|
+
};
|
|
4870
|
+
async function handleCodeUnfold(input) {
|
|
4871
|
+
let filePath;
|
|
4872
|
+
try {
|
|
4873
|
+
filePath = sanitizePath(input.path);
|
|
4874
|
+
} catch (error) {
|
|
4875
|
+
return {
|
|
4876
|
+
content: [
|
|
4877
|
+
{
|
|
4878
|
+
type: "text",
|
|
4879
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
4880
|
+
}
|
|
4881
|
+
],
|
|
4882
|
+
isError: true
|
|
4883
|
+
};
|
|
4884
|
+
}
|
|
4885
|
+
try {
|
|
4886
|
+
if (input.symbol) {
|
|
4887
|
+
const { unfoldSymbol } = await import("./dist-ALQDD67R.js");
|
|
4888
|
+
const result = await unfoldSymbol(filePath, input.symbol);
|
|
4889
|
+
const header = result.warning ? `${result.file}:${result.startLine}-${result.endLine} ${result.warning}
|
|
4890
|
+
` : `${result.file}:${result.startLine}-${result.endLine}
|
|
4891
|
+
`;
|
|
4892
|
+
return { content: [{ type: "text", text: header + result.content }] };
|
|
4893
|
+
}
|
|
4894
|
+
if (input.startLine != null && input.endLine != null) {
|
|
4895
|
+
const { unfoldRange } = await import("./dist-ALQDD67R.js");
|
|
4896
|
+
const result = await unfoldRange(filePath, input.startLine, input.endLine);
|
|
4897
|
+
const header = `${result.file}:${result.startLine}-${result.endLine}
|
|
4898
|
+
`;
|
|
4899
|
+
return { content: [{ type: "text", text: header + result.content }] };
|
|
4900
|
+
}
|
|
4901
|
+
return {
|
|
4902
|
+
content: [
|
|
4903
|
+
{
|
|
4904
|
+
type: "text",
|
|
4905
|
+
text: 'Error: Provide either "symbol" or "startLine" + "endLine".'
|
|
4906
|
+
}
|
|
4907
|
+
],
|
|
4908
|
+
isError: true
|
|
4909
|
+
};
|
|
4910
|
+
} catch (error) {
|
|
4911
|
+
return {
|
|
4912
|
+
content: [
|
|
4913
|
+
{
|
|
4914
|
+
type: "text",
|
|
4915
|
+
text: `Error: ${error instanceof Error ? error.message : String(error)}`
|
|
4916
|
+
}
|
|
4917
|
+
],
|
|
4918
|
+
isError: true
|
|
4919
|
+
};
|
|
4920
|
+
}
|
|
4921
|
+
}
|
|
4922
|
+
|
|
4450
4923
|
// src/mcp/server.ts
|
|
4451
4924
|
var TOOL_DEFINITIONS = [
|
|
4452
4925
|
validateToolDefinition,
|
|
@@ -4494,7 +4967,10 @@ var TOOL_DEFINITIONS = [
|
|
|
4494
4967
|
checkTaskIndependenceDefinition,
|
|
4495
4968
|
predictConflictsDefinition,
|
|
4496
4969
|
detectStaleConstraintsDefinition,
|
|
4497
|
-
searchSkillsDefinition
|
|
4970
|
+
searchSkillsDefinition,
|
|
4971
|
+
codeOutlineDefinition,
|
|
4972
|
+
codeSearchDefinition,
|
|
4973
|
+
codeUnfoldDefinition
|
|
4498
4974
|
];
|
|
4499
4975
|
var TOOL_HANDLERS = {
|
|
4500
4976
|
validate_project: handleValidateProject,
|
|
@@ -4542,7 +5018,10 @@ var TOOL_HANDLERS = {
|
|
|
4542
5018
|
check_task_independence: handleCheckTaskIndependence,
|
|
4543
5019
|
predict_conflicts: handlePredictConflicts,
|
|
4544
5020
|
detect_stale_constraints: handleDetectStaleConstraints,
|
|
4545
|
-
search_skills: handleSearchSkills
|
|
5021
|
+
search_skills: handleSearchSkills,
|
|
5022
|
+
code_outline: handleCodeOutline,
|
|
5023
|
+
code_search: handleCodeSearch,
|
|
5024
|
+
code_unfold: handleCodeUnfold
|
|
4546
5025
|
};
|
|
4547
5026
|
var RESOURCE_DEFINITIONS = [
|
|
4548
5027
|
{
|
|
@@ -4607,6 +5086,45 @@ var RESOURCE_HANDLERS = {
|
|
|
4607
5086
|
function getToolDefinitions() {
|
|
4608
5087
|
return TOOL_DEFINITIONS;
|
|
4609
5088
|
}
|
|
5089
|
+
function readConfigInterval(resolvedRoot) {
|
|
5090
|
+
try {
|
|
5091
|
+
const configResult = resolveProjectConfig(resolvedRoot);
|
|
5092
|
+
if (configResult.ok) {
|
|
5093
|
+
const raw = configResult.value.updateCheckInterval;
|
|
5094
|
+
if (typeof raw === "number" && Number.isInteger(raw) && raw >= 0) {
|
|
5095
|
+
return raw;
|
|
5096
|
+
}
|
|
5097
|
+
}
|
|
5098
|
+
} catch {
|
|
5099
|
+
}
|
|
5100
|
+
return void 0;
|
|
5101
|
+
}
|
|
5102
|
+
async function appendUpdateNotification(result, resolvedRoot) {
|
|
5103
|
+
try {
|
|
5104
|
+
const {
|
|
5105
|
+
getUpdateNotification,
|
|
5106
|
+
isUpdateCheckEnabled,
|
|
5107
|
+
shouldRunCheck,
|
|
5108
|
+
readCheckState,
|
|
5109
|
+
spawnBackgroundCheck
|
|
5110
|
+
} = await import("./dist-ALQDD67R.js");
|
|
5111
|
+
const { CLI_VERSION } = await import("./version-KFFPOQAX.js");
|
|
5112
|
+
const configInterval = readConfigInterval(resolvedRoot);
|
|
5113
|
+
const DEFAULT_INTERVAL = 864e5;
|
|
5114
|
+
if (!isUpdateCheckEnabled(configInterval)) return;
|
|
5115
|
+
const state = readCheckState();
|
|
5116
|
+
if (shouldRunCheck(state, configInterval ?? DEFAULT_INTERVAL)) {
|
|
5117
|
+
spawnBackgroundCheck(CLI_VERSION);
|
|
5118
|
+
}
|
|
5119
|
+
const notification = getUpdateNotification(CLI_VERSION);
|
|
5120
|
+
if (notification) {
|
|
5121
|
+
result.content.push({ type: "text", text: `
|
|
5122
|
+
---
|
|
5123
|
+
${notification}` });
|
|
5124
|
+
}
|
|
5125
|
+
} catch {
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
4610
5128
|
function createHarnessServer(projectRoot) {
|
|
4611
5129
|
const resolvedRoot = projectRoot ?? process.cwd();
|
|
4612
5130
|
let sessionChecked = false;
|
|
@@ -4626,42 +5144,7 @@ function createHarnessServer(projectRoot) {
|
|
|
4626
5144
|
const result = await handler(args ?? {});
|
|
4627
5145
|
if (!sessionChecked) {
|
|
4628
5146
|
sessionChecked = true;
|
|
4629
|
-
|
|
4630
|
-
const {
|
|
4631
|
-
getUpdateNotification,
|
|
4632
|
-
isUpdateCheckEnabled,
|
|
4633
|
-
shouldRunCheck,
|
|
4634
|
-
readCheckState,
|
|
4635
|
-
spawnBackgroundCheck
|
|
4636
|
-
} = await import("./dist-2B363XUH.js");
|
|
4637
|
-
const { CLI_VERSION: version } = await import("./version-KFFPOQAX.js");
|
|
4638
|
-
let CLI_VERSION = version;
|
|
4639
|
-
let configInterval;
|
|
4640
|
-
try {
|
|
4641
|
-
const configResult = resolveProjectConfig(resolvedRoot);
|
|
4642
|
-
if (configResult.ok) {
|
|
4643
|
-
const raw = configResult.value.updateCheckInterval;
|
|
4644
|
-
if (typeof raw === "number" && Number.isInteger(raw) && raw >= 0) {
|
|
4645
|
-
configInterval = raw;
|
|
4646
|
-
}
|
|
4647
|
-
}
|
|
4648
|
-
} catch {
|
|
4649
|
-
}
|
|
4650
|
-
const DEFAULT_INTERVAL = 864e5;
|
|
4651
|
-
if (isUpdateCheckEnabled(configInterval)) {
|
|
4652
|
-
const state = readCheckState();
|
|
4653
|
-
if (shouldRunCheck(state, configInterval ?? DEFAULT_INTERVAL)) {
|
|
4654
|
-
spawnBackgroundCheck(CLI_VERSION);
|
|
4655
|
-
}
|
|
4656
|
-
const notification = getUpdateNotification(CLI_VERSION);
|
|
4657
|
-
if (notification) {
|
|
4658
|
-
result.content.push({ type: "text", text: `
|
|
4659
|
-
---
|
|
4660
|
-
${notification}` });
|
|
4661
|
-
}
|
|
4662
|
-
}
|
|
4663
|
-
} catch {
|
|
4664
|
-
}
|
|
5147
|
+
await appendUpdateNotification(result, resolvedRoot);
|
|
4665
5148
|
}
|
|
4666
5149
|
return result;
|
|
4667
5150
|
});
|
|
@@ -4690,6 +5173,8 @@ async function startServer() {
|
|
|
4690
5173
|
}
|
|
4691
5174
|
|
|
4692
5175
|
export {
|
|
5176
|
+
persistToolingConfig,
|
|
5177
|
+
appendFrameworkAgents,
|
|
4693
5178
|
generateSlashCommands,
|
|
4694
5179
|
handleOrphanDeletion,
|
|
4695
5180
|
createGenerateSlashCommandsCommand,
|