@harness-engineering/cli 1.27.1 → 1.28.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-architecture-advisor/SKILL.md +17 -5
- package/dist/agents/skills/claude-code/harness-architecture-advisor/skill.yaml +1 -1
- package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +4 -4
- package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +1 -1
- package/dist/agents/skills/claude-code/harness-design-system/SKILL.md +6 -1
- package/dist/agents/skills/claude-code/harness-execution/SKILL.md +4 -4
- package/dist/agents/skills/claude-code/harness-i18n-process/SKILL.md +2 -2
- package/dist/agents/skills/claude-code/harness-integration/SKILL.md +1 -1
- package/dist/agents/skills/claude-code/harness-knowledge-pipeline/SKILL.md +1 -0
- package/dist/agents/skills/claude-code/harness-onboarding/SKILL.md +1 -1
- package/dist/agents/skills/claude-code/harness-planning/SKILL.md +3 -3
- package/dist/agents/skills/claude-code/harness-roadmap/SKILL.md +4 -3
- package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +150 -10
- package/dist/agents/skills/claude-code/initialize-harness-project/skill.yaml +2 -1
- package/dist/agents/skills/codex/harness-architecture-advisor/SKILL.md +17 -5
- package/dist/agents/skills/codex/harness-architecture-advisor/skill.yaml +1 -1
- package/dist/agents/skills/codex/harness-autopilot/SKILL.md +4 -4
- package/dist/agents/skills/codex/harness-code-review/SKILL.md +1 -1
- package/dist/agents/skills/codex/harness-design-system/SKILL.md +6 -1
- package/dist/agents/skills/codex/harness-execution/SKILL.md +4 -4
- package/dist/agents/skills/codex/harness-i18n-process/SKILL.md +2 -2
- package/dist/agents/skills/codex/harness-integration/SKILL.md +1 -1
- package/dist/agents/skills/codex/harness-knowledge-pipeline/SKILL.md +1 -0
- package/dist/agents/skills/codex/harness-onboarding/SKILL.md +1 -1
- package/dist/agents/skills/codex/harness-planning/SKILL.md +3 -3
- package/dist/agents/skills/codex/harness-roadmap/SKILL.md +4 -3
- package/dist/agents/skills/codex/initialize-harness-project/SKILL.md +150 -10
- package/dist/agents/skills/codex/initialize-harness-project/skill.yaml +2 -1
- package/dist/agents/skills/cursor/harness-architecture-advisor/SKILL.md +17 -5
- package/dist/agents/skills/cursor/harness-architecture-advisor/skill.yaml +1 -1
- package/dist/agents/skills/cursor/harness-autopilot/SKILL.md +4 -4
- package/dist/agents/skills/cursor/harness-code-review/SKILL.md +1 -1
- package/dist/agents/skills/cursor/harness-design-system/SKILL.md +6 -1
- package/dist/agents/skills/cursor/harness-execution/SKILL.md +4 -4
- package/dist/agents/skills/cursor/harness-i18n-process/SKILL.md +2 -2
- package/dist/agents/skills/cursor/harness-integration/SKILL.md +1 -1
- package/dist/agents/skills/cursor/harness-knowledge-pipeline/SKILL.md +1 -0
- package/dist/agents/skills/cursor/harness-onboarding/SKILL.md +1 -1
- package/dist/agents/skills/cursor/harness-planning/SKILL.md +3 -3
- package/dist/agents/skills/cursor/harness-roadmap/SKILL.md +4 -3
- package/dist/agents/skills/cursor/initialize-harness-project/SKILL.md +150 -10
- package/dist/agents/skills/cursor/initialize-harness-project/skill.yaml +2 -1
- package/dist/agents/skills/gemini-cli/harness-architecture-advisor/SKILL.md +17 -5
- package/dist/agents/skills/gemini-cli/harness-architecture-advisor/skill.yaml +1 -1
- package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +4 -4
- package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +1 -1
- package/dist/agents/skills/gemini-cli/harness-design-system/SKILL.md +6 -1
- package/dist/agents/skills/gemini-cli/harness-execution/SKILL.md +4 -4
- package/dist/agents/skills/gemini-cli/harness-i18n-process/SKILL.md +2 -2
- package/dist/agents/skills/gemini-cli/harness-integration/SKILL.md +1 -1
- package/dist/agents/skills/gemini-cli/harness-knowledge-pipeline/SKILL.md +1 -0
- package/dist/agents/skills/gemini-cli/harness-onboarding/SKILL.md +1 -1
- package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +3 -3
- package/dist/agents/skills/gemini-cli/harness-roadmap/SKILL.md +4 -3
- package/dist/agents/skills/gemini-cli/initialize-harness-project/SKILL.md +150 -10
- package/dist/agents/skills/gemini-cli/initialize-harness-project/skill.yaml +2 -1
- package/dist/{agents-md-2RN666DE.js → agents-md-76IEMV6O.js} +3 -3
- package/dist/{architecture-ZR33YKJB.js → architecture-5GVJRRUD.js} +4 -4
- package/dist/{assess-project-LLWWEQEV.js → assess-project-WDUFL7SO.js} +1 -1
- package/dist/bin/harness-mcp.js +15 -15
- package/dist/bin/harness.js +25 -25
- package/dist/{check-phase-gate-ZVG56NT2.js → check-phase-gate-QR3ONJYP.js} +4 -4
- package/dist/{chunk-FUS4OOGC.js → chunk-3WLXR23L.js} +1 -1
- package/dist/{chunk-IBTHD2UA.js → chunk-6JBJ5FSW.js} +2 -1
- package/dist/{chunk-C7RPQWBG.js → chunk-77OMOFZL.js} +1 -1
- package/dist/{chunk-YZH4I4JO.js → chunk-AJG2LCMA.js} +1 -1
- package/dist/{chunk-3AJW7VQ3.js → chunk-CS7K6ZQ3.js} +2 -2
- package/dist/{chunk-SDDQPMZK.js → chunk-D25P6763.js} +1 -1
- package/dist/{chunk-XKU37HGH.js → chunk-DHGL3SF2.js} +1 -1
- package/dist/{chunk-CJYNZMOP.js → chunk-E7QD4YHA.js} +8 -8
- package/dist/{chunk-BO4AJZRE.js → chunk-H43UQY6B.js} +1 -1
- package/dist/{chunk-SDPHFDQS.js → chunk-ILVO6LZY.js} +1 -1
- package/dist/{chunk-BZZNYHRY.js → chunk-K5ZOBBQB.js} +9 -9
- package/dist/{chunk-A4ETL34H.js → chunk-KLE6FXWM.js} +23 -1
- package/dist/{chunk-F3Y64VNF.js → chunk-L4RURCSF.js} +6 -6
- package/dist/{chunk-EGRQHLDM.js → chunk-LINAFNIK.js} +5 -5
- package/dist/{chunk-EHTUWULH.js → chunk-OPOSSZJS.js} +97 -93
- package/dist/{chunk-L3Y7L4EG.js → chunk-OQS7KURX.js} +1 -1
- package/dist/{chunk-T7UZOJF4.js → chunk-QZ46CI6E.js} +9 -9
- package/dist/{chunk-MGPVA2EM.js → chunk-TFNQ72RF.js} +5 -5
- package/dist/{chunk-3XMOWLK3.js → chunk-USG7DV5N.js} +2 -2
- package/dist/{chunk-776JFSPK.js → chunk-VITLE3PS.js} +1 -1
- package/dist/{chunk-2FOLD76X.js → chunk-WPSM7WT6.js} +2 -2
- package/dist/{chunk-GAIOIINV.js → chunk-X7XQ3ELM.js} +108 -36
- package/dist/{chunk-EICASBYP.js → chunk-YZZVOT7I.js} +1 -1
- package/dist/{chunk-VCC47EXU.js → chunk-Z63IDQ7Y.js} +860 -412
- package/dist/{chunk-Q5DKIL46.js → chunk-Z7ZF2XDL.js} +3 -3
- package/dist/{ci-workflow-JFOL4YWW.js → ci-workflow-GYQXE5X4.js} +3 -3
- package/dist/{dist-CPKL5Y2R.js → dist-46VOJIG4.js} +2 -2
- package/dist/{dist-BZS2XRLG.js → dist-BP6E6QGG.js} +7 -1
- package/dist/{docs-FUTRJQHA.js → docs-CVQMSMQC.js} +4 -4
- package/dist/{engine-FB3HCNVT.js → engine-23PJMRHA.js} +3 -3
- package/dist/{entropy-FIEE4YDA.js → entropy-K2YTZFCP.js} +3 -3
- package/dist/{feedback-7EC4T6XG.js → feedback-6HP42GTK.js} +1 -1
- package/dist/{generate-agent-definitions-FOSFADNR.js → generate-agent-definitions-6RJKL6VW.js} +4 -4
- package/dist/{graph-loader-QEI7SYOQ.js → graph-loader-BI3OFFFS.js} +1 -1
- package/dist/index.d.ts +44 -1
- package/dist/index.js +25 -25
- package/dist/{loader-HXUOBTYA.js → loader-U2WEC3PZ.js} +3 -3
- package/dist/{mcp-NUG4BMKJ.js → mcp-N6XVWPXJ.js} +15 -15
- package/dist/{performance-TYXBCTR7.js → performance-T3XAESWR.js} +4 -4
- package/dist/{review-pipeline-B4WPU6BQ.js → review-pipeline-HTC5HA5X.js} +3 -3
- package/dist/{runtime-HQQNOARD.js → runtime-UFXMNN3Q.js} +3 -3
- package/dist/{scan-M4EGDAQ2.js → scan-XRGMTO6K.js} +1 -1
- package/dist/{security-25VFLIAO.js → security-ROK5IFDA.js} +1 -1
- package/dist/{validate-EAVGKZQB.js → validate-IJHHQHT5.js} +4 -4
- package/dist/{validate-cross-check-SPL3H4O5.js → validate-cross-check-LUQYIA46.js} +3 -3
- package/package.json +6 -6
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-YZZVOT7I.js";
|
|
4
4
|
import {
|
|
5
5
|
generateAgentsMd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-77OMOFZL.js";
|
|
7
7
|
import {
|
|
8
8
|
generateCIWorkflow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3WLXR23L.js";
|
|
10
10
|
import {
|
|
11
11
|
TemplateEngine
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OQS7KURX.js";
|
|
13
13
|
import {
|
|
14
14
|
generate,
|
|
15
15
|
validate
|
|
@@ -28,21 +28,21 @@ import {
|
|
|
28
28
|
OutputMode,
|
|
29
29
|
createCheckPhaseGateCommand,
|
|
30
30
|
findFiles
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-USG7DV5N.js";
|
|
32
32
|
import {
|
|
33
33
|
createGenerateAgentDefinitionsCommand,
|
|
34
34
|
generateAgentDefinitions
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-D25P6763.js";
|
|
36
36
|
import {
|
|
37
37
|
toKebabCase
|
|
38
38
|
} from "./chunk-KET4QQZB.js";
|
|
39
39
|
import {
|
|
40
40
|
listPersonas,
|
|
41
41
|
loadPersona
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-H43UQY6B.js";
|
|
43
43
|
import {
|
|
44
44
|
createScanCommand
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-DHGL3SF2.js";
|
|
46
46
|
import {
|
|
47
47
|
appendFrameworkAgents,
|
|
48
48
|
captureHealthSnapshot,
|
|
@@ -56,17 +56,17 @@ import {
|
|
|
56
56
|
loadCachedSnapshot,
|
|
57
57
|
persistToolingConfig,
|
|
58
58
|
recommend
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-OPOSSZJS.js";
|
|
60
60
|
import {
|
|
61
61
|
loadGraphStore
|
|
62
|
-
} from "./chunk-
|
|
62
|
+
} from "./chunk-VITLE3PS.js";
|
|
63
63
|
import {
|
|
64
64
|
loadOrRebuildIndex
|
|
65
65
|
} from "./chunk-27AJKSQY.js";
|
|
66
66
|
import {
|
|
67
67
|
findConfigFile,
|
|
68
68
|
resolveConfig
|
|
69
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-KLE6FXWM.js";
|
|
70
70
|
import {
|
|
71
71
|
VALID_PLATFORMS
|
|
72
72
|
} from "./chunk-3ISINLYT.js";
|
|
@@ -151,7 +151,7 @@ import {
|
|
|
151
151
|
validateKnowledgeMap,
|
|
152
152
|
writeConfig,
|
|
153
153
|
writeLockfile
|
|
154
|
-
} from "./chunk-
|
|
154
|
+
} from "./chunk-6JBJ5FSW.js";
|
|
155
155
|
import {
|
|
156
156
|
Err,
|
|
157
157
|
Ok
|
|
@@ -176,7 +176,7 @@ import {
|
|
|
176
176
|
} from "./chunk-KFQGP6VL.js";
|
|
177
177
|
|
|
178
178
|
// src/index.ts
|
|
179
|
-
import { Command as
|
|
179
|
+
import { Command as Command86 } from "commander";
|
|
180
180
|
|
|
181
181
|
// src/commands/add.ts
|
|
182
182
|
init_dist();
|
|
@@ -365,7 +365,7 @@ function registerSkillsCommand(adoption) {
|
|
|
365
365
|
const globalOpts = cmd.optsWithGlobals();
|
|
366
366
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
367
367
|
const cwd = process.cwd();
|
|
368
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
368
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-46VOJIG4.js");
|
|
369
369
|
const records = readAdoptionRecords(cwd);
|
|
370
370
|
if (records.length === 0) {
|
|
371
371
|
if (globalOpts.json) {
|
|
@@ -401,7 +401,7 @@ function registerRecentCommand(adoption) {
|
|
|
401
401
|
const globalOpts = cmd.optsWithGlobals();
|
|
402
402
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
403
403
|
const cwd = process.cwd();
|
|
404
|
-
const { readAdoptionRecords } = await import("./dist-
|
|
404
|
+
const { readAdoptionRecords } = await import("./dist-46VOJIG4.js");
|
|
405
405
|
const records = readAdoptionRecords(cwd);
|
|
406
406
|
if (records.length === 0) {
|
|
407
407
|
if (globalOpts.json) {
|
|
@@ -479,7 +479,7 @@ function registerSkillCommand(adoption) {
|
|
|
479
479
|
adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
|
|
480
480
|
const globalOpts = cmd.optsWithGlobals();
|
|
481
481
|
const cwd = process.cwd();
|
|
482
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
482
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-46VOJIG4.js");
|
|
483
483
|
const records = readAdoptionRecords(cwd);
|
|
484
484
|
const skillRecords = records.filter((r) => r.skill === name);
|
|
485
485
|
if (skillRecords.length === 0) {
|
|
@@ -1424,10 +1424,10 @@ async function runCheckSecurity(cwd, options) {
|
|
|
1424
1424
|
const projectRoot = path8.resolve(cwd);
|
|
1425
1425
|
let configData = {};
|
|
1426
1426
|
try {
|
|
1427
|
-
const
|
|
1427
|
+
const fs42 = await import("fs");
|
|
1428
1428
|
const configPath = path8.join(projectRoot, "harness.config.json");
|
|
1429
|
-
if (
|
|
1430
|
-
const raw =
|
|
1429
|
+
if (fs42.existsSync(configPath)) {
|
|
1430
|
+
const raw = fs42.readFileSync(configPath, "utf-8");
|
|
1431
1431
|
const parsed = JSON.parse(raw);
|
|
1432
1432
|
configData = parsed.security ?? {};
|
|
1433
1433
|
}
|
|
@@ -2768,16 +2768,16 @@ import { Command as Command25 } from "commander";
|
|
|
2768
2768
|
// src/commands/graph/status.ts
|
|
2769
2769
|
import * as path15 from "path";
|
|
2770
2770
|
async function runGraphStatus(projectPath) {
|
|
2771
|
-
const { GraphStore } = await import("./dist-
|
|
2771
|
+
const { GraphStore } = await import("./dist-BP6E6QGG.js");
|
|
2772
2772
|
const graphDir = path15.join(projectPath, ".harness", "graph");
|
|
2773
2773
|
const store = new GraphStore();
|
|
2774
2774
|
const loaded = await store.load(graphDir);
|
|
2775
2775
|
if (!loaded) return { status: "no_graph", message: "No graph found. Run `harness scan` first." };
|
|
2776
|
-
const
|
|
2776
|
+
const fs42 = await import("fs/promises");
|
|
2777
2777
|
const metaPath = path15.join(graphDir, "metadata.json");
|
|
2778
2778
|
let lastScan = "unknown";
|
|
2779
2779
|
try {
|
|
2780
|
-
const meta = JSON.parse(await
|
|
2780
|
+
const meta = JSON.parse(await fs42.readFile(metaPath, "utf-8"));
|
|
2781
2781
|
lastScan = meta.lastScanTimestamp;
|
|
2782
2782
|
} catch {
|
|
2783
2783
|
}
|
|
@@ -2789,7 +2789,7 @@ async function runGraphStatus(projectPath) {
|
|
|
2789
2789
|
let connectorSyncStatus = {};
|
|
2790
2790
|
try {
|
|
2791
2791
|
const syncMetaPath = path15.join(graphDir, "sync-metadata.json");
|
|
2792
|
-
const syncMeta = JSON.parse(await
|
|
2792
|
+
const syncMeta = JSON.parse(await fs42.readFile(syncMetaPath, "utf-8"));
|
|
2793
2793
|
for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
|
|
2794
2794
|
connectorSyncStatus[name] = data.lastSyncTimestamp;
|
|
2795
2795
|
}
|
|
@@ -2808,7 +2808,7 @@ async function runGraphStatus(projectPath) {
|
|
|
2808
2808
|
// src/commands/graph/export.ts
|
|
2809
2809
|
import * as path16 from "path";
|
|
2810
2810
|
async function runGraphExport(projectPath, format) {
|
|
2811
|
-
const { GraphStore } = await import("./dist-
|
|
2811
|
+
const { GraphStore } = await import("./dist-BP6E6QGG.js");
|
|
2812
2812
|
const graphDir = path16.join(projectPath, ".harness", "graph");
|
|
2813
2813
|
const store = new GraphStore();
|
|
2814
2814
|
const loaded = await store.load(graphDir);
|
|
@@ -2844,7 +2844,7 @@ import * as path19 from "path";
|
|
|
2844
2844
|
import { Command as Command23 } from "commander";
|
|
2845
2845
|
import * as path17 from "path";
|
|
2846
2846
|
async function runQuery(projectPath, rootNodeId, opts) {
|
|
2847
|
-
const { GraphStore, ContextQL } = await import("./dist-
|
|
2847
|
+
const { GraphStore, ContextQL } = await import("./dist-BP6E6QGG.js");
|
|
2848
2848
|
const store = new GraphStore();
|
|
2849
2849
|
const graphDir = path17.join(projectPath, ".harness", "graph");
|
|
2850
2850
|
const loaded = await store.load(graphDir);
|
|
@@ -2897,9 +2897,9 @@ import { Command as Command24 } from "commander";
|
|
|
2897
2897
|
import * as path18 from "path";
|
|
2898
2898
|
async function loadConnectorConfig(projectPath, source) {
|
|
2899
2899
|
try {
|
|
2900
|
-
const
|
|
2900
|
+
const fs42 = await import("fs/promises");
|
|
2901
2901
|
const configPath = path18.join(projectPath, "harness.config.json");
|
|
2902
|
-
const config = JSON.parse(await
|
|
2902
|
+
const config = JSON.parse(await fs42.readFile(configPath, "utf-8"));
|
|
2903
2903
|
const connector = config.graph?.connectors?.[source];
|
|
2904
2904
|
return connector ?? {};
|
|
2905
2905
|
} catch {
|
|
@@ -2941,7 +2941,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
2941
2941
|
ConfluenceConnector,
|
|
2942
2942
|
FigmaConnector,
|
|
2943
2943
|
MiroConnector
|
|
2944
|
-
} = await import("./dist-
|
|
2944
|
+
} = await import("./dist-BP6E6QGG.js");
|
|
2945
2945
|
const graphDir = path18.join(projectPath, ".harness", "graph");
|
|
2946
2946
|
const store = new GraphStore();
|
|
2947
2947
|
await store.load(graphDir);
|
|
@@ -2954,7 +2954,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
2954
2954
|
path18.join(projectPath, "docs", "changes")
|
|
2955
2955
|
);
|
|
2956
2956
|
const gitResult = await new GitIngestor(store).ingest(projectPath);
|
|
2957
|
-
const { createExtractionRunner } = await import("./dist-
|
|
2957
|
+
const { createExtractionRunner } = await import("./dist-BP6E6QGG.js");
|
|
2958
2958
|
const extractedDir = path18.join(projectPath, ".harness", "knowledge", "extracted");
|
|
2959
2959
|
const signalsResult = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
2960
2960
|
const syncManager = new SyncManager(store, graphDir);
|
|
@@ -3000,7 +3000,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
3000
3000
|
);
|
|
3001
3001
|
break;
|
|
3002
3002
|
case "business-signals": {
|
|
3003
|
-
const { createExtractionRunner } = await import("./dist-
|
|
3003
|
+
const { createExtractionRunner } = await import("./dist-BP6E6QGG.js");
|
|
3004
3004
|
const extractedDir = path18.join(projectPath, ".harness", "knowledge", "extracted");
|
|
3005
3005
|
result = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
3006
3006
|
break;
|
|
@@ -5299,21 +5299,28 @@ function createKnowledgePipelineCommand() {
|
|
|
5299
5299
|
const projectDir = process.cwd();
|
|
5300
5300
|
try {
|
|
5301
5301
|
const graphDir = path38.join(projectDir, ".harness", "graph");
|
|
5302
|
-
const
|
|
5303
|
-
await
|
|
5304
|
-
const { GraphStore, KnowledgePipelineRunner } = await import("./dist-
|
|
5302
|
+
const fs42 = await import("fs/promises");
|
|
5303
|
+
await fs42.mkdir(graphDir, { recursive: true });
|
|
5304
|
+
const { GraphStore, KnowledgePipelineRunner } = await import("./dist-BP6E6QGG.js");
|
|
5305
5305
|
const store = new GraphStore();
|
|
5306
5306
|
try {
|
|
5307
5307
|
await store.load(graphDir);
|
|
5308
5308
|
} catch {
|
|
5309
5309
|
}
|
|
5310
|
+
const cfgResult = resolveConfig();
|
|
5311
|
+
const cfgKnowledge = cfgResult.ok ? cfgResult.value.knowledge : void 0;
|
|
5312
|
+
const inferenceOptions = cfgKnowledge && ((cfgKnowledge.domainPatterns?.length ?? 0) > 0 || (cfgKnowledge.domainBlocklist?.length ?? 0) > 0) ? {
|
|
5313
|
+
...(cfgKnowledge.domainPatterns?.length ?? 0) > 0 ? { extraPatterns: cfgKnowledge.domainPatterns } : {},
|
|
5314
|
+
...(cfgKnowledge.domainBlocklist?.length ?? 0) > 0 ? { extraBlocklist: cfgKnowledge.domainBlocklist } : {}
|
|
5315
|
+
} : void 0;
|
|
5310
5316
|
const pipelineOpts = {
|
|
5311
5317
|
projectDir,
|
|
5312
5318
|
fix: Boolean(opts.fix),
|
|
5313
5319
|
ci: Boolean(opts.ci),
|
|
5314
5320
|
...opts.domain ? { domain: opts.domain } : {},
|
|
5315
5321
|
graphDir,
|
|
5316
|
-
analyzeImages: Boolean(opts.analyzeImages)
|
|
5322
|
+
analyzeImages: Boolean(opts.analyzeImages),
|
|
5323
|
+
...inferenceOptions ? { inferenceOptions } : {}
|
|
5317
5324
|
};
|
|
5318
5325
|
if (opts.imagePaths) {
|
|
5319
5326
|
pipelineOpts.imagePaths = opts.imagePaths.split(",").map((p) => p.trim());
|
|
@@ -5636,7 +5643,7 @@ function createMcpCommand() {
|
|
|
5636
5643
|
parseBudget
|
|
5637
5644
|
).action(async (opts) => {
|
|
5638
5645
|
const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
|
|
5639
|
-
import("./mcp-
|
|
5646
|
+
import("./mcp-N6XVWPXJ.js"),
|
|
5640
5647
|
import("./tool-tiers-7QGZ3FKY.js")
|
|
5641
5648
|
]);
|
|
5642
5649
|
if (opts.tools && opts.tools.length > 0) {
|
|
@@ -5656,14 +5663,439 @@ function createMcpCommand() {
|
|
|
5656
5663
|
});
|
|
5657
5664
|
}
|
|
5658
5665
|
|
|
5659
|
-
// src/commands/
|
|
5666
|
+
// src/commands/migrate.ts
|
|
5667
|
+
init_dist();
|
|
5660
5668
|
import { Command as Command48 } from "commander";
|
|
5669
|
+
import * as fs24 from "fs";
|
|
5661
5670
|
import * as path40 from "path";
|
|
5671
|
+
import * as readline from "readline";
|
|
5672
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
5673
|
+
import chalk7 from "chalk";
|
|
5674
|
+
function isGitRepo(cwd) {
|
|
5675
|
+
try {
|
|
5676
|
+
execFileSync4("git", ["rev-parse", "--is-inside-work-tree"], { cwd, stdio: "pipe" });
|
|
5677
|
+
return true;
|
|
5678
|
+
} catch {
|
|
5679
|
+
return false;
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
function readJson2(p) {
|
|
5683
|
+
try {
|
|
5684
|
+
return JSON.parse(fs24.readFileSync(p, "utf8"));
|
|
5685
|
+
} catch {
|
|
5686
|
+
return null;
|
|
5687
|
+
}
|
|
5688
|
+
}
|
|
5689
|
+
function listDirs(p) {
|
|
5690
|
+
if (!fs24.existsSync(p)) return [];
|
|
5691
|
+
return fs24.readdirSync(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
5692
|
+
}
|
|
5693
|
+
function listFiles(p, predicate) {
|
|
5694
|
+
if (!fs24.existsSync(p)) return [];
|
|
5695
|
+
return fs24.readdirSync(p, { withFileTypes: true }).filter((e) => e.isFile() && predicate(e.name)).map((e) => e.name);
|
|
5696
|
+
}
|
|
5697
|
+
function walk(dir, predicate) {
|
|
5698
|
+
if (!fs24.existsSync(dir)) return [];
|
|
5699
|
+
const out = [];
|
|
5700
|
+
for (const entry of fs24.readdirSync(dir, { withFileTypes: true })) {
|
|
5701
|
+
if (entry.name === "node_modules" || entry.name === ".git" || entry.name === "dist") continue;
|
|
5702
|
+
const full = path40.join(dir, entry.name);
|
|
5703
|
+
if (entry.isDirectory()) out.push(...walk(full, predicate));
|
|
5704
|
+
else if (predicate(full)) out.push(full);
|
|
5705
|
+
}
|
|
5706
|
+
return out;
|
|
5707
|
+
}
|
|
5708
|
+
function buildAutopilotMap(cwd) {
|
|
5709
|
+
const map = /* @__PURE__ */ new Map();
|
|
5710
|
+
const sessionsDir = path40.join(cwd, ".harness", "sessions");
|
|
5711
|
+
if (!fs24.existsSync(sessionsDir)) return map;
|
|
5712
|
+
for (const session of listDirs(sessionsDir)) {
|
|
5713
|
+
if (!session.startsWith("changes--")) continue;
|
|
5714
|
+
const statePath = path40.join(sessionsDir, session, "autopilot-state.json");
|
|
5715
|
+
const state = readJson2(statePath);
|
|
5716
|
+
if (!state) continue;
|
|
5717
|
+
const specMatch = (state.specPath ?? "").match(/^docs\/changes\/([^/]+)\/proposal\.md$/);
|
|
5718
|
+
if (!specMatch || !specMatch[1]) continue;
|
|
5719
|
+
const topic = specMatch[1];
|
|
5720
|
+
for (const phase of state.phases ?? []) {
|
|
5721
|
+
if (phase.planPath) map.set(phase.planPath, topic);
|
|
5722
|
+
}
|
|
5723
|
+
}
|
|
5724
|
+
return map;
|
|
5725
|
+
}
|
|
5726
|
+
function extractTopicFromHeader(planAbs, proposalTopics) {
|
|
5727
|
+
let text;
|
|
5728
|
+
try {
|
|
5729
|
+
text = fs24.readFileSync(planAbs, "utf8").slice(0, 4e3);
|
|
5730
|
+
} catch {
|
|
5731
|
+
return null;
|
|
5732
|
+
}
|
|
5733
|
+
const patterns = [
|
|
5734
|
+
/docs\/changes\/([a-z0-9-]+)\/proposal\.md/i,
|
|
5735
|
+
/\*\*Spec[^:]*:\*\*\s*\[?[^\]]*?docs\/changes\/([a-z0-9-]+)/i
|
|
5736
|
+
];
|
|
5737
|
+
for (const re of patterns) {
|
|
5738
|
+
const m = text.match(re);
|
|
5739
|
+
if (m && m[1] && proposalTopics.has(m[1])) return m[1];
|
|
5740
|
+
}
|
|
5741
|
+
return null;
|
|
5742
|
+
}
|
|
5743
|
+
function extractTopicFromFilename(filename, proposalTopics) {
|
|
5744
|
+
const stripped = filename.replace(/\.md$/, "").replace(/^\d{4}-\d{2}-\d{2}-/, "").replace(/-plan$/, "").replace(/-phase\d+(-plan)?$/, "");
|
|
5745
|
+
if (proposalTopics.has(stripped)) return stripped;
|
|
5746
|
+
let best = null;
|
|
5747
|
+
for (const topic of proposalTopics) {
|
|
5748
|
+
const matches = stripped === topic || stripped.startsWith(`${topic}-`);
|
|
5749
|
+
if (matches && (!best || topic.length > best.length)) best = topic;
|
|
5750
|
+
}
|
|
5751
|
+
return best;
|
|
5752
|
+
}
|
|
5753
|
+
async function buildPlan(opts) {
|
|
5754
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
5755
|
+
const configPath = path40.join(cwd, "harness.config.json");
|
|
5756
|
+
const configResult = fs24.existsSync(configPath) ? resolveConfig(configPath) : null;
|
|
5757
|
+
let docsDir;
|
|
5758
|
+
if (configResult?.ok) {
|
|
5759
|
+
docsDir = configResult.value.docsDir;
|
|
5760
|
+
} else if (configResult && !configResult.ok) {
|
|
5761
|
+
docsDir = "./docs";
|
|
5762
|
+
logger.warn(
|
|
5763
|
+
` harness.config.json failed to load (${configResult.error.message}); defaulting docsDir to ./docs`
|
|
5764
|
+
);
|
|
5765
|
+
} else {
|
|
5766
|
+
docsDir = "./docs";
|
|
5767
|
+
}
|
|
5768
|
+
const docsAbs = path40.resolve(cwd, docsDir);
|
|
5769
|
+
const relRaw = path40.relative(cwd, docsAbs).replaceAll("\\", "/");
|
|
5770
|
+
const docsRel = relRaw === "" ? "docs" : relRaw;
|
|
5771
|
+
const plan = {
|
|
5772
|
+
cwd,
|
|
5773
|
+
isGitRepo: isGitRepo(cwd),
|
|
5774
|
+
docsDir: docsRel,
|
|
5775
|
+
adrMoves: [],
|
|
5776
|
+
planMoves: [],
|
|
5777
|
+
orphanPlans: [],
|
|
5778
|
+
proposalTopics: /* @__PURE__ */ new Set()
|
|
5779
|
+
};
|
|
5780
|
+
const legacyAdrDir = path40.join(cwd, ".harness", "architecture");
|
|
5781
|
+
if (fs24.existsSync(legacyAdrDir)) {
|
|
5782
|
+
for (const topic of listDirs(legacyAdrDir)) {
|
|
5783
|
+
const topicSrc = path40.join(legacyAdrDir, topic);
|
|
5784
|
+
for (const file of listFiles(topicSrc, (n) => n.endsWith(".md"))) {
|
|
5785
|
+
const src = `.harness/architecture/${topic}/${file}`;
|
|
5786
|
+
const dest = `${docsRel}/architecture/${topic}/${file}`;
|
|
5787
|
+
plan.adrMoves.push({ src, dest });
|
|
5788
|
+
}
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
const legacyPlanDir = path40.join(cwd, "docs", "plans");
|
|
5792
|
+
const changesDir = path40.join(cwd, "docs", "changes");
|
|
5793
|
+
for (const topic of listDirs(changesDir)) {
|
|
5794
|
+
if (fs24.existsSync(path40.join(changesDir, topic, "proposal.md"))) {
|
|
5795
|
+
plan.proposalTopics.add(topic);
|
|
5796
|
+
}
|
|
5797
|
+
}
|
|
5798
|
+
if (fs24.existsSync(legacyPlanDir)) {
|
|
5799
|
+
const autopilotMap = buildAutopilotMap(cwd);
|
|
5800
|
+
for (const file of listFiles(legacyPlanDir, (n) => n.endsWith(".md") && n !== "index.md")) {
|
|
5801
|
+
const planRel = `docs/plans/${file}`;
|
|
5802
|
+
const planAbs = path40.join(legacyPlanDir, file);
|
|
5803
|
+
const isVerification = file.includes("VERIFICATION");
|
|
5804
|
+
const subdir = isVerification ? "verifications" : "plans";
|
|
5805
|
+
let topic = null;
|
|
5806
|
+
let via = null;
|
|
5807
|
+
const fromAutopilot = autopilotMap.get(planRel);
|
|
5808
|
+
if (fromAutopilot) {
|
|
5809
|
+
topic = fromAutopilot;
|
|
5810
|
+
via = "autopilot";
|
|
5811
|
+
} else {
|
|
5812
|
+
const fromHeader = extractTopicFromHeader(planAbs, plan.proposalTopics);
|
|
5813
|
+
if (fromHeader) {
|
|
5814
|
+
topic = fromHeader;
|
|
5815
|
+
via = "header";
|
|
5816
|
+
} else {
|
|
5817
|
+
const fromFilename = extractTopicFromFilename(file, plan.proposalTopics);
|
|
5818
|
+
if (fromFilename) {
|
|
5819
|
+
topic = fromFilename;
|
|
5820
|
+
via = "filename";
|
|
5821
|
+
}
|
|
5822
|
+
}
|
|
5823
|
+
}
|
|
5824
|
+
if (topic && via) {
|
|
5825
|
+
plan.planMoves.push({
|
|
5826
|
+
src: planRel,
|
|
5827
|
+
dest: `docs/changes/${topic}/${subdir}/${file}`,
|
|
5828
|
+
topic,
|
|
5829
|
+
via,
|
|
5830
|
+
subdir
|
|
5831
|
+
});
|
|
5832
|
+
} else {
|
|
5833
|
+
plan.orphanPlans.push(planRel);
|
|
5834
|
+
}
|
|
5835
|
+
}
|
|
5836
|
+
}
|
|
5837
|
+
return plan;
|
|
5838
|
+
}
|
|
5839
|
+
function gitMv(cwd, src, dest, useGit) {
|
|
5840
|
+
const destAbs = path40.join(cwd, dest);
|
|
5841
|
+
fs24.mkdirSync(path40.dirname(destAbs), { recursive: true });
|
|
5842
|
+
if (useGit) {
|
|
5843
|
+
try {
|
|
5844
|
+
execFileSync4("git", ["mv", src, dest], { cwd, stdio: "pipe" });
|
|
5845
|
+
return;
|
|
5846
|
+
} catch {
|
|
5847
|
+
}
|
|
5848
|
+
}
|
|
5849
|
+
fs24.renameSync(path40.join(cwd, src), destAbs);
|
|
5850
|
+
}
|
|
5851
|
+
function executeMigration(plan) {
|
|
5852
|
+
let moved = 0;
|
|
5853
|
+
let failed = 0;
|
|
5854
|
+
const allMoves = [
|
|
5855
|
+
...plan.adrMoves.map((m) => ({ src: m.src, dest: m.dest })),
|
|
5856
|
+
...plan.planMoves.map((m) => ({ src: m.src, dest: m.dest }))
|
|
5857
|
+
];
|
|
5858
|
+
for (const { src, dest } of allMoves) {
|
|
5859
|
+
try {
|
|
5860
|
+
gitMv(plan.cwd, src, dest, plan.isGitRepo);
|
|
5861
|
+
moved++;
|
|
5862
|
+
} catch (err) {
|
|
5863
|
+
failed++;
|
|
5864
|
+
logger.error(` ${src} -> ${dest}: ${err.message}`);
|
|
5865
|
+
}
|
|
5866
|
+
}
|
|
5867
|
+
return { moved, failed };
|
|
5868
|
+
}
|
|
5869
|
+
function buildPathLookup(plan) {
|
|
5870
|
+
const lookup = /* @__PURE__ */ new Map();
|
|
5871
|
+
for (const m of plan.adrMoves) lookup.set(m.src, m.dest);
|
|
5872
|
+
for (const m of plan.planMoves) lookup.set(m.src, m.dest);
|
|
5873
|
+
return lookup;
|
|
5874
|
+
}
|
|
5875
|
+
function updateReferences(plan) {
|
|
5876
|
+
const lookup = buildPathLookup(plan);
|
|
5877
|
+
if (lookup.size === 0) return { files: 0, replacements: 0 };
|
|
5878
|
+
let files = 0;
|
|
5879
|
+
let replacements = 0;
|
|
5880
|
+
const targets = [
|
|
5881
|
+
...walk(path40.join(plan.cwd, "docs"), (p) => p.endsWith(".md")),
|
|
5882
|
+
...walk(path40.join(plan.cwd, ".harness", "sessions"), (p) => p.endsWith(".json"))
|
|
5883
|
+
];
|
|
5884
|
+
for (const target of targets) {
|
|
5885
|
+
let text;
|
|
5886
|
+
try {
|
|
5887
|
+
text = fs24.readFileSync(target, "utf8");
|
|
5888
|
+
} catch {
|
|
5889
|
+
continue;
|
|
5890
|
+
}
|
|
5891
|
+
let result = text;
|
|
5892
|
+
let count = 0;
|
|
5893
|
+
for (const [oldPath, newPath] of lookup) {
|
|
5894
|
+
if (!result.includes(oldPath)) continue;
|
|
5895
|
+
const occurrences = result.split(oldPath).length - 1;
|
|
5896
|
+
result = result.split(oldPath).join(newPath);
|
|
5897
|
+
count += occurrences;
|
|
5898
|
+
}
|
|
5899
|
+
if (count > 0 && result !== text) {
|
|
5900
|
+
fs24.writeFileSync(target, result);
|
|
5901
|
+
files++;
|
|
5902
|
+
replacements += count;
|
|
5903
|
+
}
|
|
5904
|
+
}
|
|
5905
|
+
return { files, replacements };
|
|
5906
|
+
}
|
|
5907
|
+
async function prompt(question, def = "n") {
|
|
5908
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
5909
|
+
const answer = await new Promise((resolve37) => rl.question(question, resolve37));
|
|
5910
|
+
rl.close();
|
|
5911
|
+
return (answer.trim() || def).toLowerCase();
|
|
5912
|
+
}
|
|
5913
|
+
async function resolveOrphans(plan, opts) {
|
|
5914
|
+
if (plan.orphanPlans.length === 0) return Ok({ bucketed: false });
|
|
5915
|
+
const strategy = opts.orphanStrategy ?? "ask";
|
|
5916
|
+
if (opts.dryRun && strategy === "ask") {
|
|
5917
|
+
logger.info(
|
|
5918
|
+
` Dry run + --orphan-strategy=ask: not prompting; ${plan.orphanPlans.length} orphan(s) would be asked about interactively.`
|
|
5919
|
+
);
|
|
5920
|
+
return Ok({ bucketed: false });
|
|
5921
|
+
}
|
|
5922
|
+
if (strategy === "skip" || strategy === "ask" && !process.stdin.isTTY) {
|
|
5923
|
+
logger.warn(
|
|
5924
|
+
` Leaving ${plan.orphanPlans.length} orphan plan(s) in docs/plans/ (no proposal match)`
|
|
5925
|
+
);
|
|
5926
|
+
return Ok({ bucketed: false });
|
|
5927
|
+
}
|
|
5928
|
+
if (strategy === "bucket") {
|
|
5929
|
+
const topic = opts.orphanTopic;
|
|
5930
|
+
if (!topic) {
|
|
5931
|
+
return Err(
|
|
5932
|
+
new CLIError(
|
|
5933
|
+
"--orphan-strategy=bucket requires --orphan-topic <name>",
|
|
5934
|
+
ExitCode.VALIDATION_FAILED
|
|
5935
|
+
)
|
|
5936
|
+
);
|
|
5937
|
+
}
|
|
5938
|
+
bucketOrphans(plan, topic);
|
|
5939
|
+
return Ok({ bucketed: true });
|
|
5940
|
+
}
|
|
5941
|
+
console.log("");
|
|
5942
|
+
logger.warn(`Found ${plan.orphanPlans.length} plan(s) with no matching proposal:`);
|
|
5943
|
+
for (const p of plan.orphanPlans.slice(0, 10)) console.log(` - ${p}`);
|
|
5944
|
+
if (plan.orphanPlans.length > 10) console.log(` ... and ${plan.orphanPlans.length - 10} more`);
|
|
5945
|
+
console.log("");
|
|
5946
|
+
console.log(" [s] skip \u2014 leave them in docs/plans/");
|
|
5947
|
+
console.log(" [b] bucket \u2014 move all into a single stub topic");
|
|
5948
|
+
const answer = await prompt("Choose [s/b]: ", "s");
|
|
5949
|
+
if (answer === "b") {
|
|
5950
|
+
const topicAnswer = await prompt("Stub topic name (e.g., legacy-plans): ", "legacy-plans");
|
|
5951
|
+
bucketOrphans(plan, topicAnswer);
|
|
5952
|
+
return Ok({ bucketed: true });
|
|
5953
|
+
}
|
|
5954
|
+
logger.info(` Leaving ${plan.orphanPlans.length} orphan plan(s) in docs/plans/`);
|
|
5955
|
+
return Ok({ bucketed: false });
|
|
5956
|
+
}
|
|
5957
|
+
function bucketOrphans(plan, topic) {
|
|
5958
|
+
for (const src of plan.orphanPlans) {
|
|
5959
|
+
const file = path40.basename(src);
|
|
5960
|
+
const isVerification = file.includes("VERIFICATION");
|
|
5961
|
+
const subdir = isVerification ? "verifications" : "plans";
|
|
5962
|
+
plan.planMoves.push({
|
|
5963
|
+
src,
|
|
5964
|
+
dest: `docs/changes/${topic}/${subdir}/${file}`,
|
|
5965
|
+
topic,
|
|
5966
|
+
via: "orphan-bucket",
|
|
5967
|
+
subdir
|
|
5968
|
+
});
|
|
5969
|
+
}
|
|
5970
|
+
plan.orphanPlans = [];
|
|
5971
|
+
}
|
|
5972
|
+
function summarize(plan) {
|
|
5973
|
+
console.log("");
|
|
5974
|
+
console.log(chalk7.bold(" Migration plan"));
|
|
5975
|
+
console.log("");
|
|
5976
|
+
if (plan.adrMoves.length === 0 && plan.planMoves.length === 0 && plan.orphanPlans.length === 0) {
|
|
5977
|
+
logger.success(" Nothing to migrate. Layout is already up to date.");
|
|
5978
|
+
return;
|
|
5979
|
+
}
|
|
5980
|
+
if (plan.adrMoves.length > 0) {
|
|
5981
|
+
logger.info(
|
|
5982
|
+
` ADR moves: ${plan.adrMoves.length} file(s) \u2014 .harness/architecture/ \u2192 ${plan.docsDir}/architecture/`
|
|
5983
|
+
);
|
|
5984
|
+
}
|
|
5985
|
+
if (plan.planMoves.length > 0) {
|
|
5986
|
+
const byVia = plan.planMoves.reduce((acc, m) => {
|
|
5987
|
+
acc[m.via] = (acc[m.via] ?? 0) + 1;
|
|
5988
|
+
return acc;
|
|
5989
|
+
}, {});
|
|
5990
|
+
logger.info(
|
|
5991
|
+
` Plan moves: ${plan.planMoves.length} file(s) \u2014 docs/plans/ \u2192 docs/changes/<topic>/{plans,verifications}/`
|
|
5992
|
+
);
|
|
5993
|
+
for (const [via, n] of Object.entries(byVia)) console.log(chalk7.dim(` via ${via}: ${n}`));
|
|
5994
|
+
}
|
|
5995
|
+
if (plan.orphanPlans.length > 0) {
|
|
5996
|
+
logger.warn(` Orphan plans: ${plan.orphanPlans.length} (no proposal match \u2014 needs decision)`);
|
|
5997
|
+
}
|
|
5998
|
+
console.log("");
|
|
5999
|
+
}
|
|
6000
|
+
async function detectLegacyArtifacts(cwd) {
|
|
6001
|
+
const adrLegacyDir = path40.join(cwd, ".harness", "architecture");
|
|
6002
|
+
const adrLegacy = fs24.existsSync(adrLegacyDir) && listDirs(adrLegacyDir).length > 0;
|
|
6003
|
+
const planLegacyDir = path40.join(cwd, "docs", "plans");
|
|
6004
|
+
const planLegacy = fs24.existsSync(planLegacyDir) && listFiles(planLegacyDir, (n) => n.endsWith(".md") && n !== "index.md").length > 0;
|
|
6005
|
+
return { adrLegacy, planLegacy };
|
|
6006
|
+
}
|
|
6007
|
+
async function runMigrate(opts) {
|
|
6008
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
6009
|
+
const plan = await buildPlan({ ...opts, cwd });
|
|
6010
|
+
summarize(plan);
|
|
6011
|
+
const empty = {
|
|
6012
|
+
movesPlanned: 0,
|
|
6013
|
+
movesApplied: 0,
|
|
6014
|
+
movesFailed: 0,
|
|
6015
|
+
orphansRemaining: 0,
|
|
6016
|
+
references: { files: 0, replacements: 0 },
|
|
6017
|
+
dryRun: opts.dryRun ?? false,
|
|
6018
|
+
cancelled: false
|
|
6019
|
+
};
|
|
6020
|
+
if (plan.adrMoves.length === 0 && plan.planMoves.length === 0 && plan.orphanPlans.length === 0) {
|
|
6021
|
+
return Ok(empty);
|
|
6022
|
+
}
|
|
6023
|
+
const orphanResult = await resolveOrphans(plan, opts);
|
|
6024
|
+
if (!orphanResult.ok) return orphanResult;
|
|
6025
|
+
if (orphanResult.value.bucketed) summarize(plan);
|
|
6026
|
+
const movesPlanned = plan.adrMoves.length + plan.planMoves.length;
|
|
6027
|
+
if (opts.dryRun) {
|
|
6028
|
+
logger.info(" Dry run \u2014 no files moved.");
|
|
6029
|
+
return Ok({
|
|
6030
|
+
...empty,
|
|
6031
|
+
movesPlanned,
|
|
6032
|
+
orphansRemaining: plan.orphanPlans.length
|
|
6033
|
+
});
|
|
6034
|
+
}
|
|
6035
|
+
if (!opts.yes && process.stdin.isTTY) {
|
|
6036
|
+
const answer = await prompt("Apply this migration? [y/N] ", "n");
|
|
6037
|
+
if (answer !== "y" && answer !== "yes") {
|
|
6038
|
+
logger.info(" Cancelled.");
|
|
6039
|
+
return Ok({ ...empty, movesPlanned, cancelled: true });
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
6042
|
+
const { moved, failed } = executeMigration(plan);
|
|
6043
|
+
console.log("");
|
|
6044
|
+
if (failed > 0) logger.error(` Moved ${moved} file(s); ${failed} failed.`);
|
|
6045
|
+
else logger.success(` Moved ${moved} file(s).`);
|
|
6046
|
+
let references = { files: 0, replacements: 0 };
|
|
6047
|
+
if (!opts.skipReferences) {
|
|
6048
|
+
references = updateReferences(plan);
|
|
6049
|
+
if (references.replacements > 0) {
|
|
6050
|
+
logger.success(
|
|
6051
|
+
` Updated ${references.replacements} reference(s) across ${references.files} file(s).`
|
|
6052
|
+
);
|
|
6053
|
+
} else {
|
|
6054
|
+
logger.info(" No reference updates needed.");
|
|
6055
|
+
}
|
|
6056
|
+
}
|
|
6057
|
+
console.log("");
|
|
6058
|
+
logger.info(" Next: review changes with `git status` / `git diff`, then commit.");
|
|
6059
|
+
return Ok({
|
|
6060
|
+
movesPlanned,
|
|
6061
|
+
movesApplied: moved,
|
|
6062
|
+
movesFailed: failed,
|
|
6063
|
+
orphansRemaining: plan.orphanPlans.length,
|
|
6064
|
+
references,
|
|
6065
|
+
dryRun: false,
|
|
6066
|
+
cancelled: false
|
|
6067
|
+
});
|
|
6068
|
+
}
|
|
6069
|
+
function createMigrateCommand() {
|
|
6070
|
+
return new Command48("migrate").description("Migrate legacy harness artifact locations to current layout").option("--dry-run", "Show the migration plan without moving files", false).option("--yes", "Skip confirmation prompt", false).option(
|
|
6071
|
+
"--skip-references",
|
|
6072
|
+
"Do not update path references in docs/sessions after moves",
|
|
6073
|
+
false
|
|
6074
|
+
).option("--orphan-strategy <strategy>", "How to handle orphan plans (ask|skip|bucket)", "ask").option("--orphan-topic <name>", "Stub topic name when --orphan-strategy=bucket").action(async (options) => {
|
|
6075
|
+
const result = await runMigrate({
|
|
6076
|
+
cwd: process.cwd(),
|
|
6077
|
+
dryRun: options.dryRun,
|
|
6078
|
+
yes: options.yes,
|
|
6079
|
+
skipReferences: options.skipReferences,
|
|
6080
|
+
orphanStrategy: options.orphanStrategy,
|
|
6081
|
+
orphanTopic: options.orphanTopic
|
|
6082
|
+
});
|
|
6083
|
+
if (!result.ok) {
|
|
6084
|
+
logger.error(result.error.message);
|
|
6085
|
+
process.exit(result.error.exitCode);
|
|
6086
|
+
}
|
|
6087
|
+
process.exit(result.value.movesFailed === 0 ? ExitCode.SUCCESS : ExitCode.ERROR);
|
|
6088
|
+
});
|
|
6089
|
+
}
|
|
6090
|
+
|
|
6091
|
+
// src/commands/orchestrator.ts
|
|
6092
|
+
import { Command as Command49 } from "commander";
|
|
6093
|
+
import * as path41 from "path";
|
|
5662
6094
|
import { Orchestrator, WorkflowLoader, launchTUI } from "@harness-engineering/orchestrator";
|
|
5663
6095
|
function createOrchestratorCommand() {
|
|
5664
|
-
const orchestrator = new
|
|
6096
|
+
const orchestrator = new Command49("orchestrator");
|
|
5665
6097
|
orchestrator.command("run").description("Run the orchestrator daemon").option("-w, --workflow <path>", "Path to harness.orchestrator.md", "harness.orchestrator.md").option("--headless", "Run without TUI (server-only mode for use with web dashboard)").action(async (opts) => {
|
|
5666
|
-
const workflowPath =
|
|
6098
|
+
const workflowPath = path41.resolve(process.cwd(), opts.workflow);
|
|
5667
6099
|
const loader = new WorkflowLoader();
|
|
5668
6100
|
const result = await loader.loadWorkflow(workflowPath);
|
|
5669
6101
|
if (!result.ok) {
|
|
@@ -5696,13 +6128,13 @@ function createOrchestratorCommand() {
|
|
|
5696
6128
|
|
|
5697
6129
|
// src/commands/perf.ts
|
|
5698
6130
|
init_dist();
|
|
5699
|
-
import { Command as
|
|
5700
|
-
import * as
|
|
6131
|
+
import { Command as Command50 } from "commander";
|
|
6132
|
+
import * as path42 from "path";
|
|
5701
6133
|
function registerBenchCommand(perf) {
|
|
5702
6134
|
perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
|
|
5703
6135
|
const globalOpts = cmd.optsWithGlobals();
|
|
5704
6136
|
const cwd = process.cwd();
|
|
5705
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
6137
|
+
const { BenchmarkRunner } = await import("./dist-46VOJIG4.js");
|
|
5706
6138
|
const runner = new BenchmarkRunner();
|
|
5707
6139
|
const benchFiles = runner.discover(cwd, glob2);
|
|
5708
6140
|
if (benchFiles.length === 0) {
|
|
@@ -5766,7 +6198,7 @@ async function runBaselinesShow(globalOpts) {
|
|
|
5766
6198
|
}
|
|
5767
6199
|
async function runBaselinesUpdate(globalOpts) {
|
|
5768
6200
|
const cwd = process.cwd();
|
|
5769
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
6201
|
+
const { BenchmarkRunner } = await import("./dist-46VOJIG4.js");
|
|
5770
6202
|
const runner = new BenchmarkRunner();
|
|
5771
6203
|
const manager = new BaselineManager(cwd);
|
|
5772
6204
|
logger.info("Running benchmarks to update baselines...");
|
|
@@ -5807,9 +6239,9 @@ function registerReportCommand(perf) {
|
|
|
5807
6239
|
perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
|
|
5808
6240
|
const globalOpts = cmd.optsWithGlobals();
|
|
5809
6241
|
const cwd = process.cwd();
|
|
5810
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-
|
|
6242
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-46VOJIG4.js");
|
|
5811
6243
|
const analyzer = new EntropyAnalyzer2({
|
|
5812
|
-
rootDir:
|
|
6244
|
+
rootDir: path42.resolve(cwd),
|
|
5813
6245
|
analyze: { complexity: true, coupling: true }
|
|
5814
6246
|
});
|
|
5815
6247
|
const result = await analyzer.analyze();
|
|
@@ -5868,7 +6300,7 @@ function registerCriticalPathsCommand(perf) {
|
|
|
5868
6300
|
});
|
|
5869
6301
|
}
|
|
5870
6302
|
function createPerfCommand() {
|
|
5871
|
-
const perf = new
|
|
6303
|
+
const perf = new Command50("perf").description("Performance benchmark and baseline management");
|
|
5872
6304
|
registerBenchCommand(perf);
|
|
5873
6305
|
registerBaselinesCommands(perf);
|
|
5874
6306
|
registerReportCommand(perf);
|
|
@@ -5877,10 +6309,10 @@ function createPerfCommand() {
|
|
|
5877
6309
|
}
|
|
5878
6310
|
|
|
5879
6311
|
// src/commands/persona/index.ts
|
|
5880
|
-
import { Command as
|
|
6312
|
+
import { Command as Command53 } from "commander";
|
|
5881
6313
|
|
|
5882
6314
|
// src/commands/persona/list.ts
|
|
5883
|
-
import { Command as
|
|
6315
|
+
import { Command as Command51 } from "commander";
|
|
5884
6316
|
function printPersonaList(personas) {
|
|
5885
6317
|
if (personas.length === 0) {
|
|
5886
6318
|
logger.info("No personas found.");
|
|
@@ -5894,7 +6326,7 @@ function printPersonaList(personas) {
|
|
|
5894
6326
|
}
|
|
5895
6327
|
}
|
|
5896
6328
|
function createListCommand2() {
|
|
5897
|
-
return new
|
|
6329
|
+
return new Command51("list").description("List available agent personas").action(async (_opts, cmd) => {
|
|
5898
6330
|
const globalOpts = cmd.optsWithGlobals();
|
|
5899
6331
|
const personasDir = resolvePersonasDir();
|
|
5900
6332
|
const result = listPersonas(personasDir);
|
|
@@ -5914,51 +6346,51 @@ function createListCommand2() {
|
|
|
5914
6346
|
}
|
|
5915
6347
|
|
|
5916
6348
|
// src/commands/persona/generate.ts
|
|
5917
|
-
import { Command as
|
|
5918
|
-
import * as
|
|
5919
|
-
import * as
|
|
6349
|
+
import { Command as Command52 } from "commander";
|
|
6350
|
+
import * as fs25 from "fs";
|
|
6351
|
+
import * as path43 from "path";
|
|
5920
6352
|
function generatePersonaArtifacts(persona, outputDir, only) {
|
|
5921
6353
|
const slug = toKebabCase(persona.name);
|
|
5922
6354
|
const generated = [];
|
|
5923
6355
|
if (!only || only === "runtime") {
|
|
5924
6356
|
const result = generateRuntime(persona);
|
|
5925
6357
|
if (result.ok) {
|
|
5926
|
-
const outPath =
|
|
5927
|
-
|
|
5928
|
-
|
|
6358
|
+
const outPath = path43.join(outputDir, `${slug}.runtime.json`);
|
|
6359
|
+
fs25.mkdirSync(path43.dirname(outPath), { recursive: true });
|
|
6360
|
+
fs25.writeFileSync(outPath, result.value);
|
|
5929
6361
|
generated.push(outPath);
|
|
5930
6362
|
}
|
|
5931
6363
|
}
|
|
5932
6364
|
if (!only || only === "agents-md") {
|
|
5933
6365
|
const result = generateAgentsMd(persona);
|
|
5934
6366
|
if (result.ok) {
|
|
5935
|
-
const outPath =
|
|
5936
|
-
|
|
6367
|
+
const outPath = path43.join(outputDir, `${slug}.agents.md`);
|
|
6368
|
+
fs25.writeFileSync(outPath, result.value);
|
|
5937
6369
|
generated.push(outPath);
|
|
5938
6370
|
}
|
|
5939
6371
|
}
|
|
5940
6372
|
if (!only || only === "ci") {
|
|
5941
6373
|
const result = generateCIWorkflow(persona, "github");
|
|
5942
6374
|
if (result.ok) {
|
|
5943
|
-
const outPath =
|
|
5944
|
-
|
|
5945
|
-
|
|
6375
|
+
const outPath = path43.join(outputDir, ".github", "workflows", `${slug}.yml`);
|
|
6376
|
+
fs25.mkdirSync(path43.dirname(outPath), { recursive: true });
|
|
6377
|
+
fs25.writeFileSync(outPath, result.value);
|
|
5946
6378
|
generated.push(outPath);
|
|
5947
6379
|
}
|
|
5948
6380
|
}
|
|
5949
6381
|
return generated;
|
|
5950
6382
|
}
|
|
5951
6383
|
function createGenerateCommand3() {
|
|
5952
|
-
return new
|
|
6384
|
+
return new Command52("generate").description("Generate artifacts from a persona config").argument("<name>", "Persona name (e.g., architecture-enforcer)").option("--output-dir <dir>", "Output directory", ".").option("--only <type>", "Generate only: ci, agents-md, runtime").action(async (name, opts, cmd) => {
|
|
5953
6385
|
const globalOpts = cmd.optsWithGlobals();
|
|
5954
|
-
const personaResult = loadPersona(
|
|
6386
|
+
const personaResult = loadPersona(path43.join(resolvePersonasDir(), `${name}.yaml`));
|
|
5955
6387
|
if (!personaResult.ok) {
|
|
5956
6388
|
logger.error(personaResult.error.message);
|
|
5957
6389
|
process.exit(ExitCode.ERROR);
|
|
5958
6390
|
}
|
|
5959
6391
|
const generated = generatePersonaArtifacts(
|
|
5960
6392
|
personaResult.value,
|
|
5961
|
-
|
|
6393
|
+
path43.resolve(opts.outputDir),
|
|
5962
6394
|
opts.only
|
|
5963
6395
|
);
|
|
5964
6396
|
if (!globalOpts.quiet) {
|
|
@@ -5971,7 +6403,7 @@ function createGenerateCommand3() {
|
|
|
5971
6403
|
|
|
5972
6404
|
// src/commands/persona/index.ts
|
|
5973
6405
|
function createPersonaCommand() {
|
|
5974
|
-
const command = new
|
|
6406
|
+
const command = new Command53("persona").description("Agent persona management commands");
|
|
5975
6407
|
command.addCommand(createListCommand2());
|
|
5976
6408
|
command.addCommand(createGenerateCommand3());
|
|
5977
6409
|
return command;
|
|
@@ -5979,8 +6411,8 @@ function createPersonaCommand() {
|
|
|
5979
6411
|
|
|
5980
6412
|
// src/commands/predict.ts
|
|
5981
6413
|
init_dist();
|
|
5982
|
-
import { Command as
|
|
5983
|
-
import
|
|
6414
|
+
import { Command as Command54 } from "commander";
|
|
6415
|
+
import chalk8 from "chalk";
|
|
5984
6416
|
var CATEGORY_ORDER = [
|
|
5985
6417
|
"circular-deps",
|
|
5986
6418
|
"layer-violations",
|
|
@@ -6001,11 +6433,11 @@ function crossingLabel(weeks) {
|
|
|
6001
6433
|
function severityPrefix(severity) {
|
|
6002
6434
|
switch (severity) {
|
|
6003
6435
|
case "critical":
|
|
6004
|
-
return
|
|
6436
|
+
return chalk8.red("[critical]");
|
|
6005
6437
|
case "warning":
|
|
6006
|
-
return
|
|
6438
|
+
return chalk8.yellow("[warning]");
|
|
6007
6439
|
case "info":
|
|
6008
|
-
return
|
|
6440
|
+
return chalk8.blue("[info]");
|
|
6009
6441
|
}
|
|
6010
6442
|
}
|
|
6011
6443
|
function printCategoryTable(result) {
|
|
@@ -6085,7 +6517,7 @@ function handlePredictError(err, mode) {
|
|
|
6085
6517
|
process.exit(ExitCode.ERROR);
|
|
6086
6518
|
}
|
|
6087
6519
|
function createPredictCommand() {
|
|
6088
|
-
const command = new
|
|
6520
|
+
const command = new Command54("predict").description("Predict which architectural constraints will break and when").option("--category <name>", "Filter to a single metric category").option("--no-roadmap", "Baseline only \u2014 skip roadmap spec impact").option("--horizon <weeks>", "Forecast horizon in weeks (default: 12)", "12").action(async (opts, cmd) => {
|
|
6089
6521
|
const globalOpts = cmd.optsWithGlobals();
|
|
6090
6522
|
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
6091
6523
|
try {
|
|
@@ -6108,9 +6540,9 @@ function createPredictCommand() {
|
|
|
6108
6540
|
}
|
|
6109
6541
|
|
|
6110
6542
|
// src/commands/publish-analyses.ts
|
|
6111
|
-
import * as
|
|
6112
|
-
import * as
|
|
6113
|
-
import { Command as
|
|
6543
|
+
import * as fs26 from "fs";
|
|
6544
|
+
import * as path44 from "path";
|
|
6545
|
+
import { Command as Command55 } from "commander";
|
|
6114
6546
|
init_dist();
|
|
6115
6547
|
import {
|
|
6116
6548
|
renderAnalysisComment,
|
|
@@ -6118,7 +6550,7 @@ import {
|
|
|
6118
6550
|
savePublishedIndex
|
|
6119
6551
|
} from "@harness-engineering/orchestrator";
|
|
6120
6552
|
function bootstrapTrackerCommand(opts, verb) {
|
|
6121
|
-
const projectPath =
|
|
6553
|
+
const projectPath = path44.resolve(opts.dir);
|
|
6122
6554
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
6123
6555
|
if (!trackerConfig) {
|
|
6124
6556
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -6130,8 +6562,8 @@ function bootstrapTrackerCommand(opts, verb) {
|
|
|
6130
6562
|
);
|
|
6131
6563
|
return null;
|
|
6132
6564
|
}
|
|
6133
|
-
const projectEnvPath =
|
|
6134
|
-
if (
|
|
6565
|
+
const projectEnvPath = path44.join(projectPath, ".env");
|
|
6566
|
+
if (fs26.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
6135
6567
|
__require("dotenv").config({ path: projectEnvPath });
|
|
6136
6568
|
}
|
|
6137
6569
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -6142,13 +6574,13 @@ function bootstrapTrackerCommand(opts, verb) {
|
|
|
6142
6574
|
return { token, projectPath, trackerConfig };
|
|
6143
6575
|
}
|
|
6144
6576
|
function buildNameToExternalIdMap(projectPath) {
|
|
6145
|
-
const roadmapFile =
|
|
6146
|
-
if (!
|
|
6577
|
+
const roadmapFile = path44.join(projectPath, "docs", "roadmap.md");
|
|
6578
|
+
if (!fs26.existsSync(roadmapFile)) {
|
|
6147
6579
|
logger.error("No docs/roadmap.md found. Cannot map issue hashes to external IDs.");
|
|
6148
6580
|
return null;
|
|
6149
6581
|
}
|
|
6150
6582
|
const { parseRoadmap } = (init_dist(), __toCommonJS(dist_exports));
|
|
6151
|
-
const roadmapParsed = parseRoadmap(
|
|
6583
|
+
const roadmapParsed = parseRoadmap(fs26.readFileSync(roadmapFile, "utf-8"));
|
|
6152
6584
|
if (!roadmapParsed.ok) {
|
|
6153
6585
|
logger.error("Failed to parse docs/roadmap.md");
|
|
6154
6586
|
return null;
|
|
@@ -6198,8 +6630,8 @@ async function runPublishAnalyses(opts) {
|
|
|
6198
6630
|
const nameToExternalId = buildNameToExternalIdMap(projectPath);
|
|
6199
6631
|
if (!nameToExternalId) return process.exit(1);
|
|
6200
6632
|
const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
|
|
6201
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
6202
|
-
const archive = new AnalysisArchive(
|
|
6633
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-46VOJIG4.js");
|
|
6634
|
+
const archive = new AnalysisArchive(path44.join(projectPath, ".harness", "analyses"));
|
|
6203
6635
|
const analyses = await archive.list();
|
|
6204
6636
|
if (analyses.length === 0) {
|
|
6205
6637
|
logger.info("No analyses found to publish.");
|
|
@@ -6221,7 +6653,7 @@ async function runPublishAnalyses(opts) {
|
|
|
6221
6653
|
}
|
|
6222
6654
|
}
|
|
6223
6655
|
function createPublishAnalysesCommand() {
|
|
6224
|
-
const command = new
|
|
6656
|
+
const command = new Command55("publish-analyses").description(
|
|
6225
6657
|
"Publishes locally generated intelligence analyses to the external issue tracker (e.g., GitHub)"
|
|
6226
6658
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
6227
6659
|
try {
|
|
@@ -6237,8 +6669,8 @@ function createPublishAnalysesCommand() {
|
|
|
6237
6669
|
}
|
|
6238
6670
|
|
|
6239
6671
|
// src/commands/recommend.ts
|
|
6240
|
-
import { Command as
|
|
6241
|
-
import
|
|
6672
|
+
import { Command as Command56 } from "commander";
|
|
6673
|
+
import chalk9 from "chalk";
|
|
6242
6674
|
async function resolveSnapshot(cwd, noCache) {
|
|
6243
6675
|
if (!noCache) {
|
|
6244
6676
|
const cached = loadCachedSnapshot(cwd);
|
|
@@ -6272,12 +6704,12 @@ async function runRecommend(options) {
|
|
|
6272
6704
|
function formatRecommendation(rec) {
|
|
6273
6705
|
const lines = [];
|
|
6274
6706
|
if (rec.urgency === "critical") {
|
|
6275
|
-
lines.push(` ${
|
|
6707
|
+
lines.push(` ${chalk9.red("[CRITICAL]")} ${rec.sequence}. ${rec.skillName}`);
|
|
6276
6708
|
} else {
|
|
6277
6709
|
lines.push(` ${rec.sequence}. ${rec.skillName} (${rec.score.toFixed(2)})`);
|
|
6278
6710
|
}
|
|
6279
6711
|
for (const reason of rec.reasons) {
|
|
6280
|
-
lines.push(` ${
|
|
6712
|
+
lines.push(` ${chalk9.dim("\u2192")} ${reason}`);
|
|
6281
6713
|
}
|
|
6282
6714
|
return lines.join("\n");
|
|
6283
6715
|
}
|
|
@@ -6301,7 +6733,7 @@ function printRecommendations(result) {
|
|
|
6301
6733
|
console.log("");
|
|
6302
6734
|
}
|
|
6303
6735
|
function createRecommendCommand() {
|
|
6304
|
-
const command = new
|
|
6736
|
+
const command = new Command56("recommend").description("Recommend skills based on codebase health analysis").option("--no-cache", "Force fresh health snapshot").option("--top <n>", "Max recommendations (default 5)", "5").action(async (opts, cmd) => {
|
|
6305
6737
|
const globalOpts = cmd.optsWithGlobals();
|
|
6306
6738
|
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
6307
6739
|
try {
|
|
@@ -6338,9 +6770,9 @@ function createRecommendCommand() {
|
|
|
6338
6770
|
|
|
6339
6771
|
// src/commands/scan-config.ts
|
|
6340
6772
|
init_dist();
|
|
6341
|
-
import { Command as
|
|
6342
|
-
import { existsSync as
|
|
6343
|
-
import { join as
|
|
6773
|
+
import { Command as Command57 } from "commander";
|
|
6774
|
+
import { existsSync as existsSync23, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
|
|
6775
|
+
import { join as join32, relative as relative3 } from "path";
|
|
6344
6776
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
6345
6777
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
6346
6778
|
const highLines = /* @__PURE__ */ new Set();
|
|
@@ -6366,17 +6798,17 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
|
|
|
6366
6798
|
if (!hasHighSeverity) return;
|
|
6367
6799
|
const { cleaned, linesStripped } = stripHighSeverityPatterns(content, injectionFindings);
|
|
6368
6800
|
if (linesStripped > 0) {
|
|
6369
|
-
|
|
6801
|
+
writeFileSync13(filePath, cleaned);
|
|
6370
6802
|
logger.info(
|
|
6371
|
-
`scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${
|
|
6803
|
+
`scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${relative3(targetDir, filePath).replaceAll("\\", "/")}`
|
|
6372
6804
|
);
|
|
6373
6805
|
}
|
|
6374
6806
|
}
|
|
6375
6807
|
function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
6376
|
-
if (!
|
|
6808
|
+
if (!existsSync23(filePath)) return null;
|
|
6377
6809
|
let content;
|
|
6378
6810
|
try {
|
|
6379
|
-
content =
|
|
6811
|
+
content = readFileSync15(filePath, "utf8");
|
|
6380
6812
|
} catch {
|
|
6381
6813
|
return null;
|
|
6382
6814
|
}
|
|
@@ -6388,7 +6820,7 @@ function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
|
6388
6820
|
applyFix(filePath, targetDir, content, injectionFindings);
|
|
6389
6821
|
}
|
|
6390
6822
|
return {
|
|
6391
|
-
file:
|
|
6823
|
+
file: relative3(targetDir, filePath).replaceAll("\\", "/"),
|
|
6392
6824
|
findings,
|
|
6393
6825
|
overallSeverity: computeOverallSeverity(findings)
|
|
6394
6826
|
};
|
|
@@ -6397,7 +6829,7 @@ async function runScanConfig(targetDir, options) {
|
|
|
6397
6829
|
const scanner = new SecurityScanner(parseSecurityConfig({}));
|
|
6398
6830
|
const results = [];
|
|
6399
6831
|
for (const configFile of CONFIG_FILES) {
|
|
6400
|
-
const result = scanSingleFile(
|
|
6832
|
+
const result = scanSingleFile(join32(targetDir, configFile), targetDir, scanner, options);
|
|
6401
6833
|
if (result) results.push(result);
|
|
6402
6834
|
}
|
|
6403
6835
|
return { exitCode: computeScanExitCode(results), results };
|
|
@@ -6427,7 +6859,7 @@ function formatTextOutput(result) {
|
|
|
6427
6859
|
}
|
|
6428
6860
|
}
|
|
6429
6861
|
function createScanConfigCommand() {
|
|
6430
|
-
const command = new
|
|
6862
|
+
const command = new Command57("scan-config").description(
|
|
6431
6863
|
"Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
|
|
6432
6864
|
).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
|
|
6433
6865
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -6444,24 +6876,24 @@ function createScanConfigCommand() {
|
|
|
6444
6876
|
}
|
|
6445
6877
|
|
|
6446
6878
|
// src/commands/setup.ts
|
|
6447
|
-
import { Command as
|
|
6448
|
-
import * as
|
|
6879
|
+
import { Command as Command58 } from "commander";
|
|
6880
|
+
import * as fs29 from "fs";
|
|
6449
6881
|
import * as os7 from "os";
|
|
6450
|
-
import * as
|
|
6451
|
-
import
|
|
6882
|
+
import * as path47 from "path";
|
|
6883
|
+
import chalk11 from "chalk";
|
|
6452
6884
|
|
|
6453
6885
|
// src/utils/first-run.ts
|
|
6454
|
-
import * as
|
|
6886
|
+
import * as fs27 from "fs";
|
|
6455
6887
|
import * as os6 from "os";
|
|
6456
|
-
import * as
|
|
6457
|
-
var HARNESS_DIR =
|
|
6458
|
-
var MARKER_FILE =
|
|
6888
|
+
import * as path45 from "path";
|
|
6889
|
+
var HARNESS_DIR = path45.join(os6.homedir(), ".harness");
|
|
6890
|
+
var MARKER_FILE = path45.join(HARNESS_DIR, ".setup-complete");
|
|
6459
6891
|
function isFirstRun() {
|
|
6460
|
-
return !
|
|
6892
|
+
return !fs27.existsSync(MARKER_FILE);
|
|
6461
6893
|
}
|
|
6462
6894
|
function markSetupComplete() {
|
|
6463
|
-
|
|
6464
|
-
|
|
6895
|
+
fs27.mkdirSync(HARNESS_DIR, { recursive: true });
|
|
6896
|
+
fs27.writeFileSync(MARKER_FILE, "", "utf-8");
|
|
6465
6897
|
}
|
|
6466
6898
|
function printFirstRunWelcome() {
|
|
6467
6899
|
try {
|
|
@@ -6474,31 +6906,31 @@ function printFirstRunWelcome() {
|
|
|
6474
6906
|
}
|
|
6475
6907
|
|
|
6476
6908
|
// src/commands/telemetry-wizard.ts
|
|
6477
|
-
import * as
|
|
6478
|
-
import * as
|
|
6479
|
-
import
|
|
6480
|
-
import
|
|
6481
|
-
function
|
|
6482
|
-
const rl =
|
|
6909
|
+
import * as fs28 from "fs";
|
|
6910
|
+
import * as path46 from "path";
|
|
6911
|
+
import readline2 from "readline";
|
|
6912
|
+
import chalk10 from "chalk";
|
|
6913
|
+
function prompt2(question) {
|
|
6914
|
+
const rl = readline2.createInterface({
|
|
6483
6915
|
input: process.stdin,
|
|
6484
6916
|
output: process.stdout
|
|
6485
6917
|
});
|
|
6486
|
-
return new Promise((
|
|
6918
|
+
return new Promise((resolve37) => {
|
|
6487
6919
|
rl.question(question, (answer) => {
|
|
6488
6920
|
rl.close();
|
|
6489
|
-
|
|
6921
|
+
resolve37(answer.trim().toLowerCase());
|
|
6490
6922
|
});
|
|
6491
6923
|
});
|
|
6492
6924
|
}
|
|
6493
6925
|
function promptRaw(question) {
|
|
6494
|
-
const rl =
|
|
6926
|
+
const rl = readline2.createInterface({
|
|
6495
6927
|
input: process.stdin,
|
|
6496
6928
|
output: process.stdout
|
|
6497
6929
|
});
|
|
6498
|
-
return new Promise((
|
|
6930
|
+
return new Promise((resolve37) => {
|
|
6499
6931
|
rl.question(question, (answer) => {
|
|
6500
6932
|
rl.close();
|
|
6501
|
-
|
|
6933
|
+
resolve37(answer.trim());
|
|
6502
6934
|
});
|
|
6503
6935
|
});
|
|
6504
6936
|
}
|
|
@@ -6506,21 +6938,21 @@ function isNo(answer) {
|
|
|
6506
6938
|
return answer === "n" || answer === "no";
|
|
6507
6939
|
}
|
|
6508
6940
|
function isTelemetryConfigured(cwd) {
|
|
6509
|
-
const configPath =
|
|
6941
|
+
const configPath = path46.join(cwd, "harness.config.json");
|
|
6510
6942
|
try {
|
|
6511
|
-
const raw = JSON.parse(
|
|
6943
|
+
const raw = JSON.parse(fs28.readFileSync(configPath, "utf-8"));
|
|
6512
6944
|
return raw.telemetry !== void 0 && raw.adoption !== void 0;
|
|
6513
6945
|
} catch {
|
|
6514
6946
|
return false;
|
|
6515
6947
|
}
|
|
6516
6948
|
}
|
|
6517
6949
|
function writeTelemetryConfig(cwd, result) {
|
|
6518
|
-
const configPath =
|
|
6950
|
+
const configPath = path46.join(cwd, "harness.config.json");
|
|
6519
6951
|
try {
|
|
6520
|
-
const raw = JSON.parse(
|
|
6952
|
+
const raw = JSON.parse(fs28.readFileSync(configPath, "utf-8"));
|
|
6521
6953
|
raw.telemetry = { enabled: result.telemetryEnabled };
|
|
6522
6954
|
raw.adoption = { enabled: result.adoptionEnabled };
|
|
6523
|
-
|
|
6955
|
+
fs28.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
6524
6956
|
} catch (err) {
|
|
6525
6957
|
console.warn(
|
|
6526
6958
|
`\u26A0 Could not update ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -6528,18 +6960,18 @@ function writeTelemetryConfig(cwd, result) {
|
|
|
6528
6960
|
}
|
|
6529
6961
|
const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
|
|
6530
6962
|
if (hasIdentity) {
|
|
6531
|
-
const harnessDir =
|
|
6532
|
-
|
|
6533
|
-
const telemetryFile =
|
|
6534
|
-
|
|
6963
|
+
const harnessDir = path46.join(cwd, ".harness");
|
|
6964
|
+
fs28.mkdirSync(harnessDir, { recursive: true });
|
|
6965
|
+
const telemetryFile = path46.join(harnessDir, "telemetry.json");
|
|
6966
|
+
fs28.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
|
|
6535
6967
|
}
|
|
6536
6968
|
}
|
|
6537
6969
|
async function promptIdentity() {
|
|
6538
6970
|
console.log(
|
|
6539
|
-
` ${
|
|
6971
|
+
` ${chalk10.cyan("Identity")} (optional) \u2014 tag telemetry with project/team/alias for filtering.`
|
|
6540
6972
|
);
|
|
6541
6973
|
console.log(" Leave blank to keep anonymous.");
|
|
6542
|
-
const answer = await
|
|
6974
|
+
const answer = await prompt2(" Set identity fields? (y/N) ");
|
|
6543
6975
|
const identity = {};
|
|
6544
6976
|
if (answer === "y" || answer === "yes") {
|
|
6545
6977
|
const projectName = await promptRaw(" Project name: ");
|
|
@@ -6554,31 +6986,31 @@ async function promptIdentity() {
|
|
|
6554
6986
|
async function runTelemetryWizard() {
|
|
6555
6987
|
if (!process.stdin.isTTY) return null;
|
|
6556
6988
|
console.log("");
|
|
6557
|
-
console.log(` ${
|
|
6989
|
+
console.log(` ${chalk10.bold("Telemetry Configuration")}`);
|
|
6558
6990
|
console.log("");
|
|
6559
6991
|
console.log(" Harness can collect anonymous usage data to help improve the tool.");
|
|
6560
6992
|
console.log(" You can change these settings anytime in harness.config.json.");
|
|
6561
6993
|
console.log("");
|
|
6562
6994
|
console.log(
|
|
6563
|
-
` ${
|
|
6995
|
+
` ${chalk10.cyan("Anonymous telemetry")} \u2014 error rates, command usage, performance metrics.`
|
|
6564
6996
|
);
|
|
6565
6997
|
console.log(" No code, file contents, or personally identifiable info is sent.");
|
|
6566
|
-
const telemetryAnswer = await
|
|
6998
|
+
const telemetryAnswer = await prompt2(" Enable anonymous telemetry? (Y/n) ");
|
|
6567
6999
|
const telemetryEnabled = !isNo(telemetryAnswer);
|
|
6568
7000
|
console.log("");
|
|
6569
7001
|
console.log(
|
|
6570
|
-
` ${
|
|
7002
|
+
` ${chalk10.cyan("Adoption tracking")} \u2014 which skills are used, how often, and success rates.`
|
|
6571
7003
|
);
|
|
6572
7004
|
console.log(" Stored locally in .harness/adoption/. Never sent externally.");
|
|
6573
|
-
const adoptionAnswer = await
|
|
7005
|
+
const adoptionAnswer = await prompt2(" Enable local adoption tracking? (Y/n) ");
|
|
6574
7006
|
const adoptionEnabled = !isNo(adoptionAnswer);
|
|
6575
7007
|
console.log("");
|
|
6576
7008
|
const identity = await promptIdentity();
|
|
6577
7009
|
return { telemetryEnabled, adoptionEnabled, identity };
|
|
6578
7010
|
}
|
|
6579
7011
|
async function ensureTelemetryConfigured(cwd) {
|
|
6580
|
-
const configPath =
|
|
6581
|
-
if (!
|
|
7012
|
+
const configPath = path46.join(cwd, "harness.config.json");
|
|
7013
|
+
if (!fs28.existsSync(configPath)) {
|
|
6582
7014
|
return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
|
|
6583
7015
|
}
|
|
6584
7016
|
if (isTelemetryConfigured(cwd)) {
|
|
@@ -6624,7 +7056,7 @@ function runSlashCommandGeneration() {
|
|
|
6624
7056
|
}
|
|
6625
7057
|
}
|
|
6626
7058
|
function detectClient(dirName) {
|
|
6627
|
-
return
|
|
7059
|
+
return fs29.existsSync(path47.join(os7.homedir(), dirName));
|
|
6628
7060
|
}
|
|
6629
7061
|
async function runMcpSetup(cwd) {
|
|
6630
7062
|
const results = [];
|
|
@@ -6661,12 +7093,12 @@ async function runMcpSetup(cwd) {
|
|
|
6661
7093
|
return results;
|
|
6662
7094
|
}
|
|
6663
7095
|
function formatStep(result) {
|
|
6664
|
-
const icon = result.status === "pass" ?
|
|
7096
|
+
const icon = result.status === "pass" ? chalk11.green("\u2713") : result.status === "warn" ? chalk11.yellow("\u26A0") : chalk11.red("\u2717");
|
|
6665
7097
|
return ` ${icon} ${result.message}`;
|
|
6666
7098
|
}
|
|
6667
7099
|
function configureTier0Integrations(cwd) {
|
|
6668
7100
|
try {
|
|
6669
|
-
const mcpPath =
|
|
7101
|
+
const mcpPath = path47.join(cwd, ".mcp.json");
|
|
6670
7102
|
const config = readMcpConfig(mcpPath);
|
|
6671
7103
|
const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
|
|
6672
7104
|
const added = [];
|
|
@@ -6675,9 +7107,9 @@ function configureTier0Integrations(cwd) {
|
|
|
6675
7107
|
writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
|
|
6676
7108
|
added.push(integration.displayName);
|
|
6677
7109
|
}
|
|
6678
|
-
const geminiDir =
|
|
6679
|
-
if (
|
|
6680
|
-
const geminiPath =
|
|
7110
|
+
const geminiDir = path47.join(cwd, ".gemini");
|
|
7111
|
+
if (fs29.existsSync(geminiDir)) {
|
|
7112
|
+
const geminiPath = path47.join(geminiDir, "settings.json");
|
|
6681
7113
|
const geminiConfig = readMcpConfig(geminiPath);
|
|
6682
7114
|
for (const integration of tier0) {
|
|
6683
7115
|
if (!geminiConfig.mcpServers[integration.name]) {
|
|
@@ -6698,14 +7130,14 @@ function configureTier0Integrations(cwd) {
|
|
|
6698
7130
|
}
|
|
6699
7131
|
}
|
|
6700
7132
|
function ensureHooks(cwd) {
|
|
6701
|
-
const configPath =
|
|
6702
|
-
if (!
|
|
7133
|
+
const configPath = path47.join(cwd, "harness.config.json");
|
|
7134
|
+
if (!fs29.existsSync(configPath)) {
|
|
6703
7135
|
return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
|
|
6704
7136
|
}
|
|
6705
7137
|
let profile = "standard";
|
|
6706
|
-
const profilePath =
|
|
7138
|
+
const profilePath = path47.join(cwd, ".harness", "hooks", "profile.json");
|
|
6707
7139
|
try {
|
|
6708
|
-
const data = JSON.parse(
|
|
7140
|
+
const data = JSON.parse(fs29.readFileSync(profilePath, "utf-8"));
|
|
6709
7141
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
6710
7142
|
profile = data.profile;
|
|
6711
7143
|
}
|
|
@@ -6722,9 +7154,22 @@ function ensureHooks(cwd) {
|
|
|
6722
7154
|
return { status: "warn", message: `Hook installation skipped \u2014 ${msg}` };
|
|
6723
7155
|
}
|
|
6724
7156
|
}
|
|
7157
|
+
async function detectLegacyLayout(cwd) {
|
|
7158
|
+
const { adrLegacy, planLegacy } = await detectLegacyArtifacts(cwd);
|
|
7159
|
+
if (!adrLegacy && !planLegacy) {
|
|
7160
|
+
return { status: "pass", message: "Layout up to date" };
|
|
7161
|
+
}
|
|
7162
|
+
const parts = [];
|
|
7163
|
+
if (adrLegacy) parts.push(".harness/architecture/");
|
|
7164
|
+
if (planLegacy) parts.push("docs/plans/");
|
|
7165
|
+
return {
|
|
7166
|
+
status: "warn",
|
|
7167
|
+
message: `Legacy paths detected (${parts.join(", ")}) \u2014 run \`harness migrate\` to upgrade`
|
|
7168
|
+
};
|
|
7169
|
+
}
|
|
6725
7170
|
async function runInitialGraphScan(cwd) {
|
|
6726
7171
|
try {
|
|
6727
|
-
const { runScan: runScan2 } = await import("./scan-
|
|
7172
|
+
const { runScan: runScan2 } = await import("./scan-XRGMTO6K.js");
|
|
6728
7173
|
const result = await runScan2(cwd);
|
|
6729
7174
|
return {
|
|
6730
7175
|
status: "pass",
|
|
@@ -6754,6 +7199,8 @@ async function runSetup(cwd) {
|
|
|
6754
7199
|
steps.push(telemetryResult);
|
|
6755
7200
|
const graphResult = await runInitialGraphScan(cwd);
|
|
6756
7201
|
steps.push(graphResult);
|
|
7202
|
+
const legacyResult = await detectLegacyLayout(cwd);
|
|
7203
|
+
steps.push(legacyResult);
|
|
6757
7204
|
const success = steps.every((s) => s.status !== "fail");
|
|
6758
7205
|
if (success) {
|
|
6759
7206
|
markSetupComplete();
|
|
@@ -6761,10 +7208,10 @@ async function runSetup(cwd) {
|
|
|
6761
7208
|
return { steps, success };
|
|
6762
7209
|
}
|
|
6763
7210
|
function createSetupCommand() {
|
|
6764
|
-
return new
|
|
7211
|
+
return new Command58("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
|
|
6765
7212
|
const cwd = process.cwd();
|
|
6766
7213
|
console.log("");
|
|
6767
|
-
console.log(` ${
|
|
7214
|
+
console.log(` ${chalk11.bold("harness setup")}`);
|
|
6768
7215
|
console.log("");
|
|
6769
7216
|
const { steps, success } = await runSetup(cwd);
|
|
6770
7217
|
for (const step of steps) {
|
|
@@ -6784,15 +7231,15 @@ function createSetupCommand() {
|
|
|
6784
7231
|
|
|
6785
7232
|
// src/commands/share.ts
|
|
6786
7233
|
init_dist();
|
|
6787
|
-
import { Command as
|
|
6788
|
-
import * as
|
|
6789
|
-
import * as
|
|
7234
|
+
import { Command as Command59 } from "commander";
|
|
7235
|
+
import * as fs30 from "fs";
|
|
7236
|
+
import * as path48 from "path";
|
|
6790
7237
|
import { parse as parseYaml } from "yaml";
|
|
6791
7238
|
var MANIFEST_FILENAME = "constraints.yaml";
|
|
6792
7239
|
async function runShareAction(projectPath, options) {
|
|
6793
|
-
const rootDir =
|
|
6794
|
-
const manifestPath =
|
|
6795
|
-
if (!
|
|
7240
|
+
const rootDir = path48.resolve(projectPath);
|
|
7241
|
+
const manifestPath = path48.join(rootDir, MANIFEST_FILENAME);
|
|
7242
|
+
if (!fs30.existsSync(manifestPath)) {
|
|
6796
7243
|
logger.error(
|
|
6797
7244
|
`No ${MANIFEST_FILENAME} found at ${manifestPath}.
|
|
6798
7245
|
Create a constraints.yaml in your project root to define what to share.`
|
|
@@ -6801,7 +7248,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
6801
7248
|
}
|
|
6802
7249
|
let parsed;
|
|
6803
7250
|
try {
|
|
6804
|
-
const raw =
|
|
7251
|
+
const raw = fs30.readFileSync(manifestPath, "utf-8");
|
|
6805
7252
|
parsed = parseYaml(raw);
|
|
6806
7253
|
} catch (err) {
|
|
6807
7254
|
logger.error(
|
|
@@ -6815,7 +7262,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
6815
7262
|
process.exit(1);
|
|
6816
7263
|
}
|
|
6817
7264
|
const manifest = manifestResult.value;
|
|
6818
|
-
const configResult = resolveConfig(
|
|
7265
|
+
const configResult = resolveConfig(path48.join(rootDir, "harness.config.json"));
|
|
6819
7266
|
if (!configResult.ok) {
|
|
6820
7267
|
logger.error(configResult.error.message);
|
|
6821
7268
|
process.exit(1);
|
|
@@ -6833,8 +7280,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
6833
7280
|
);
|
|
6834
7281
|
process.exit(1);
|
|
6835
7282
|
}
|
|
6836
|
-
const outputDir =
|
|
6837
|
-
const outputPath =
|
|
7283
|
+
const outputDir = path48.resolve(options.output);
|
|
7284
|
+
const outputPath = path48.join(outputDir, `${manifest.name}.harness-constraints.json`);
|
|
6838
7285
|
const writeResult = await writeConfig(outputPath, bundle);
|
|
6839
7286
|
if (!writeResult.ok) {
|
|
6840
7287
|
logger.error(`Failed to write bundle: ${writeResult.error.message}`);
|
|
@@ -6843,28 +7290,28 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
6843
7290
|
logger.success(`Bundle written to ${outputPath}`);
|
|
6844
7291
|
}
|
|
6845
7292
|
function createShareCommand() {
|
|
6846
|
-
return new
|
|
7293
|
+
return new Command59("share").description("Extract and publish a constraints bundle from constraints.yaml").argument("[path]", "Path to the project root", ".").option("-o, --output <dir>", "Output directory for the bundle", ".").action(async (projectPath, options) => {
|
|
6847
7294
|
await runShareAction(projectPath, options);
|
|
6848
7295
|
});
|
|
6849
7296
|
}
|
|
6850
7297
|
|
|
6851
7298
|
// src/commands/skill/index.ts
|
|
6852
|
-
import { Command as
|
|
7299
|
+
import { Command as Command67 } from "commander";
|
|
6853
7300
|
|
|
6854
7301
|
// src/commands/skill/list.ts
|
|
6855
|
-
import { Command as
|
|
6856
|
-
import * as
|
|
6857
|
-
import * as
|
|
7302
|
+
import { Command as Command60 } from "commander";
|
|
7303
|
+
import * as fs31 from "fs";
|
|
7304
|
+
import * as path49 from "path";
|
|
6858
7305
|
import { parse } from "yaml";
|
|
6859
7306
|
function scanDirectory(dirPath, source) {
|
|
6860
|
-
if (!
|
|
6861
|
-
const entries =
|
|
7307
|
+
if (!fs31.existsSync(dirPath)) return [];
|
|
7308
|
+
const entries = fs31.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
6862
7309
|
const skills = [];
|
|
6863
7310
|
for (const name of entries) {
|
|
6864
|
-
const yamlPath =
|
|
6865
|
-
if (!
|
|
7311
|
+
const yamlPath = path49.join(dirPath, name, "skill.yaml");
|
|
7312
|
+
if (!fs31.existsSync(yamlPath)) continue;
|
|
6866
7313
|
try {
|
|
6867
|
-
const raw =
|
|
7314
|
+
const raw = fs31.readFileSync(yamlPath, "utf-8");
|
|
6868
7315
|
const parsed = parse(raw);
|
|
6869
7316
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
6870
7317
|
if (result.success) {
|
|
@@ -6882,10 +7329,10 @@ function scanDirectory(dirPath, source) {
|
|
|
6882
7329
|
}
|
|
6883
7330
|
function collectCommunitySkills(seen, allSkills) {
|
|
6884
7331
|
const globalDir = resolveGlobalSkillsDir();
|
|
6885
|
-
const skillsDir =
|
|
6886
|
-
const communityBase =
|
|
6887
|
-
const communityPlatformDir =
|
|
6888
|
-
const lockfilePath =
|
|
7332
|
+
const skillsDir = path49.dirname(globalDir);
|
|
7333
|
+
const communityBase = path49.join(skillsDir, "community");
|
|
7334
|
+
const communityPlatformDir = path49.join(communityBase, "claude-code");
|
|
7335
|
+
const lockfilePath = path49.join(communityBase, "skills-lock.json");
|
|
6889
7336
|
const lockfile = readLockfile2(lockfilePath);
|
|
6890
7337
|
const communitySkills = scanDirectory(communityPlatformDir, "community");
|
|
6891
7338
|
for (const skill of communitySkills) {
|
|
@@ -6955,7 +7402,7 @@ function printSkillsVerbose(skills) {
|
|
|
6955
7402
|
for (const s of skills) printSkillEntry(s);
|
|
6956
7403
|
}
|
|
6957
7404
|
function createListCommand3() {
|
|
6958
|
-
return new
|
|
7405
|
+
return new Command60("list").description("List available skills").option("--installed", "Show only community-installed skills").option("--local", "Show only project-local skills").option("--all", "Show all skills (default)").action(async (opts, cmd) => {
|
|
6959
7406
|
const globalOpts = cmd.optsWithGlobals();
|
|
6960
7407
|
const skills = collectSkills({ filter: resolveFilter(opts) });
|
|
6961
7408
|
if (globalOpts.json) {
|
|
@@ -6970,13 +7417,13 @@ function createListCommand3() {
|
|
|
6970
7417
|
}
|
|
6971
7418
|
|
|
6972
7419
|
// src/commands/skill/run.ts
|
|
6973
|
-
import { Command as
|
|
6974
|
-
import * as
|
|
6975
|
-
import * as
|
|
7420
|
+
import { Command as Command61 } from "commander";
|
|
7421
|
+
import * as fs32 from "fs";
|
|
7422
|
+
import * as path50 from "path";
|
|
6976
7423
|
import { parse as parse2 } from "yaml";
|
|
6977
7424
|
|
|
6978
7425
|
// src/skill/complexity.ts
|
|
6979
|
-
import { execFileSync as
|
|
7426
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
6980
7427
|
function evaluateSignals(signals) {
|
|
6981
7428
|
if (signals.fileCount >= 3) return "thorough";
|
|
6982
7429
|
if (signals.newDir) return "thorough";
|
|
@@ -6988,17 +7435,17 @@ function evaluateSignals(signals) {
|
|
|
6988
7435
|
}
|
|
6989
7436
|
function detectComplexity(projectPath) {
|
|
6990
7437
|
try {
|
|
6991
|
-
const base =
|
|
7438
|
+
const base = execFileSync5("git", ["merge-base", "HEAD", "main"], {
|
|
6992
7439
|
cwd: projectPath,
|
|
6993
7440
|
encoding: "utf-8",
|
|
6994
7441
|
stdio: ["pipe", "pipe", "pipe"]
|
|
6995
7442
|
}).trim();
|
|
6996
|
-
const diffFiles =
|
|
7443
|
+
const diffFiles = execFileSync5("git", ["diff", "--name-only", base], {
|
|
6997
7444
|
cwd: projectPath,
|
|
6998
7445
|
encoding: "utf-8",
|
|
6999
7446
|
stdio: ["pipe", "pipe", "pipe"]
|
|
7000
7447
|
}).trim().split("\n").filter(Boolean);
|
|
7001
|
-
const diffStat =
|
|
7448
|
+
const diffStat = execFileSync5("git", ["diff", "--stat", base], {
|
|
7002
7449
|
cwd: projectPath,
|
|
7003
7450
|
encoding: "utf-8",
|
|
7004
7451
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -7068,10 +7515,10 @@ ${options.principles}`);
|
|
|
7068
7515
|
|
|
7069
7516
|
// src/commands/skill/run.ts
|
|
7070
7517
|
function loadSkillMetadata(skillDir) {
|
|
7071
|
-
const yamlPath =
|
|
7072
|
-
if (!
|
|
7518
|
+
const yamlPath = path50.join(skillDir, "skill.yaml");
|
|
7519
|
+
if (!fs32.existsSync(yamlPath)) return null;
|
|
7073
7520
|
try {
|
|
7074
|
-
const result = SkillMetadataSchema.safeParse(parse2(
|
|
7521
|
+
const result = SkillMetadataSchema.safeParse(parse2(fs32.readFileSync(yamlPath, "utf-8")));
|
|
7075
7522
|
return result.success ? result.data : null;
|
|
7076
7523
|
} catch {
|
|
7077
7524
|
return null;
|
|
@@ -7083,22 +7530,22 @@ function resolveComplexity(metadata, requested, projectPath) {
|
|
|
7083
7530
|
return requested;
|
|
7084
7531
|
}
|
|
7085
7532
|
function loadPrinciples(projectPath) {
|
|
7086
|
-
const principlesPath =
|
|
7087
|
-
return
|
|
7533
|
+
const principlesPath = path50.join(projectPath, "docs", "principles.md");
|
|
7534
|
+
return fs32.existsSync(principlesPath) ? fs32.readFileSync(principlesPath, "utf-8") : void 0;
|
|
7088
7535
|
}
|
|
7089
7536
|
function readMostRecentFileInDir(dirPath) {
|
|
7090
|
-
const files =
|
|
7091
|
-
if (files.length > 0) return
|
|
7537
|
+
const files = fs32.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs32.statSync(path50.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
7538
|
+
if (files.length > 0) return fs32.readFileSync(path50.join(dirPath, files[0].name), "utf-8");
|
|
7092
7539
|
return void 0;
|
|
7093
7540
|
}
|
|
7094
7541
|
function loadPriorState(metadata, projectPath) {
|
|
7095
7542
|
if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
|
|
7096
7543
|
for (const stateFilePath of metadata.state.files) {
|
|
7097
|
-
const fullPath =
|
|
7098
|
-
if (!
|
|
7099
|
-
const stat =
|
|
7544
|
+
const fullPath = path50.join(projectPath, stateFilePath);
|
|
7545
|
+
if (!fs32.existsSync(fullPath)) continue;
|
|
7546
|
+
const stat = fs32.statSync(fullPath);
|
|
7100
7547
|
if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
|
|
7101
|
-
return
|
|
7548
|
+
return fs32.readFileSync(fullPath, "utf-8");
|
|
7102
7549
|
}
|
|
7103
7550
|
return void 0;
|
|
7104
7551
|
}
|
|
@@ -7118,9 +7565,9 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
7118
7565
|
}
|
|
7119
7566
|
function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
7120
7567
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
7121
|
-
const stateFile =
|
|
7122
|
-
if (!
|
|
7123
|
-
const stateContent =
|
|
7568
|
+
const stateFile = path50.join(projectPath, ".harness", "state.json");
|
|
7569
|
+
if (!fs32.existsSync(stateFile)) return content;
|
|
7570
|
+
const stateContent = fs32.readFileSync(stateFile, "utf-8");
|
|
7124
7571
|
return content + `
|
|
7125
7572
|
|
|
7126
7573
|
---
|
|
@@ -7132,14 +7579,14 @@ ${stateContent}
|
|
|
7132
7579
|
}
|
|
7133
7580
|
async function runSkill(name, opts) {
|
|
7134
7581
|
const skillsDir = resolveSkillsDir();
|
|
7135
|
-
const skillDir =
|
|
7136
|
-
if (!
|
|
7582
|
+
const skillDir = path50.join(skillsDir, name);
|
|
7583
|
+
if (!fs32.existsSync(skillDir)) {
|
|
7137
7584
|
logger.error(`Skill not found: ${name}`);
|
|
7138
7585
|
process.exit(ExitCode.ERROR);
|
|
7139
7586
|
return;
|
|
7140
7587
|
}
|
|
7141
7588
|
const metadata = loadSkillMetadata(skillDir);
|
|
7142
|
-
const projectPath = opts.path ?
|
|
7589
|
+
const projectPath = opts.path ? path50.resolve(opts.path) : process.cwd();
|
|
7143
7590
|
const complexity = resolveComplexity(
|
|
7144
7591
|
metadata,
|
|
7145
7592
|
opts.complexity ?? "standard",
|
|
@@ -7166,14 +7613,14 @@ async function runSkill(name, opts) {
|
|
|
7166
7613
|
...stateWarning !== void 0 && { stateWarning },
|
|
7167
7614
|
...opts.party !== void 0 && { party: opts.party }
|
|
7168
7615
|
});
|
|
7169
|
-
const skillMdPath =
|
|
7170
|
-
if (!
|
|
7616
|
+
const skillMdPath = path50.join(skillDir, "SKILL.md");
|
|
7617
|
+
if (!fs32.existsSync(skillMdPath)) {
|
|
7171
7618
|
logger.error(`SKILL.md not found for skill: ${name}`);
|
|
7172
7619
|
process.exit(ExitCode.ERROR);
|
|
7173
7620
|
return;
|
|
7174
7621
|
}
|
|
7175
7622
|
const content = appendProjectState(
|
|
7176
|
-
|
|
7623
|
+
fs32.readFileSync(skillMdPath, "utf-8"),
|
|
7177
7624
|
metadata,
|
|
7178
7625
|
projectPath,
|
|
7179
7626
|
!!opts.path
|
|
@@ -7182,13 +7629,13 @@ async function runSkill(name, opts) {
|
|
|
7182
7629
|
process.exit(ExitCode.SUCCESS);
|
|
7183
7630
|
}
|
|
7184
7631
|
function createRunCommand2() {
|
|
7185
|
-
return new
|
|
7632
|
+
return new Command61("run").description("Run a skill (outputs SKILL.md content with context preamble)").argument("<name>", "Skill name (e.g., harness-tdd)").option("--path <path>", "Project root path for context injection").option("--complexity <level>", "Rigor level: fast, standard, thorough", "standard").option("--phase <name>", "Start at a specific phase (for re-entry)").option("--party", "Enable multi-perspective evaluation").action(async (name, opts) => runSkill(name, opts));
|
|
7186
7633
|
}
|
|
7187
7634
|
|
|
7188
7635
|
// src/commands/skill/validate.ts
|
|
7189
|
-
import { Command as
|
|
7190
|
-
import * as
|
|
7191
|
-
import * as
|
|
7636
|
+
import { Command as Command62 } from "commander";
|
|
7637
|
+
import * as fs33 from "fs";
|
|
7638
|
+
import * as path51 from "path";
|
|
7192
7639
|
import { parse as parse3 } from "yaml";
|
|
7193
7640
|
var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
7194
7641
|
"## When to Use",
|
|
@@ -7200,11 +7647,11 @@ var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
|
7200
7647
|
];
|
|
7201
7648
|
var KNOWLEDGE_REQUIRED_SECTIONS = ["## Instructions"];
|
|
7202
7649
|
function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
7203
|
-
if (!
|
|
7650
|
+
if (!fs33.existsSync(skillMdPath)) {
|
|
7204
7651
|
errors.push(`${name}: missing SKILL.md`);
|
|
7205
7652
|
return;
|
|
7206
7653
|
}
|
|
7207
|
-
const mdContent =
|
|
7654
|
+
const mdContent = fs33.readFileSync(skillMdPath, "utf-8");
|
|
7208
7655
|
if (!mdContent.trim().startsWith("# ")) {
|
|
7209
7656
|
errors.push(`${name}/SKILL.md: must start with an h1 heading`);
|
|
7210
7657
|
}
|
|
@@ -7229,20 +7676,20 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
|
7229
7676
|
}
|
|
7230
7677
|
}
|
|
7231
7678
|
function validateSkillEntry(name, skillsDir, errors) {
|
|
7232
|
-
const skillDir =
|
|
7233
|
-
const yamlPath =
|
|
7234
|
-
if (!
|
|
7679
|
+
const skillDir = path51.join(skillsDir, name);
|
|
7680
|
+
const yamlPath = path51.join(skillDir, "skill.yaml");
|
|
7681
|
+
if (!fs33.existsSync(yamlPath)) {
|
|
7235
7682
|
errors.push(`${name}: missing skill.yaml`);
|
|
7236
7683
|
return false;
|
|
7237
7684
|
}
|
|
7238
7685
|
try {
|
|
7239
|
-
const raw =
|
|
7686
|
+
const raw = fs33.readFileSync(yamlPath, "utf-8");
|
|
7240
7687
|
const result = SkillMetadataSchema.safeParse(parse3(raw));
|
|
7241
7688
|
if (!result.success) {
|
|
7242
7689
|
errors.push(`${name}/skill.yaml: ${result.error.message}`);
|
|
7243
7690
|
return false;
|
|
7244
7691
|
}
|
|
7245
|
-
validateSkillMd(name,
|
|
7692
|
+
validateSkillMd(name, path51.join(skillDir, "SKILL.md"), result.data.type, errors);
|
|
7246
7693
|
return true;
|
|
7247
7694
|
} catch (e) {
|
|
7248
7695
|
errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -7250,15 +7697,15 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
7250
7697
|
}
|
|
7251
7698
|
}
|
|
7252
7699
|
function createValidateCommand2() {
|
|
7253
|
-
return new
|
|
7700
|
+
return new Command62("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
7254
7701
|
const globalOpts = cmd.optsWithGlobals();
|
|
7255
7702
|
const skillsDir = resolveSkillsDir();
|
|
7256
|
-
if (!
|
|
7703
|
+
if (!fs33.existsSync(skillsDir)) {
|
|
7257
7704
|
logger.info("No skills directory found.");
|
|
7258
7705
|
process.exit(ExitCode.SUCCESS);
|
|
7259
7706
|
return;
|
|
7260
7707
|
}
|
|
7261
|
-
const entries =
|
|
7708
|
+
const entries = fs33.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
7262
7709
|
const errors = [];
|
|
7263
7710
|
let validated = 0;
|
|
7264
7711
|
for (const name of entries) {
|
|
@@ -7280,24 +7727,24 @@ function createValidateCommand2() {
|
|
|
7280
7727
|
}
|
|
7281
7728
|
|
|
7282
7729
|
// src/commands/skill/info.ts
|
|
7283
|
-
import { Command as
|
|
7284
|
-
import * as
|
|
7285
|
-
import * as
|
|
7730
|
+
import { Command as Command63 } from "commander";
|
|
7731
|
+
import * as fs34 from "fs";
|
|
7732
|
+
import * as path52 from "path";
|
|
7286
7733
|
import { parse as parse4 } from "yaml";
|
|
7287
7734
|
function loadSkillMetadata2(name) {
|
|
7288
7735
|
const skillsDir = resolveSkillsDir();
|
|
7289
|
-
const skillDir =
|
|
7290
|
-
if (!
|
|
7736
|
+
const skillDir = path52.join(skillsDir, name);
|
|
7737
|
+
if (!fs34.existsSync(skillDir)) {
|
|
7291
7738
|
logger.error(`Skill not found: ${name}`);
|
|
7292
7739
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
7293
7740
|
}
|
|
7294
|
-
const yamlPath =
|
|
7295
|
-
if (!
|
|
7741
|
+
const yamlPath = path52.join(skillDir, "skill.yaml");
|
|
7742
|
+
if (!fs34.existsSync(yamlPath)) {
|
|
7296
7743
|
logger.error(`skill.yaml not found for skill: ${name}`);
|
|
7297
7744
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
7298
7745
|
}
|
|
7299
7746
|
try {
|
|
7300
|
-
const raw =
|
|
7747
|
+
const raw = fs34.readFileSync(yamlPath, "utf-8");
|
|
7301
7748
|
const parsed = parse4(raw);
|
|
7302
7749
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
7303
7750
|
if (!result.success) {
|
|
@@ -7326,7 +7773,7 @@ function printSkillInfo(skill) {
|
|
|
7326
7773
|
console.log(`Persistent: ${skill.state.persistent}`);
|
|
7327
7774
|
}
|
|
7328
7775
|
function createInfoCommand() {
|
|
7329
|
-
return new
|
|
7776
|
+
return new Command63("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
|
|
7330
7777
|
const globalOpts = cmd.optsWithGlobals();
|
|
7331
7778
|
const loaded = loadSkillMetadata2(name);
|
|
7332
7779
|
if (!loaded.ok) {
|
|
@@ -7343,7 +7790,7 @@ function createInfoCommand() {
|
|
|
7343
7790
|
}
|
|
7344
7791
|
|
|
7345
7792
|
// src/commands/skill/search.ts
|
|
7346
|
-
import { Command as
|
|
7793
|
+
import { Command as Command64 } from "commander";
|
|
7347
7794
|
async function runSearch(query, opts) {
|
|
7348
7795
|
const results = await searchNpmRegistry(query, opts.registry);
|
|
7349
7796
|
return results.filter((r) => {
|
|
@@ -7388,15 +7835,15 @@ async function runSearchAction(query, opts, globalOpts) {
|
|
|
7388
7835
|
}
|
|
7389
7836
|
}
|
|
7390
7837
|
function createSearchCommand() {
|
|
7391
|
-
return new
|
|
7838
|
+
return new Command64("search").description("Search for community skills on the @harness-skills registry").argument("<query>", "Search query").option("--platform <platform>", "Filter by platform (e.g., claude-code)").option("--trigger <trigger>", "Filter by trigger type (e.g., manual, automatic)").option("--registry <url>", "Use a custom npm registry URL").action(async (query, opts, cmd) => {
|
|
7392
7839
|
await runSearchAction(query, opts, cmd.optsWithGlobals());
|
|
7393
7840
|
});
|
|
7394
7841
|
}
|
|
7395
7842
|
|
|
7396
7843
|
// src/commands/skill/create.ts
|
|
7397
|
-
import { Command as
|
|
7398
|
-
import * as
|
|
7399
|
-
import * as
|
|
7844
|
+
import { Command as Command65 } from "commander";
|
|
7845
|
+
import * as path53 from "path";
|
|
7846
|
+
import * as fs35 from "fs";
|
|
7400
7847
|
import YAML from "yaml";
|
|
7401
7848
|
var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
7402
7849
|
function buildReadme(name, description) {
|
|
@@ -7480,22 +7927,22 @@ function runCreate(name, opts) {
|
|
|
7480
7927
|
if (!KEBAB_CASE_RE.test(name)) {
|
|
7481
7928
|
throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
|
|
7482
7929
|
}
|
|
7483
|
-
const baseDir = opts.outputDir ??
|
|
7484
|
-
const skillDir =
|
|
7485
|
-
if (
|
|
7930
|
+
const baseDir = opts.outputDir ?? path53.join(process.cwd(), "agents", "skills", "claude-code");
|
|
7931
|
+
const skillDir = path53.join(baseDir, name);
|
|
7932
|
+
if (fs35.existsSync(skillDir)) {
|
|
7486
7933
|
throw new Error(`Skill directory already exists: ${skillDir}`);
|
|
7487
7934
|
}
|
|
7488
|
-
|
|
7935
|
+
fs35.mkdirSync(skillDir, { recursive: true });
|
|
7489
7936
|
const description = opts.description || `A community skill: ${name}`;
|
|
7490
7937
|
const skillYaml = buildSkillYaml(name, opts);
|
|
7491
|
-
const skillYamlPath =
|
|
7492
|
-
|
|
7938
|
+
const skillYamlPath = path53.join(skillDir, "skill.yaml");
|
|
7939
|
+
fs35.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
|
|
7493
7940
|
const skillMd = buildSkillMd(name, description);
|
|
7494
|
-
const skillMdPath =
|
|
7495
|
-
|
|
7941
|
+
const skillMdPath = path53.join(skillDir, "SKILL.md");
|
|
7942
|
+
fs35.writeFileSync(skillMdPath, skillMd);
|
|
7496
7943
|
const readme = buildReadme(name, description);
|
|
7497
|
-
const readmePath =
|
|
7498
|
-
|
|
7944
|
+
const readmePath = path53.join(skillDir, "README.md");
|
|
7945
|
+
fs35.writeFileSync(readmePath, readme);
|
|
7499
7946
|
return {
|
|
7500
7947
|
name,
|
|
7501
7948
|
directory: skillDir,
|
|
@@ -7509,12 +7956,12 @@ function printCreateResult(name, result) {
|
|
|
7509
7956
|
}
|
|
7510
7957
|
logger.info(`
|
|
7511
7958
|
Next steps:`);
|
|
7512
|
-
logger.info(` 1. Edit ${
|
|
7959
|
+
logger.info(` 1. Edit ${path53.join(result.directory, "SKILL.md")} with your skill content`);
|
|
7513
7960
|
logger.info(` 2. Run: harness skill validate ${name}`);
|
|
7514
7961
|
logger.info(` 3. Run: harness skills publish`);
|
|
7515
7962
|
}
|
|
7516
7963
|
function createCreateCommand() {
|
|
7517
|
-
return new
|
|
7964
|
+
return new Command65("create").description("Scaffold a new community skill").argument("<name>", "Skill name (kebab-case)").option("--description <desc>", "Skill description").option("--type <type>", "Skill type: rigid or flexible", "flexible").option("--platforms <platforms>", "Comma-separated platforms (default: claude-code)").option("--triggers <triggers>", "Comma-separated triggers (default: manual)").option("--output-dir <dir>", "Output directory (default: agents/skills/claude-code/)").action(async (name, opts, cmd) => {
|
|
7518
7965
|
const globalOpts = cmd.optsWithGlobals();
|
|
7519
7966
|
try {
|
|
7520
7967
|
const result = runCreate(name, {
|
|
@@ -7537,26 +7984,26 @@ function createCreateCommand() {
|
|
|
7537
7984
|
}
|
|
7538
7985
|
|
|
7539
7986
|
// src/commands/skill/publish.ts
|
|
7540
|
-
import { Command as
|
|
7541
|
-
import * as
|
|
7542
|
-
import * as
|
|
7543
|
-
import { execFileSync as
|
|
7987
|
+
import { Command as Command66 } from "commander";
|
|
7988
|
+
import * as fs37 from "fs";
|
|
7989
|
+
import * as path55 from "path";
|
|
7990
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
7544
7991
|
|
|
7545
7992
|
// src/registry/validator.ts
|
|
7546
|
-
import * as
|
|
7547
|
-
import * as
|
|
7993
|
+
import * as fs36 from "fs";
|
|
7994
|
+
import * as path54 from "path";
|
|
7548
7995
|
import { parse as parse5 } from "yaml";
|
|
7549
7996
|
import semver4 from "semver";
|
|
7550
7997
|
async function validateForPublish(skillDir, registryUrl) {
|
|
7551
7998
|
const errors = [];
|
|
7552
|
-
const skillYamlPath =
|
|
7553
|
-
if (!
|
|
7999
|
+
const skillYamlPath = path54.join(skillDir, "skill.yaml");
|
|
8000
|
+
if (!fs36.existsSync(skillYamlPath)) {
|
|
7554
8001
|
errors.push("skill.yaml not found. Create one with: harness skill create <name>");
|
|
7555
8002
|
return { valid: false, errors };
|
|
7556
8003
|
}
|
|
7557
8004
|
let skillMeta;
|
|
7558
8005
|
try {
|
|
7559
|
-
const raw =
|
|
8006
|
+
const raw = fs36.readFileSync(skillYamlPath, "utf-8");
|
|
7560
8007
|
const parsed = parse5(raw);
|
|
7561
8008
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
7562
8009
|
if (!result.success) {
|
|
@@ -7578,11 +8025,11 @@ async function validateForPublish(skillDir, registryUrl) {
|
|
|
7578
8025
|
if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
|
|
7579
8026
|
errors.push("At least one trigger is required. Add triggers to skill.yaml.");
|
|
7580
8027
|
}
|
|
7581
|
-
const skillMdPath =
|
|
7582
|
-
if (!
|
|
8028
|
+
const skillMdPath = path54.join(skillDir, "SKILL.md");
|
|
8029
|
+
if (!fs36.existsSync(skillMdPath)) {
|
|
7583
8030
|
errors.push("SKILL.md not found. Create it with content describing your skill.");
|
|
7584
8031
|
} else {
|
|
7585
|
-
const content =
|
|
8032
|
+
const content = fs36.readFileSync(skillMdPath, "utf-8");
|
|
7586
8033
|
if (!content.includes("## When to Use")) {
|
|
7587
8034
|
errors.push('SKILL.md must contain a "## When to Use" section.');
|
|
7588
8035
|
}
|
|
@@ -7662,11 +8109,11 @@ ${errorList}`);
|
|
|
7662
8109
|
}
|
|
7663
8110
|
const meta = validation.skillMeta;
|
|
7664
8111
|
const pkg = derivePackageJson(meta);
|
|
7665
|
-
const pkgPath =
|
|
7666
|
-
|
|
7667
|
-
const readmePath =
|
|
7668
|
-
if (!
|
|
7669
|
-
const skillMdContent =
|
|
8112
|
+
const pkgPath = path55.join(skillDir, "package.json");
|
|
8113
|
+
fs37.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
8114
|
+
const readmePath = path55.join(skillDir, "README.md");
|
|
8115
|
+
if (!fs37.existsSync(readmePath)) {
|
|
8116
|
+
const skillMdContent = fs37.readFileSync(path55.join(skillDir, "SKILL.md"), "utf-8");
|
|
7670
8117
|
const readme = `# ${pkg.name}
|
|
7671
8118
|
|
|
7672
8119
|
${meta.description}
|
|
@@ -7680,7 +8127,7 @@ harness install ${meta.name}
|
|
|
7680
8127
|
---
|
|
7681
8128
|
|
|
7682
8129
|
${skillMdContent}`;
|
|
7683
|
-
|
|
8130
|
+
fs37.writeFileSync(readmePath, readme);
|
|
7684
8131
|
}
|
|
7685
8132
|
if (opts.dryRun) {
|
|
7686
8133
|
return {
|
|
@@ -7694,7 +8141,7 @@ ${skillMdContent}`;
|
|
|
7694
8141
|
if (opts.registry) {
|
|
7695
8142
|
publishArgs.push("--registry", opts.registry);
|
|
7696
8143
|
}
|
|
7697
|
-
|
|
8144
|
+
execFileSync6("npm", publishArgs, {
|
|
7698
8145
|
cwd: skillDir,
|
|
7699
8146
|
stdio: "pipe",
|
|
7700
8147
|
timeout: 6e4
|
|
@@ -7706,7 +8153,7 @@ ${skillMdContent}`;
|
|
|
7706
8153
|
};
|
|
7707
8154
|
}
|
|
7708
8155
|
function createPublishCommand() {
|
|
7709
|
-
return new
|
|
8156
|
+
return new Command66("publish").description("Validate and publish a skill to @harness-skills on npm").option("--dry-run", "Run validation and generate package.json without publishing").option("--dir <dir>", "Skill directory (default: current directory)").option("--registry <url>", "Use a custom npm registry URL").action(async (opts, cmd) => {
|
|
7710
8157
|
const globalOpts = cmd.optsWithGlobals();
|
|
7711
8158
|
const skillDir = opts.dir || process.cwd();
|
|
7712
8159
|
try {
|
|
@@ -7731,7 +8178,7 @@ function createPublishCommand() {
|
|
|
7731
8178
|
|
|
7732
8179
|
// src/commands/skill/index.ts
|
|
7733
8180
|
function createSkillCommand() {
|
|
7734
|
-
const command = new
|
|
8181
|
+
const command = new Command67("skill").description("Skill management commands");
|
|
7735
8182
|
command.addCommand(createListCommand3());
|
|
7736
8183
|
command.addCommand(createRunCommand2());
|
|
7737
8184
|
command.addCommand(createValidateCommand2());
|
|
@@ -7744,9 +8191,9 @@ function createSkillCommand() {
|
|
|
7744
8191
|
|
|
7745
8192
|
// src/commands/snapshot.ts
|
|
7746
8193
|
init_dist();
|
|
7747
|
-
import { Command as
|
|
8194
|
+
import { Command as Command68 } from "commander";
|
|
7748
8195
|
import { execSync as execSync5 } from "child_process";
|
|
7749
|
-
import
|
|
8196
|
+
import chalk12 from "chalk";
|
|
7750
8197
|
function getCommitHash3(cwd) {
|
|
7751
8198
|
try {
|
|
7752
8199
|
return execSync5("git rev-parse --short HEAD", { cwd, encoding: "utf-8" }).toString().trim();
|
|
@@ -7771,9 +8218,9 @@ function formatDelta(delta) {
|
|
|
7771
8218
|
function directionSymbol(direction) {
|
|
7772
8219
|
switch (direction) {
|
|
7773
8220
|
case "improving":
|
|
7774
|
-
return
|
|
8221
|
+
return chalk12.green("improving");
|
|
7775
8222
|
case "declining":
|
|
7776
|
-
return
|
|
8223
|
+
return chalk12.red("declining");
|
|
7777
8224
|
case "stable":
|
|
7778
8225
|
return "=";
|
|
7779
8226
|
}
|
|
@@ -7926,7 +8373,7 @@ function registerListCommand(parent) {
|
|
|
7926
8373
|
});
|
|
7927
8374
|
}
|
|
7928
8375
|
function createSnapshotCommand() {
|
|
7929
|
-
const command = new
|
|
8376
|
+
const command = new Command68("snapshot").description("Architecture timeline snapshot commands");
|
|
7930
8377
|
registerCaptureCommand(command);
|
|
7931
8378
|
registerTrendsCommand(command);
|
|
7932
8379
|
registerListCommand(command);
|
|
@@ -7934,12 +8381,12 @@ function createSnapshotCommand() {
|
|
|
7934
8381
|
}
|
|
7935
8382
|
|
|
7936
8383
|
// src/commands/state/index.ts
|
|
7937
|
-
import { Command as
|
|
8384
|
+
import { Command as Command73 } from "commander";
|
|
7938
8385
|
|
|
7939
8386
|
// src/commands/state/show.ts
|
|
7940
8387
|
init_dist();
|
|
7941
|
-
import { Command as
|
|
7942
|
-
import * as
|
|
8388
|
+
import { Command as Command69 } from "commander";
|
|
8389
|
+
import * as path56 from "path";
|
|
7943
8390
|
function printStateText(state, stream) {
|
|
7944
8391
|
if (stream) console.log(`Stream: ${stream}`);
|
|
7945
8392
|
console.log(`Schema Version: ${state.schemaVersion}`);
|
|
@@ -7963,9 +8410,9 @@ function printStateProgress(progress) {
|
|
|
7963
8410
|
}
|
|
7964
8411
|
}
|
|
7965
8412
|
function createShowCommand() {
|
|
7966
|
-
return new
|
|
8413
|
+
return new Command69("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
|
|
7967
8414
|
const globalOpts = cmd.optsWithGlobals();
|
|
7968
|
-
const result = await loadState(
|
|
8415
|
+
const result = await loadState(path56.resolve(opts.path), opts.stream);
|
|
7969
8416
|
if (!result.ok) {
|
|
7970
8417
|
logger.error(result.error.message);
|
|
7971
8418
|
process.exit(ExitCode.ERROR);
|
|
@@ -7981,10 +8428,10 @@ function createShowCommand() {
|
|
|
7981
8428
|
|
|
7982
8429
|
// src/commands/state/reset.ts
|
|
7983
8430
|
init_dist();
|
|
7984
|
-
import { Command as
|
|
7985
|
-
import * as
|
|
7986
|
-
import * as
|
|
7987
|
-
import * as
|
|
8431
|
+
import { Command as Command70 } from "commander";
|
|
8432
|
+
import * as fs38 from "fs";
|
|
8433
|
+
import * as path57 from "path";
|
|
8434
|
+
import * as readline3 from "readline";
|
|
7988
8435
|
async function resolveStatePath(projectPath, stream) {
|
|
7989
8436
|
if (stream) {
|
|
7990
8437
|
const streamResult = await resolveStreamPath(projectPath, { stream });
|
|
@@ -7992,22 +8439,22 @@ async function resolveStatePath(projectPath, stream) {
|
|
|
7992
8439
|
logger.error(streamResult.error.message);
|
|
7993
8440
|
process.exit(ExitCode.ERROR);
|
|
7994
8441
|
}
|
|
7995
|
-
return
|
|
8442
|
+
return path57.join(streamResult.value, "state.json");
|
|
7996
8443
|
}
|
|
7997
|
-
return
|
|
8444
|
+
return path57.join(projectPath, ".harness", "state.json");
|
|
7998
8445
|
}
|
|
7999
8446
|
async function confirmReset() {
|
|
8000
|
-
const rl =
|
|
8001
|
-
const answer = await new Promise((
|
|
8002
|
-
rl.question("Reset project state? This cannot be undone. [y/N] ",
|
|
8447
|
+
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
8448
|
+
const answer = await new Promise((resolve37) => {
|
|
8449
|
+
rl.question("Reset project state? This cannot be undone. [y/N] ", resolve37);
|
|
8003
8450
|
});
|
|
8004
8451
|
rl.close();
|
|
8005
8452
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
8006
8453
|
}
|
|
8007
8454
|
async function runReset(opts) {
|
|
8008
|
-
const projectPath =
|
|
8455
|
+
const projectPath = path57.resolve(opts.path);
|
|
8009
8456
|
const statePath = await resolveStatePath(projectPath, opts.stream);
|
|
8010
|
-
if (!
|
|
8457
|
+
if (!fs38.existsSync(statePath)) {
|
|
8011
8458
|
logger.info("No state file found. Nothing to reset.");
|
|
8012
8459
|
process.exit(ExitCode.SUCCESS);
|
|
8013
8460
|
}
|
|
@@ -8016,7 +8463,7 @@ async function runReset(opts) {
|
|
|
8016
8463
|
process.exit(ExitCode.SUCCESS);
|
|
8017
8464
|
}
|
|
8018
8465
|
try {
|
|
8019
|
-
|
|
8466
|
+
fs38.unlinkSync(statePath);
|
|
8020
8467
|
logger.success("Project state reset.");
|
|
8021
8468
|
} catch (e) {
|
|
8022
8469
|
logger.error(`Failed to reset state: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -8025,16 +8472,16 @@ async function runReset(opts) {
|
|
|
8025
8472
|
process.exit(ExitCode.SUCCESS);
|
|
8026
8473
|
}
|
|
8027
8474
|
function createResetCommand() {
|
|
8028
|
-
return new
|
|
8475
|
+
return new Command70("reset").description("Reset project state (deletes .harness/state.json)").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").option("--yes", "Skip confirmation prompt").action(async (opts) => runReset(opts));
|
|
8029
8476
|
}
|
|
8030
8477
|
|
|
8031
8478
|
// src/commands/state/learn.ts
|
|
8032
8479
|
init_dist();
|
|
8033
|
-
import { Command as
|
|
8034
|
-
import * as
|
|
8480
|
+
import { Command as Command71 } from "commander";
|
|
8481
|
+
import * as path58 from "path";
|
|
8035
8482
|
function createLearnCommand() {
|
|
8036
|
-
return new
|
|
8037
|
-
const projectPath =
|
|
8483
|
+
return new Command71("learn").description("Append a learning to .harness/learnings.md").argument("<message>", "The learning to record").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (message, opts, _cmd) => {
|
|
8484
|
+
const projectPath = path58.resolve(opts.path);
|
|
8038
8485
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
8039
8486
|
if (!result.ok) {
|
|
8040
8487
|
logger.error(result.error.message);
|
|
@@ -8048,10 +8495,10 @@ function createLearnCommand() {
|
|
|
8048
8495
|
|
|
8049
8496
|
// src/commands/state/streams.ts
|
|
8050
8497
|
init_dist();
|
|
8051
|
-
import { Command as
|
|
8052
|
-
import * as
|
|
8498
|
+
import { Command as Command72 } from "commander";
|
|
8499
|
+
import * as path59 from "path";
|
|
8053
8500
|
async function runListStreams(opts, globalOpts) {
|
|
8054
|
-
const projectPath =
|
|
8501
|
+
const projectPath = path59.resolve(opts.path);
|
|
8055
8502
|
const indexResult = await loadStreamIndex(projectPath);
|
|
8056
8503
|
const result = await listStreams(projectPath);
|
|
8057
8504
|
if (!result.ok) {
|
|
@@ -8072,7 +8519,7 @@ async function runListStreams(opts, globalOpts) {
|
|
|
8072
8519
|
process.exit(ExitCode.SUCCESS);
|
|
8073
8520
|
}
|
|
8074
8521
|
async function runCreateStream(name, opts) {
|
|
8075
|
-
const result = await createStream(
|
|
8522
|
+
const result = await createStream(path59.resolve(opts.path), name, opts.branch);
|
|
8076
8523
|
if (!result.ok) {
|
|
8077
8524
|
logger.error(result.error.message);
|
|
8078
8525
|
process.exit(ExitCode.ERROR);
|
|
@@ -8081,7 +8528,7 @@ async function runCreateStream(name, opts) {
|
|
|
8081
8528
|
process.exit(ExitCode.SUCCESS);
|
|
8082
8529
|
}
|
|
8083
8530
|
async function runArchiveStream(name, opts) {
|
|
8084
|
-
const result = await archiveStream(
|
|
8531
|
+
const result = await archiveStream(path59.resolve(opts.path), name);
|
|
8085
8532
|
if (!result.ok) {
|
|
8086
8533
|
logger.error(result.error.message);
|
|
8087
8534
|
process.exit(ExitCode.ERROR);
|
|
@@ -8090,7 +8537,7 @@ async function runArchiveStream(name, opts) {
|
|
|
8090
8537
|
process.exit(ExitCode.SUCCESS);
|
|
8091
8538
|
}
|
|
8092
8539
|
async function runActivateStream(name, opts) {
|
|
8093
|
-
const result = await setActiveStream(
|
|
8540
|
+
const result = await setActiveStream(path59.resolve(opts.path), name);
|
|
8094
8541
|
if (!result.ok) {
|
|
8095
8542
|
logger.error(result.error.message);
|
|
8096
8543
|
process.exit(ExitCode.ERROR);
|
|
@@ -8099,7 +8546,7 @@ async function runActivateStream(name, opts) {
|
|
|
8099
8546
|
process.exit(ExitCode.SUCCESS);
|
|
8100
8547
|
}
|
|
8101
8548
|
function createStreamsCommand() {
|
|
8102
|
-
const command = new
|
|
8549
|
+
const command = new Command72("streams").description("Manage state streams");
|
|
8103
8550
|
command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
|
|
8104
8551
|
command.command("create <name>").description("Create a new stream").option("--path <path>", "Project root path", ".").option("--branch <branch>", "Associate with a git branch").action(async (name, opts) => runCreateStream(name, opts));
|
|
8105
8552
|
command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
|
|
@@ -8109,7 +8556,7 @@ function createStreamsCommand() {
|
|
|
8109
8556
|
|
|
8110
8557
|
// src/commands/state/index.ts
|
|
8111
8558
|
function createStateCommand() {
|
|
8112
|
-
const command = new
|
|
8559
|
+
const command = new Command73("state").description("Project state management commands");
|
|
8113
8560
|
command.addCommand(createShowCommand());
|
|
8114
8561
|
command.addCommand(createResetCommand());
|
|
8115
8562
|
command.addCommand(createLearnCommand());
|
|
@@ -8118,9 +8565,9 @@ function createStateCommand() {
|
|
|
8118
8565
|
}
|
|
8119
8566
|
|
|
8120
8567
|
// src/commands/sync-analyses.ts
|
|
8121
|
-
import * as
|
|
8122
|
-
import * as
|
|
8123
|
-
import { Command as
|
|
8568
|
+
import * as fs39 from "fs";
|
|
8569
|
+
import * as path60 from "path";
|
|
8570
|
+
import { Command as Command74 } from "commander";
|
|
8124
8571
|
init_dist();
|
|
8125
8572
|
function isValidRecord(record) {
|
|
8126
8573
|
return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
|
|
@@ -8146,7 +8593,7 @@ function extractAnalysisFromComments(comments) {
|
|
|
8146
8593
|
return null;
|
|
8147
8594
|
}
|
|
8148
8595
|
function bootstrapTrackerCommand2(opts, verb) {
|
|
8149
|
-
const projectPath =
|
|
8596
|
+
const projectPath = path60.resolve(opts.dir);
|
|
8150
8597
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
8151
8598
|
if (!trackerConfig) {
|
|
8152
8599
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -8158,8 +8605,8 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
8158
8605
|
);
|
|
8159
8606
|
return null;
|
|
8160
8607
|
}
|
|
8161
|
-
const projectEnvPath =
|
|
8162
|
-
if (
|
|
8608
|
+
const projectEnvPath = path60.join(projectPath, ".env");
|
|
8609
|
+
if (fs39.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
8163
8610
|
__require("dotenv").config({ path: projectEnvPath });
|
|
8164
8611
|
}
|
|
8165
8612
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -8170,13 +8617,13 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
8170
8617
|
return { token, projectPath, trackerConfig };
|
|
8171
8618
|
}
|
|
8172
8619
|
function loadRoadmapFeatures(projectPath) {
|
|
8173
|
-
const roadmapFile =
|
|
8174
|
-
if (!
|
|
8620
|
+
const roadmapFile = path60.join(projectPath, "docs", "roadmap.md");
|
|
8621
|
+
if (!fs39.existsSync(roadmapFile)) {
|
|
8175
8622
|
logger.error("No docs/roadmap.md found. Cannot discover features with externalIds.");
|
|
8176
8623
|
return null;
|
|
8177
8624
|
}
|
|
8178
8625
|
const { parseRoadmap } = (init_dist(), __toCommonJS(dist_exports));
|
|
8179
|
-
const roadmapParsed = parseRoadmap(
|
|
8626
|
+
const roadmapParsed = parseRoadmap(fs39.readFileSync(roadmapFile, "utf-8"));
|
|
8180
8627
|
if (!roadmapParsed.ok) {
|
|
8181
8628
|
logger.error("Failed to parse docs/roadmap.md");
|
|
8182
8629
|
return null;
|
|
@@ -8227,9 +8674,9 @@ async function runSyncAnalyses(opts) {
|
|
|
8227
8674
|
return;
|
|
8228
8675
|
}
|
|
8229
8676
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
8230
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
8677
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-46VOJIG4.js");
|
|
8231
8678
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
8232
|
-
const archive = new AnalysisArchive(
|
|
8679
|
+
const archive = new AnalysisArchive(path60.join(projectPath, ".harness", "analyses"));
|
|
8233
8680
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
8234
8681
|
const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
|
|
8235
8682
|
features,
|
|
@@ -8249,7 +8696,7 @@ async function runSyncAnalyses(opts) {
|
|
|
8249
8696
|
}
|
|
8250
8697
|
}
|
|
8251
8698
|
function createSyncAnalysesCommand() {
|
|
8252
|
-
const command = new
|
|
8699
|
+
const command = new Command74("sync-analyses").description(
|
|
8253
8700
|
"Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
|
|
8254
8701
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
8255
8702
|
try {
|
|
@@ -8264,7 +8711,7 @@ function createSyncAnalysesCommand() {
|
|
|
8264
8711
|
|
|
8265
8712
|
// src/commands/taint.ts
|
|
8266
8713
|
init_dist();
|
|
8267
|
-
import { Command as
|
|
8714
|
+
import { Command as Command75 } from "commander";
|
|
8268
8715
|
function getProjectRoot() {
|
|
8269
8716
|
return process.cwd();
|
|
8270
8717
|
}
|
|
@@ -8336,28 +8783,28 @@ function registerStatusCommand(taint) {
|
|
|
8336
8783
|
});
|
|
8337
8784
|
}
|
|
8338
8785
|
function createTaintCommand() {
|
|
8339
|
-
const taint = new
|
|
8786
|
+
const taint = new Command75("taint").description("Manage sentinel session taint state");
|
|
8340
8787
|
registerClearCommand(taint);
|
|
8341
8788
|
registerStatusCommand(taint);
|
|
8342
8789
|
return taint;
|
|
8343
8790
|
}
|
|
8344
8791
|
|
|
8345
8792
|
// src/commands/telemetry/index.ts
|
|
8346
|
-
import { Command as
|
|
8793
|
+
import { Command as Command79 } from "commander";
|
|
8347
8794
|
|
|
8348
8795
|
// src/commands/telemetry/identify.ts
|
|
8349
8796
|
init_dist();
|
|
8350
|
-
import { Command as
|
|
8351
|
-
import * as
|
|
8352
|
-
import * as
|
|
8797
|
+
import { Command as Command76 } from "commander";
|
|
8798
|
+
import * as fs40 from "fs";
|
|
8799
|
+
import * as path61 from "path";
|
|
8353
8800
|
function telemetryFilePath(cwd) {
|
|
8354
|
-
return
|
|
8801
|
+
return path61.join(cwd, ".harness", "telemetry.json");
|
|
8355
8802
|
}
|
|
8356
8803
|
function writeTelemetryFile(cwd, data) {
|
|
8357
8804
|
const filePath = telemetryFilePath(cwd);
|
|
8358
|
-
const dir =
|
|
8359
|
-
|
|
8360
|
-
|
|
8805
|
+
const dir = path61.dirname(filePath);
|
|
8806
|
+
fs40.mkdirSync(dir, { recursive: true });
|
|
8807
|
+
fs40.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
8361
8808
|
}
|
|
8362
8809
|
var IDENTITY_FIELDS = ["project", "team", "alias"];
|
|
8363
8810
|
function applyIdentityFields(opts, identity) {
|
|
@@ -8371,7 +8818,7 @@ function printIdentity(identity) {
|
|
|
8371
8818
|
}
|
|
8372
8819
|
}
|
|
8373
8820
|
function createIdentifyCommand() {
|
|
8374
|
-
const cmd = new
|
|
8821
|
+
const cmd = new Command76("identify").description("Set or clear telemetry identity fields in .harness/telemetry.json").option("--project <name>", "Project name").option("--team <name>", "Team name").option("--alias <name>", "User alias").option("--clear", "Remove all identity fields").action((opts) => {
|
|
8375
8822
|
const cwd = process.cwd();
|
|
8376
8823
|
if (opts.clear) {
|
|
8377
8824
|
writeTelemetryFile(cwd, { identity: {} });
|
|
@@ -8396,7 +8843,7 @@ function createIdentifyCommand() {
|
|
|
8396
8843
|
|
|
8397
8844
|
// src/commands/telemetry/status.ts
|
|
8398
8845
|
init_dist();
|
|
8399
|
-
import { Command as
|
|
8846
|
+
import { Command as Command77 } from "commander";
|
|
8400
8847
|
function gatherEnvOverrides() {
|
|
8401
8848
|
const overrides = {};
|
|
8402
8849
|
if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
|
|
@@ -8438,7 +8885,7 @@ function printHumanStatus(result) {
|
|
|
8438
8885
|
printEnvOverrides(result.envOverrides);
|
|
8439
8886
|
}
|
|
8440
8887
|
function createStatusCommand() {
|
|
8441
|
-
const cmd = new
|
|
8888
|
+
const cmd = new Command77("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
|
|
8442
8889
|
const cwd = process.cwd();
|
|
8443
8890
|
const envOverrides = gatherEnvOverrides();
|
|
8444
8891
|
const consent = resolveConsent(cwd, void 0);
|
|
@@ -8471,20 +8918,20 @@ function createStatusCommand() {
|
|
|
8471
8918
|
|
|
8472
8919
|
// src/commands/telemetry/test.ts
|
|
8473
8920
|
init_dist();
|
|
8474
|
-
import { Command as
|
|
8921
|
+
import { Command as Command78 } from "commander";
|
|
8475
8922
|
|
|
8476
8923
|
// src/bin/command-telemetry.ts
|
|
8477
|
-
import { existsSync as
|
|
8478
|
-
import { join as
|
|
8924
|
+
import { existsSync as existsSync37, readFileSync as readFileSync26, mkdirSync as mkdirSync16, appendFileSync, writeFileSync as writeFileSync19 } from "fs";
|
|
8925
|
+
import { join as join47, parse as parsePath, resolve as resolve34 } from "path";
|
|
8479
8926
|
import { spawn as spawn2 } from "child_process";
|
|
8480
8927
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
8481
8928
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
8482
8929
|
function findProjectRoot(cwd) {
|
|
8483
|
-
let dir =
|
|
8930
|
+
let dir = resolve34(cwd);
|
|
8484
8931
|
const { root } = parsePath(dir);
|
|
8485
8932
|
while (dir !== root) {
|
|
8486
|
-
if (
|
|
8487
|
-
dir =
|
|
8933
|
+
if (existsSync37(join47(dir, "harness.config.json"))) return dir;
|
|
8934
|
+
dir = resolve34(dir, "..");
|
|
8488
8935
|
}
|
|
8489
8936
|
return cwd;
|
|
8490
8937
|
}
|
|
@@ -8521,8 +8968,8 @@ function resolveCommandName(cmd) {
|
|
|
8521
8968
|
}
|
|
8522
8969
|
function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
8523
8970
|
try {
|
|
8524
|
-
const metricsDir =
|
|
8525
|
-
|
|
8971
|
+
const metricsDir = join47(cwd, ".harness", "metrics");
|
|
8972
|
+
mkdirSync16(metricsDir, { recursive: true });
|
|
8526
8973
|
const record = {
|
|
8527
8974
|
skill: command,
|
|
8528
8975
|
session: `cli-${Date.now()}`,
|
|
@@ -8531,16 +8978,16 @@ function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
|
8531
8978
|
outcome,
|
|
8532
8979
|
phasesReached: []
|
|
8533
8980
|
};
|
|
8534
|
-
const adoptionFile =
|
|
8981
|
+
const adoptionFile = join47(metricsDir, "adoption.jsonl");
|
|
8535
8982
|
appendFileSync(adoptionFile, JSON.stringify(record) + "\n");
|
|
8536
8983
|
} catch {
|
|
8537
8984
|
}
|
|
8538
8985
|
}
|
|
8539
8986
|
function isTelemetryDisabledByConfig(cwd) {
|
|
8540
|
-
const configPath =
|
|
8541
|
-
if (!
|
|
8987
|
+
const configPath = join47(cwd, "harness.config.json");
|
|
8988
|
+
if (!existsSync37(configPath)) return false;
|
|
8542
8989
|
try {
|
|
8543
|
-
const config = JSON.parse(
|
|
8990
|
+
const config = JSON.parse(readFileSync26(configPath, "utf-8"));
|
|
8544
8991
|
return config?.telemetry?.enabled === false;
|
|
8545
8992
|
} catch {
|
|
8546
8993
|
return false;
|
|
@@ -8552,11 +8999,11 @@ function isTelemetryOptedOut(cwd) {
|
|
|
8552
8999
|
}
|
|
8553
9000
|
function flushTelemetryBackground(cwd) {
|
|
8554
9001
|
try {
|
|
8555
|
-
const adoptionFile =
|
|
8556
|
-
if (!
|
|
9002
|
+
const adoptionFile = join47(cwd, ".harness", "metrics", "adoption.jsonl");
|
|
9003
|
+
if (!existsSync37(adoptionFile)) return;
|
|
8557
9004
|
if (isTelemetryOptedOut(cwd)) return;
|
|
8558
|
-
const reporterPath =
|
|
8559
|
-
if (!
|
|
9005
|
+
const reporterPath = join47(cwd, ".harness", "hooks", "telemetry-reporter.js");
|
|
9006
|
+
if (!existsSync37(reporterPath)) return;
|
|
8560
9007
|
const child = spawn2(process.execPath, [reporterPath], {
|
|
8561
9008
|
cwd,
|
|
8562
9009
|
stdio: ["pipe", "ignore", "ignore"],
|
|
@@ -8593,7 +9040,7 @@ function reportTestSuccess(status, distinctId, identity) {
|
|
|
8593
9040
|
if (identity.team) logger.info(` team: ${identity.team}`);
|
|
8594
9041
|
}
|
|
8595
9042
|
function createTestCommand() {
|
|
8596
|
-
return new
|
|
9043
|
+
return new Command78("test").description("Send a test event to PostHog and verify connectivity").action(async () => {
|
|
8597
9044
|
const cwd = process.cwd();
|
|
8598
9045
|
const consent = resolveConsent(cwd, void 0);
|
|
8599
9046
|
if (!consent.allowed) {
|
|
@@ -8630,7 +9077,7 @@ function createTestCommand() {
|
|
|
8630
9077
|
|
|
8631
9078
|
// src/commands/telemetry/index.ts
|
|
8632
9079
|
function createTelemetryCommand() {
|
|
8633
|
-
const command = new
|
|
9080
|
+
const command = new Command79("telemetry").description("Telemetry identity and status management");
|
|
8634
9081
|
command.addCommand(createIdentifyCommand());
|
|
8635
9082
|
command.addCommand(createStatusCommand());
|
|
8636
9083
|
command.addCommand(createTestCommand());
|
|
@@ -8638,8 +9085,8 @@ function createTelemetryCommand() {
|
|
|
8638
9085
|
}
|
|
8639
9086
|
|
|
8640
9087
|
// src/commands/traceability.ts
|
|
8641
|
-
import { Command as
|
|
8642
|
-
import
|
|
9088
|
+
import { Command as Command80 } from "commander";
|
|
9089
|
+
import chalk13 from "chalk";
|
|
8643
9090
|
function confidenceLabel(maxConfidence) {
|
|
8644
9091
|
if (maxConfidence >= 0.8) return "explicit";
|
|
8645
9092
|
if (maxConfidence > 0) return "inferred";
|
|
@@ -8648,13 +9095,13 @@ function confidenceLabel(maxConfidence) {
|
|
|
8648
9095
|
function statusIcon(status) {
|
|
8649
9096
|
switch (status) {
|
|
8650
9097
|
case "full":
|
|
8651
|
-
return
|
|
9098
|
+
return chalk13.green("\u2713 full");
|
|
8652
9099
|
case "code-only":
|
|
8653
|
-
return
|
|
9100
|
+
return chalk13.yellow("\u25D0 code-only");
|
|
8654
9101
|
case "test-only":
|
|
8655
|
-
return
|
|
9102
|
+
return chalk13.yellow("\u25D0 test-only");
|
|
8656
9103
|
case "none":
|
|
8657
|
-
return
|
|
9104
|
+
return chalk13.red("\u2717 none");
|
|
8658
9105
|
default:
|
|
8659
9106
|
return status;
|
|
8660
9107
|
}
|
|
@@ -8701,14 +9148,14 @@ function handleEmptyResults(mode) {
|
|
|
8701
9148
|
}
|
|
8702
9149
|
function printResultTable(result, mode) {
|
|
8703
9150
|
const specLabel = result.specPath || result.featureName || "Unknown";
|
|
8704
|
-
console.log(` ${
|
|
9151
|
+
console.log(` ${chalk13.cyan(specLabel)} ${chalk13.dim(`(${result.summary.total} requirements)`)}`);
|
|
8705
9152
|
console.log("");
|
|
8706
9153
|
const numWidth = 4;
|
|
8707
9154
|
const nameWidth = mode === OutputMode.VERBOSE ? 44 : 36;
|
|
8708
9155
|
const codeWidth = 6;
|
|
8709
9156
|
const testWidth = 7;
|
|
8710
9157
|
const confWidth = 12;
|
|
8711
|
-
const header =
|
|
9158
|
+
const header = chalk13.dim(
|
|
8712
9159
|
` ${pad("#", numWidth)}${pad("Requirement", nameWidth)}${pad("Code", codeWidth)}${pad("Tests", testWidth)}${pad("Confidence", confWidth)}Status`
|
|
8713
9160
|
);
|
|
8714
9161
|
console.log(header);
|
|
@@ -8737,10 +9184,10 @@ function printRequirementRows(requirements, mode, cols) {
|
|
|
8737
9184
|
);
|
|
8738
9185
|
if (mode === OutputMode.VERBOSE) {
|
|
8739
9186
|
for (const f of req.codeFiles) {
|
|
8740
|
-
console.log(
|
|
9187
|
+
console.log(chalk13.dim(` code: ${f.path} (${f.method})`));
|
|
8741
9188
|
}
|
|
8742
9189
|
for (const f of req.testFiles) {
|
|
8743
|
-
console.log(
|
|
9190
|
+
console.log(chalk13.dim(` test: ${f.path} (${f.method})`));
|
|
8744
9191
|
}
|
|
8745
9192
|
}
|
|
8746
9193
|
}
|
|
@@ -8751,7 +9198,7 @@ function printCoverageSummary(summary) {
|
|
|
8751
9198
|
const codePct = s.total > 0 ? Math.round(s.withCode / s.total * 100) : 0;
|
|
8752
9199
|
const testPct = s.total > 0 ? Math.round(s.withTests / s.total * 100) : 0;
|
|
8753
9200
|
console.log(
|
|
8754
|
-
` ${
|
|
9201
|
+
` ${chalk13.bold("Coverage:")} ${fullyPct}% fully traced (${s.fullyTraced}/${s.total}), ${codePct}% with code (${s.withCode}/${s.total}), ${testPct}% with tests (${s.withTests}/${s.total})`
|
|
8755
9202
|
);
|
|
8756
9203
|
}
|
|
8757
9204
|
async function runTraceability(options) {
|
|
@@ -8759,7 +9206,7 @@ async function runTraceability(options) {
|
|
|
8759
9206
|
const projectPath = process.cwd();
|
|
8760
9207
|
const store = await loadGraphStore(projectPath);
|
|
8761
9208
|
if (!store) handleNoStore(mode);
|
|
8762
|
-
const graphModule = await import("./dist-
|
|
9209
|
+
const graphModule = await import("./dist-BP6E6QGG.js");
|
|
8763
9210
|
const filterOptions = buildFilterOptions(options);
|
|
8764
9211
|
const results = graphModule.queryTraceability(store, filterOptions);
|
|
8765
9212
|
if (results.length === 0) handleEmptyResults(mode);
|
|
@@ -8767,14 +9214,14 @@ async function runTraceability(options) {
|
|
|
8767
9214
|
console.log(JSON.stringify(results, null, 2));
|
|
8768
9215
|
} else {
|
|
8769
9216
|
console.log("");
|
|
8770
|
-
console.log(
|
|
9217
|
+
console.log(chalk13.bold("Spec-to-Implementation Traceability"));
|
|
8771
9218
|
console.log("");
|
|
8772
9219
|
for (const result of results) printResultTable(result, mode);
|
|
8773
9220
|
}
|
|
8774
9221
|
process.exit(ExitCode.SUCCESS);
|
|
8775
9222
|
}
|
|
8776
9223
|
function createTraceabilityCommand() {
|
|
8777
|
-
return new
|
|
9224
|
+
return new Command80("traceability").description("Show spec-to-implementation traceability from the knowledge graph").option("--spec <path>", "Filter by spec file path").option("--feature <name>", "Filter by feature name").action(async (opts, cmd) => {
|
|
8778
9225
|
const globalOpts = cmd.optsWithGlobals();
|
|
8779
9226
|
await runTraceability({
|
|
8780
9227
|
spec: opts.spec,
|
|
@@ -8787,15 +9234,15 @@ function createTraceabilityCommand() {
|
|
|
8787
9234
|
}
|
|
8788
9235
|
|
|
8789
9236
|
// src/commands/uninstall.ts
|
|
8790
|
-
import * as
|
|
8791
|
-
import { Command as
|
|
9237
|
+
import * as path62 from "path";
|
|
9238
|
+
import { Command as Command81 } from "commander";
|
|
8792
9239
|
async function runUninstall(skillName, options) {
|
|
8793
9240
|
const packageName = resolvePackageName(skillName);
|
|
8794
9241
|
const shortName = extractSkillName(packageName);
|
|
8795
9242
|
const globalDir = resolveGlobalSkillsDir();
|
|
8796
|
-
const skillsDir =
|
|
8797
|
-
const communityBase =
|
|
8798
|
-
const lockfilePath =
|
|
9243
|
+
const skillsDir = path62.dirname(globalDir);
|
|
9244
|
+
const communityBase = path62.join(skillsDir, "community");
|
|
9245
|
+
const lockfilePath = path62.join(communityBase, "skills-lock.json");
|
|
8799
9246
|
const lockfile = readLockfile2(lockfilePath);
|
|
8800
9247
|
const entry = lockfile.skills[packageName];
|
|
8801
9248
|
if (!entry) {
|
|
@@ -8825,7 +9272,7 @@ async function runUninstall(skillName, options) {
|
|
|
8825
9272
|
return result;
|
|
8826
9273
|
}
|
|
8827
9274
|
function createUninstallCommand() {
|
|
8828
|
-
const cmd = new
|
|
9275
|
+
const cmd = new Command81("uninstall");
|
|
8829
9276
|
cmd.description("Uninstall a community skill").argument("<skill>", "Skill name or @harness-skills/scoped package name").option("--force", "Remove even if other skills depend on this one").action(async (skill, opts) => {
|
|
8830
9277
|
try {
|
|
8831
9278
|
const result = await runUninstall(skill, opts);
|
|
@@ -8845,9 +9292,9 @@ function createUninstallCommand() {
|
|
|
8845
9292
|
|
|
8846
9293
|
// src/commands/uninstall-constraints.ts
|
|
8847
9294
|
init_dist();
|
|
8848
|
-
import * as
|
|
8849
|
-
import * as
|
|
8850
|
-
import { Command as
|
|
9295
|
+
import * as fs41 from "fs/promises";
|
|
9296
|
+
import * as path63 from "path";
|
|
9297
|
+
import { Command as Command82 } from "commander";
|
|
8851
9298
|
async function runUninstallConstraints(options) {
|
|
8852
9299
|
const { packageName, configPath, lockfilePath } = options;
|
|
8853
9300
|
const lockfileResult = await readLockfile(lockfilePath);
|
|
@@ -8867,7 +9314,7 @@ async function runUninstallConstraints(options) {
|
|
|
8867
9314
|
}
|
|
8868
9315
|
let localConfig;
|
|
8869
9316
|
try {
|
|
8870
|
-
const raw = await
|
|
9317
|
+
const raw = await fs41.readFile(configPath, "utf-8");
|
|
8871
9318
|
localConfig = JSON.parse(raw);
|
|
8872
9319
|
} catch (err) {
|
|
8873
9320
|
return {
|
|
@@ -8904,11 +9351,11 @@ async function runUninstallConstraints(options) {
|
|
|
8904
9351
|
};
|
|
8905
9352
|
}
|
|
8906
9353
|
function createUninstallConstraintsCommand() {
|
|
8907
|
-
const cmd = new
|
|
9354
|
+
const cmd = new Command82("uninstall-constraints");
|
|
8908
9355
|
cmd.description("Remove a previously installed constraints package").argument("<name>", "Name of the constraint package to uninstall").option("-c, --config <path>", "Path to harness.config.json").action(async (name, opts) => {
|
|
8909
9356
|
let configPath;
|
|
8910
9357
|
if (opts.config) {
|
|
8911
|
-
configPath =
|
|
9358
|
+
configPath = path63.resolve(opts.config);
|
|
8912
9359
|
} else {
|
|
8913
9360
|
const found = findConfigFile();
|
|
8914
9361
|
if (!found.ok) {
|
|
@@ -8917,8 +9364,8 @@ function createUninstallConstraintsCommand() {
|
|
|
8917
9364
|
}
|
|
8918
9365
|
configPath = found.value;
|
|
8919
9366
|
}
|
|
8920
|
-
const projectRoot =
|
|
8921
|
-
const lockfilePath =
|
|
9367
|
+
const projectRoot = path63.dirname(configPath);
|
|
9368
|
+
const lockfilePath = path63.join(projectRoot, ".harness", "constraints.lock.json");
|
|
8922
9369
|
const result = await runUninstallConstraints({
|
|
8923
9370
|
packageName: name,
|
|
8924
9371
|
configPath,
|
|
@@ -8943,13 +9390,13 @@ function createUninstallConstraintsCommand() {
|
|
|
8943
9390
|
}
|
|
8944
9391
|
|
|
8945
9392
|
// src/commands/update.ts
|
|
8946
|
-
import { Command as
|
|
8947
|
-
import { execFile, execFileSync as
|
|
8948
|
-
import { realpathSync, existsSync as
|
|
8949
|
-
import { join as
|
|
9393
|
+
import { Command as Command83 } from "commander";
|
|
9394
|
+
import { execFile, execFileSync as execFileSync7 } from "child_process";
|
|
9395
|
+
import { realpathSync, existsSync as existsSync38, readFileSync as readFileSync27 } from "fs";
|
|
9396
|
+
import { join as join50 } from "path";
|
|
8950
9397
|
import { promisify } from "util";
|
|
8951
|
-
import
|
|
8952
|
-
import
|
|
9398
|
+
import readline4 from "readline";
|
|
9399
|
+
import chalk14 from "chalk";
|
|
8953
9400
|
function detectPackageManager() {
|
|
8954
9401
|
try {
|
|
8955
9402
|
const argv1 = process.argv[1];
|
|
@@ -8978,7 +9425,7 @@ async function getLatestVersionAsync(pkg) {
|
|
|
8978
9425
|
function getInstalledVersions(pm, packages) {
|
|
8979
9426
|
const versions = {};
|
|
8980
9427
|
try {
|
|
8981
|
-
const output =
|
|
9428
|
+
const output = execFileSync7(pm, ["list", "-g", "--json"], {
|
|
8982
9429
|
encoding: "utf-8",
|
|
8983
9430
|
timeout: 15e3
|
|
8984
9431
|
});
|
|
@@ -8996,7 +9443,7 @@ function getInstalledVersions(pm, packages) {
|
|
|
8996
9443
|
}
|
|
8997
9444
|
function getInstalledPackages(pm) {
|
|
8998
9445
|
try {
|
|
8999
|
-
const output =
|
|
9446
|
+
const output = execFileSync7(pm, ["list", "-g", "--json"], {
|
|
9000
9447
|
encoding: "utf-8",
|
|
9001
9448
|
timeout: 15e3
|
|
9002
9449
|
});
|
|
@@ -9007,15 +9454,15 @@ function getInstalledPackages(pm) {
|
|
|
9007
9454
|
return ["@harness-engineering/cli", "@harness-engineering/core"];
|
|
9008
9455
|
}
|
|
9009
9456
|
}
|
|
9010
|
-
function
|
|
9011
|
-
const rl =
|
|
9457
|
+
function prompt3(question) {
|
|
9458
|
+
const rl = readline4.createInterface({
|
|
9012
9459
|
input: process.stdin,
|
|
9013
9460
|
output: process.stdout
|
|
9014
9461
|
});
|
|
9015
|
-
return new Promise((
|
|
9462
|
+
return new Promise((resolve37) => {
|
|
9016
9463
|
rl.question(question, (answer) => {
|
|
9017
9464
|
rl.close();
|
|
9018
|
-
|
|
9465
|
+
resolve37(answer.trim().toLowerCase());
|
|
9019
9466
|
});
|
|
9020
9467
|
});
|
|
9021
9468
|
}
|
|
@@ -9030,12 +9477,12 @@ async function ensureTelemetryIfNeeded() {
|
|
|
9030
9477
|
}
|
|
9031
9478
|
function refreshHooks() {
|
|
9032
9479
|
const cwd = process.cwd();
|
|
9033
|
-
const configPath =
|
|
9034
|
-
if (!
|
|
9480
|
+
const configPath = join50(cwd, "harness.config.json");
|
|
9481
|
+
if (!existsSync38(configPath)) return;
|
|
9035
9482
|
let profile = "standard";
|
|
9036
|
-
const profilePath =
|
|
9483
|
+
const profilePath = join50(cwd, ".harness", "hooks", "profile.json");
|
|
9037
9484
|
try {
|
|
9038
|
-
const data = JSON.parse(
|
|
9485
|
+
const data = JSON.parse(readFileSync27(profilePath, "utf-8"));
|
|
9039
9486
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
9040
9487
|
profile = data.profile;
|
|
9041
9488
|
}
|
|
@@ -9052,27 +9499,27 @@ function refreshHooks() {
|
|
|
9052
9499
|
function runLocalGraphScan() {
|
|
9053
9500
|
try {
|
|
9054
9501
|
logger.info("Scanning codebase to rebuild knowledge graph...");
|
|
9055
|
-
|
|
9502
|
+
execFileSync7("harness", ["graph", "scan", "."], { stdio: "inherit" });
|
|
9056
9503
|
} catch {
|
|
9057
9504
|
logger.warn("Graph scan failed. Run manually:");
|
|
9058
|
-
console.log(` ${
|
|
9505
|
+
console.log(` ${chalk14.cyan("harness graph scan .")}`);
|
|
9059
9506
|
}
|
|
9060
9507
|
}
|
|
9061
9508
|
async function offerRegeneration() {
|
|
9062
9509
|
console.log("");
|
|
9063
|
-
const regenAnswer = await
|
|
9510
|
+
const regenAnswer = await prompt3(
|
|
9064
9511
|
"Regenerate slash commands, agent definitions, and knowledge graph? (Y/n) "
|
|
9065
9512
|
);
|
|
9066
9513
|
if (regenAnswer === "n" || regenAnswer === "no") return;
|
|
9067
|
-
const scopeAnswer = await
|
|
9514
|
+
const scopeAnswer = await prompt3("Generate for (G)lobal or (l)ocal project? (G/l) ");
|
|
9068
9515
|
const isGlobal = scopeAnswer !== "l" && scopeAnswer !== "local";
|
|
9069
9516
|
try {
|
|
9070
|
-
|
|
9517
|
+
execFileSync7("harness", ["generate", ...isGlobal ? ["--global"] : []], {
|
|
9071
9518
|
stdio: "inherit"
|
|
9072
9519
|
});
|
|
9073
9520
|
} catch {
|
|
9074
9521
|
logger.warn("Generation failed. Run manually:");
|
|
9075
|
-
console.log(` ${
|
|
9522
|
+
console.log(` ${chalk14.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
|
|
9076
9523
|
}
|
|
9077
9524
|
if (!isGlobal) {
|
|
9078
9525
|
runLocalGraphScan();
|
|
@@ -9135,8 +9582,8 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
9135
9582
|
console.log("");
|
|
9136
9583
|
for (const { pkg, current, latest } of outdated) {
|
|
9137
9584
|
const shortName = pkg.replace("@harness-engineering/", "");
|
|
9138
|
-
const currentStr = current ?
|
|
9139
|
-
logger.info(`${shortName}: ${currentStr} \u2192 ${
|
|
9585
|
+
const currentStr = current ? chalk14.dim(`v${current}`) : chalk14.dim("not installed");
|
|
9586
|
+
logger.info(`${shortName}: ${currentStr} \u2192 ${chalk14.green(`v${latest}`)}`);
|
|
9140
9587
|
}
|
|
9141
9588
|
console.log("");
|
|
9142
9589
|
}
|
|
@@ -9146,13 +9593,13 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
9146
9593
|
}
|
|
9147
9594
|
try {
|
|
9148
9595
|
logger.info("Updating packages...");
|
|
9149
|
-
|
|
9596
|
+
execFileSync7(pm, ["install", "-g", ...installPkgs], { stdio: "inherit", timeout: 12e4 });
|
|
9150
9597
|
console.log("");
|
|
9151
9598
|
logger.success("Update complete");
|
|
9152
9599
|
} catch {
|
|
9153
9600
|
console.log("");
|
|
9154
9601
|
logger.error("Update failed. You can try manually:");
|
|
9155
|
-
console.log(` ${
|
|
9602
|
+
console.log(` ${chalk14.cyan(installCmd)}`);
|
|
9156
9603
|
process.exit(ExitCode.ERROR);
|
|
9157
9604
|
}
|
|
9158
9605
|
refreshHooks();
|
|
@@ -9161,7 +9608,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
9161
9608
|
process.exit(ExitCode.SUCCESS);
|
|
9162
9609
|
}
|
|
9163
9610
|
function createUpdateCommand() {
|
|
9164
|
-
return new
|
|
9611
|
+
return new Command83("update").description("Update all @harness-engineering packages to the latest version").option("--version <semver>", "Pin @harness-engineering/cli to a specific version").option("--force", "Force update even if versions match").option(
|
|
9165
9612
|
"--regenerate",
|
|
9166
9613
|
"Only regenerate slash commands and agent definitions (skip package updates)"
|
|
9167
9614
|
).action(async (opts, cmd) => {
|
|
@@ -9171,9 +9618,9 @@ function createUpdateCommand() {
|
|
|
9171
9618
|
}
|
|
9172
9619
|
|
|
9173
9620
|
// src/commands/usage.ts
|
|
9174
|
-
import { Command as
|
|
9621
|
+
import { Command as Command84 } from "commander";
|
|
9175
9622
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
9176
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
9623
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-46VOJIG4.js");
|
|
9177
9624
|
const records = readCostRecords(cwd);
|
|
9178
9625
|
if (includeClaudeSessions) {
|
|
9179
9626
|
const ccRecords = parseCCRecords();
|
|
@@ -9266,7 +9713,7 @@ function registerDailyCommand(usage) {
|
|
|
9266
9713
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
9267
9714
|
return;
|
|
9268
9715
|
}
|
|
9269
|
-
const { aggregateByDay } = await import("./dist-
|
|
9716
|
+
const { aggregateByDay } = await import("./dist-46VOJIG4.js");
|
|
9270
9717
|
const limited = aggregateByDay(records).slice(0, days);
|
|
9271
9718
|
if (globalOpts.json) {
|
|
9272
9719
|
console.log(
|
|
@@ -9295,7 +9742,7 @@ function registerSessionsCommand(usage) {
|
|
|
9295
9742
|
}
|
|
9296
9743
|
return;
|
|
9297
9744
|
}
|
|
9298
|
-
const { aggregateBySession } = await import("./dist-
|
|
9745
|
+
const { aggregateBySession } = await import("./dist-46VOJIG4.js");
|
|
9299
9746
|
const sessionData = aggregateBySession(records);
|
|
9300
9747
|
const limited = sessionData.slice(0, limit);
|
|
9301
9748
|
if (globalOpts.json) {
|
|
@@ -9375,7 +9822,7 @@ function registerSessionCommand(usage) {
|
|
|
9375
9822
|
cwd,
|
|
9376
9823
|
globalOpts.includeClaudeSessions
|
|
9377
9824
|
);
|
|
9378
|
-
const { aggregateBySession } = await import("./dist-
|
|
9825
|
+
const { aggregateBySession } = await import("./dist-46VOJIG4.js");
|
|
9379
9826
|
const sessionData = aggregateBySession(records);
|
|
9380
9827
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
9381
9828
|
if (!match) {
|
|
@@ -9410,7 +9857,7 @@ function registerLatestCommand(usage) {
|
|
|
9410
9857
|
}
|
|
9411
9858
|
return;
|
|
9412
9859
|
}
|
|
9413
|
-
const { aggregateBySession } = await import("./dist-
|
|
9860
|
+
const { aggregateBySession } = await import("./dist-46VOJIG4.js");
|
|
9414
9861
|
const sessionData = aggregateBySession(records);
|
|
9415
9862
|
const latest = sessionData[0];
|
|
9416
9863
|
if (!latest) {
|
|
@@ -9436,7 +9883,7 @@ function registerLatestCommand(usage) {
|
|
|
9436
9883
|
});
|
|
9437
9884
|
}
|
|
9438
9885
|
function createUsageCommand() {
|
|
9439
|
-
const usage = new
|
|
9886
|
+
const usage = new Command84("usage").description("Token usage and cost tracking");
|
|
9440
9887
|
usage.option(
|
|
9441
9888
|
"--include-claude-sessions",
|
|
9442
9889
|
"Include Claude Code session data from ~/.claude/projects/"
|
|
@@ -9451,15 +9898,15 @@ function createUsageCommand() {
|
|
|
9451
9898
|
// src/commands/validate.ts
|
|
9452
9899
|
init_dist();
|
|
9453
9900
|
init_dist();
|
|
9454
|
-
import { Command as
|
|
9455
|
-
import * as
|
|
9901
|
+
import { Command as Command85 } from "commander";
|
|
9902
|
+
import * as path64 from "path";
|
|
9456
9903
|
async function runValidate(options) {
|
|
9457
9904
|
const configResult = resolveConfig(options.configPath);
|
|
9458
9905
|
if (!configResult.ok) {
|
|
9459
9906
|
return configResult;
|
|
9460
9907
|
}
|
|
9461
9908
|
const config = configResult.value;
|
|
9462
|
-
const cwd = options.cwd ?? (options.configPath ?
|
|
9909
|
+
const cwd = options.cwd ?? (options.configPath ? path64.dirname(path64.resolve(options.configPath)) : process.cwd());
|
|
9463
9910
|
const result = {
|
|
9464
9911
|
valid: true,
|
|
9465
9912
|
checks: {
|
|
@@ -9469,7 +9916,7 @@ async function runValidate(options) {
|
|
|
9469
9916
|
},
|
|
9470
9917
|
issues: []
|
|
9471
9918
|
};
|
|
9472
|
-
const agentsMapPath =
|
|
9919
|
+
const agentsMapPath = path64.resolve(cwd, config.agentsMapPath);
|
|
9473
9920
|
const agentsResult = await validateAgentsMap(agentsMapPath);
|
|
9474
9921
|
if (agentsResult.ok) {
|
|
9475
9922
|
result.checks.agentsMap = true;
|
|
@@ -9534,11 +9981,11 @@ function resolveValidateMode(globalOpts) {
|
|
|
9534
9981
|
return OutputMode.TEXT;
|
|
9535
9982
|
}
|
|
9536
9983
|
async function printCrossCheckWarnings(mode) {
|
|
9537
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
9984
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-LUQYIA46.js");
|
|
9538
9985
|
const cwd = process.cwd();
|
|
9539
9986
|
const crossResult = await runCrossCheck2({
|
|
9540
|
-
specsDir:
|
|
9541
|
-
plansDir:
|
|
9987
|
+
specsDir: path64.join(cwd, "docs", "specs"),
|
|
9988
|
+
plansDir: path64.join(cwd, "docs", "plans"),
|
|
9542
9989
|
projectPath: cwd
|
|
9543
9990
|
});
|
|
9544
9991
|
if (!crossResult.ok || crossResult.value.warnings === 0) return;
|
|
@@ -9550,7 +9997,7 @@ async function printCrossCheckWarnings(mode) {
|
|
|
9550
9997
|
${crossResult.value.warnings} warnings`);
|
|
9551
9998
|
}
|
|
9552
9999
|
function createValidateCommand3() {
|
|
9553
|
-
const command = new
|
|
10000
|
+
const command = new Command85("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
|
|
9554
10001
|
"--agent-configs",
|
|
9555
10002
|
"Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
|
|
9556
10003
|
).option("--strict", "Treat warnings as errors (applies to --agent-configs)").option("--agnix-bin <path>", "Override the agnix binary path discovered on PATH").action(async (opts, cmd) => runValidateAction(opts, cmd.optsWithGlobals()));
|
|
@@ -9600,8 +10047,8 @@ Agent configs checked via ${engineLabel}${note}`);
|
|
|
9600
10047
|
// src/commands/_registry.ts
|
|
9601
10048
|
var commandCreators = [
|
|
9602
10049
|
createAddCommand,
|
|
9603
|
-
createAdviseSkillsCommand,
|
|
9604
10050
|
createAdoptionCommand,
|
|
10051
|
+
createAdviseSkillsCommand,
|
|
9605
10052
|
createAgentCommand,
|
|
9606
10053
|
createAuditProtectedCommand,
|
|
9607
10054
|
createBlueprintCommand,
|
|
@@ -9633,6 +10080,7 @@ var commandCreators = [
|
|
|
9633
10080
|
createLearningsCommand,
|
|
9634
10081
|
createLinterCommand,
|
|
9635
10082
|
createMcpCommand,
|
|
10083
|
+
createMigrateCommand,
|
|
9636
10084
|
createOrchestratorCommand,
|
|
9637
10085
|
createPerfCommand,
|
|
9638
10086
|
createPersonaCommand,
|
|
@@ -9661,7 +10109,7 @@ var commandCreators = [
|
|
|
9661
10109
|
|
|
9662
10110
|
// src/index.ts
|
|
9663
10111
|
function createProgram() {
|
|
9664
|
-
const program = new
|
|
10112
|
+
const program = new Command86();
|
|
9665
10113
|
program.name("harness").description("CLI for Harness Engineering toolkit").version(CLI_VERSION).option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--verbose", "Verbose output").option("--quiet", "Minimal output");
|
|
9666
10114
|
for (const creator of commandCreators) {
|
|
9667
10115
|
program.addCommand(creator());
|