@harness-engineering/cli 2.7.0 → 2.8.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/personas/adversarial-reviewer.yaml +43 -0
- package/dist/agents/personas/frontend-races-reviewer.yaml +54 -0
- package/dist/agents/personas/typescript-strict-reviewer.yaml +53 -0
- package/dist/agents/skills/CHANGELOG.md +12 -0
- package/dist/agents/skills/claude-code/align-design-system/SKILL.md +18 -0
- package/dist/agents/skills/claude-code/align-design-system/skill.yaml +3 -0
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +37 -5
- package/dist/agents/skills/claude-code/harness-code-review/SKILL.md +107 -22
- package/dist/agents/skills/claude-code/harness-code-review/references/confidence-rubric.md +45 -0
- package/dist/agents/skills/claude-code/harness-code-review/references/risk-keywords.md +40 -0
- package/dist/agents/skills/claude-code/harness-code-review/skill.yaml +3 -0
- package/dist/agents/skills/claude-code/harness-compound/SKILL.md +7 -4
- package/dist/agents/skills/claude-code/harness-ideate/SKILL.md +277 -0
- package/dist/agents/skills/claude-code/harness-ideate/references/scoring.md +88 -0
- package/dist/agents/skills/claude-code/harness-ideate/skill.yaml +66 -0
- package/dist/agents/skills/claude-code/harness-planning/SKILL.md +14 -4
- package/dist/agents/skills/claude-code/harness-pulse/SKILL.md +12 -16
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +34 -6
- package/dist/agents/skills/claude-code/harness-strategy/SKILL.md +152 -0
- package/dist/agents/skills/claude-code/harness-strategy/references/interview.md +122 -0
- package/dist/agents/skills/claude-code/harness-strategy/skill.yaml +54 -0
- package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +76 -10
- package/dist/agents/skills/claude-code/naming-craft/SKILL.md +74 -2
- package/dist/agents/skills/codex/align-design-system/SKILL.md +18 -0
- package/dist/agents/skills/codex/align-design-system/skill.yaml +3 -0
- package/dist/agents/skills/codex/harness-brainstorming/SKILL.md +37 -5
- package/dist/agents/skills/codex/harness-code-review/SKILL.md +107 -22
- package/dist/agents/skills/codex/harness-code-review/references/confidence-rubric.md +45 -0
- package/dist/agents/skills/codex/harness-code-review/references/risk-keywords.md +40 -0
- package/dist/agents/skills/codex/harness-code-review/skill.yaml +3 -0
- package/dist/agents/skills/codex/harness-compound/SKILL.md +7 -4
- package/dist/agents/skills/codex/harness-ideate/SKILL.md +277 -0
- package/dist/agents/skills/codex/harness-ideate/references/scoring.md +88 -0
- package/dist/agents/skills/codex/harness-ideate/skill.yaml +66 -0
- package/dist/agents/skills/codex/harness-planning/SKILL.md +14 -4
- package/dist/agents/skills/codex/harness-pulse/SKILL.md +12 -16
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +34 -6
- package/dist/agents/skills/codex/harness-strategy/SKILL.md +152 -0
- package/dist/agents/skills/codex/harness-strategy/references/interview.md +122 -0
- package/dist/agents/skills/codex/harness-strategy/skill.yaml +54 -0
- package/dist/agents/skills/codex/initialize-harness-project/SKILL.md +76 -10
- package/dist/agents/skills/codex/naming-craft/SKILL.md +74 -2
- package/dist/agents/skills/cursor/align-design-system/SKILL.md +18 -0
- package/dist/agents/skills/cursor/align-design-system/skill.yaml +3 -0
- package/dist/agents/skills/cursor/harness-brainstorming/SKILL.md +37 -5
- package/dist/agents/skills/cursor/harness-code-review/SKILL.md +107 -22
- package/dist/agents/skills/cursor/harness-code-review/references/confidence-rubric.md +45 -0
- package/dist/agents/skills/cursor/harness-code-review/references/risk-keywords.md +40 -0
- package/dist/agents/skills/cursor/harness-code-review/skill.yaml +3 -0
- package/dist/agents/skills/cursor/harness-compound/SKILL.md +7 -4
- package/dist/agents/skills/cursor/harness-ideate/SKILL.md +277 -0
- package/dist/agents/skills/cursor/harness-ideate/references/scoring.md +88 -0
- package/dist/agents/skills/cursor/harness-ideate/skill.yaml +66 -0
- package/dist/agents/skills/cursor/harness-planning/SKILL.md +14 -4
- package/dist/agents/skills/cursor/harness-pulse/SKILL.md +12 -16
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +34 -6
- package/dist/agents/skills/cursor/harness-strategy/SKILL.md +152 -0
- package/dist/agents/skills/cursor/harness-strategy/references/interview.md +122 -0
- package/dist/agents/skills/cursor/harness-strategy/skill.yaml +54 -0
- package/dist/agents/skills/cursor/initialize-harness-project/SKILL.md +76 -10
- package/dist/agents/skills/cursor/naming-craft/SKILL.md +74 -2
- package/dist/agents/skills/gemini-cli/align-design-system/SKILL.md +18 -0
- package/dist/agents/skills/gemini-cli/align-design-system/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +37 -5
- package/dist/agents/skills/gemini-cli/harness-code-review/SKILL.md +107 -22
- package/dist/agents/skills/gemini-cli/harness-code-review/references/confidence-rubric.md +45 -0
- package/dist/agents/skills/gemini-cli/harness-code-review/references/risk-keywords.md +40 -0
- package/dist/agents/skills/gemini-cli/harness-code-review/skill.yaml +3 -0
- package/dist/agents/skills/gemini-cli/harness-compound/SKILL.md +7 -4
- package/dist/agents/skills/gemini-cli/harness-ideate/SKILL.md +277 -0
- package/dist/agents/skills/gemini-cli/harness-ideate/references/scoring.md +88 -0
- package/dist/agents/skills/gemini-cli/harness-ideate/skill.yaml +66 -0
- package/dist/agents/skills/gemini-cli/harness-planning/SKILL.md +14 -4
- package/dist/agents/skills/gemini-cli/harness-pulse/SKILL.md +12 -16
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +34 -6
- package/dist/agents/skills/gemini-cli/harness-strategy/SKILL.md +152 -0
- package/dist/agents/skills/gemini-cli/harness-strategy/references/interview.md +122 -0
- package/dist/agents/skills/gemini-cli/harness-strategy/skill.yaml +54 -0
- package/dist/agents/skills/gemini-cli/initialize-harness-project/SKILL.md +76 -10
- package/dist/agents/skills/gemini-cli/naming-craft/SKILL.md +74 -2
- package/dist/agents/skills/package.json +1 -1
- package/dist/agents/skills/tests/harness-strategy.test.ts +145 -0
- package/dist/agents-md-WMJO5HSM.js +13 -0
- package/dist/analyzer-V633GUHY-MBCQWKDU.js +26 -0
- package/dist/{architecture-ZTEHGIHD.js → architecture-EVYVGPRZ.js} +6 -6
- package/dist/{assess-project-OKDW3YSK.js → assess-project-KZG563SO.js} +1 -1
- package/dist/bin/harness-mcp.js +17 -17
- package/dist/bin/harness.js +27 -27
- package/dist/{check-phase-gate-YJWFDW66.js → check-phase-gate-MK364VXS.js} +6 -6
- package/dist/{chunk-6UHDQP2N.js → chunk-2N4OENGE.js} +1 -1
- package/dist/{chunk-OLTVDPA2.js → chunk-2SNRCAC3.js} +3 -3
- package/dist/{chunk-HLPY3RHE.js → chunk-4BDOSCNY.js} +909 -559
- package/dist/{chunk-S7GEONXL.js → chunk-5XL5QJ5B.js} +2 -2
- package/dist/{chunk-OQE6ME2Z.js → chunk-7EQAZKJ5.js} +2 -2
- package/dist/{chunk-QUPC37UB.js → chunk-ACOOG3UW.js} +6 -6
- package/dist/{chunk-B4OLYXZ4.js → chunk-B7KMTHN3.js} +89 -7
- package/dist/{chunk-OH3NXXUY.js → chunk-BDGTZRZ6.js} +11 -1
- package/dist/{chunk-P7EO7USC.js → chunk-C7J55C6E.js} +10 -10
- package/dist/{chunk-4EGPFB7V.js → chunk-CAQPSFPT.js} +4753 -744
- package/dist/{chunk-TIH53QXY.js → chunk-CLI4K2CH.js} +1 -1
- package/dist/{chunk-ZKA3TGYS.js → chunk-EEFRPEWT.js} +1 -1
- package/dist/{chunk-TIZ3L6CU.js → chunk-FZ5MBXEG.js} +6 -6
- package/dist/{chunk-IY2TZLY5.js → chunk-GOHWCHCS.js} +1809 -812
- package/dist/{chunk-VG6UK6G6.js → chunk-HRSE6IQD.js} +2 -2
- package/dist/{chunk-67RP7MLR.js → chunk-KBCPELBC.js} +3 -3
- package/dist/{chunk-MQG7FHXX.js → chunk-KI6PHH6Q.js} +3 -3
- package/dist/{chunk-HMMSWRWR.js → chunk-LADPYELQ.js} +23 -3
- package/dist/{chunk-W3PDVZ4I.js → chunk-N55WOGN3.js} +90 -21
- package/dist/{chunk-H2ZJOJ7A.js → chunk-PP6ZRL5T.js} +316 -42
- package/dist/{chunk-B3B64OPD.js → chunk-STESM73J.js} +2 -2
- package/dist/{chunk-WOXR6I3R.js → chunk-UBZQE3JN.js} +2 -2
- package/dist/{chunk-6T5AYG5S.js → chunk-UJFNIWC7.js} +5 -5
- package/dist/{chunk-CXAHK5PJ.js → chunk-UUO4K7NX.js} +9 -9
- package/dist/{chunk-Z2GUITAS.js → chunk-UXPWSV3B.js} +9 -9
- package/dist/{chunk-PARV3G2F.js → chunk-VBNJAKUH.js} +2 -2
- package/dist/{chunk-XTIPZUPR.js → chunk-X4EFYVCZ.js} +2 -2
- package/dist/{ci-workflow-YGTQTNVC.js → ci-workflow-IW2H5DC4.js} +5 -5
- package/dist/{dist-E3SAAHCM.js → dist-ICW4QUEW.js} +5 -1
- package/dist/{dist-XIJWWQ5N.js → dist-JCFK263L.js} +52 -4
- package/dist/{dist-R3TOOPJ7.js → dist-OWHMNL4W.js} +1 -1
- package/dist/{docs-KCTLXK2G.js → docs-7CP6VV42.js} +6 -6
- package/dist/engine-MKJEBWU7.js +13 -0
- package/dist/{entropy-M27JUL3V.js → entropy-6JOZJYG7.js} +5 -5
- package/dist/{feedback-W25QO3OL.js → feedback-T65AEJEG.js} +1 -1
- package/dist/{generate-agent-definitions-RNMU5U5R.js → generate-agent-definitions-4VI4YTO2.js} +6 -6
- package/dist/{glob-helper-EX4YZKZO.js → glob-helper-GLZAURJC.js} +1 -1
- package/dist/{graph-loader-EIFEOUVI.js → graph-loader-HHXN5FLP.js} +1 -1
- package/dist/index.d.ts +627 -6
- package/dist/index.js +27 -27
- package/dist/{loader-HZKJ5ABV.js → loader-AWTHHE3A.js} +5 -5
- package/dist/{mcp-NEAPIDTT.js → mcp-JBIBINO2.js} +17 -17
- package/dist/{performance-3AVQUDRG.js → performance-NCOUDOMW.js} +6 -6
- package/dist/{review-pipeline-KA7SGUXJ.js → review-pipeline-YIC6JPMY.js} +5 -5
- package/dist/{runtime-IP6FVV5M.js → runtime-5MHV4UEE.js} +5 -5
- package/dist/{scan-LYKLM4EQ.js → scan-P7YFKB77.js} +1 -1
- package/dist/{security-EAR4FJWI.js → security-VZPJNIDP.js} +1 -1
- package/dist/{validate-EVH3UXIC.js → validate-7BE4VTFI.js} +6 -6
- package/dist/validate-cross-check-F7GEBRDH.js +13 -0
- package/package.json +7 -7
- package/dist/agents-md-267R2NUJ.js +0 -13
- package/dist/analyzer-VY6DJVOU-WYKV3TTW.js +0 -26
- package/dist/engine-J64WPH5K.js +0 -13
- package/dist/validate-cross-check-SNE3SQOB.js +0 -13
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5XL5QJ5B.js";
|
|
4
4
|
import {
|
|
5
5
|
generateAgentsMd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7EQAZKJ5.js";
|
|
7
7
|
import {
|
|
8
8
|
generateCIWorkflow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-STESM73J.js";
|
|
10
10
|
import {
|
|
11
11
|
createGenerateAgentDefinitionsCommand,
|
|
12
12
|
generateAgentDefinitions
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-EEFRPEWT.js";
|
|
14
14
|
import {
|
|
15
15
|
toKebabCase
|
|
16
16
|
} from "./chunk-KET4QQZB.js";
|
|
17
17
|
import {
|
|
18
18
|
createScanCommand,
|
|
19
19
|
loadIngestOptions
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-2N4OENGE.js";
|
|
21
21
|
import {
|
|
22
22
|
TemplateEngine
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-UBZQE3JN.js";
|
|
24
24
|
import {
|
|
25
25
|
generate,
|
|
26
26
|
validate
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
import {
|
|
32
32
|
listPersonas,
|
|
33
33
|
loadPersona
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-X4EFYVCZ.js";
|
|
35
35
|
import {
|
|
36
36
|
runPersona
|
|
37
37
|
} from "./chunk-GISMXMVL.js";
|
|
@@ -46,19 +46,21 @@ import {
|
|
|
46
46
|
OutputMode,
|
|
47
47
|
createCheckPhaseGateCommand,
|
|
48
48
|
findFiles
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-2SNRCAC3.js";
|
|
50
50
|
import {
|
|
51
51
|
appendFrameworkAgents,
|
|
52
52
|
captureHealthSnapshot,
|
|
53
53
|
createAdviseSkillsCommand,
|
|
54
54
|
createGenerateSlashCommandsCommand,
|
|
55
55
|
ensureHarnessGitignore,
|
|
56
|
+
findOrchestratorMd,
|
|
56
57
|
generateSlashCommands,
|
|
57
58
|
handleGetImpact,
|
|
58
59
|
handleOrphanDeletion,
|
|
59
60
|
isSnapshotFresh,
|
|
60
61
|
loadCachedSnapshot,
|
|
61
62
|
persistToolingConfig,
|
|
63
|
+
readBackendsFromOrchestratorMd,
|
|
62
64
|
recommend,
|
|
63
65
|
runAlignDesignSystem,
|
|
64
66
|
runAudit,
|
|
@@ -72,13 +74,13 @@ import {
|
|
|
72
74
|
runSecurityCraft,
|
|
73
75
|
runSpecCraft,
|
|
74
76
|
runTestCraft
|
|
75
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-CAQPSFPT.js";
|
|
76
78
|
import {
|
|
77
79
|
VALID_PLATFORMS
|
|
78
80
|
} from "./chunk-3ISINLYT.js";
|
|
79
81
|
import {
|
|
80
82
|
loadGraphStore
|
|
81
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-CLI4K2CH.js";
|
|
82
84
|
import {
|
|
83
85
|
loadOrRebuildIndex
|
|
84
86
|
} from "./chunk-27AJKSQY.js";
|
|
@@ -97,7 +99,7 @@ import {
|
|
|
97
99
|
BranchingConfigSchema,
|
|
98
100
|
findConfigFile,
|
|
99
101
|
resolveConfig
|
|
100
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-B7KMTHN3.js";
|
|
101
103
|
import {
|
|
102
104
|
logger
|
|
103
105
|
} from "./chunk-EBJQ6N4M.js";
|
|
@@ -189,9 +191,10 @@ import {
|
|
|
189
191
|
validatePulseConfig,
|
|
190
192
|
validateRoadmapMode,
|
|
191
193
|
validateSolutionsDir,
|
|
194
|
+
validateStrategy,
|
|
192
195
|
writeConfig,
|
|
193
196
|
writeLockfile
|
|
194
|
-
} from "./chunk-
|
|
197
|
+
} from "./chunk-GOHWCHCS.js";
|
|
195
198
|
import {
|
|
196
199
|
CLI_VERSION
|
|
197
200
|
} from "./chunk-BM3PWGXQ.js";
|
|
@@ -204,20 +207,20 @@ import {
|
|
|
204
207
|
detectDocDrift,
|
|
205
208
|
generateSuggestions,
|
|
206
209
|
validateDependencies
|
|
207
|
-
} from "./chunk-
|
|
210
|
+
} from "./chunk-N55WOGN3.js";
|
|
208
211
|
import {
|
|
209
212
|
Err,
|
|
210
213
|
INDEXED_FILE_KINDS,
|
|
211
214
|
INSIGHTS_KEYS,
|
|
212
215
|
Ok,
|
|
213
216
|
init_dist
|
|
214
|
-
} from "./chunk-
|
|
217
|
+
} from "./chunk-BDGTZRZ6.js";
|
|
215
218
|
import {
|
|
216
219
|
DEFAULT_SKIP_DIRS,
|
|
217
220
|
DesignConstraintAdapter,
|
|
218
221
|
GraphStore,
|
|
219
222
|
init_dist as init_dist2
|
|
220
|
-
} from "./chunk-
|
|
223
|
+
} from "./chunk-PP6ZRL5T.js";
|
|
221
224
|
import {
|
|
222
225
|
ArchConfigSchema,
|
|
223
226
|
TimelineManager
|
|
@@ -228,7 +231,7 @@ import {
|
|
|
228
231
|
} from "./chunk-KFQGP6VL.js";
|
|
229
232
|
|
|
230
233
|
// src/index.ts
|
|
231
|
-
import { Command as
|
|
234
|
+
import { Command as Command120 } from "commander";
|
|
232
235
|
|
|
233
236
|
// src/commands/add.ts
|
|
234
237
|
init_dist3();
|
|
@@ -417,7 +420,7 @@ function registerSkillsCommand(adoption) {
|
|
|
417
420
|
const globalOpts = cmd.optsWithGlobals();
|
|
418
421
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
419
422
|
const cwd = process.cwd();
|
|
420
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
423
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-JCFK263L.js");
|
|
421
424
|
const records = readAdoptionRecords(cwd);
|
|
422
425
|
if (records.length === 0) {
|
|
423
426
|
if (globalOpts.json) {
|
|
@@ -453,7 +456,7 @@ function registerRecentCommand(adoption) {
|
|
|
453
456
|
const globalOpts = cmd.optsWithGlobals();
|
|
454
457
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
455
458
|
const cwd = process.cwd();
|
|
456
|
-
const { readAdoptionRecords } = await import("./dist-
|
|
459
|
+
const { readAdoptionRecords } = await import("./dist-JCFK263L.js");
|
|
457
460
|
const records = readAdoptionRecords(cwd);
|
|
458
461
|
if (records.length === 0) {
|
|
459
462
|
if (globalOpts.json) {
|
|
@@ -531,7 +534,7 @@ function registerSkillCommand(adoption) {
|
|
|
531
534
|
adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
|
|
532
535
|
const globalOpts = cmd.optsWithGlobals();
|
|
533
536
|
const cwd = process.cwd();
|
|
534
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
537
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-JCFK263L.js");
|
|
535
538
|
const records = readAdoptionRecords(cwd);
|
|
536
539
|
const skillRecords = records.filter((r) => r.skill === name);
|
|
537
540
|
if (skillRecords.length === 0) {
|
|
@@ -846,6 +849,9 @@ function createAlignDesignSystemCommand() {
|
|
|
846
849
|
).option(
|
|
847
850
|
"--design-strictness <level>",
|
|
848
851
|
"Override design.strictness: strict | standard | permissive"
|
|
852
|
+
).option(
|
|
853
|
+
"--revert",
|
|
854
|
+
"Inverse-apply the most-recent batch recorded at .harness/align/last-batch.json. Skips files edited externally since the apply."
|
|
849
855
|
).action(async (opts, cmd) => {
|
|
850
856
|
const globalOpts = cmd.optsWithGlobals();
|
|
851
857
|
const outputMode = resolveOutputMode(globalOpts);
|
|
@@ -856,6 +862,7 @@ function createAlignDesignSystemCommand() {
|
|
|
856
862
|
if (opts.files !== void 0) input.files = opts.files;
|
|
857
863
|
if (opts.mode !== void 0) input.mode = opts.mode;
|
|
858
864
|
if (opts.designStrictness !== void 0) input.designStrictness = opts.designStrictness;
|
|
865
|
+
if (opts.revert === true) input.revert = true;
|
|
859
866
|
let result;
|
|
860
867
|
try {
|
|
861
868
|
result = await runAlignDesignSystem(input);
|
|
@@ -882,7 +889,11 @@ function printAlignResult(result, mode, _formatter) {
|
|
|
882
889
|
const verbose = mode === OutputMode.VERBOSE;
|
|
883
890
|
const { summary, outcomes, meta } = result;
|
|
884
891
|
if (outcomes.length === 0) {
|
|
885
|
-
|
|
892
|
+
if (meta.revert === true) {
|
|
893
|
+
console.log("No batch to revert (.harness/align/last-batch.json missing or empty).");
|
|
894
|
+
} else {
|
|
895
|
+
console.log("No drift findings to align.");
|
|
896
|
+
}
|
|
886
897
|
return;
|
|
887
898
|
}
|
|
888
899
|
const byFile = /* @__PURE__ */ new Map();
|
|
@@ -912,8 +923,9 @@ ${file}`);
|
|
|
912
923
|
}
|
|
913
924
|
}
|
|
914
925
|
console.log("");
|
|
926
|
+
const action = meta.revert === true ? "reverted" : "applied";
|
|
915
927
|
console.log(
|
|
916
|
-
`Summary: ${summary.applied}
|
|
928
|
+
`Summary: ${summary.applied} ${action}, ${summary.suggestions} suggestions, ${summary.skipped} skipped, ${summary.failed} failed (${summary.filesModified} files modified, ${summary.durationMs}ms)`
|
|
917
929
|
);
|
|
918
930
|
if (meta.dryRun) {
|
|
919
931
|
console.log("(dry-run \u2014 no files written)");
|
|
@@ -1124,7 +1136,7 @@ function runBackfillSkillProvenance(projectRoot2) {
|
|
|
1124
1136
|
}
|
|
1125
1137
|
function createBackfillSkillProvenanceCommand() {
|
|
1126
1138
|
const cmd = new Command8("backfill-skill-provenance").description(
|
|
1127
|
-
"Stamp `provenance: user-authored` on every catalog skill missing the field (
|
|
1139
|
+
"Stamp `provenance: user-authored` on every catalog skill missing the field (one-shot migration)"
|
|
1128
1140
|
).option("--root <path>", "Project root containing agents/skills/", process.cwd()).action((opts) => {
|
|
1129
1141
|
const result = runBackfillSkillProvenance(opts.root);
|
|
1130
1142
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -1652,9 +1664,9 @@ ${name} (${count} finding${count === 1 ? "" : "s"})`);
|
|
|
1652
1664
|
}
|
|
1653
1665
|
function printAnatomyFindings(findings, verbose) {
|
|
1654
1666
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1655
|
-
for (const [file,
|
|
1667
|
+
for (const [file, fs48] of byFile) {
|
|
1656
1668
|
console.log(` ${file}`);
|
|
1657
|
-
for (const f of
|
|
1669
|
+
for (const f of fs48) {
|
|
1658
1670
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1659
1671
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1660
1672
|
if (verbose) {
|
|
@@ -1665,9 +1677,9 @@ function printAnatomyFindings(findings, verbose) {
|
|
|
1665
1677
|
}
|
|
1666
1678
|
function printCraftFindings(findings, verbose) {
|
|
1667
1679
|
const byFile = groupByFile(findings, (f) => f.target.file);
|
|
1668
|
-
for (const [file,
|
|
1680
|
+
for (const [file, fs48] of byFile) {
|
|
1669
1681
|
console.log(` ${file}`);
|
|
1670
|
-
for (const f of
|
|
1682
|
+
for (const f of fs48) {
|
|
1671
1683
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
1672
1684
|
const sev = craftTierToSeverity(f.tier);
|
|
1673
1685
|
console.log(
|
|
@@ -1681,9 +1693,9 @@ function printCraftFindings(findings, verbose) {
|
|
|
1681
1693
|
}
|
|
1682
1694
|
function printDriftFindings(findings, verbose) {
|
|
1683
1695
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1684
|
-
for (const [file,
|
|
1696
|
+
for (const [file, fs48] of byFile) {
|
|
1685
1697
|
console.log(` ${file}`);
|
|
1686
|
-
for (const f of
|
|
1698
|
+
for (const f of fs48) {
|
|
1687
1699
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1688
1700
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1689
1701
|
if (verbose) {
|
|
@@ -1694,9 +1706,9 @@ function printDriftFindings(findings, verbose) {
|
|
|
1694
1706
|
}
|
|
1695
1707
|
function printBrandFindings(findings, verbose) {
|
|
1696
1708
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1697
|
-
for (const [file,
|
|
1709
|
+
for (const [file, fs48] of byFile) {
|
|
1698
1710
|
console.log(` ${file}`);
|
|
1699
|
-
for (const f of
|
|
1711
|
+
for (const f of fs48) {
|
|
1700
1712
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1701
1713
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1702
1714
|
if (verbose) {
|
|
@@ -2015,10 +2027,10 @@ async function runCheckSecurity(cwd, options) {
|
|
|
2015
2027
|
const projectRoot2 = path9.resolve(cwd);
|
|
2016
2028
|
let configData = {};
|
|
2017
2029
|
try {
|
|
2018
|
-
const
|
|
2030
|
+
const fs48 = await import("fs");
|
|
2019
2031
|
const configPath = path9.join(projectRoot2, "harness.config.json");
|
|
2020
|
-
if (
|
|
2021
|
-
const raw =
|
|
2032
|
+
if (fs48.existsSync(configPath)) {
|
|
2033
|
+
const raw = fs48.readFileSync(configPath, "utf-8");
|
|
2022
2034
|
const parsed = JSON.parse(raw);
|
|
2023
2035
|
configData = parsed.security ?? {};
|
|
2024
2036
|
}
|
|
@@ -2786,10 +2798,10 @@ ${dryRun ? "Would remove" : "Removed"} ${totalRemoved} stale entry(ies) across t
|
|
|
2786
2798
|
function createCleanupSessionsCommand() {
|
|
2787
2799
|
const command = new Command21("cleanup-sessions").description(
|
|
2788
2800
|
"Remove stale entries from .harness/. Default: only .harness/sessions/ (no write in 24h)."
|
|
2789
|
-
).option("--dry-run", "List stale entries without deleting them", false).option("--path <path>", "Project root path", ".").option("--all", "
|
|
2801
|
+
).option("--dry-run", "List stale entries without deleting them", false).option("--path <path>", "Project root path", ".").option("--all", "Sweep every registered .harness/ target").option(
|
|
2790
2802
|
"--include <list>",
|
|
2791
|
-
"
|
|
2792
|
-
).option("--exclude <list>", "
|
|
2803
|
+
"Comma-separated target names (mutually-exclusive with --exclude/--all)"
|
|
2804
|
+
).option("--exclude <list>", "Comma-separated target names to skip").action(async (opts, cmd) => {
|
|
2793
2805
|
const globalOpts = cmd.optsWithGlobals();
|
|
2794
2806
|
const cwd = path12.resolve(opts.path);
|
|
2795
2807
|
const asJson = Boolean(globalOpts.json);
|
|
@@ -2979,10 +2991,10 @@ function printResult2(result, mode, _formatter) {
|
|
|
2979
2991
|
list.push(f);
|
|
2980
2992
|
bySurface.set(f.target.surface, list);
|
|
2981
2993
|
}
|
|
2982
|
-
for (const [surface,
|
|
2994
|
+
for (const [surface, fs48] of bySurface) {
|
|
2983
2995
|
console.log(`
|
|
2984
2996
|
[${surface}]`);
|
|
2985
|
-
for (const f of
|
|
2997
|
+
for (const f of fs48) {
|
|
2986
2998
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
2987
2999
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.file}${line}`);
|
|
2988
3000
|
console.log(` "${truncate(f.target.snippet, 80)}"`);
|
|
@@ -4210,11 +4222,11 @@ import { Command as Command35 } from "commander";
|
|
|
4210
4222
|
// src/commands/graph/status.ts
|
|
4211
4223
|
import * as path16 from "path";
|
|
4212
4224
|
async function readConnectorSyncStatus(graphDir) {
|
|
4213
|
-
const
|
|
4225
|
+
const fs48 = await import("fs/promises");
|
|
4214
4226
|
const result = {};
|
|
4215
4227
|
try {
|
|
4216
4228
|
const syncMetaPath = path16.join(graphDir, "sync-metadata.json");
|
|
4217
|
-
const syncMeta = JSON.parse(await
|
|
4229
|
+
const syncMeta = JSON.parse(await fs48.readFile(syncMetaPath, "utf-8"));
|
|
4218
4230
|
for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
|
|
4219
4231
|
result[name] = data.lastSyncTimestamp;
|
|
4220
4232
|
}
|
|
@@ -4223,7 +4235,7 @@ async function readConnectorSyncStatus(graphDir) {
|
|
|
4223
4235
|
return result;
|
|
4224
4236
|
}
|
|
4225
4237
|
async function runGraphStatus(projectPath) {
|
|
4226
|
-
const { GraphStore: GraphStore2, loadGraphMetadata } = await import("./dist-
|
|
4238
|
+
const { GraphStore: GraphStore2, loadGraphMetadata } = await import("./dist-OWHMNL4W.js");
|
|
4227
4239
|
const graphDir = path16.join(projectPath, ".harness", "graph");
|
|
4228
4240
|
const metaResult = await loadGraphMetadata(graphDir);
|
|
4229
4241
|
if (metaResult.status === "not_found") {
|
|
@@ -4275,7 +4287,7 @@ async function runGraphStatus(projectPath) {
|
|
|
4275
4287
|
// src/commands/graph/export.ts
|
|
4276
4288
|
import * as path17 from "path";
|
|
4277
4289
|
async function runGraphExport(projectPath, format) {
|
|
4278
|
-
const { GraphStore: GraphStore2 } = await import("./dist-
|
|
4290
|
+
const { GraphStore: GraphStore2 } = await import("./dist-OWHMNL4W.js");
|
|
4279
4291
|
const graphDir = path17.join(projectPath, ".harness", "graph");
|
|
4280
4292
|
const store = new GraphStore2();
|
|
4281
4293
|
const loaded = await store.load(graphDir);
|
|
@@ -4311,7 +4323,7 @@ import * as path20 from "path";
|
|
|
4311
4323
|
import { Command as Command33 } from "commander";
|
|
4312
4324
|
import * as path18 from "path";
|
|
4313
4325
|
async function runQuery(projectPath, rootNodeId, opts) {
|
|
4314
|
-
const { GraphStore: GraphStore2, ContextQL } = await import("./dist-
|
|
4326
|
+
const { GraphStore: GraphStore2, ContextQL } = await import("./dist-OWHMNL4W.js");
|
|
4315
4327
|
const store = new GraphStore2();
|
|
4316
4328
|
const graphDir = path18.join(projectPath, ".harness", "graph");
|
|
4317
4329
|
const loaded = await store.load(graphDir);
|
|
@@ -4364,15 +4376,22 @@ import { Command as Command34 } from "commander";
|
|
|
4364
4376
|
import * as path19 from "path";
|
|
4365
4377
|
async function loadConnectorConfig(projectPath, source) {
|
|
4366
4378
|
try {
|
|
4367
|
-
const
|
|
4379
|
+
const fs48 = await import("fs/promises");
|
|
4368
4380
|
const configPath = path19.join(projectPath, "harness.config.json");
|
|
4369
|
-
const config = JSON.parse(await
|
|
4381
|
+
const config = JSON.parse(await fs48.readFile(configPath, "utf-8"));
|
|
4370
4382
|
const connector = config.graph?.connectors?.[source];
|
|
4371
4383
|
return connector ?? {};
|
|
4372
4384
|
} catch {
|
|
4373
4385
|
return {};
|
|
4374
4386
|
}
|
|
4375
4387
|
}
|
|
4388
|
+
async function ingestBusinessKnowledge(bk, projectPath) {
|
|
4389
|
+
return mergeResults(
|
|
4390
|
+
await bk.ingest(path19.join(projectPath, "docs", "knowledge")),
|
|
4391
|
+
await bk.ingestSolutions(path19.join(projectPath, "docs", "solutions")),
|
|
4392
|
+
await bk.ingestStrategy(path19.join(projectPath, "STRATEGY.md"))
|
|
4393
|
+
);
|
|
4394
|
+
}
|
|
4376
4395
|
function mergeResults(...results) {
|
|
4377
4396
|
return results.reduce(
|
|
4378
4397
|
(acc, r) => ({
|
|
@@ -4399,6 +4418,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4399
4418
|
CodeIngestor,
|
|
4400
4419
|
TopologicalLinker,
|
|
4401
4420
|
KnowledgeIngestor,
|
|
4421
|
+
BusinessKnowledgeIngestor,
|
|
4402
4422
|
GitIngestor,
|
|
4403
4423
|
RequirementIngestor,
|
|
4404
4424
|
SyncManager,
|
|
@@ -4408,7 +4428,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4408
4428
|
ConfluenceConnector,
|
|
4409
4429
|
FigmaConnector,
|
|
4410
4430
|
MiroConnector
|
|
4411
|
-
} = await import("./dist-
|
|
4431
|
+
} = await import("./dist-OWHMNL4W.js");
|
|
4412
4432
|
const graphDir = path19.join(projectPath, ".harness", "graph");
|
|
4413
4433
|
const store = new GraphStore2();
|
|
4414
4434
|
await store.load(graphDir);
|
|
@@ -4418,11 +4438,13 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4418
4438
|
const codeResult = await new CodeIngestor(store, ingestOptions).ingest(projectPath);
|
|
4419
4439
|
new TopologicalLinker(store).link();
|
|
4420
4440
|
const knowledgeResult = await new KnowledgeIngestor(store).ingestAll(projectPath);
|
|
4441
|
+
const bkInst = new BusinessKnowledgeIngestor(store);
|
|
4442
|
+
const bkResult = await ingestBusinessKnowledge(bkInst, projectPath);
|
|
4421
4443
|
const reqResult = await new RequirementIngestor(store).ingestSpecs(
|
|
4422
4444
|
path19.join(projectPath, "docs", "changes")
|
|
4423
4445
|
);
|
|
4424
4446
|
const gitResult = await new GitIngestor(store).ingest(projectPath);
|
|
4425
|
-
const { createExtractionRunner } = await import("./dist-
|
|
4447
|
+
const { createExtractionRunner } = await import("./dist-OWHMNL4W.js");
|
|
4426
4448
|
const extractedDir = path19.join(projectPath, ".harness", "knowledge", "extracted");
|
|
4427
4449
|
const signalsResult = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
4428
4450
|
const syncManager = new SyncManager(store, graphDir);
|
|
@@ -4443,6 +4465,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4443
4465
|
const merged = mergeResults(
|
|
4444
4466
|
codeResult,
|
|
4445
4467
|
knowledgeResult,
|
|
4468
|
+
bkResult,
|
|
4446
4469
|
reqResult,
|
|
4447
4470
|
gitResult,
|
|
4448
4471
|
signalsResult,
|
|
@@ -4456,9 +4479,12 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4456
4479
|
result = await new CodeIngestor(store, ingestOptions).ingest(projectPath);
|
|
4457
4480
|
new TopologicalLinker(store).link();
|
|
4458
4481
|
break;
|
|
4459
|
-
case "knowledge":
|
|
4460
|
-
|
|
4482
|
+
case "knowledge": {
|
|
4483
|
+
const knowledge = await new KnowledgeIngestor(store).ingestAll(projectPath);
|
|
4484
|
+
const bk = await ingestBusinessKnowledge(new BusinessKnowledgeIngestor(store), projectPath);
|
|
4485
|
+
result = mergeResults(knowledge, bk);
|
|
4461
4486
|
break;
|
|
4487
|
+
}
|
|
4462
4488
|
case "git":
|
|
4463
4489
|
result = await new GitIngestor(store).ingest(projectPath);
|
|
4464
4490
|
break;
|
|
@@ -4468,7 +4494,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4468
4494
|
);
|
|
4469
4495
|
break;
|
|
4470
4496
|
case "business-signals": {
|
|
4471
|
-
const { createExtractionRunner } = await import("./dist-
|
|
4497
|
+
const { createExtractionRunner } = await import("./dist-OWHMNL4W.js");
|
|
4472
4498
|
const extractedDir = path19.join(projectPath, ".harness", "knowledge", "extracted");
|
|
4473
4499
|
result = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
4474
4500
|
break;
|
|
@@ -4504,35 +4530,41 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4504
4530
|
await store.save(graphDir);
|
|
4505
4531
|
return result;
|
|
4506
4532
|
}
|
|
4533
|
+
function printIngestSummary(result, label) {
|
|
4534
|
+
console.log(
|
|
4535
|
+
`Ingested (${label}): +${result.nodesAdded} nodes, +${result.edgesAdded} edges (${result.durationMs}ms)`
|
|
4536
|
+
);
|
|
4537
|
+
if (result.errors.length === 0) return;
|
|
4538
|
+
console.warn(` ${result.errors.length} parse/skip warning(s):`);
|
|
4539
|
+
for (const err of result.errors) console.warn(` - ${err}`);
|
|
4540
|
+
}
|
|
4541
|
+
async function handleIngestAction(opts, cmd) {
|
|
4542
|
+
if (!opts.source && !opts.all) {
|
|
4543
|
+
console.error("Error: --source or --all is required");
|
|
4544
|
+
process.exit(1);
|
|
4545
|
+
}
|
|
4546
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
4547
|
+
const projectPath = path19.resolve(globalOpts.config ? path19.dirname(globalOpts.config) : ".");
|
|
4548
|
+
try {
|
|
4549
|
+
const runOpts = {};
|
|
4550
|
+
if (opts.full !== void 0) runOpts.full = opts.full;
|
|
4551
|
+
if (opts.all !== void 0) runOpts.all = opts.all;
|
|
4552
|
+
const result = await runIngest(projectPath, opts.source ?? "", runOpts);
|
|
4553
|
+
if (globalOpts.json) {
|
|
4554
|
+
console.log(JSON.stringify(result));
|
|
4555
|
+
} else {
|
|
4556
|
+
printIngestSummary(result, opts.all ? "all" : opts.source ?? "");
|
|
4557
|
+
}
|
|
4558
|
+
} catch (err) {
|
|
4559
|
+
console.error("Ingest failed:", err instanceof Error ? err.message : err);
|
|
4560
|
+
process.exit(2);
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
4507
4563
|
function createIngestCommand() {
|
|
4508
4564
|
return new Command34("ingest").description("Ingest data into the knowledge graph").option(
|
|
4509
4565
|
"--source <name>",
|
|
4510
4566
|
"Source to ingest (code, knowledge, git, requirements, business-signals, jira, slack, ci, confluence, figma, miro)"
|
|
4511
|
-
).option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(
|
|
4512
|
-
if (!opts.source && !opts.all) {
|
|
4513
|
-
console.error("Error: --source or --all is required");
|
|
4514
|
-
process.exit(1);
|
|
4515
|
-
}
|
|
4516
|
-
const globalOpts = cmd.optsWithGlobals();
|
|
4517
|
-
const projectPath = path19.resolve(globalOpts.config ? path19.dirname(globalOpts.config) : ".");
|
|
4518
|
-
try {
|
|
4519
|
-
const result = await runIngest(projectPath, opts.source ?? "", {
|
|
4520
|
-
full: opts.full,
|
|
4521
|
-
all: opts.all
|
|
4522
|
-
});
|
|
4523
|
-
if (globalOpts.json) {
|
|
4524
|
-
console.log(JSON.stringify(result));
|
|
4525
|
-
} else {
|
|
4526
|
-
const label = opts.all ? "all" : opts.source;
|
|
4527
|
-
console.log(
|
|
4528
|
-
`Ingested (${label}): +${result.nodesAdded} nodes, +${result.edgesAdded} edges (${result.durationMs}ms)`
|
|
4529
|
-
);
|
|
4530
|
-
}
|
|
4531
|
-
} catch (err) {
|
|
4532
|
-
console.error("Ingest failed:", err instanceof Error ? err.message : err);
|
|
4533
|
-
process.exit(2);
|
|
4534
|
-
}
|
|
4535
|
-
});
|
|
4567
|
+
).option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(handleIngestAction);
|
|
4536
4568
|
}
|
|
4537
4569
|
|
|
4538
4570
|
// src/commands/graph/index.ts
|
|
@@ -5356,6 +5388,7 @@ var ALL_MCP_TOOLS = [
|
|
|
5356
5388
|
"run_design_pipeline",
|
|
5357
5389
|
// craft-pipeline #1 — naming-craft LLM-judgment ceiling skill
|
|
5358
5390
|
"naming_craft",
|
|
5391
|
+
"naming_craft_finalize",
|
|
5359
5392
|
// craft-pipeline #6 — spec-craft LLM-judgment ceiling skill
|
|
5360
5393
|
"spec_craft",
|
|
5361
5394
|
// craft-pipeline #5 — copy-craft LLM-judgment ceiling skill (6 surfaces)
|
|
@@ -5365,7 +5398,19 @@ var ALL_MCP_TOOLS = [
|
|
|
5365
5398
|
// craft-pipeline #9 — knowledge-craft LLM-judgment ceiling skill (docs/knowledge/ entries)
|
|
5366
5399
|
"knowledge_craft",
|
|
5367
5400
|
// craft-pipeline #10 — security-craft LLM-judgment ceiling skill (AST-driven, conservative-confidence)
|
|
5368
|
-
"security_craft"
|
|
5401
|
+
"security_craft",
|
|
5402
|
+
// strategic-anchor — STRATEGY.md read/validate/write so projects can call
|
|
5403
|
+
// these via MCP without needing @harness-engineering/core in node_modules
|
|
5404
|
+
"validate_strategy",
|
|
5405
|
+
"read_strategy",
|
|
5406
|
+
"write_strategy",
|
|
5407
|
+
// strategic-anchor — pulse config writer + STRATEGY.md seed extractor
|
|
5408
|
+
"write_pulse_config",
|
|
5409
|
+
"seed_pulse_from_strategy",
|
|
5410
|
+
// strategic-anchor — compound lock acquire/release (server holds the handle;
|
|
5411
|
+
// skill threads a token between calls)
|
|
5412
|
+
"acquire_compound_lock",
|
|
5413
|
+
"release_compound_lock"
|
|
5369
5414
|
];
|
|
5370
5415
|
async function runCursorToolPicker() {
|
|
5371
5416
|
try {
|
|
@@ -5738,12 +5783,10 @@ function renderPretty(report) {
|
|
|
5738
5783
|
}
|
|
5739
5784
|
}
|
|
5740
5785
|
function createInsightsCommand() {
|
|
5741
|
-
return new Command44("insights").description(
|
|
5742
|
-
"Composite project report \u2014 health, entropy, decay, attention, impact (Hermes Phase 1)."
|
|
5743
|
-
).option("--json", "Emit JSON to stdout instead of pretty text").option("--skip <list>", `Comma-separated keys to skip (${INSIGHTS_KEYS.join(",")})`).action(async (opts) => {
|
|
5786
|
+
return new Command44("insights").description("Composite project report \u2014 health, entropy, decay, attention, impact.").option("--json", "Emit JSON to stdout instead of pretty text").option("--skip <list>", `Comma-separated keys to skip (${INSIGHTS_KEYS.join(",")})`).action(async (opts) => {
|
|
5744
5787
|
const cwd = process.cwd();
|
|
5745
5788
|
const skip = parseSkip2(opts.skip);
|
|
5746
|
-
const { composeInsights } = await import("./dist-
|
|
5789
|
+
const { composeInsights } = await import("./dist-JCFK263L.js");
|
|
5747
5790
|
const report = await composeInsights(cwd, { skip });
|
|
5748
5791
|
if (opts.json) {
|
|
5749
5792
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -6915,10 +6958,10 @@ function printResult3(result, mode, _formatter) {
|
|
|
6915
6958
|
list.push(f);
|
|
6916
6959
|
byFile.set(f.target.file, list);
|
|
6917
6960
|
}
|
|
6918
|
-
for (const [file,
|
|
6961
|
+
for (const [file, fs48] of byFile) {
|
|
6919
6962
|
console.log(`
|
|
6920
6963
|
${file}`);
|
|
6921
|
-
for (const f of
|
|
6964
|
+
for (const f of fs48) {
|
|
6922
6965
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.relative}`);
|
|
6923
6966
|
console.log(` ${f.message}`);
|
|
6924
6967
|
if (verbose) console.log(` source: ${f.cite.source}`);
|
|
@@ -6941,9 +6984,9 @@ function createKnowledgePipelineCommand() {
|
|
|
6941
6984
|
const projectDir = process.cwd();
|
|
6942
6985
|
try {
|
|
6943
6986
|
const graphDir = path39.join(projectDir, ".harness", "graph");
|
|
6944
|
-
const
|
|
6945
|
-
await
|
|
6946
|
-
const { GraphStore: GraphStore2, KnowledgePipelineRunner } = await import("./dist-
|
|
6987
|
+
const fs48 = await import("fs/promises");
|
|
6988
|
+
await fs48.mkdir(graphDir, { recursive: true });
|
|
6989
|
+
const { GraphStore: GraphStore2, KnowledgePipelineRunner } = await import("./dist-OWHMNL4W.js");
|
|
6947
6990
|
const store = new GraphStore2();
|
|
6948
6991
|
try {
|
|
6949
6992
|
await store.load(graphDir);
|
|
@@ -6999,6 +7042,7 @@ function createKnowledgePipelineCommand() {
|
|
|
6999
7042
|
iterations: result.iterations,
|
|
7000
7043
|
findings: result.findings,
|
|
7001
7044
|
extraction: result.extraction,
|
|
7045
|
+
errors: result.errors,
|
|
7002
7046
|
gaps: {
|
|
7003
7047
|
domains: result.gaps.domains.length,
|
|
7004
7048
|
totalEntries: result.gaps.totalEntries,
|
|
@@ -7039,7 +7083,7 @@ function createKnowledgePipelineCommand() {
|
|
|
7039
7083
|
` Findings: ${result.findings.new} new, ${result.findings.stale} stale, ${result.findings.drifted} drifted, ${result.findings.contradicting} contradicting`
|
|
7040
7084
|
);
|
|
7041
7085
|
console.log(
|
|
7042
|
-
` Extraction: ${result.extraction.codeSignals} code signals, ${result.extraction.diagrams} diagrams, ${result.extraction.linkerFacts} linker facts, ${result.extraction.businessKnowledge} business knowledge, ${result.extraction.images} images`
|
|
7086
|
+
` Extraction: ${result.extraction.codeSignals} code signals, ${result.extraction.diagrams} diagrams, ${result.extraction.linkerFacts} linker facts, ${result.extraction.businessKnowledge} business knowledge, ${result.extraction.decisions} decisions, ${result.extraction.images} images`
|
|
7043
7087
|
);
|
|
7044
7088
|
console.log(
|
|
7045
7089
|
` Gaps: ${result.gaps.domains.length} domains \u2014 ${result.gaps.totalEntries} documented / ${result.gaps.totalExtracted} extracted / ${result.gaps.totalGaps} undocumented`
|
|
@@ -7050,6 +7094,13 @@ function createKnowledgePipelineCommand() {
|
|
|
7050
7094
|
if (result.remediations.length > 0) {
|
|
7051
7095
|
console.log(` Remediations: ${result.remediations.length} applied`);
|
|
7052
7096
|
}
|
|
7097
|
+
if (result.errors.length > 0) {
|
|
7098
|
+
console.warn("");
|
|
7099
|
+
console.warn(` ${result.errors.length} ingestion warning(s):`);
|
|
7100
|
+
for (const err of result.errors) {
|
|
7101
|
+
console.warn(` - ${err}`);
|
|
7102
|
+
}
|
|
7103
|
+
}
|
|
7053
7104
|
if (result.materialization) {
|
|
7054
7105
|
const mat = result.materialization;
|
|
7055
7106
|
console.log(
|
|
@@ -7351,7 +7402,7 @@ function printListTable(rows) {
|
|
|
7351
7402
|
}
|
|
7352
7403
|
function createMaintenanceCommand() {
|
|
7353
7404
|
const command = new Command59("maintenance").description(
|
|
7354
|
-
"
|
|
7405
|
+
"Inspect built-in + custom maintenance tasks and their persisted outputs"
|
|
7355
7406
|
);
|
|
7356
7407
|
command.command("list").description("List all resolved maintenance tasks (built-in + customTasks)").option("--json", "Emit machine-readable JSON", false).option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
7357
7408
|
const cwd = path41.resolve(opts.path);
|
|
@@ -7445,7 +7496,7 @@ function createMcpCommand() {
|
|
|
7445
7496
|
parseBudget
|
|
7446
7497
|
).action(async (opts) => {
|
|
7447
7498
|
const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
|
|
7448
|
-
import("./mcp-
|
|
7499
|
+
import("./mcp-JBIBINO2.js"),
|
|
7449
7500
|
import("./tool-tiers-B7JC2XC3.js")
|
|
7450
7501
|
]);
|
|
7451
7502
|
if (opts.tools && opts.tools.length > 0) {
|
|
@@ -7606,14 +7657,150 @@ function createMcpGuardCommand() {
|
|
|
7606
7657
|
}
|
|
7607
7658
|
|
|
7608
7659
|
// src/commands/migrate.ts
|
|
7609
|
-
|
|
7610
|
-
|
|
7660
|
+
import { Command as Command63 } from "commander";
|
|
7661
|
+
|
|
7662
|
+
// src/commands/migrate-backends.ts
|
|
7611
7663
|
import { Command as Command62 } from "commander";
|
|
7612
7664
|
import * as fs27 from "fs";
|
|
7613
7665
|
import * as path43 from "path";
|
|
7666
|
+
import chalk9 from "chalk";
|
|
7667
|
+
import { parse as parseYaml2 } from "yaml";
|
|
7668
|
+
async function runMigrateBackends(opts = {}) {
|
|
7669
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
7670
|
+
const mdPath = findOrchestratorMd(cwd);
|
|
7671
|
+
if (mdPath === null) {
|
|
7672
|
+
return {
|
|
7673
|
+
status: "noop",
|
|
7674
|
+
message: "No harness.orchestrator.md found \u2014 nothing to migrate.",
|
|
7675
|
+
exitCode: ExitCode.SUCCESS
|
|
7676
|
+
};
|
|
7677
|
+
}
|
|
7678
|
+
const backends = readBackendsFromOrchestratorMd(cwd);
|
|
7679
|
+
if (backends === null) {
|
|
7680
|
+
return {
|
|
7681
|
+
status: "noop",
|
|
7682
|
+
message: `Found ${mdPath} but no agent.backends to migrate.`,
|
|
7683
|
+
exitCode: ExitCode.SUCCESS
|
|
7684
|
+
};
|
|
7685
|
+
}
|
|
7686
|
+
const routing = readRoutingFromOrchestratorMd(mdPath);
|
|
7687
|
+
const jsonPath = path43.join(path43.dirname(mdPath), "harness.config.json");
|
|
7688
|
+
if (!fs27.existsSync(jsonPath)) {
|
|
7689
|
+
return {
|
|
7690
|
+
status: "error",
|
|
7691
|
+
message: `harness.config.json not found at ${jsonPath}. Run \`harness init\` first, then re-run this migration.`,
|
|
7692
|
+
exitCode: ExitCode.ERROR
|
|
7693
|
+
};
|
|
7694
|
+
}
|
|
7695
|
+
let configRaw;
|
|
7696
|
+
try {
|
|
7697
|
+
configRaw = fs27.readFileSync(jsonPath, "utf-8");
|
|
7698
|
+
} catch (err) {
|
|
7699
|
+
return {
|
|
7700
|
+
status: "error",
|
|
7701
|
+
message: `Failed to read ${jsonPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
7702
|
+
exitCode: ExitCode.ERROR
|
|
7703
|
+
};
|
|
7704
|
+
}
|
|
7705
|
+
let configObj;
|
|
7706
|
+
try {
|
|
7707
|
+
configObj = JSON.parse(configRaw);
|
|
7708
|
+
} catch (err) {
|
|
7709
|
+
return {
|
|
7710
|
+
status: "error",
|
|
7711
|
+
message: `${jsonPath} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
7712
|
+
exitCode: ExitCode.ERROR
|
|
7713
|
+
};
|
|
7714
|
+
}
|
|
7715
|
+
const agent = configObj.agent ?? {};
|
|
7716
|
+
const existingBackends = agent.backends;
|
|
7717
|
+
if (existingBackends !== void 0 && !opts.force) {
|
|
7718
|
+
return {
|
|
7719
|
+
status: "error",
|
|
7720
|
+
message: `${jsonPath} already declares agent.backends. Re-run with --force to overwrite, or merge manually.`,
|
|
7721
|
+
exitCode: ExitCode.ERROR
|
|
7722
|
+
};
|
|
7723
|
+
}
|
|
7724
|
+
const nextAgent = { ...agent, backends };
|
|
7725
|
+
if (routing !== null) nextAgent.routing = routing;
|
|
7726
|
+
const nextConfig = { ...configObj, agent: nextAgent };
|
|
7727
|
+
const nextJson = JSON.stringify(nextConfig, null, 2) + "\n";
|
|
7728
|
+
if (opts.dryRun) {
|
|
7729
|
+
logger.info(chalk9.bold("--- dry run ---"));
|
|
7730
|
+
logger.info(`Would write ${Object.keys(backends).length} backend(s) to ${jsonPath}:`);
|
|
7731
|
+
for (const name of Object.keys(backends)) {
|
|
7732
|
+
logger.info(` \u2022 ${name}`);
|
|
7733
|
+
}
|
|
7734
|
+
if (routing !== null) {
|
|
7735
|
+
logger.info("Would also copy agent.routing.");
|
|
7736
|
+
}
|
|
7737
|
+
return {
|
|
7738
|
+
status: "ok",
|
|
7739
|
+
message: "(dry-run) migration plan above \u2014 re-run without --dry-run to apply.",
|
|
7740
|
+
exitCode: ExitCode.SUCCESS
|
|
7741
|
+
};
|
|
7742
|
+
}
|
|
7743
|
+
try {
|
|
7744
|
+
fs27.writeFileSync(jsonPath, nextJson);
|
|
7745
|
+
} catch (err) {
|
|
7746
|
+
return {
|
|
7747
|
+
status: "error",
|
|
7748
|
+
message: `Failed to write ${jsonPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
7749
|
+
exitCode: ExitCode.ERROR
|
|
7750
|
+
};
|
|
7751
|
+
}
|
|
7752
|
+
return {
|
|
7753
|
+
status: "ok",
|
|
7754
|
+
message: `Migrated ${Object.keys(backends).length} backend(s)${routing !== null ? " + routing" : ""} from ${mdPath} to ${jsonPath}. You can leave the entries in harness.orchestrator.md (orchestrator still reads them) or remove them once the orchestrator config picks up the JSON source.`,
|
|
7755
|
+
exitCode: ExitCode.SUCCESS
|
|
7756
|
+
};
|
|
7757
|
+
}
|
|
7758
|
+
function readRoutingFromOrchestratorMd(mdPath) {
|
|
7759
|
+
let raw;
|
|
7760
|
+
try {
|
|
7761
|
+
raw = fs27.readFileSync(mdPath, "utf-8");
|
|
7762
|
+
} catch {
|
|
7763
|
+
return null;
|
|
7764
|
+
}
|
|
7765
|
+
const parts = raw.split("---");
|
|
7766
|
+
if (parts.length < 3) return null;
|
|
7767
|
+
let parsed;
|
|
7768
|
+
try {
|
|
7769
|
+
parsed = parseYaml2(parts[1].trim());
|
|
7770
|
+
} catch {
|
|
7771
|
+
return null;
|
|
7772
|
+
}
|
|
7773
|
+
if (parsed === null || typeof parsed !== "object") return null;
|
|
7774
|
+
const agent = parsed.agent;
|
|
7775
|
+
if (agent === null || typeof agent !== "object") return null;
|
|
7776
|
+
const routing = agent.routing;
|
|
7777
|
+
return routing ?? null;
|
|
7778
|
+
}
|
|
7779
|
+
function createBackendsSubcommand() {
|
|
7780
|
+
return new Command62("backends").description(
|
|
7781
|
+
"Copy agent.backends (and routing) from harness.orchestrator.md into harness.config.json."
|
|
7782
|
+
).option("--dry-run", "Show what would change without writing", false).option("--force", "Overwrite existing agent.backends in harness.config.json", false).action(async (options) => {
|
|
7783
|
+
const result = await runMigrateBackends({
|
|
7784
|
+
dryRun: options.dryRun ?? false,
|
|
7785
|
+
force: options.force ?? false
|
|
7786
|
+
});
|
|
7787
|
+
if (result.status === "error") {
|
|
7788
|
+
logger.error(result.message);
|
|
7789
|
+
process.exit(result.exitCode);
|
|
7790
|
+
}
|
|
7791
|
+
logger.info(result.message);
|
|
7792
|
+
process.exit(result.exitCode);
|
|
7793
|
+
});
|
|
7794
|
+
}
|
|
7795
|
+
|
|
7796
|
+
// src/commands/migrate.ts
|
|
7797
|
+
init_dist3();
|
|
7798
|
+
init_dist2();
|
|
7799
|
+
import * as fs28 from "fs";
|
|
7800
|
+
import * as path44 from "path";
|
|
7614
7801
|
import * as readline from "readline";
|
|
7615
7802
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
7616
|
-
import
|
|
7803
|
+
import chalk10 from "chalk";
|
|
7617
7804
|
function isGitRepo(cwd) {
|
|
7618
7805
|
try {
|
|
7619
7806
|
execFileSync4("git", ["rev-parse", "--is-inside-work-tree"], { cwd, stdio: "pipe" });
|
|
@@ -7624,25 +7811,25 @@ function isGitRepo(cwd) {
|
|
|
7624
7811
|
}
|
|
7625
7812
|
function readJson2(p) {
|
|
7626
7813
|
try {
|
|
7627
|
-
return JSON.parse(
|
|
7814
|
+
return JSON.parse(fs28.readFileSync(p, "utf8"));
|
|
7628
7815
|
} catch {
|
|
7629
7816
|
return null;
|
|
7630
7817
|
}
|
|
7631
7818
|
}
|
|
7632
7819
|
function listDirs(p) {
|
|
7633
|
-
if (!
|
|
7634
|
-
return
|
|
7820
|
+
if (!fs28.existsSync(p)) return [];
|
|
7821
|
+
return fs28.readdirSync(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
7635
7822
|
}
|
|
7636
7823
|
function listFiles(p, predicate) {
|
|
7637
|
-
if (!
|
|
7638
|
-
return
|
|
7824
|
+
if (!fs28.existsSync(p)) return [];
|
|
7825
|
+
return fs28.readdirSync(p, { withFileTypes: true }).filter((e) => e.isFile() && predicate(e.name)).map((e) => e.name);
|
|
7639
7826
|
}
|
|
7640
7827
|
function walk(dir, predicate) {
|
|
7641
|
-
if (!
|
|
7828
|
+
if (!fs28.existsSync(dir)) return [];
|
|
7642
7829
|
const out = [];
|
|
7643
|
-
for (const entry of
|
|
7830
|
+
for (const entry of fs28.readdirSync(dir, { withFileTypes: true })) {
|
|
7644
7831
|
if (DEFAULT_SKIP_DIRS.has(entry.name)) continue;
|
|
7645
|
-
const full =
|
|
7832
|
+
const full = path44.join(dir, entry.name);
|
|
7646
7833
|
if (entry.isDirectory()) out.push(...walk(full, predicate));
|
|
7647
7834
|
else if (predicate(full)) out.push(full);
|
|
7648
7835
|
}
|
|
@@ -7654,11 +7841,11 @@ function extractAutopilotTopic(state) {
|
|
|
7654
7841
|
}
|
|
7655
7842
|
function buildAutopilotMap(cwd) {
|
|
7656
7843
|
const map = /* @__PURE__ */ new Map();
|
|
7657
|
-
const sessionsDir =
|
|
7658
|
-
if (!
|
|
7844
|
+
const sessionsDir = path44.join(cwd, ".harness", "sessions");
|
|
7845
|
+
if (!fs28.existsSync(sessionsDir)) return map;
|
|
7659
7846
|
for (const session of listDirs(sessionsDir)) {
|
|
7660
7847
|
if (!session.startsWith("changes--")) continue;
|
|
7661
|
-
const state = readJson2(
|
|
7848
|
+
const state = readJson2(path44.join(sessionsDir, session, "autopilot-state.json"));
|
|
7662
7849
|
if (!state) continue;
|
|
7663
7850
|
const topic = extractAutopilotTopic(state);
|
|
7664
7851
|
if (!topic) continue;
|
|
@@ -7671,7 +7858,7 @@ function buildAutopilotMap(cwd) {
|
|
|
7671
7858
|
function extractTopicFromHeader(planAbs, proposalTopics) {
|
|
7672
7859
|
let text;
|
|
7673
7860
|
try {
|
|
7674
|
-
text =
|
|
7861
|
+
text = fs28.readFileSync(planAbs, "utf8").slice(0, 4e3);
|
|
7675
7862
|
} catch {
|
|
7676
7863
|
return null;
|
|
7677
7864
|
}
|
|
@@ -7696,8 +7883,8 @@ function extractTopicFromFilename(filename, proposalTopics) {
|
|
|
7696
7883
|
return best;
|
|
7697
7884
|
}
|
|
7698
7885
|
function resolveDocsDir(cwd) {
|
|
7699
|
-
const configPath =
|
|
7700
|
-
const configResult =
|
|
7886
|
+
const configPath = path44.join(cwd, "harness.config.json");
|
|
7887
|
+
const configResult = fs28.existsSync(configPath) ? resolveConfig(configPath) : null;
|
|
7701
7888
|
let docsDir;
|
|
7702
7889
|
if (configResult?.ok) {
|
|
7703
7890
|
docsDir = configResult.value.docsDir;
|
|
@@ -7709,16 +7896,16 @@ function resolveDocsDir(cwd) {
|
|
|
7709
7896
|
} else {
|
|
7710
7897
|
docsDir = "./docs";
|
|
7711
7898
|
}
|
|
7712
|
-
const docsAbs =
|
|
7713
|
-
const relRaw =
|
|
7899
|
+
const docsAbs = path44.resolve(cwd, docsDir);
|
|
7900
|
+
const relRaw = path44.relative(cwd, docsAbs).replaceAll("\\", "/");
|
|
7714
7901
|
return relRaw === "" ? "docs" : relRaw;
|
|
7715
7902
|
}
|
|
7716
7903
|
function collectAdrMoves(cwd, docsRel) {
|
|
7717
7904
|
const moves = [];
|
|
7718
|
-
const legacyAdrDir =
|
|
7719
|
-
if (!
|
|
7905
|
+
const legacyAdrDir = path44.join(cwd, ".harness", "architecture");
|
|
7906
|
+
if (!fs28.existsSync(legacyAdrDir)) return moves;
|
|
7720
7907
|
for (const topic of listDirs(legacyAdrDir)) {
|
|
7721
|
-
const topicSrc =
|
|
7908
|
+
const topicSrc = path44.join(legacyAdrDir, topic);
|
|
7722
7909
|
for (const file of listFiles(topicSrc, (n) => n.endsWith(".md"))) {
|
|
7723
7910
|
moves.push({
|
|
7724
7911
|
src: `.harness/architecture/${topic}/${file}`,
|
|
@@ -7730,9 +7917,9 @@ function collectAdrMoves(cwd, docsRel) {
|
|
|
7730
7917
|
}
|
|
7731
7918
|
function collectProposalTopics(cwd) {
|
|
7732
7919
|
const topics = /* @__PURE__ */ new Set();
|
|
7733
|
-
const changesDir =
|
|
7920
|
+
const changesDir = path44.join(cwd, "docs", "changes");
|
|
7734
7921
|
for (const topic of listDirs(changesDir)) {
|
|
7735
|
-
if (
|
|
7922
|
+
if (fs28.existsSync(path44.join(changesDir, topic, "proposal.md"))) {
|
|
7736
7923
|
topics.add(topic);
|
|
7737
7924
|
}
|
|
7738
7925
|
}
|
|
@@ -7759,12 +7946,12 @@ async function buildPlan(opts) {
|
|
|
7759
7946
|
orphanPlans: [],
|
|
7760
7947
|
proposalTopics: collectProposalTopics(cwd)
|
|
7761
7948
|
};
|
|
7762
|
-
const legacyPlanDir =
|
|
7763
|
-
if (!
|
|
7949
|
+
const legacyPlanDir = path44.join(cwd, "docs", "plans");
|
|
7950
|
+
if (!fs28.existsSync(legacyPlanDir)) return plan;
|
|
7764
7951
|
const autopilotMap = buildAutopilotMap(cwd);
|
|
7765
7952
|
for (const file of listFiles(legacyPlanDir, (n) => n.endsWith(".md") && n !== "index.md")) {
|
|
7766
7953
|
const planRel = `docs/plans/${file}`;
|
|
7767
|
-
const planAbs =
|
|
7954
|
+
const planAbs = path44.join(legacyPlanDir, file);
|
|
7768
7955
|
const subdir = file.includes("VERIFICATION") ? "verifications" : "plans";
|
|
7769
7956
|
const classified = classifyPlan(planRel, planAbs, file, autopilotMap, plan.proposalTopics);
|
|
7770
7957
|
if (classified) {
|
|
@@ -7782,8 +7969,8 @@ async function buildPlan(opts) {
|
|
|
7782
7969
|
return plan;
|
|
7783
7970
|
}
|
|
7784
7971
|
function gitMv(cwd, src, dest, useGit) {
|
|
7785
|
-
const destAbs =
|
|
7786
|
-
|
|
7972
|
+
const destAbs = path44.join(cwd, dest);
|
|
7973
|
+
fs28.mkdirSync(path44.dirname(destAbs), { recursive: true });
|
|
7787
7974
|
if (useGit) {
|
|
7788
7975
|
try {
|
|
7789
7976
|
execFileSync4("git", ["mv", src, dest], { cwd, stdio: "pipe" });
|
|
@@ -7791,7 +7978,7 @@ function gitMv(cwd, src, dest, useGit) {
|
|
|
7791
7978
|
} catch {
|
|
7792
7979
|
}
|
|
7793
7980
|
}
|
|
7794
|
-
|
|
7981
|
+
fs28.renameSync(path44.join(cwd, src), destAbs);
|
|
7795
7982
|
}
|
|
7796
7983
|
function executeMigration(plan) {
|
|
7797
7984
|
let moved = 0;
|
|
@@ -7823,13 +8010,13 @@ function updateReferences(plan) {
|
|
|
7823
8010
|
let files = 0;
|
|
7824
8011
|
let replacements = 0;
|
|
7825
8012
|
const targets = [
|
|
7826
|
-
...walk(
|
|
7827
|
-
...walk(
|
|
8013
|
+
...walk(path44.join(plan.cwd, "docs"), (p) => p.endsWith(".md")),
|
|
8014
|
+
...walk(path44.join(plan.cwd, ".harness", "sessions"), (p) => p.endsWith(".json"))
|
|
7828
8015
|
];
|
|
7829
8016
|
for (const target of targets) {
|
|
7830
8017
|
let text;
|
|
7831
8018
|
try {
|
|
7832
|
-
text =
|
|
8019
|
+
text = fs28.readFileSync(target, "utf8");
|
|
7833
8020
|
} catch {
|
|
7834
8021
|
continue;
|
|
7835
8022
|
}
|
|
@@ -7842,7 +8029,7 @@ function updateReferences(plan) {
|
|
|
7842
8029
|
count += occurrences;
|
|
7843
8030
|
}
|
|
7844
8031
|
if (count > 0 && result !== text) {
|
|
7845
|
-
|
|
8032
|
+
fs28.writeFileSync(target, result);
|
|
7846
8033
|
files++;
|
|
7847
8034
|
replacements += count;
|
|
7848
8035
|
}
|
|
@@ -7901,7 +8088,7 @@ async function resolveOrphans(plan, opts) {
|
|
|
7901
8088
|
}
|
|
7902
8089
|
function bucketOrphans(plan, topic) {
|
|
7903
8090
|
for (const src of plan.orphanPlans) {
|
|
7904
|
-
const file =
|
|
8091
|
+
const file = path44.basename(src);
|
|
7905
8092
|
const isVerification = file.includes("VERIFICATION");
|
|
7906
8093
|
const subdir = isVerification ? "verifications" : "plans";
|
|
7907
8094
|
plan.planMoves.push({
|
|
@@ -7916,7 +8103,7 @@ function bucketOrphans(plan, topic) {
|
|
|
7916
8103
|
}
|
|
7917
8104
|
function summarize(plan) {
|
|
7918
8105
|
console.log("");
|
|
7919
|
-
console.log(
|
|
8106
|
+
console.log(chalk10.bold(" Migration plan"));
|
|
7920
8107
|
console.log("");
|
|
7921
8108
|
if (plan.adrMoves.length === 0 && plan.planMoves.length === 0 && plan.orphanPlans.length === 0) {
|
|
7922
8109
|
logger.success(" Nothing to migrate. Layout is already up to date.");
|
|
@@ -7935,7 +8122,7 @@ function summarize(plan) {
|
|
|
7935
8122
|
logger.info(
|
|
7936
8123
|
` Plan moves: ${plan.planMoves.length} file(s) \u2014 docs/plans/ \u2192 docs/changes/<topic>/{plans,verifications}/`
|
|
7937
8124
|
);
|
|
7938
|
-
for (const [via, n] of Object.entries(byVia)) console.log(
|
|
8125
|
+
for (const [via, n] of Object.entries(byVia)) console.log(chalk10.dim(` via ${via}: ${n}`));
|
|
7939
8126
|
}
|
|
7940
8127
|
if (plan.orphanPlans.length > 0) {
|
|
7941
8128
|
logger.warn(` Orphan plans: ${plan.orphanPlans.length} (no proposal match \u2014 needs decision)`);
|
|
@@ -7943,10 +8130,10 @@ function summarize(plan) {
|
|
|
7943
8130
|
console.log("");
|
|
7944
8131
|
}
|
|
7945
8132
|
async function detectLegacyArtifacts(cwd) {
|
|
7946
|
-
const adrLegacyDir =
|
|
7947
|
-
const adrLegacy =
|
|
7948
|
-
const planLegacyDir =
|
|
7949
|
-
const planLegacy =
|
|
8133
|
+
const adrLegacyDir = path44.join(cwd, ".harness", "architecture");
|
|
8134
|
+
const adrLegacy = fs28.existsSync(adrLegacyDir) && listDirs(adrLegacyDir).length > 0;
|
|
8135
|
+
const planLegacyDir = path44.join(cwd, "docs", "plans");
|
|
8136
|
+
const planLegacy = fs28.existsSync(planLegacyDir) && listFiles(planLegacyDir, (n) => n.endsWith(".md") && n !== "index.md").length > 0;
|
|
7950
8137
|
return { adrLegacy, planLegacy };
|
|
7951
8138
|
}
|
|
7952
8139
|
async function confirmApply(opts) {
|
|
@@ -8014,7 +8201,7 @@ async function runMigrate(opts) {
|
|
|
8014
8201
|
});
|
|
8015
8202
|
}
|
|
8016
8203
|
function createMigrateCommand() {
|
|
8017
|
-
|
|
8204
|
+
const cmd = new Command63("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(
|
|
8018
8205
|
"--skip-references",
|
|
8019
8206
|
"Do not update path references in docs/sessions after moves",
|
|
8020
8207
|
false
|
|
@@ -8033,12 +8220,158 @@ function createMigrateCommand() {
|
|
|
8033
8220
|
}
|
|
8034
8221
|
process.exit(result.value.movesFailed === 0 ? ExitCode.SUCCESS : ExitCode.ERROR);
|
|
8035
8222
|
});
|
|
8223
|
+
cmd.addCommand(createBackendsSubcommand());
|
|
8224
|
+
return cmd;
|
|
8225
|
+
}
|
|
8226
|
+
|
|
8227
|
+
// src/commands/models.ts
|
|
8228
|
+
import { Command as Command64 } from "commander";
|
|
8229
|
+
import { defaultFetchModels } from "@harness-engineering/orchestrator";
|
|
8230
|
+
function errorResult(message, partial) {
|
|
8231
|
+
return {
|
|
8232
|
+
status: "error",
|
|
8233
|
+
endpoint: "",
|
|
8234
|
+
configured: [],
|
|
8235
|
+
detected: [],
|
|
8236
|
+
resolved: null,
|
|
8237
|
+
error: message,
|
|
8238
|
+
exitCode: ExitCode.ERROR,
|
|
8239
|
+
...partial
|
|
8240
|
+
};
|
|
8241
|
+
}
|
|
8242
|
+
function pickConfiguredList(model) {
|
|
8243
|
+
if (typeof model === "string") return [model];
|
|
8244
|
+
if (Array.isArray(model)) return model.filter((m) => typeof m === "string");
|
|
8245
|
+
return [];
|
|
8246
|
+
}
|
|
8247
|
+
function locateBackend(opts) {
|
|
8248
|
+
const resolved = resolveConfig(opts.configPath);
|
|
8249
|
+
if (!resolved.ok) {
|
|
8250
|
+
return errorResult(`Could not load harness.config.json: ${resolved.error.message}`);
|
|
8251
|
+
}
|
|
8252
|
+
const backends = resolved.value.agent?.backends;
|
|
8253
|
+
const name = opts.backend ?? defaultLocalBackend(backends);
|
|
8254
|
+
if (name === null) {
|
|
8255
|
+
return errorResult(
|
|
8256
|
+
'No local backend in agent.backends. Declare one with type "local" or "pi", or pass --endpoint <url>.'
|
|
8257
|
+
);
|
|
8258
|
+
}
|
|
8259
|
+
const def = backends?.[name];
|
|
8260
|
+
if (def === void 0) {
|
|
8261
|
+
return errorResult(`agent.backends["${name}"] not found.`);
|
|
8262
|
+
}
|
|
8263
|
+
return { name, def };
|
|
8264
|
+
}
|
|
8265
|
+
function resolveProbeContext(opts) {
|
|
8266
|
+
if (opts.endpoint !== void 0) {
|
|
8267
|
+
return {
|
|
8268
|
+
endpoint: opts.endpoint,
|
|
8269
|
+
configured: [],
|
|
8270
|
+
apiKey: opts.apiKey ?? "local",
|
|
8271
|
+
backendLabel: opts.backend
|
|
8272
|
+
};
|
|
8273
|
+
}
|
|
8274
|
+
const located = locateBackend(opts);
|
|
8275
|
+
if ("status" in located) return located;
|
|
8276
|
+
const ep = located.def.endpoint;
|
|
8277
|
+
if (typeof ep !== "string") {
|
|
8278
|
+
return errorResult(`agent.backends["${located.name}"]: missing or invalid 'endpoint'.`);
|
|
8279
|
+
}
|
|
8280
|
+
const explicitKey = typeof located.def.apiKey === "string" ? located.def.apiKey : void 0;
|
|
8281
|
+
return {
|
|
8282
|
+
endpoint: ep,
|
|
8283
|
+
configured: pickConfiguredList(located.def.model),
|
|
8284
|
+
apiKey: opts.apiKey ?? explicitKey ?? "local",
|
|
8285
|
+
backendLabel: located.name
|
|
8286
|
+
};
|
|
8287
|
+
}
|
|
8288
|
+
async function runModelsProbe(opts) {
|
|
8289
|
+
const ctx = resolveProbeContext(opts);
|
|
8290
|
+
if ("status" in ctx) return ctx;
|
|
8291
|
+
let detected;
|
|
8292
|
+
try {
|
|
8293
|
+
detected = await defaultFetchModels(ctx.endpoint, ctx.apiKey);
|
|
8294
|
+
} catch (err) {
|
|
8295
|
+
return {
|
|
8296
|
+
status: "error",
|
|
8297
|
+
...ctx.backendLabel !== void 0 ? { backend: ctx.backendLabel } : {},
|
|
8298
|
+
endpoint: ctx.endpoint,
|
|
8299
|
+
configured: ctx.configured,
|
|
8300
|
+
detected: [],
|
|
8301
|
+
resolved: null,
|
|
8302
|
+
error: err instanceof Error ? err.message : String(err),
|
|
8303
|
+
exitCode: ExitCode.ERROR
|
|
8304
|
+
};
|
|
8305
|
+
}
|
|
8306
|
+
const match = ctx.configured.find((id) => detected.includes(id)) ?? null;
|
|
8307
|
+
return {
|
|
8308
|
+
status: match !== null ? "ok" : "no-match",
|
|
8309
|
+
...ctx.backendLabel !== void 0 ? { backend: ctx.backendLabel } : {},
|
|
8310
|
+
endpoint: ctx.endpoint,
|
|
8311
|
+
configured: ctx.configured,
|
|
8312
|
+
detected,
|
|
8313
|
+
resolved: match,
|
|
8314
|
+
exitCode: match !== null ? ExitCode.SUCCESS : ExitCode.ERROR
|
|
8315
|
+
};
|
|
8316
|
+
}
|
|
8317
|
+
function defaultLocalBackend(backends) {
|
|
8318
|
+
if (backends === void 0) return null;
|
|
8319
|
+
for (const [name, def] of Object.entries(backends)) {
|
|
8320
|
+
const t = def.type;
|
|
8321
|
+
if (t === "local" || t === "pi") return name;
|
|
8322
|
+
}
|
|
8323
|
+
return null;
|
|
8324
|
+
}
|
|
8325
|
+
function formatProbe(result) {
|
|
8326
|
+
if (result.status === "error") {
|
|
8327
|
+
return `error probing ${result.endpoint || "(no endpoint)"}: ${result.error}`;
|
|
8328
|
+
}
|
|
8329
|
+
const lines = [];
|
|
8330
|
+
lines.push(
|
|
8331
|
+
`endpoint: ${result.endpoint}${result.backend !== void 0 ? ` (agent.backends["${result.backend}"])` : ""}`
|
|
8332
|
+
);
|
|
8333
|
+
lines.push(`configured: [${result.configured.join(", ")}]`);
|
|
8334
|
+
lines.push(`detected: [${result.detected.join(", ")}]`);
|
|
8335
|
+
if (result.resolved !== null) {
|
|
8336
|
+
lines.push(`resolved: ${result.resolved}`);
|
|
8337
|
+
} else {
|
|
8338
|
+
lines.push("resolved: (no configured model is loaded)");
|
|
8339
|
+
lines.push(
|
|
8340
|
+
"hint: load one of the configured models or update agent.backends.<name>.model to include a detected id."
|
|
8341
|
+
);
|
|
8342
|
+
}
|
|
8343
|
+
return lines.join("\n");
|
|
8344
|
+
}
|
|
8345
|
+
function createModelsCommand() {
|
|
8346
|
+
const cmd = new Command64("models").description(
|
|
8347
|
+
"Inspect and manage local LLM backends. Currently ships `probe`; LMLM phases add status/suggest/pool/proposals."
|
|
8348
|
+
);
|
|
8349
|
+
cmd.command("probe").description(
|
|
8350
|
+
"Probe a local backend's /v1/models endpoint and report which configured model is loaded."
|
|
8351
|
+
).option(
|
|
8352
|
+
"--backend <name>",
|
|
8353
|
+
"Name of an entry in agent.backends. Defaults to the first local/pi entry."
|
|
8354
|
+
).option("--endpoint <url>", "Override the backend endpoint (bypasses harness.config.json).").option("--api-key <key>", "Override the API key.").option("--json", "Print machine-readable JSON instead of a human summary.", false).action(async (options) => {
|
|
8355
|
+
const result = await runModelsProbe(options);
|
|
8356
|
+
if (options.json) {
|
|
8357
|
+
console.log(JSON.stringify(result, null, 2));
|
|
8358
|
+
} else {
|
|
8359
|
+
const text = formatProbe(result);
|
|
8360
|
+
if (result.status === "error" || result.status === "no-match") {
|
|
8361
|
+
logger.error(text);
|
|
8362
|
+
} else {
|
|
8363
|
+
logger.info(text);
|
|
8364
|
+
}
|
|
8365
|
+
}
|
|
8366
|
+
process.exit(result.exitCode);
|
|
8367
|
+
});
|
|
8368
|
+
return cmd;
|
|
8036
8369
|
}
|
|
8037
8370
|
|
|
8038
8371
|
// src/commands/naming-craft.ts
|
|
8039
|
-
import { Command as
|
|
8372
|
+
import { Command as Command65 } from "commander";
|
|
8040
8373
|
function createNamingCraftCommand() {
|
|
8041
|
-
return new
|
|
8374
|
+
return new Command65("naming-craft").description(
|
|
8042
8375
|
"LLM-judgment critique of identifier names (variables, functions, types, files). First craft-pipeline ceiling skill; uses curated rubric catalog from Martin/Beck/Karlton."
|
|
8043
8376
|
).option("-f, --files <files...>", "Optional file/glob scope").option(
|
|
8044
8377
|
"-k, --kinds <kinds...>",
|
|
@@ -8088,10 +8421,10 @@ function printResult4(result, mode, _formatter) {
|
|
|
8088
8421
|
list.push(f);
|
|
8089
8422
|
byFile.set(f.target.file, list);
|
|
8090
8423
|
}
|
|
8091
|
-
for (const [file,
|
|
8424
|
+
for (const [file, fs48] of byFile) {
|
|
8092
8425
|
console.log(`
|
|
8093
8426
|
${file}`);
|
|
8094
|
-
for (const f of
|
|
8427
|
+
for (const f of fs48) {
|
|
8095
8428
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
8096
8429
|
console.log(
|
|
8097
8430
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.kind} ${f.target.identifier}${line}`
|
|
@@ -8111,11 +8444,11 @@ ${file}`);
|
|
|
8111
8444
|
}
|
|
8112
8445
|
|
|
8113
8446
|
// src/commands/notifications/index.ts
|
|
8114
|
-
import { Command as
|
|
8447
|
+
import { Command as Command67 } from "commander";
|
|
8115
8448
|
|
|
8116
8449
|
// src/commands/notifications/test.ts
|
|
8117
8450
|
init_dist3();
|
|
8118
|
-
import { Command as
|
|
8451
|
+
import { Command as Command66 } from "commander";
|
|
8119
8452
|
import { randomBytes } from "crypto";
|
|
8120
8453
|
import { resolve as resolve28 } from "path";
|
|
8121
8454
|
import { SinkRegistry, SinkConfigError, wrapAsEnvelope } from "@harness-engineering/orchestrator";
|
|
@@ -8167,7 +8500,7 @@ async function runNotificationsTest(sinkId, opts, projectRoot2 = process.cwd())
|
|
|
8167
8500
|
return { ok: false, error: result.error };
|
|
8168
8501
|
}
|
|
8169
8502
|
function createNotificationsTestSubcommand() {
|
|
8170
|
-
return new
|
|
8503
|
+
return new Command66("test").description("Send a synthetic notification.test event through the named sink").argument("<sink-id>", "Sink id from harness.config.json `notifications.sinks[].id`").option("--message <text>", "Override the default test message").action(async (sinkId, opts, cmd) => {
|
|
8171
8504
|
const globalOpts = cmd.optsWithGlobals();
|
|
8172
8505
|
const projectRoot2 = typeof globalOpts["cwd"] === "string" ? resolve28(globalOpts["cwd"]) : process.cwd();
|
|
8173
8506
|
const result = await runNotificationsTest(sinkId, opts, projectRoot2);
|
|
@@ -8184,7 +8517,7 @@ function createNotificationsTestSubcommand() {
|
|
|
8184
8517
|
|
|
8185
8518
|
// src/commands/notifications/index.ts
|
|
8186
8519
|
function createNotificationsCommand() {
|
|
8187
|
-
const cmd = new
|
|
8520
|
+
const cmd = new Command67("notifications").description(
|
|
8188
8521
|
"Manage notification sinks (Slack and others)"
|
|
8189
8522
|
);
|
|
8190
8523
|
cmd.addCommand(createNotificationsTestSubcommand());
|
|
@@ -8192,13 +8525,13 @@ function createNotificationsCommand() {
|
|
|
8192
8525
|
}
|
|
8193
8526
|
|
|
8194
8527
|
// src/commands/orchestrator.ts
|
|
8195
|
-
import { Command as
|
|
8196
|
-
import * as
|
|
8528
|
+
import { Command as Command68 } from "commander";
|
|
8529
|
+
import * as path45 from "path";
|
|
8197
8530
|
import { Orchestrator, WorkflowLoader as WorkflowLoader2, launchTUI } from "@harness-engineering/orchestrator";
|
|
8198
8531
|
function createOrchestratorCommand() {
|
|
8199
|
-
const orchestrator = new
|
|
8532
|
+
const orchestrator = new Command68("orchestrator");
|
|
8200
8533
|
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) => {
|
|
8201
|
-
const workflowPath =
|
|
8534
|
+
const workflowPath = path45.resolve(process.cwd(), opts.workflow);
|
|
8202
8535
|
const loader = new WorkflowLoader2();
|
|
8203
8536
|
const result = await loader.loadWorkflow(workflowPath);
|
|
8204
8537
|
if (!result.ok) {
|
|
@@ -8232,13 +8565,13 @@ function createOrchestratorCommand() {
|
|
|
8232
8565
|
|
|
8233
8566
|
// src/commands/perf.ts
|
|
8234
8567
|
init_dist3();
|
|
8235
|
-
import { Command as
|
|
8236
|
-
import * as
|
|
8568
|
+
import { Command as Command69 } from "commander";
|
|
8569
|
+
import * as path46 from "path";
|
|
8237
8570
|
function registerBenchCommand(perf) {
|
|
8238
8571
|
perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
|
|
8239
8572
|
const globalOpts = cmd.optsWithGlobals();
|
|
8240
8573
|
const cwd = process.cwd();
|
|
8241
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
8574
|
+
const { BenchmarkRunner } = await import("./dist-JCFK263L.js");
|
|
8242
8575
|
const runner = new BenchmarkRunner();
|
|
8243
8576
|
const benchFiles = runner.discover(cwd, glob2);
|
|
8244
8577
|
if (benchFiles.length === 0) {
|
|
@@ -8302,7 +8635,7 @@ async function runBaselinesShow(globalOpts) {
|
|
|
8302
8635
|
}
|
|
8303
8636
|
async function runBaselinesUpdate(globalOpts) {
|
|
8304
8637
|
const cwd = process.cwd();
|
|
8305
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
8638
|
+
const { BenchmarkRunner } = await import("./dist-JCFK263L.js");
|
|
8306
8639
|
const runner = new BenchmarkRunner();
|
|
8307
8640
|
const manager = new BaselineManager(cwd);
|
|
8308
8641
|
logger.info("Running benchmarks to update baselines...");
|
|
@@ -8343,9 +8676,9 @@ function registerReportCommand(perf) {
|
|
|
8343
8676
|
perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
|
|
8344
8677
|
const globalOpts = cmd.optsWithGlobals();
|
|
8345
8678
|
const cwd = process.cwd();
|
|
8346
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-
|
|
8679
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-JCFK263L.js");
|
|
8347
8680
|
const analyzer = new EntropyAnalyzer2({
|
|
8348
|
-
rootDir:
|
|
8681
|
+
rootDir: path46.resolve(cwd),
|
|
8349
8682
|
analyze: { complexity: true, coupling: true }
|
|
8350
8683
|
});
|
|
8351
8684
|
const result = await analyzer.analyze();
|
|
@@ -8404,7 +8737,7 @@ function registerCriticalPathsCommand(perf) {
|
|
|
8404
8737
|
});
|
|
8405
8738
|
}
|
|
8406
8739
|
function createPerfCommand() {
|
|
8407
|
-
const perf = new
|
|
8740
|
+
const perf = new Command69("perf").description("Performance benchmark and baseline management");
|
|
8408
8741
|
registerBenchCommand(perf);
|
|
8409
8742
|
registerBaselinesCommands(perf);
|
|
8410
8743
|
registerReportCommand(perf);
|
|
@@ -8413,10 +8746,10 @@ function createPerfCommand() {
|
|
|
8413
8746
|
}
|
|
8414
8747
|
|
|
8415
8748
|
// src/commands/persona/index.ts
|
|
8416
|
-
import { Command as
|
|
8749
|
+
import { Command as Command72 } from "commander";
|
|
8417
8750
|
|
|
8418
8751
|
// src/commands/persona/list.ts
|
|
8419
|
-
import { Command as
|
|
8752
|
+
import { Command as Command70 } from "commander";
|
|
8420
8753
|
function printPersonaList(personas) {
|
|
8421
8754
|
if (personas.length === 0) {
|
|
8422
8755
|
logger.info("No personas found.");
|
|
@@ -8430,7 +8763,7 @@ function printPersonaList(personas) {
|
|
|
8430
8763
|
}
|
|
8431
8764
|
}
|
|
8432
8765
|
function createListCommand2() {
|
|
8433
|
-
return new
|
|
8766
|
+
return new Command70("list").description("List available agent personas").action(async (_opts, cmd) => {
|
|
8434
8767
|
const globalOpts = cmd.optsWithGlobals();
|
|
8435
8768
|
const personasDir = resolvePersonasDir();
|
|
8436
8769
|
const result = listPersonas(personasDir);
|
|
@@ -8450,51 +8783,51 @@ function createListCommand2() {
|
|
|
8450
8783
|
}
|
|
8451
8784
|
|
|
8452
8785
|
// src/commands/persona/generate.ts
|
|
8453
|
-
import { Command as
|
|
8454
|
-
import * as
|
|
8455
|
-
import * as
|
|
8786
|
+
import { Command as Command71 } from "commander";
|
|
8787
|
+
import * as fs29 from "fs";
|
|
8788
|
+
import * as path47 from "path";
|
|
8456
8789
|
function generatePersonaArtifacts(persona, outputDir, only) {
|
|
8457
8790
|
const slug = toKebabCase(persona.name);
|
|
8458
8791
|
const generated = [];
|
|
8459
8792
|
if (!only || only === "runtime") {
|
|
8460
8793
|
const result = generateRuntime(persona);
|
|
8461
8794
|
if (result.ok) {
|
|
8462
|
-
const outPath =
|
|
8463
|
-
|
|
8464
|
-
|
|
8795
|
+
const outPath = path47.join(outputDir, `${slug}.runtime.json`);
|
|
8796
|
+
fs29.mkdirSync(path47.dirname(outPath), { recursive: true });
|
|
8797
|
+
fs29.writeFileSync(outPath, result.value);
|
|
8465
8798
|
generated.push(outPath);
|
|
8466
8799
|
}
|
|
8467
8800
|
}
|
|
8468
8801
|
if (!only || only === "agents-md") {
|
|
8469
8802
|
const result = generateAgentsMd(persona);
|
|
8470
8803
|
if (result.ok) {
|
|
8471
|
-
const outPath =
|
|
8472
|
-
|
|
8804
|
+
const outPath = path47.join(outputDir, `${slug}.agents.md`);
|
|
8805
|
+
fs29.writeFileSync(outPath, result.value);
|
|
8473
8806
|
generated.push(outPath);
|
|
8474
8807
|
}
|
|
8475
8808
|
}
|
|
8476
8809
|
if (!only || only === "ci") {
|
|
8477
8810
|
const result = generateCIWorkflow(persona, "github");
|
|
8478
8811
|
if (result.ok) {
|
|
8479
|
-
const outPath =
|
|
8480
|
-
|
|
8481
|
-
|
|
8812
|
+
const outPath = path47.join(outputDir, ".github", "workflows", `${slug}.yml`);
|
|
8813
|
+
fs29.mkdirSync(path47.dirname(outPath), { recursive: true });
|
|
8814
|
+
fs29.writeFileSync(outPath, result.value);
|
|
8482
8815
|
generated.push(outPath);
|
|
8483
8816
|
}
|
|
8484
8817
|
}
|
|
8485
8818
|
return generated;
|
|
8486
8819
|
}
|
|
8487
8820
|
function createGenerateCommand3() {
|
|
8488
|
-
return new
|
|
8821
|
+
return new Command71("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) => {
|
|
8489
8822
|
const globalOpts = cmd.optsWithGlobals();
|
|
8490
|
-
const personaResult = loadPersona(
|
|
8823
|
+
const personaResult = loadPersona(path47.join(resolvePersonasDir(), `${name}.yaml`));
|
|
8491
8824
|
if (!personaResult.ok) {
|
|
8492
8825
|
logger.error(personaResult.error.message);
|
|
8493
8826
|
process.exit(ExitCode.ERROR);
|
|
8494
8827
|
}
|
|
8495
8828
|
const generated = generatePersonaArtifacts(
|
|
8496
8829
|
personaResult.value,
|
|
8497
|
-
|
|
8830
|
+
path47.resolve(opts.outputDir),
|
|
8498
8831
|
opts.only
|
|
8499
8832
|
);
|
|
8500
8833
|
if (!globalOpts.quiet) {
|
|
@@ -8507,7 +8840,7 @@ function createGenerateCommand3() {
|
|
|
8507
8840
|
|
|
8508
8841
|
// src/commands/persona/index.ts
|
|
8509
8842
|
function createPersonaCommand() {
|
|
8510
|
-
const command = new
|
|
8843
|
+
const command = new Command72("persona").description("Agent persona management commands");
|
|
8511
8844
|
command.addCommand(createListCommand2());
|
|
8512
8845
|
command.addCommand(createGenerateCommand3());
|
|
8513
8846
|
return command;
|
|
@@ -8515,8 +8848,8 @@ function createPersonaCommand() {
|
|
|
8515
8848
|
|
|
8516
8849
|
// src/commands/predict.ts
|
|
8517
8850
|
init_dist3();
|
|
8518
|
-
import { Command as
|
|
8519
|
-
import
|
|
8851
|
+
import { Command as Command73 } from "commander";
|
|
8852
|
+
import chalk11 from "chalk";
|
|
8520
8853
|
var CATEGORY_ORDER = [
|
|
8521
8854
|
"circular-deps",
|
|
8522
8855
|
"layer-violations",
|
|
@@ -8537,11 +8870,11 @@ function crossingLabel(weeks) {
|
|
|
8537
8870
|
function severityPrefix(severity) {
|
|
8538
8871
|
switch (severity) {
|
|
8539
8872
|
case "critical":
|
|
8540
|
-
return
|
|
8873
|
+
return chalk11.red("[critical]");
|
|
8541
8874
|
case "warning":
|
|
8542
|
-
return
|
|
8875
|
+
return chalk11.yellow("[warning]");
|
|
8543
8876
|
case "info":
|
|
8544
|
-
return
|
|
8877
|
+
return chalk11.blue("[info]");
|
|
8545
8878
|
}
|
|
8546
8879
|
}
|
|
8547
8880
|
function printCategoryTable(result) {
|
|
@@ -8621,7 +8954,7 @@ function handlePredictError(err, mode) {
|
|
|
8621
8954
|
process.exit(ExitCode.ERROR);
|
|
8622
8955
|
}
|
|
8623
8956
|
function createPredictCommand() {
|
|
8624
|
-
const command = new
|
|
8957
|
+
const command = new Command73("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) => {
|
|
8625
8958
|
const globalOpts = cmd.optsWithGlobals();
|
|
8626
8959
|
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
8627
8960
|
try {
|
|
@@ -8645,7 +8978,7 @@ function createPredictCommand() {
|
|
|
8645
8978
|
|
|
8646
8979
|
// src/commands/proposals.ts
|
|
8647
8980
|
init_dist3();
|
|
8648
|
-
import { Command as
|
|
8981
|
+
import { Command as Command74 } from "commander";
|
|
8649
8982
|
import { resolve as resolve32 } from "path";
|
|
8650
8983
|
function projectRoot() {
|
|
8651
8984
|
return resolve32(process.env["HARNESS_PROJECT_ROOT"] ?? process.cwd());
|
|
@@ -8740,7 +9073,7 @@ async function actApproveCommand(id) {
|
|
|
8740
9073
|
}
|
|
8741
9074
|
}
|
|
8742
9075
|
function createProposalsCommand() {
|
|
8743
|
-
const cmd = new
|
|
9076
|
+
const cmd = new Command74("proposals").description("Skill-proposal review queue");
|
|
8744
9077
|
cmd.command("list").description("List skill proposals in the local queue").option(
|
|
8745
9078
|
"--status <status>",
|
|
8746
9079
|
`Filter by status \u2014 one of ${ALLOWED_STATUSES.join(" | ")}`,
|
|
@@ -8755,9 +9088,9 @@ function createProposalsCommand() {
|
|
|
8755
9088
|
}
|
|
8756
9089
|
|
|
8757
9090
|
// src/commands/publish-analyses.ts
|
|
8758
|
-
import * as
|
|
8759
|
-
import * as
|
|
8760
|
-
import { Command as
|
|
9091
|
+
import * as fs30 from "fs";
|
|
9092
|
+
import * as path48 from "path";
|
|
9093
|
+
import { Command as Command75 } from "commander";
|
|
8761
9094
|
init_dist3();
|
|
8762
9095
|
import {
|
|
8763
9096
|
renderAnalysisComment,
|
|
@@ -8765,7 +9098,7 @@ import {
|
|
|
8765
9098
|
savePublishedIndex
|
|
8766
9099
|
} from "@harness-engineering/orchestrator";
|
|
8767
9100
|
function bootstrapTrackerCommand(opts, verb) {
|
|
8768
|
-
const projectPath =
|
|
9101
|
+
const projectPath = path48.resolve(opts.dir);
|
|
8769
9102
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
8770
9103
|
if (!trackerConfig) {
|
|
8771
9104
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -8777,8 +9110,8 @@ function bootstrapTrackerCommand(opts, verb) {
|
|
|
8777
9110
|
);
|
|
8778
9111
|
return null;
|
|
8779
9112
|
}
|
|
8780
|
-
const projectEnvPath =
|
|
8781
|
-
if (
|
|
9113
|
+
const projectEnvPath = path48.join(projectPath, ".env");
|
|
9114
|
+
if (fs30.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
8782
9115
|
__require("dotenv").config({ path: projectEnvPath });
|
|
8783
9116
|
}
|
|
8784
9117
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -8814,13 +9147,13 @@ async function buildNameToExternalIdMap(projectPath) {
|
|
|
8814
9147
|
}
|
|
8815
9148
|
return fileLessMap;
|
|
8816
9149
|
}
|
|
8817
|
-
const roadmapFile =
|
|
8818
|
-
if (!
|
|
9150
|
+
const roadmapFile = path48.join(projectPath, "docs", "roadmap.md");
|
|
9151
|
+
if (!fs30.existsSync(roadmapFile)) {
|
|
8819
9152
|
logger.error("No docs/roadmap.md found. Cannot map issue hashes to external IDs.");
|
|
8820
9153
|
return null;
|
|
8821
9154
|
}
|
|
8822
9155
|
const { parseRoadmap: parseRoadmap2 } = (init_dist3(), __toCommonJS(dist_exports));
|
|
8823
|
-
const roadmapParsed = parseRoadmap2(
|
|
9156
|
+
const roadmapParsed = parseRoadmap2(fs30.readFileSync(roadmapFile, "utf-8"));
|
|
8824
9157
|
if (!roadmapParsed.ok) {
|
|
8825
9158
|
logger.error("Failed to parse docs/roadmap.md");
|
|
8826
9159
|
return null;
|
|
@@ -8870,8 +9203,8 @@ async function runPublishAnalyses(opts) {
|
|
|
8870
9203
|
const nameToExternalId = await buildNameToExternalIdMap(projectPath);
|
|
8871
9204
|
if (!nameToExternalId) return process.exit(1);
|
|
8872
9205
|
const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
|
|
8873
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
8874
|
-
const archive = new AnalysisArchive(
|
|
9206
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-JCFK263L.js");
|
|
9207
|
+
const archive = new AnalysisArchive(path48.join(projectPath, ".harness", "analyses"));
|
|
8875
9208
|
const analyses = await archive.list();
|
|
8876
9209
|
if (analyses.length === 0) {
|
|
8877
9210
|
logger.info("No analyses found to publish.");
|
|
@@ -8893,7 +9226,7 @@ async function runPublishAnalyses(opts) {
|
|
|
8893
9226
|
}
|
|
8894
9227
|
}
|
|
8895
9228
|
function createPublishAnalysesCommand() {
|
|
8896
|
-
const command = new
|
|
9229
|
+
const command = new Command75("publish-analyses").description(
|
|
8897
9230
|
"Publishes locally generated intelligence analyses to the external issue tracker (e.g., GitHub)"
|
|
8898
9231
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
8899
9232
|
try {
|
|
@@ -8909,18 +9242,18 @@ function createPublishAnalysesCommand() {
|
|
|
8909
9242
|
}
|
|
8910
9243
|
|
|
8911
9244
|
// src/commands/pulse/index.ts
|
|
8912
|
-
import { Command as
|
|
9245
|
+
import { Command as Command77 } from "commander";
|
|
8913
9246
|
|
|
8914
9247
|
// src/commands/pulse/run.ts
|
|
8915
9248
|
init_dist3();
|
|
8916
|
-
import { readFileSync as
|
|
8917
|
-
import { dirname as
|
|
8918
|
-
import { Command as
|
|
9249
|
+
import { readFileSync as readFileSync19, mkdirSync as mkdirSync13, writeFileSync as writeFileSync16 } from "fs";
|
|
9250
|
+
import { dirname as dirname18, join as join39, resolve as resolve34 } from "path";
|
|
9251
|
+
import { Command as Command76 } from "commander";
|
|
8919
9252
|
async function runPulseRunCommand(opts) {
|
|
8920
9253
|
const startedAt = Date.now();
|
|
8921
9254
|
let raw;
|
|
8922
9255
|
try {
|
|
8923
|
-
raw = JSON.parse(
|
|
9256
|
+
raw = JSON.parse(readFileSync19(opts.configPath, "utf-8"));
|
|
8924
9257
|
} catch (err) {
|
|
8925
9258
|
return emit2(
|
|
8926
9259
|
{
|
|
@@ -8946,9 +9279,9 @@ async function runPulseRunCommand(opts) {
|
|
|
8946
9279
|
const report = assembleReport(result, productName, lookback);
|
|
8947
9280
|
const now = /* @__PURE__ */ new Date();
|
|
8948
9281
|
const ts = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}_${String(now.getHours()).padStart(2, "0")}-${String(now.getMinutes()).padStart(2, "0")}`;
|
|
8949
|
-
const filePath =
|
|
8950
|
-
mkdirSync13(
|
|
8951
|
-
|
|
9282
|
+
const filePath = join39(opts.outputDir, `${ts}.md`);
|
|
9283
|
+
mkdirSync13(dirname18(filePath), { recursive: true });
|
|
9284
|
+
writeFileSync16(filePath, report);
|
|
8952
9285
|
const headlines = extractHeadlines(report);
|
|
8953
9286
|
return emit2(
|
|
8954
9287
|
{
|
|
@@ -8980,7 +9313,7 @@ function emit2(status, nonInteractive) {
|
|
|
8980
9313
|
return status;
|
|
8981
9314
|
}
|
|
8982
9315
|
function createRunCommand2() {
|
|
8983
|
-
return new
|
|
9316
|
+
return new Command76("run").description("Run a pulse: query configured adapters, sanitize, assemble single-page report").option(
|
|
8984
9317
|
"--lookback <window>",
|
|
8985
9318
|
"Lookback window (e.g. 24h, 7d). Defaults to pulse.lookbackDefault."
|
|
8986
9319
|
).option(
|
|
@@ -9004,14 +9337,14 @@ function createRunCommand2() {
|
|
|
9004
9337
|
|
|
9005
9338
|
// src/commands/pulse/index.ts
|
|
9006
9339
|
function createPulseCommand() {
|
|
9007
|
-
const command = new
|
|
9340
|
+
const command = new Command77("pulse").description("Pulse (read-side observability) commands");
|
|
9008
9341
|
command.addCommand(createRunCommand2());
|
|
9009
9342
|
return command;
|
|
9010
9343
|
}
|
|
9011
9344
|
|
|
9012
9345
|
// src/commands/recommend.ts
|
|
9013
|
-
import { Command as
|
|
9014
|
-
import
|
|
9346
|
+
import { Command as Command78 } from "commander";
|
|
9347
|
+
import chalk12 from "chalk";
|
|
9015
9348
|
async function resolveSnapshot(cwd, noCache) {
|
|
9016
9349
|
if (!noCache) {
|
|
9017
9350
|
const cached = loadCachedSnapshot(cwd);
|
|
@@ -9045,12 +9378,12 @@ async function runRecommend(options) {
|
|
|
9045
9378
|
function formatRecommendation(rec) {
|
|
9046
9379
|
const lines = [];
|
|
9047
9380
|
if (rec.urgency === "critical") {
|
|
9048
|
-
lines.push(` ${
|
|
9381
|
+
lines.push(` ${chalk12.red("[CRITICAL]")} ${rec.sequence}. ${rec.skillName}`);
|
|
9049
9382
|
} else {
|
|
9050
9383
|
lines.push(` ${rec.sequence}. ${rec.skillName} (${rec.score.toFixed(2)})`);
|
|
9051
9384
|
}
|
|
9052
9385
|
for (const reason of rec.reasons) {
|
|
9053
|
-
lines.push(` ${
|
|
9386
|
+
lines.push(` ${chalk12.dim("\u2192")} ${reason}`);
|
|
9054
9387
|
}
|
|
9055
9388
|
return lines.join("\n");
|
|
9056
9389
|
}
|
|
@@ -9074,7 +9407,7 @@ function printRecommendations(result) {
|
|
|
9074
9407
|
console.log("");
|
|
9075
9408
|
}
|
|
9076
9409
|
function createRecommendCommand() {
|
|
9077
|
-
const command = new
|
|
9410
|
+
const command = new Command78("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) => {
|
|
9078
9411
|
const globalOpts = cmd.optsWithGlobals();
|
|
9079
9412
|
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
9080
9413
|
try {
|
|
@@ -9110,18 +9443,18 @@ function createRecommendCommand() {
|
|
|
9110
9443
|
}
|
|
9111
9444
|
|
|
9112
9445
|
// src/commands/roadmap/index.ts
|
|
9113
|
-
import { Command as
|
|
9446
|
+
import { Command as Command80 } from "commander";
|
|
9114
9447
|
|
|
9115
9448
|
// src/commands/roadmap/migrate.ts
|
|
9116
9449
|
init_dist3();
|
|
9117
|
-
import { Command as
|
|
9118
|
-
import * as
|
|
9119
|
-
import * as
|
|
9120
|
-
import
|
|
9450
|
+
import { Command as Command79 } from "commander";
|
|
9451
|
+
import * as fs32 from "fs";
|
|
9452
|
+
import * as path50 from "path";
|
|
9453
|
+
import chalk13 from "chalk";
|
|
9121
9454
|
|
|
9122
9455
|
// src/commands/roadmap/migrate-lock.ts
|
|
9123
|
-
import * as
|
|
9124
|
-
import * as
|
|
9456
|
+
import * as fs31 from "fs";
|
|
9457
|
+
import * as path49 from "path";
|
|
9125
9458
|
import * as os6 from "os";
|
|
9126
9459
|
var LOCK_FILE_NAME = "migrate.lock";
|
|
9127
9460
|
var STALE_LOCK_MS = 30 * 60 * 1e3;
|
|
@@ -9135,13 +9468,13 @@ function isPidAlive(pid) {
|
|
|
9135
9468
|
}
|
|
9136
9469
|
}
|
|
9137
9470
|
function acquireMigrateLock(projectRoot2) {
|
|
9138
|
-
const harnessDir =
|
|
9139
|
-
const lockPath =
|
|
9140
|
-
if (!
|
|
9141
|
-
if (
|
|
9471
|
+
const harnessDir = path49.join(projectRoot2, ".harness");
|
|
9472
|
+
const lockPath = path49.join(harnessDir, LOCK_FILE_NAME);
|
|
9473
|
+
if (!fs31.existsSync(harnessDir)) fs31.mkdirSync(harnessDir, { recursive: true });
|
|
9474
|
+
if (fs31.existsSync(lockPath)) {
|
|
9142
9475
|
const existingRaw = (() => {
|
|
9143
9476
|
try {
|
|
9144
|
-
return
|
|
9477
|
+
return fs31.readFileSync(lockPath, "utf-8");
|
|
9145
9478
|
} catch {
|
|
9146
9479
|
return null;
|
|
9147
9480
|
}
|
|
@@ -9171,11 +9504,11 @@ function acquireMigrateLock(projectRoot2) {
|
|
|
9171
9504
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9172
9505
|
hostname: os6.hostname()
|
|
9173
9506
|
};
|
|
9174
|
-
|
|
9507
|
+
fs31.writeFileSync(lockPath, JSON.stringify(payload, null, 2) + "\n");
|
|
9175
9508
|
return {
|
|
9176
9509
|
release: () => {
|
|
9177
9510
|
try {
|
|
9178
|
-
|
|
9511
|
+
fs31.unlinkSync(lockPath);
|
|
9179
9512
|
} catch {
|
|
9180
9513
|
}
|
|
9181
9514
|
}
|
|
@@ -9206,7 +9539,7 @@ function reportToExitCode(report) {
|
|
|
9206
9539
|
return MigrateExitCode.GENERIC_FAILURE;
|
|
9207
9540
|
}
|
|
9208
9541
|
function printPlanSummary(plan, dryRun) {
|
|
9209
|
-
const banner = dryRun ?
|
|
9542
|
+
const banner = dryRun ? chalk13.cyan("DRY RUN ") : "";
|
|
9210
9543
|
console.log(`${banner}Migration plan:`);
|
|
9211
9544
|
console.log(` Would create: ${plan.toCreate.length}`);
|
|
9212
9545
|
for (const e of plan.toCreate) console.log(` - ${e.name}`);
|
|
@@ -9335,11 +9668,11 @@ async function runRoadmapMigrate(opts) {
|
|
|
9335
9668
|
if (!clientR.ok) return Err(new CLIError(clientR.error.message));
|
|
9336
9669
|
client = clientR.value;
|
|
9337
9670
|
}
|
|
9338
|
-
const roadmapPath =
|
|
9339
|
-
if (!
|
|
9671
|
+
const roadmapPath = path50.join(cwd, "docs", "roadmap.md");
|
|
9672
|
+
if (!fs32.existsSync(roadmapPath)) {
|
|
9340
9673
|
return Err(new CLIError(`docs/roadmap.md not found in ${cwd}`));
|
|
9341
9674
|
}
|
|
9342
|
-
const roadmapR = parseRoadmap(
|
|
9675
|
+
const roadmapR = parseRoadmap(fs32.readFileSync(roadmapPath, "utf-8"));
|
|
9343
9676
|
if (!roadmapR.ok) {
|
|
9344
9677
|
return Err(new CLIError(`failed to parse docs/roadmap.md: ${roadmapR.error.message}`));
|
|
9345
9678
|
}
|
|
@@ -9360,10 +9693,10 @@ async function runRoadmapMigrate(opts) {
|
|
|
9360
9693
|
if (!isJson) printPlanSummary(plan, opts.dryRun);
|
|
9361
9694
|
const deps = {
|
|
9362
9695
|
client,
|
|
9363
|
-
readFile: (p) =>
|
|
9364
|
-
writeFile: (p, b) =>
|
|
9365
|
-
renameFile: (from, to) =>
|
|
9366
|
-
existsFile: (p) =>
|
|
9696
|
+
readFile: (p) => fs32.readFileSync(p, "utf-8"),
|
|
9697
|
+
writeFile: (p, b) => fs32.writeFileSync(p, b),
|
|
9698
|
+
renameFile: (from, to) => fs32.renameSync(from, to),
|
|
9699
|
+
existsFile: (p) => fs32.existsSync(p)
|
|
9367
9700
|
};
|
|
9368
9701
|
const reportR = await migrate_exports.runMigrationPlan(plan, deps, {
|
|
9369
9702
|
projectRoot: cwd,
|
|
@@ -9384,7 +9717,7 @@ async function runRoadmapMigrate(opts) {
|
|
|
9384
9717
|
}
|
|
9385
9718
|
}
|
|
9386
9719
|
function createRoadmapMigrateCommand() {
|
|
9387
|
-
return new
|
|
9720
|
+
return new Command79("migrate").description("Migrate the project roadmap to a different storage mode").requiredOption("--to <target>", 'Migration target (only "file-less" supported today)').option("--dry-run", "Print the migration plan without making any changes", false).option(
|
|
9388
9721
|
"--format <fmt>",
|
|
9389
9722
|
'Output format: "human" (default) or "json" (single JSON object for CI consumers)',
|
|
9390
9723
|
"human"
|
|
@@ -9419,16 +9752,16 @@ function createRoadmapMigrateCommand() {
|
|
|
9419
9752
|
|
|
9420
9753
|
// src/commands/roadmap/index.ts
|
|
9421
9754
|
function createRoadmapCommand() {
|
|
9422
|
-
const roadmap = new
|
|
9755
|
+
const roadmap = new Command80("roadmap").description("Roadmap management");
|
|
9423
9756
|
roadmap.addCommand(createRoadmapMigrateCommand());
|
|
9424
9757
|
return roadmap;
|
|
9425
9758
|
}
|
|
9426
9759
|
|
|
9427
9760
|
// src/commands/routing/index.ts
|
|
9428
|
-
import { Command as
|
|
9761
|
+
import { Command as Command84 } from "commander";
|
|
9429
9762
|
|
|
9430
9763
|
// src/commands/routing/config.ts
|
|
9431
|
-
import { Command as
|
|
9764
|
+
import { Command as Command81 } from "commander";
|
|
9432
9765
|
|
|
9433
9766
|
// src/commands/routing/http-client.ts
|
|
9434
9767
|
function orchestratorBase() {
|
|
@@ -9438,9 +9771,9 @@ function authHeader() {
|
|
|
9438
9771
|
const tok = process.env["HARNESS_API_TOKEN"];
|
|
9439
9772
|
return tok ? { Authorization: `Bearer ${tok}` } : {};
|
|
9440
9773
|
}
|
|
9441
|
-
async function getJson(
|
|
9774
|
+
async function getJson(path72) {
|
|
9442
9775
|
try {
|
|
9443
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
9776
|
+
const res = await fetch(`${orchestratorBase()}${path72}`, {
|
|
9444
9777
|
headers: { ...authHeader() }
|
|
9445
9778
|
});
|
|
9446
9779
|
const text = await res.text();
|
|
@@ -9455,9 +9788,9 @@ async function getJson(path71) {
|
|
|
9455
9788
|
};
|
|
9456
9789
|
}
|
|
9457
9790
|
}
|
|
9458
|
-
async function postJson(
|
|
9791
|
+
async function postJson(path72, body) {
|
|
9459
9792
|
try {
|
|
9460
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
9793
|
+
const res = await fetch(`${orchestratorBase()}${path72}`, {
|
|
9461
9794
|
method: "POST",
|
|
9462
9795
|
headers: { "Content-Type": "application/json", ...authHeader() },
|
|
9463
9796
|
body: JSON.stringify(body)
|
|
@@ -9493,7 +9826,7 @@ function renderHuman(data) {
|
|
|
9493
9826
|
}
|
|
9494
9827
|
}
|
|
9495
9828
|
function createConfigCommand() {
|
|
9496
|
-
return new
|
|
9829
|
+
return new Command81("config").description("Print active routing config and resolved fallback chains").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
9497
9830
|
const r = await getJson("/api/v1/routing/config");
|
|
9498
9831
|
if (!r.ok) {
|
|
9499
9832
|
if (r.status === 0) {
|
|
@@ -9519,7 +9852,7 @@ function createConfigCommand() {
|
|
|
9519
9852
|
}
|
|
9520
9853
|
|
|
9521
9854
|
// src/commands/routing/trace.ts
|
|
9522
|
-
import { Command as
|
|
9855
|
+
import { Command as Command82 } from "commander";
|
|
9523
9856
|
function buildUseCase(opts) {
|
|
9524
9857
|
if (opts.skill) {
|
|
9525
9858
|
return opts.mode ? { kind: "skill", skillName: opts.skill, cognitiveMode: opts.mode } : { kind: "skill", skillName: opts.skill };
|
|
@@ -9540,7 +9873,7 @@ function renderHuman2(r) {
|
|
|
9540
9873
|
}
|
|
9541
9874
|
}
|
|
9542
9875
|
function createTraceCommand() {
|
|
9543
|
-
return new
|
|
9876
|
+
return new Command82("trace").description("Dry-run a routing decision without dispatching (Spec B F7)").option("--skill <name>", "Skill name to trace").option("--mode <m>", "Cognitive mode to trace (or attach to --skill per spec D12)").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
9544
9877
|
const useCase = buildUseCase(opts);
|
|
9545
9878
|
if (!useCase) {
|
|
9546
9879
|
logger.error("Either --skill <name> or --mode <m> is required");
|
|
@@ -9572,7 +9905,7 @@ function createTraceCommand() {
|
|
|
9572
9905
|
}
|
|
9573
9906
|
|
|
9574
9907
|
// src/commands/routing/decisions.ts
|
|
9575
|
-
import { Command as
|
|
9908
|
+
import { Command as Command83 } from "commander";
|
|
9576
9909
|
function summarizeUseCase(uc) {
|
|
9577
9910
|
switch (uc.kind) {
|
|
9578
9911
|
case "skill":
|
|
@@ -9620,7 +9953,7 @@ function buildQuery(opts) {
|
|
|
9620
9953
|
return q ? `?${q}` : "";
|
|
9621
9954
|
}
|
|
9622
9955
|
function createDecisionsCommand() {
|
|
9623
|
-
return new
|
|
9956
|
+
return new Command83("decisions").description("List recent routing decisions from the orchestrator ring buffer (Spec B F8)").option("--skill <name>", "Filter by useCase.skillName").option("--mode <m>", "Filter by useCase.cognitiveMode").option("--backend <name>", "Filter by chosen backendName").option("--last <N>", "Limit to the N most recent decisions").option("--json", "Emit JSON to stdout instead of human-readable text").action(
|
|
9624
9957
|
async (opts) => {
|
|
9625
9958
|
const query = buildQuery(opts);
|
|
9626
9959
|
const r = await getJson(`/api/v1/routing/decisions${query}`);
|
|
@@ -9650,7 +9983,7 @@ function createDecisionsCommand() {
|
|
|
9650
9983
|
|
|
9651
9984
|
// src/commands/routing/index.ts
|
|
9652
9985
|
function createRoutingCommand() {
|
|
9653
|
-
const cmd = new
|
|
9986
|
+
const cmd = new Command84("routing").description(
|
|
9654
9987
|
"Inspect routing config, trace decisions, and read recent dispatches"
|
|
9655
9988
|
);
|
|
9656
9989
|
cmd.addCommand(createConfigCommand());
|
|
@@ -9661,9 +9994,9 @@ function createRoutingCommand() {
|
|
|
9661
9994
|
|
|
9662
9995
|
// src/commands/scan-config.ts
|
|
9663
9996
|
init_dist3();
|
|
9664
|
-
import { Command as
|
|
9665
|
-
import { existsSync as
|
|
9666
|
-
import { join as
|
|
9997
|
+
import { Command as Command85 } from "commander";
|
|
9998
|
+
import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync19 } from "fs";
|
|
9999
|
+
import { join as join42, relative as relative3 } from "path";
|
|
9667
10000
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
9668
10001
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
9669
10002
|
const highLines = /* @__PURE__ */ new Set();
|
|
@@ -9689,17 +10022,17 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
|
|
|
9689
10022
|
if (!hasHighSeverity) return;
|
|
9690
10023
|
const { cleaned, linesStripped } = stripHighSeverityPatterns(content, injectionFindings);
|
|
9691
10024
|
if (linesStripped > 0) {
|
|
9692
|
-
|
|
10025
|
+
writeFileSync19(filePath, cleaned);
|
|
9693
10026
|
logger.info(
|
|
9694
10027
|
`scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${relative3(targetDir, filePath).replaceAll("\\", "/")}`
|
|
9695
10028
|
);
|
|
9696
10029
|
}
|
|
9697
10030
|
}
|
|
9698
10031
|
function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
9699
|
-
if (!
|
|
10032
|
+
if (!existsSync29(filePath)) return null;
|
|
9700
10033
|
let content;
|
|
9701
10034
|
try {
|
|
9702
|
-
content =
|
|
10035
|
+
content = readFileSync22(filePath, "utf8");
|
|
9703
10036
|
} catch {
|
|
9704
10037
|
return null;
|
|
9705
10038
|
}
|
|
@@ -9720,7 +10053,7 @@ async function runScanConfig(targetDir, options) {
|
|
|
9720
10053
|
const scanner = new SecurityScanner(parseSecurityConfig({}));
|
|
9721
10054
|
const results = [];
|
|
9722
10055
|
for (const configFile of CONFIG_FILES) {
|
|
9723
|
-
const result = scanSingleFile(
|
|
10056
|
+
const result = scanSingleFile(join42(targetDir, configFile), targetDir, scanner, options);
|
|
9724
10057
|
if (result) results.push(result);
|
|
9725
10058
|
}
|
|
9726
10059
|
return { exitCode: computeScanExitCode(results), results };
|
|
@@ -9750,7 +10083,7 @@ function formatTextOutput(result) {
|
|
|
9750
10083
|
}
|
|
9751
10084
|
}
|
|
9752
10085
|
function createScanConfigCommand() {
|
|
9753
|
-
const command = new
|
|
10086
|
+
const command = new Command85("scan-config").description(
|
|
9754
10087
|
"Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
|
|
9755
10088
|
).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
|
|
9756
10089
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -9768,7 +10101,7 @@ function createScanConfigCommand() {
|
|
|
9768
10101
|
|
|
9769
10102
|
// src/commands/search.ts
|
|
9770
10103
|
init_dist();
|
|
9771
|
-
import { Command as
|
|
10104
|
+
import { Command as Command86 } from "commander";
|
|
9772
10105
|
function parseFileKinds(raw) {
|
|
9773
10106
|
if (!raw) return void 0;
|
|
9774
10107
|
const set = /* @__PURE__ */ new Set();
|
|
@@ -9789,7 +10122,7 @@ function fmtBytes(n) {
|
|
|
9789
10122
|
return n.toFixed(0);
|
|
9790
10123
|
}
|
|
9791
10124
|
function createSearchCommand() {
|
|
9792
|
-
return new
|
|
10125
|
+
return new Command86("search").description("Full-text search over archived + live session content.").argument(
|
|
9793
10126
|
"<query>",
|
|
9794
10127
|
"FTS5 query (bare words AND-joined; quotes/AND/OR/NOT/column: for advanced syntax)"
|
|
9795
10128
|
).option("-n, --limit <n>", "Max results", "20").option("--archived-only", "Skip live sessions, only search archived ones").option("--json", "Emit JSON to stdout instead of pretty text").option(
|
|
@@ -9849,9 +10182,9 @@ function createSearchCommand() {
|
|
|
9849
10182
|
}
|
|
9850
10183
|
|
|
9851
10184
|
// src/commands/security-craft.ts
|
|
9852
|
-
import { Command as
|
|
10185
|
+
import { Command as Command87 } from "commander";
|
|
9853
10186
|
function createSecurityCraftCommand() {
|
|
9854
|
-
return new
|
|
10187
|
+
return new Command87("security-craft").description(
|
|
9855
10188
|
"LLM-judgment critique of security posture (TS/JS source). Sixth non-design craft-pipeline ceiling skill (the final sub-project). AST-driven signal detection fires only on files with security-relevant constructs; conservative confidence defaults manage the FP risk inherent in judgment-based security."
|
|
9856
10189
|
).option("-f, --files <files...>", "Optional file scope (overrides discovery)").option("-p, --packages <names...>", "Restrict to specific packages under packages/").option("--max-files <n>", "Cap source-file count (default: 100)").option("--max-signals-per-file <n>", "Cap per-file signal critique (default: 10)").action(async (opts, cmd) => {
|
|
9857
10190
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -9898,10 +10231,10 @@ function printResult5(result, mode, _formatter) {
|
|
|
9898
10231
|
list.push(f);
|
|
9899
10232
|
byFile.set(f.target.file, list);
|
|
9900
10233
|
}
|
|
9901
|
-
for (const [file,
|
|
10234
|
+
for (const [file, fs48] of byFile) {
|
|
9902
10235
|
console.log(`
|
|
9903
10236
|
${file}`);
|
|
9904
|
-
for (const f of
|
|
10237
|
+
for (const f of fs48) {
|
|
9905
10238
|
console.log(
|
|
9906
10239
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.signal}:${f.target.line}`
|
|
9907
10240
|
);
|
|
@@ -9917,24 +10250,24 @@ ${file}`);
|
|
|
9917
10250
|
}
|
|
9918
10251
|
|
|
9919
10252
|
// src/commands/setup.ts
|
|
9920
|
-
import { Command as
|
|
9921
|
-
import * as
|
|
10253
|
+
import { Command as Command88 } from "commander";
|
|
10254
|
+
import * as fs35 from "fs";
|
|
9922
10255
|
import * as os8 from "os";
|
|
9923
|
-
import * as
|
|
9924
|
-
import
|
|
10256
|
+
import * as path53 from "path";
|
|
10257
|
+
import chalk15 from "chalk";
|
|
9925
10258
|
|
|
9926
10259
|
// src/utils/first-run.ts
|
|
9927
|
-
import * as
|
|
10260
|
+
import * as fs33 from "fs";
|
|
9928
10261
|
import * as os7 from "os";
|
|
9929
|
-
import * as
|
|
9930
|
-
var HARNESS_DIR =
|
|
9931
|
-
var MARKER_FILE =
|
|
10262
|
+
import * as path51 from "path";
|
|
10263
|
+
var HARNESS_DIR = path51.join(os7.homedir(), ".harness");
|
|
10264
|
+
var MARKER_FILE = path51.join(HARNESS_DIR, ".setup-complete");
|
|
9932
10265
|
function isFirstRun() {
|
|
9933
|
-
return !
|
|
10266
|
+
return !fs33.existsSync(MARKER_FILE);
|
|
9934
10267
|
}
|
|
9935
10268
|
function markSetupComplete() {
|
|
9936
|
-
|
|
9937
|
-
|
|
10269
|
+
fs33.mkdirSync(HARNESS_DIR, { recursive: true });
|
|
10270
|
+
fs33.writeFileSync(MARKER_FILE, "", "utf-8");
|
|
9938
10271
|
}
|
|
9939
10272
|
function printFirstRunWelcome() {
|
|
9940
10273
|
try {
|
|
@@ -9947,10 +10280,10 @@ function printFirstRunWelcome() {
|
|
|
9947
10280
|
}
|
|
9948
10281
|
|
|
9949
10282
|
// src/commands/telemetry-wizard.ts
|
|
9950
|
-
import * as
|
|
9951
|
-
import * as
|
|
10283
|
+
import * as fs34 from "fs";
|
|
10284
|
+
import * as path52 from "path";
|
|
9952
10285
|
import readline2 from "readline";
|
|
9953
|
-
import
|
|
10286
|
+
import chalk14 from "chalk";
|
|
9954
10287
|
function prompt2(question) {
|
|
9955
10288
|
const rl = readline2.createInterface({
|
|
9956
10289
|
input: process.stdin,
|
|
@@ -9979,21 +10312,21 @@ function isNo(answer) {
|
|
|
9979
10312
|
return answer === "n" || answer === "no";
|
|
9980
10313
|
}
|
|
9981
10314
|
function isTelemetryConfigured(cwd) {
|
|
9982
|
-
const configPath =
|
|
10315
|
+
const configPath = path52.join(cwd, "harness.config.json");
|
|
9983
10316
|
try {
|
|
9984
|
-
const raw = JSON.parse(
|
|
10317
|
+
const raw = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
|
|
9985
10318
|
return raw.telemetry !== void 0 && raw.adoption !== void 0;
|
|
9986
10319
|
} catch {
|
|
9987
10320
|
return false;
|
|
9988
10321
|
}
|
|
9989
10322
|
}
|
|
9990
10323
|
function writeTelemetryConfig(cwd, result) {
|
|
9991
|
-
const configPath =
|
|
10324
|
+
const configPath = path52.join(cwd, "harness.config.json");
|
|
9992
10325
|
try {
|
|
9993
|
-
const raw = JSON.parse(
|
|
10326
|
+
const raw = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
|
|
9994
10327
|
raw.telemetry = { enabled: result.telemetryEnabled };
|
|
9995
10328
|
raw.adoption = { enabled: result.adoptionEnabled };
|
|
9996
|
-
|
|
10329
|
+
fs34.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
9997
10330
|
} catch (err) {
|
|
9998
10331
|
console.warn(
|
|
9999
10332
|
`\u26A0 Could not update ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -10001,15 +10334,15 @@ function writeTelemetryConfig(cwd, result) {
|
|
|
10001
10334
|
}
|
|
10002
10335
|
const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
|
|
10003
10336
|
if (hasIdentity) {
|
|
10004
|
-
const harnessDir =
|
|
10005
|
-
|
|
10006
|
-
const telemetryFile =
|
|
10007
|
-
|
|
10337
|
+
const harnessDir = path52.join(cwd, ".harness");
|
|
10338
|
+
fs34.mkdirSync(harnessDir, { recursive: true });
|
|
10339
|
+
const telemetryFile = path52.join(harnessDir, "telemetry.json");
|
|
10340
|
+
fs34.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
|
|
10008
10341
|
}
|
|
10009
10342
|
}
|
|
10010
10343
|
async function promptIdentity() {
|
|
10011
10344
|
console.log(
|
|
10012
|
-
` ${
|
|
10345
|
+
` ${chalk14.cyan("Identity")} (optional) \u2014 tag telemetry with project/team/alias for filtering.`
|
|
10013
10346
|
);
|
|
10014
10347
|
console.log(" Leave blank to keep anonymous.");
|
|
10015
10348
|
const answer = await prompt2(" Set identity fields? (y/N) ");
|
|
@@ -10027,20 +10360,20 @@ async function promptIdentity() {
|
|
|
10027
10360
|
async function runTelemetryWizard() {
|
|
10028
10361
|
if (!process.stdin.isTTY) return null;
|
|
10029
10362
|
console.log("");
|
|
10030
|
-
console.log(` ${
|
|
10363
|
+
console.log(` ${chalk14.bold("Telemetry Configuration")}`);
|
|
10031
10364
|
console.log("");
|
|
10032
10365
|
console.log(" Harness can collect anonymous usage data to help improve the tool.");
|
|
10033
10366
|
console.log(" You can change these settings anytime in harness.config.json.");
|
|
10034
10367
|
console.log("");
|
|
10035
10368
|
console.log(
|
|
10036
|
-
` ${
|
|
10369
|
+
` ${chalk14.cyan("Anonymous telemetry")} \u2014 error rates, command usage, performance metrics.`
|
|
10037
10370
|
);
|
|
10038
10371
|
console.log(" No code, file contents, or personally identifiable info is sent.");
|
|
10039
10372
|
const telemetryAnswer = await prompt2(" Enable anonymous telemetry? (Y/n) ");
|
|
10040
10373
|
const telemetryEnabled = !isNo(telemetryAnswer);
|
|
10041
10374
|
console.log("");
|
|
10042
10375
|
console.log(
|
|
10043
|
-
` ${
|
|
10376
|
+
` ${chalk14.cyan("Adoption tracking")} \u2014 which skills are used, how often, and success rates.`
|
|
10044
10377
|
);
|
|
10045
10378
|
console.log(" Stored locally in .harness/adoption/. Never sent externally.");
|
|
10046
10379
|
const adoptionAnswer = await prompt2(" Enable local adoption tracking? (Y/n) ");
|
|
@@ -10050,8 +10383,8 @@ async function runTelemetryWizard() {
|
|
|
10050
10383
|
return { telemetryEnabled, adoptionEnabled, identity };
|
|
10051
10384
|
}
|
|
10052
10385
|
async function ensureTelemetryConfigured(cwd) {
|
|
10053
|
-
const configPath =
|
|
10054
|
-
if (!
|
|
10386
|
+
const configPath = path52.join(cwd, "harness.config.json");
|
|
10387
|
+
if (!fs34.existsSync(configPath)) {
|
|
10055
10388
|
return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
|
|
10056
10389
|
}
|
|
10057
10390
|
if (isTelemetryConfigured(cwd)) {
|
|
@@ -10097,7 +10430,7 @@ function runSlashCommandGeneration() {
|
|
|
10097
10430
|
}
|
|
10098
10431
|
}
|
|
10099
10432
|
function detectClient(dirName) {
|
|
10100
|
-
return
|
|
10433
|
+
return fs35.existsSync(path53.join(os8.homedir(), dirName));
|
|
10101
10434
|
}
|
|
10102
10435
|
async function runMcpSetup(cwd) {
|
|
10103
10436
|
const results = [];
|
|
@@ -10113,7 +10446,7 @@ async function runMcpSetup(cwd) {
|
|
|
10113
10446
|
{ name: "Cursor", dir: ".cursor", client: "cursor", configTarget: ".cursor/mcp.json" },
|
|
10114
10447
|
{
|
|
10115
10448
|
name: "OpenCode",
|
|
10116
|
-
dir:
|
|
10449
|
+
dir: path53.join(".config", "opencode"),
|
|
10117
10450
|
client: "opencode",
|
|
10118
10451
|
configTarget: "opencode.json"
|
|
10119
10452
|
}
|
|
@@ -10140,12 +10473,12 @@ async function runMcpSetup(cwd) {
|
|
|
10140
10473
|
return results;
|
|
10141
10474
|
}
|
|
10142
10475
|
function formatStep(result) {
|
|
10143
|
-
const icon = result.status === "pass" ?
|
|
10476
|
+
const icon = result.status === "pass" ? chalk15.green("\u2713") : result.status === "warn" ? chalk15.yellow("\u26A0") : chalk15.red("\u2717");
|
|
10144
10477
|
return ` ${icon} ${result.message}`;
|
|
10145
10478
|
}
|
|
10146
10479
|
function configureTier0Integrations(cwd) {
|
|
10147
10480
|
try {
|
|
10148
|
-
const mcpPath =
|
|
10481
|
+
const mcpPath = path53.join(cwd, ".mcp.json");
|
|
10149
10482
|
const config = readMcpConfig(mcpPath);
|
|
10150
10483
|
const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
|
|
10151
10484
|
const added = [];
|
|
@@ -10154,9 +10487,9 @@ function configureTier0Integrations(cwd) {
|
|
|
10154
10487
|
writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
|
|
10155
10488
|
added.push(integration.displayName);
|
|
10156
10489
|
}
|
|
10157
|
-
const geminiDir =
|
|
10158
|
-
if (
|
|
10159
|
-
const geminiPath =
|
|
10490
|
+
const geminiDir = path53.join(cwd, ".gemini");
|
|
10491
|
+
if (fs35.existsSync(geminiDir)) {
|
|
10492
|
+
const geminiPath = path53.join(geminiDir, "settings.json");
|
|
10160
10493
|
const geminiConfig = readMcpConfig(geminiPath);
|
|
10161
10494
|
for (const integration of tier0) {
|
|
10162
10495
|
if (!geminiConfig.mcpServers[integration.name]) {
|
|
@@ -10164,9 +10497,9 @@ function configureTier0Integrations(cwd) {
|
|
|
10164
10497
|
}
|
|
10165
10498
|
}
|
|
10166
10499
|
}
|
|
10167
|
-
const opencodePath =
|
|
10168
|
-
const opencodeGlobalDir =
|
|
10169
|
-
if (
|
|
10500
|
+
const opencodePath = path53.join(cwd, "opencode.json");
|
|
10501
|
+
const opencodeGlobalDir = path53.join(os8.homedir(), ".config", "opencode");
|
|
10502
|
+
if (fs35.existsSync(opencodePath) || fs35.existsSync(opencodeGlobalDir)) {
|
|
10170
10503
|
for (const integration of tier0) {
|
|
10171
10504
|
writeOpencodeMcpEntry(opencodePath, integration.name, integration.mcpConfig);
|
|
10172
10505
|
}
|
|
@@ -10184,14 +10517,14 @@ function configureTier0Integrations(cwd) {
|
|
|
10184
10517
|
}
|
|
10185
10518
|
}
|
|
10186
10519
|
function ensureHooks(cwd) {
|
|
10187
|
-
const configPath =
|
|
10188
|
-
if (!
|
|
10520
|
+
const configPath = path53.join(cwd, "harness.config.json");
|
|
10521
|
+
if (!fs35.existsSync(configPath)) {
|
|
10189
10522
|
return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
|
|
10190
10523
|
}
|
|
10191
10524
|
let profile = "standard";
|
|
10192
|
-
const profilePath =
|
|
10525
|
+
const profilePath = path53.join(cwd, ".harness", "hooks", "profile.json");
|
|
10193
10526
|
try {
|
|
10194
|
-
const data = JSON.parse(
|
|
10527
|
+
const data = JSON.parse(fs35.readFileSync(profilePath, "utf-8"));
|
|
10195
10528
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
10196
10529
|
profile = data.profile;
|
|
10197
10530
|
}
|
|
@@ -10223,7 +10556,7 @@ async function detectLegacyLayout(cwd) {
|
|
|
10223
10556
|
}
|
|
10224
10557
|
async function runInitialGraphScan(cwd) {
|
|
10225
10558
|
try {
|
|
10226
|
-
const { runScan: runScan2 } = await import("./scan-
|
|
10559
|
+
const { runScan: runScan2 } = await import("./scan-P7YFKB77.js");
|
|
10227
10560
|
const result = await runScan2(cwd);
|
|
10228
10561
|
return {
|
|
10229
10562
|
status: "pass",
|
|
@@ -10262,10 +10595,10 @@ async function runSetup(cwd) {
|
|
|
10262
10595
|
return { steps, success };
|
|
10263
10596
|
}
|
|
10264
10597
|
function createSetupCommand() {
|
|
10265
|
-
return new
|
|
10598
|
+
return new Command88("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
|
|
10266
10599
|
const cwd = process.cwd();
|
|
10267
10600
|
console.log("");
|
|
10268
|
-
console.log(` ${
|
|
10601
|
+
console.log(` ${chalk15.bold("harness setup")}`);
|
|
10269
10602
|
console.log("");
|
|
10270
10603
|
const { steps, success } = await runSetup(cwd);
|
|
10271
10604
|
for (const step of steps) {
|
|
@@ -10285,15 +10618,15 @@ function createSetupCommand() {
|
|
|
10285
10618
|
|
|
10286
10619
|
// src/commands/share.ts
|
|
10287
10620
|
init_dist3();
|
|
10288
|
-
import { Command as
|
|
10289
|
-
import * as
|
|
10290
|
-
import * as
|
|
10291
|
-
import { parse as
|
|
10621
|
+
import { Command as Command89 } from "commander";
|
|
10622
|
+
import * as fs36 from "fs";
|
|
10623
|
+
import * as path54 from "path";
|
|
10624
|
+
import { parse as parseYaml3 } from "yaml";
|
|
10292
10625
|
var MANIFEST_FILENAME = "constraints.yaml";
|
|
10293
10626
|
async function runShareAction(projectPath, options) {
|
|
10294
|
-
const rootDir =
|
|
10295
|
-
const manifestPath =
|
|
10296
|
-
if (!
|
|
10627
|
+
const rootDir = path54.resolve(projectPath);
|
|
10628
|
+
const manifestPath = path54.join(rootDir, MANIFEST_FILENAME);
|
|
10629
|
+
if (!fs36.existsSync(manifestPath)) {
|
|
10297
10630
|
logger.error(
|
|
10298
10631
|
`No ${MANIFEST_FILENAME} found at ${manifestPath}.
|
|
10299
10632
|
Create a constraints.yaml in your project root to define what to share.`
|
|
@@ -10302,8 +10635,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10302
10635
|
}
|
|
10303
10636
|
let parsed;
|
|
10304
10637
|
try {
|
|
10305
|
-
const raw =
|
|
10306
|
-
parsed =
|
|
10638
|
+
const raw = fs36.readFileSync(manifestPath, "utf-8");
|
|
10639
|
+
parsed = parseYaml3(raw);
|
|
10307
10640
|
} catch (err) {
|
|
10308
10641
|
logger.error(
|
|
10309
10642
|
`Failed to read ${MANIFEST_FILENAME}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -10316,7 +10649,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10316
10649
|
process.exit(1);
|
|
10317
10650
|
}
|
|
10318
10651
|
const manifest = manifestResult.value;
|
|
10319
|
-
const configResult = resolveConfig(
|
|
10652
|
+
const configResult = resolveConfig(path54.join(rootDir, "harness.config.json"));
|
|
10320
10653
|
if (!configResult.ok) {
|
|
10321
10654
|
logger.error(configResult.error.message);
|
|
10322
10655
|
process.exit(1);
|
|
@@ -10334,8 +10667,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10334
10667
|
);
|
|
10335
10668
|
process.exit(1);
|
|
10336
10669
|
}
|
|
10337
|
-
const outputDir =
|
|
10338
|
-
const outputPath =
|
|
10670
|
+
const outputDir = path54.resolve(options.output);
|
|
10671
|
+
const outputPath = path54.join(outputDir, `${manifest.name}.harness-constraints.json`);
|
|
10339
10672
|
const writeResult = await writeConfig(outputPath, bundle);
|
|
10340
10673
|
if (!writeResult.ok) {
|
|
10341
10674
|
logger.error(`Failed to write bundle: ${writeResult.error.message}`);
|
|
@@ -10344,28 +10677,28 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10344
10677
|
logger.success(`Bundle written to ${outputPath}`);
|
|
10345
10678
|
}
|
|
10346
10679
|
function createShareCommand() {
|
|
10347
|
-
return new
|
|
10680
|
+
return new Command89("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) => {
|
|
10348
10681
|
await runShareAction(projectPath, options);
|
|
10349
10682
|
});
|
|
10350
10683
|
}
|
|
10351
10684
|
|
|
10352
10685
|
// src/commands/skill/index.ts
|
|
10353
|
-
import { Command as
|
|
10686
|
+
import { Command as Command97 } from "commander";
|
|
10354
10687
|
|
|
10355
10688
|
// src/commands/skill/list.ts
|
|
10356
|
-
import { Command as
|
|
10357
|
-
import * as
|
|
10358
|
-
import * as
|
|
10689
|
+
import { Command as Command90 } from "commander";
|
|
10690
|
+
import * as fs37 from "fs";
|
|
10691
|
+
import * as path55 from "path";
|
|
10359
10692
|
import { parse } from "yaml";
|
|
10360
10693
|
function scanDirectory(dirPath, source) {
|
|
10361
|
-
if (!
|
|
10362
|
-
const entries =
|
|
10694
|
+
if (!fs37.existsSync(dirPath)) return [];
|
|
10695
|
+
const entries = fs37.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
10363
10696
|
const skills = [];
|
|
10364
10697
|
for (const name of entries) {
|
|
10365
|
-
const yamlPath =
|
|
10366
|
-
if (!
|
|
10698
|
+
const yamlPath = path55.join(dirPath, name, "skill.yaml");
|
|
10699
|
+
if (!fs37.existsSync(yamlPath)) continue;
|
|
10367
10700
|
try {
|
|
10368
|
-
const raw =
|
|
10701
|
+
const raw = fs37.readFileSync(yamlPath, "utf-8");
|
|
10369
10702
|
const parsed = parse(raw);
|
|
10370
10703
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
10371
10704
|
if (result.success) {
|
|
@@ -10383,10 +10716,10 @@ function scanDirectory(dirPath, source) {
|
|
|
10383
10716
|
}
|
|
10384
10717
|
function collectCommunitySkills(seen, allSkills) {
|
|
10385
10718
|
const globalDir = resolveGlobalSkillsDir();
|
|
10386
|
-
const skillsDir =
|
|
10387
|
-
const communityBase =
|
|
10388
|
-
const communityPlatformDir =
|
|
10389
|
-
const lockfilePath =
|
|
10719
|
+
const skillsDir = path55.dirname(globalDir);
|
|
10720
|
+
const communityBase = path55.join(skillsDir, "community");
|
|
10721
|
+
const communityPlatformDir = path55.join(communityBase, "claude-code");
|
|
10722
|
+
const lockfilePath = path55.join(communityBase, "skills-lock.json");
|
|
10390
10723
|
const lockfile = readLockfile2(lockfilePath);
|
|
10391
10724
|
const communitySkills = scanDirectory(communityPlatformDir, "community");
|
|
10392
10725
|
for (const skill of communitySkills) {
|
|
@@ -10456,7 +10789,7 @@ function printSkillsVerbose(skills) {
|
|
|
10456
10789
|
for (const s of skills) printSkillEntry(s);
|
|
10457
10790
|
}
|
|
10458
10791
|
function createListCommand3() {
|
|
10459
|
-
return new
|
|
10792
|
+
return new Command90("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) => {
|
|
10460
10793
|
const globalOpts = cmd.optsWithGlobals();
|
|
10461
10794
|
const skills = collectSkills({ filter: resolveFilter(opts) });
|
|
10462
10795
|
if (globalOpts.json) {
|
|
@@ -10471,9 +10804,9 @@ function createListCommand3() {
|
|
|
10471
10804
|
}
|
|
10472
10805
|
|
|
10473
10806
|
// src/commands/skill/run.ts
|
|
10474
|
-
import { Command as
|
|
10475
|
-
import * as
|
|
10476
|
-
import * as
|
|
10807
|
+
import { Command as Command91 } from "commander";
|
|
10808
|
+
import * as fs38 from "fs";
|
|
10809
|
+
import * as path56 from "path";
|
|
10477
10810
|
import { parse as parse2 } from "yaml";
|
|
10478
10811
|
|
|
10479
10812
|
// src/skill/complexity.ts
|
|
@@ -10569,10 +10902,10 @@ ${options.principles}`);
|
|
|
10569
10902
|
|
|
10570
10903
|
// src/commands/skill/run.ts
|
|
10571
10904
|
function loadSkillMetadata(skillDir) {
|
|
10572
|
-
const yamlPath =
|
|
10573
|
-
if (!
|
|
10905
|
+
const yamlPath = path56.join(skillDir, "skill.yaml");
|
|
10906
|
+
if (!fs38.existsSync(yamlPath)) return null;
|
|
10574
10907
|
try {
|
|
10575
|
-
const result = SkillMetadataSchema.safeParse(parse2(
|
|
10908
|
+
const result = SkillMetadataSchema.safeParse(parse2(fs38.readFileSync(yamlPath, "utf-8")));
|
|
10576
10909
|
return result.success ? result.data : null;
|
|
10577
10910
|
} catch {
|
|
10578
10911
|
return null;
|
|
@@ -10584,22 +10917,22 @@ function resolveComplexity(metadata, requested, projectPath) {
|
|
|
10584
10917
|
return requested;
|
|
10585
10918
|
}
|
|
10586
10919
|
function loadPrinciples(projectPath) {
|
|
10587
|
-
const principlesPath =
|
|
10588
|
-
return
|
|
10920
|
+
const principlesPath = path56.join(projectPath, "docs", "principles.md");
|
|
10921
|
+
return fs38.existsSync(principlesPath) ? fs38.readFileSync(principlesPath, "utf-8") : void 0;
|
|
10589
10922
|
}
|
|
10590
10923
|
function readMostRecentFileInDir(dirPath) {
|
|
10591
|
-
const files =
|
|
10592
|
-
if (files.length > 0) return
|
|
10924
|
+
const files = fs38.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs38.statSync(path56.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
10925
|
+
if (files.length > 0) return fs38.readFileSync(path56.join(dirPath, files[0].name), "utf-8");
|
|
10593
10926
|
return void 0;
|
|
10594
10927
|
}
|
|
10595
10928
|
function loadPriorState(metadata, projectPath) {
|
|
10596
10929
|
if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
|
|
10597
10930
|
for (const stateFilePath of metadata.state.files) {
|
|
10598
|
-
const fullPath =
|
|
10599
|
-
if (!
|
|
10600
|
-
const stat =
|
|
10931
|
+
const fullPath = path56.join(projectPath, stateFilePath);
|
|
10932
|
+
if (!fs38.existsSync(fullPath)) continue;
|
|
10933
|
+
const stat = fs38.statSync(fullPath);
|
|
10601
10934
|
if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
|
|
10602
|
-
return
|
|
10935
|
+
return fs38.readFileSync(fullPath, "utf-8");
|
|
10603
10936
|
}
|
|
10604
10937
|
return void 0;
|
|
10605
10938
|
}
|
|
@@ -10619,9 +10952,9 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
10619
10952
|
}
|
|
10620
10953
|
function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
10621
10954
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
10622
|
-
const stateFile =
|
|
10623
|
-
if (!
|
|
10624
|
-
const stateContent =
|
|
10955
|
+
const stateFile = path56.join(projectPath, ".harness", "state.json");
|
|
10956
|
+
if (!fs38.existsSync(stateFile)) return content;
|
|
10957
|
+
const stateContent = fs38.readFileSync(stateFile, "utf-8");
|
|
10625
10958
|
return content + `
|
|
10626
10959
|
|
|
10627
10960
|
---
|
|
@@ -10633,14 +10966,14 @@ ${stateContent}
|
|
|
10633
10966
|
}
|
|
10634
10967
|
async function runSkill(name, opts) {
|
|
10635
10968
|
const skillsDir = resolveSkillsDir();
|
|
10636
|
-
const skillDir =
|
|
10637
|
-
if (!
|
|
10969
|
+
const skillDir = path56.join(skillsDir, name);
|
|
10970
|
+
if (!fs38.existsSync(skillDir)) {
|
|
10638
10971
|
logger.error(`Skill not found: ${name}`);
|
|
10639
10972
|
process.exit(ExitCode.ERROR);
|
|
10640
10973
|
return;
|
|
10641
10974
|
}
|
|
10642
10975
|
const metadata = loadSkillMetadata(skillDir);
|
|
10643
|
-
const projectPath = opts.path ?
|
|
10976
|
+
const projectPath = opts.path ? path56.resolve(opts.path) : process.cwd();
|
|
10644
10977
|
const complexity = resolveComplexity(
|
|
10645
10978
|
metadata,
|
|
10646
10979
|
opts.complexity ?? "standard",
|
|
@@ -10667,14 +11000,14 @@ async function runSkill(name, opts) {
|
|
|
10667
11000
|
...stateWarning !== void 0 && { stateWarning },
|
|
10668
11001
|
...opts.party !== void 0 && { party: opts.party }
|
|
10669
11002
|
});
|
|
10670
|
-
const skillMdPath =
|
|
10671
|
-
if (!
|
|
11003
|
+
const skillMdPath = path56.join(skillDir, "SKILL.md");
|
|
11004
|
+
if (!fs38.existsSync(skillMdPath)) {
|
|
10672
11005
|
logger.error(`SKILL.md not found for skill: ${name}`);
|
|
10673
11006
|
process.exit(ExitCode.ERROR);
|
|
10674
11007
|
return;
|
|
10675
11008
|
}
|
|
10676
11009
|
const content = appendProjectState(
|
|
10677
|
-
|
|
11010
|
+
fs38.readFileSync(skillMdPath, "utf-8"),
|
|
10678
11011
|
metadata,
|
|
10679
11012
|
projectPath,
|
|
10680
11013
|
!!opts.path
|
|
@@ -10685,16 +11018,16 @@ async function runSkill(name, opts) {
|
|
|
10685
11018
|
process.exit(ExitCode.SUCCESS);
|
|
10686
11019
|
}
|
|
10687
11020
|
function createRunCommand3() {
|
|
10688
|
-
return new
|
|
11021
|
+
return new Command91("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").option(
|
|
10689
11022
|
"--backend <name>",
|
|
10690
11023
|
"Spec B: one-shot routing override forwarded to the orchestrator as HARNESS_BACKEND_OVERRIDE"
|
|
10691
11024
|
).action(async (name, opts) => runSkill(name, opts));
|
|
10692
11025
|
}
|
|
10693
11026
|
|
|
10694
11027
|
// src/commands/skill/validate.ts
|
|
10695
|
-
import { Command as
|
|
10696
|
-
import * as
|
|
10697
|
-
import * as
|
|
11028
|
+
import { Command as Command92 } from "commander";
|
|
11029
|
+
import * as fs39 from "fs";
|
|
11030
|
+
import * as path57 from "path";
|
|
10698
11031
|
import { parse as parse3 } from "yaml";
|
|
10699
11032
|
var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
10700
11033
|
"## When to Use",
|
|
@@ -10706,11 +11039,11 @@ var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
|
10706
11039
|
];
|
|
10707
11040
|
var KNOWLEDGE_REQUIRED_SECTIONS = ["## Instructions"];
|
|
10708
11041
|
function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
10709
|
-
if (!
|
|
11042
|
+
if (!fs39.existsSync(skillMdPath)) {
|
|
10710
11043
|
errors.push(`${name}: missing SKILL.md`);
|
|
10711
11044
|
return;
|
|
10712
11045
|
}
|
|
10713
|
-
const mdContent =
|
|
11046
|
+
const mdContent = fs39.readFileSync(skillMdPath, "utf-8");
|
|
10714
11047
|
if (!mdContent.trim().startsWith("# ")) {
|
|
10715
11048
|
errors.push(`${name}/SKILL.md: must start with an h1 heading`);
|
|
10716
11049
|
}
|
|
@@ -10735,20 +11068,20 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
|
10735
11068
|
}
|
|
10736
11069
|
}
|
|
10737
11070
|
function validateSkillEntry(name, skillsDir, errors) {
|
|
10738
|
-
const skillDir =
|
|
10739
|
-
const yamlPath =
|
|
10740
|
-
if (!
|
|
11071
|
+
const skillDir = path57.join(skillsDir, name);
|
|
11072
|
+
const yamlPath = path57.join(skillDir, "skill.yaml");
|
|
11073
|
+
if (!fs39.existsSync(yamlPath)) {
|
|
10741
11074
|
errors.push(`${name}: missing skill.yaml`);
|
|
10742
11075
|
return false;
|
|
10743
11076
|
}
|
|
10744
11077
|
try {
|
|
10745
|
-
const raw =
|
|
11078
|
+
const raw = fs39.readFileSync(yamlPath, "utf-8");
|
|
10746
11079
|
const result = SkillMetadataSchema.safeParse(parse3(raw));
|
|
10747
11080
|
if (!result.success) {
|
|
10748
11081
|
errors.push(`${name}/skill.yaml: ${result.error.message}`);
|
|
10749
11082
|
return false;
|
|
10750
11083
|
}
|
|
10751
|
-
validateSkillMd(name,
|
|
11084
|
+
validateSkillMd(name, path57.join(skillDir, "SKILL.md"), result.data.type, errors);
|
|
10752
11085
|
return true;
|
|
10753
11086
|
} catch (e) {
|
|
10754
11087
|
errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -10756,15 +11089,15 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
10756
11089
|
}
|
|
10757
11090
|
}
|
|
10758
11091
|
function createValidateCommand2() {
|
|
10759
|
-
return new
|
|
11092
|
+
return new Command92("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
10760
11093
|
const globalOpts = cmd.optsWithGlobals();
|
|
10761
11094
|
const skillsDir = resolveSkillsDir();
|
|
10762
|
-
if (!
|
|
11095
|
+
if (!fs39.existsSync(skillsDir)) {
|
|
10763
11096
|
logger.info("No skills directory found.");
|
|
10764
11097
|
process.exit(ExitCode.SUCCESS);
|
|
10765
11098
|
return;
|
|
10766
11099
|
}
|
|
10767
|
-
const entries =
|
|
11100
|
+
const entries = fs39.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
10768
11101
|
const errors = [];
|
|
10769
11102
|
let validated = 0;
|
|
10770
11103
|
for (const name of entries) {
|
|
@@ -10786,24 +11119,24 @@ function createValidateCommand2() {
|
|
|
10786
11119
|
}
|
|
10787
11120
|
|
|
10788
11121
|
// src/commands/skill/info.ts
|
|
10789
|
-
import { Command as
|
|
10790
|
-
import * as
|
|
10791
|
-
import * as
|
|
11122
|
+
import { Command as Command93 } from "commander";
|
|
11123
|
+
import * as fs40 from "fs";
|
|
11124
|
+
import * as path58 from "path";
|
|
10792
11125
|
import { parse as parse4 } from "yaml";
|
|
10793
11126
|
function loadSkillMetadata2(name) {
|
|
10794
11127
|
const skillsDir = resolveSkillsDir();
|
|
10795
|
-
const skillDir =
|
|
10796
|
-
if (!
|
|
11128
|
+
const skillDir = path58.join(skillsDir, name);
|
|
11129
|
+
if (!fs40.existsSync(skillDir)) {
|
|
10797
11130
|
logger.error(`Skill not found: ${name}`);
|
|
10798
11131
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
10799
11132
|
}
|
|
10800
|
-
const yamlPath =
|
|
10801
|
-
if (!
|
|
11133
|
+
const yamlPath = path58.join(skillDir, "skill.yaml");
|
|
11134
|
+
if (!fs40.existsSync(yamlPath)) {
|
|
10802
11135
|
logger.error(`skill.yaml not found for skill: ${name}`);
|
|
10803
11136
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
10804
11137
|
}
|
|
10805
11138
|
try {
|
|
10806
|
-
const raw =
|
|
11139
|
+
const raw = fs40.readFileSync(yamlPath, "utf-8");
|
|
10807
11140
|
const parsed = parse4(raw);
|
|
10808
11141
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
10809
11142
|
if (!result.success) {
|
|
@@ -10832,7 +11165,7 @@ function printSkillInfo(skill) {
|
|
|
10832
11165
|
console.log(`Persistent: ${skill.state.persistent}`);
|
|
10833
11166
|
}
|
|
10834
11167
|
function createInfoCommand() {
|
|
10835
|
-
return new
|
|
11168
|
+
return new Command93("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
|
|
10836
11169
|
const globalOpts = cmd.optsWithGlobals();
|
|
10837
11170
|
const loaded = loadSkillMetadata2(name);
|
|
10838
11171
|
if (!loaded.ok) {
|
|
@@ -10849,7 +11182,7 @@ function createInfoCommand() {
|
|
|
10849
11182
|
}
|
|
10850
11183
|
|
|
10851
11184
|
// src/commands/skill/search.ts
|
|
10852
|
-
import { Command as
|
|
11185
|
+
import { Command as Command94 } from "commander";
|
|
10853
11186
|
async function runSearch(query, opts) {
|
|
10854
11187
|
const results = await searchNpmRegistry(query, opts.registry);
|
|
10855
11188
|
return results.filter((r) => {
|
|
@@ -10894,15 +11227,15 @@ async function runSearchAction(query, opts, globalOpts) {
|
|
|
10894
11227
|
}
|
|
10895
11228
|
}
|
|
10896
11229
|
function createSearchCommand2() {
|
|
10897
|
-
return new
|
|
11230
|
+
return new Command94("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) => {
|
|
10898
11231
|
await runSearchAction(query, opts, cmd.optsWithGlobals());
|
|
10899
11232
|
});
|
|
10900
11233
|
}
|
|
10901
11234
|
|
|
10902
11235
|
// src/commands/skill/create.ts
|
|
10903
|
-
import { Command as
|
|
10904
|
-
import * as
|
|
10905
|
-
import * as
|
|
11236
|
+
import { Command as Command95 } from "commander";
|
|
11237
|
+
import * as path59 from "path";
|
|
11238
|
+
import * as fs41 from "fs";
|
|
10906
11239
|
import YAML from "yaml";
|
|
10907
11240
|
var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
10908
11241
|
function buildReadme(name, description) {
|
|
@@ -10986,22 +11319,22 @@ function runCreate(name, opts) {
|
|
|
10986
11319
|
if (!KEBAB_CASE_RE.test(name)) {
|
|
10987
11320
|
throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
|
|
10988
11321
|
}
|
|
10989
|
-
const baseDir = opts.outputDir ??
|
|
10990
|
-
const skillDir =
|
|
10991
|
-
if (
|
|
11322
|
+
const baseDir = opts.outputDir ?? path59.join(process.cwd(), "agents", "skills", "claude-code");
|
|
11323
|
+
const skillDir = path59.join(baseDir, name);
|
|
11324
|
+
if (fs41.existsSync(skillDir)) {
|
|
10992
11325
|
throw new Error(`Skill directory already exists: ${skillDir}`);
|
|
10993
11326
|
}
|
|
10994
|
-
|
|
11327
|
+
fs41.mkdirSync(skillDir, { recursive: true });
|
|
10995
11328
|
const description = opts.description || `A community skill: ${name}`;
|
|
10996
11329
|
const skillYaml = buildSkillYaml(name, opts);
|
|
10997
|
-
const skillYamlPath =
|
|
10998
|
-
|
|
11330
|
+
const skillYamlPath = path59.join(skillDir, "skill.yaml");
|
|
11331
|
+
fs41.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
|
|
10999
11332
|
const skillMd = buildSkillMd(name, description);
|
|
11000
|
-
const skillMdPath =
|
|
11001
|
-
|
|
11333
|
+
const skillMdPath = path59.join(skillDir, "SKILL.md");
|
|
11334
|
+
fs41.writeFileSync(skillMdPath, skillMd);
|
|
11002
11335
|
const readme = buildReadme(name, description);
|
|
11003
|
-
const readmePath =
|
|
11004
|
-
|
|
11336
|
+
const readmePath = path59.join(skillDir, "README.md");
|
|
11337
|
+
fs41.writeFileSync(readmePath, readme);
|
|
11005
11338
|
return {
|
|
11006
11339
|
name,
|
|
11007
11340
|
directory: skillDir,
|
|
@@ -11015,12 +11348,12 @@ function printCreateResult(name, result) {
|
|
|
11015
11348
|
}
|
|
11016
11349
|
logger.info(`
|
|
11017
11350
|
Next steps:`);
|
|
11018
|
-
logger.info(` 1. Edit ${
|
|
11351
|
+
logger.info(` 1. Edit ${path59.join(result.directory, "SKILL.md")} with your skill content`);
|
|
11019
11352
|
logger.info(` 2. Run: harness skill validate ${name}`);
|
|
11020
11353
|
logger.info(` 3. Run: harness skills publish`);
|
|
11021
11354
|
}
|
|
11022
11355
|
function createCreateCommand() {
|
|
11023
|
-
return new
|
|
11356
|
+
return new Command95("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) => {
|
|
11024
11357
|
const globalOpts = cmd.optsWithGlobals();
|
|
11025
11358
|
try {
|
|
11026
11359
|
const result = runCreate(name, {
|
|
@@ -11043,26 +11376,26 @@ function createCreateCommand() {
|
|
|
11043
11376
|
}
|
|
11044
11377
|
|
|
11045
11378
|
// src/commands/skill/publish.ts
|
|
11046
|
-
import { Command as
|
|
11047
|
-
import * as
|
|
11048
|
-
import * as
|
|
11379
|
+
import { Command as Command96 } from "commander";
|
|
11380
|
+
import * as fs43 from "fs";
|
|
11381
|
+
import * as path61 from "path";
|
|
11049
11382
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
11050
11383
|
|
|
11051
11384
|
// src/registry/validator.ts
|
|
11052
|
-
import * as
|
|
11053
|
-
import * as
|
|
11385
|
+
import * as fs42 from "fs";
|
|
11386
|
+
import * as path60 from "path";
|
|
11054
11387
|
import { parse as parse5 } from "yaml";
|
|
11055
11388
|
import semver4 from "semver";
|
|
11056
11389
|
async function validateForPublish(skillDir, registryUrl) {
|
|
11057
11390
|
const errors = [];
|
|
11058
|
-
const skillYamlPath =
|
|
11059
|
-
if (!
|
|
11391
|
+
const skillYamlPath = path60.join(skillDir, "skill.yaml");
|
|
11392
|
+
if (!fs42.existsSync(skillYamlPath)) {
|
|
11060
11393
|
errors.push("skill.yaml not found. Create one with: harness skill create <name>");
|
|
11061
11394
|
return { valid: false, errors };
|
|
11062
11395
|
}
|
|
11063
11396
|
let skillMeta;
|
|
11064
11397
|
try {
|
|
11065
|
-
const raw =
|
|
11398
|
+
const raw = fs42.readFileSync(skillYamlPath, "utf-8");
|
|
11066
11399
|
const parsed = parse5(raw);
|
|
11067
11400
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
11068
11401
|
if (!result.success) {
|
|
@@ -11084,11 +11417,11 @@ async function validateForPublish(skillDir, registryUrl) {
|
|
|
11084
11417
|
if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
|
|
11085
11418
|
errors.push("At least one trigger is required. Add triggers to skill.yaml.");
|
|
11086
11419
|
}
|
|
11087
|
-
const skillMdPath =
|
|
11088
|
-
if (!
|
|
11420
|
+
const skillMdPath = path60.join(skillDir, "SKILL.md");
|
|
11421
|
+
if (!fs42.existsSync(skillMdPath)) {
|
|
11089
11422
|
errors.push("SKILL.md not found. Create it with content describing your skill.");
|
|
11090
11423
|
} else {
|
|
11091
|
-
const content =
|
|
11424
|
+
const content = fs42.readFileSync(skillMdPath, "utf-8");
|
|
11092
11425
|
if (!content.includes("## When to Use")) {
|
|
11093
11426
|
errors.push('SKILL.md must contain a "## When to Use" section.');
|
|
11094
11427
|
}
|
|
@@ -11168,11 +11501,11 @@ ${errorList}`);
|
|
|
11168
11501
|
}
|
|
11169
11502
|
const meta = validation.skillMeta;
|
|
11170
11503
|
const pkg = derivePackageJson(meta);
|
|
11171
|
-
const pkgPath =
|
|
11172
|
-
|
|
11173
|
-
const readmePath =
|
|
11174
|
-
if (!
|
|
11175
|
-
const skillMdContent =
|
|
11504
|
+
const pkgPath = path61.join(skillDir, "package.json");
|
|
11505
|
+
fs43.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
11506
|
+
const readmePath = path61.join(skillDir, "README.md");
|
|
11507
|
+
if (!fs43.existsSync(readmePath)) {
|
|
11508
|
+
const skillMdContent = fs43.readFileSync(path61.join(skillDir, "SKILL.md"), "utf-8");
|
|
11176
11509
|
const readme = `# ${pkg.name}
|
|
11177
11510
|
|
|
11178
11511
|
${meta.description}
|
|
@@ -11186,7 +11519,7 @@ harness install ${meta.name}
|
|
|
11186
11519
|
---
|
|
11187
11520
|
|
|
11188
11521
|
${skillMdContent}`;
|
|
11189
|
-
|
|
11522
|
+
fs43.writeFileSync(readmePath, readme);
|
|
11190
11523
|
}
|
|
11191
11524
|
if (opts.dryRun) {
|
|
11192
11525
|
return {
|
|
@@ -11212,7 +11545,7 @@ ${skillMdContent}`;
|
|
|
11212
11545
|
};
|
|
11213
11546
|
}
|
|
11214
11547
|
function createPublishCommand() {
|
|
11215
|
-
return new
|
|
11548
|
+
return new Command96("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) => {
|
|
11216
11549
|
const globalOpts = cmd.optsWithGlobals();
|
|
11217
11550
|
const skillDir = opts.dir || process.cwd();
|
|
11218
11551
|
try {
|
|
@@ -11237,7 +11570,7 @@ function createPublishCommand() {
|
|
|
11237
11570
|
|
|
11238
11571
|
// src/commands/skill/index.ts
|
|
11239
11572
|
function createSkillCommand() {
|
|
11240
|
-
const command = new
|
|
11573
|
+
const command = new Command97("skill").description("Skill management commands");
|
|
11241
11574
|
command.addCommand(createListCommand3());
|
|
11242
11575
|
command.addCommand(createRunCommand3());
|
|
11243
11576
|
command.addCommand(createValidateCommand2());
|
|
@@ -11250,9 +11583,9 @@ function createSkillCommand() {
|
|
|
11250
11583
|
|
|
11251
11584
|
// src/commands/snapshot.ts
|
|
11252
11585
|
init_dist3();
|
|
11253
|
-
import { Command as
|
|
11586
|
+
import { Command as Command98 } from "commander";
|
|
11254
11587
|
import { execSync as execSync5 } from "child_process";
|
|
11255
|
-
import
|
|
11588
|
+
import chalk16 from "chalk";
|
|
11256
11589
|
function getCommitHash3(cwd) {
|
|
11257
11590
|
try {
|
|
11258
11591
|
return execSync5("git rev-parse --short HEAD", { cwd, encoding: "utf-8" }).toString().trim();
|
|
@@ -11277,9 +11610,9 @@ function formatDelta(delta) {
|
|
|
11277
11610
|
function directionSymbol(direction) {
|
|
11278
11611
|
switch (direction) {
|
|
11279
11612
|
case "improving":
|
|
11280
|
-
return
|
|
11613
|
+
return chalk16.green("improving");
|
|
11281
11614
|
case "declining":
|
|
11282
|
-
return
|
|
11615
|
+
return chalk16.red("declining");
|
|
11283
11616
|
case "stable":
|
|
11284
11617
|
return "=";
|
|
11285
11618
|
}
|
|
@@ -11432,7 +11765,7 @@ function registerListCommand(parent) {
|
|
|
11432
11765
|
});
|
|
11433
11766
|
}
|
|
11434
11767
|
function createSnapshotCommand() {
|
|
11435
|
-
const command = new
|
|
11768
|
+
const command = new Command98("snapshot").description("Architecture timeline snapshot commands");
|
|
11436
11769
|
registerCaptureCommand(command);
|
|
11437
11770
|
registerTrendsCommand(command);
|
|
11438
11771
|
registerListCommand(command);
|
|
@@ -11440,9 +11773,9 @@ function createSnapshotCommand() {
|
|
|
11440
11773
|
}
|
|
11441
11774
|
|
|
11442
11775
|
// src/commands/spec-craft.ts
|
|
11443
|
-
import { Command as
|
|
11776
|
+
import { Command as Command99 } from "commander";
|
|
11444
11777
|
function createSpecCraftCommand() {
|
|
11445
|
-
return new
|
|
11778
|
+
return new Command99("spec-craft").description(
|
|
11446
11779
|
"LLM-judgment critique of spec quality (proposals + ADRs). Second craft-pipeline ceiling skill; 7 seed rubrics from the spec-quality canon. Per-section critique."
|
|
11447
11780
|
).option("-f, --files <files...>", "Optional spec file/glob scope").option("-k, --kinds <kinds...>", "Restrict to proposal / adr (default: both)").option("-s, --sections <names...>", "Restrict to specific canonical section names").option("--max-files <n>", "Cap doc count (default: 50)").option("--max-sections-per-file <n>", "Cap per-doc section critique (default: 10)").action(async (opts, cmd) => {
|
|
11448
11781
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -11490,10 +11823,10 @@ function printResult6(result, mode, _formatter) {
|
|
|
11490
11823
|
list.push(f);
|
|
11491
11824
|
byFile.set(f.target.file, list);
|
|
11492
11825
|
}
|
|
11493
|
-
for (const [file,
|
|
11826
|
+
for (const [file, fs48] of byFile) {
|
|
11494
11827
|
console.log(`
|
|
11495
11828
|
${file}`);
|
|
11496
|
-
for (const f of
|
|
11829
|
+
for (const f of fs48) {
|
|
11497
11830
|
console.log(
|
|
11498
11831
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ## ${f.target.section}:${f.target.line}`
|
|
11499
11832
|
);
|
|
@@ -11509,12 +11842,12 @@ ${file}`);
|
|
|
11509
11842
|
}
|
|
11510
11843
|
|
|
11511
11844
|
// src/commands/state/index.ts
|
|
11512
|
-
import { Command as
|
|
11845
|
+
import { Command as Command104 } from "commander";
|
|
11513
11846
|
|
|
11514
11847
|
// src/commands/state/show.ts
|
|
11515
11848
|
init_dist3();
|
|
11516
|
-
import { Command as
|
|
11517
|
-
import * as
|
|
11849
|
+
import { Command as Command100 } from "commander";
|
|
11850
|
+
import * as path62 from "path";
|
|
11518
11851
|
function printStateText(state, stream) {
|
|
11519
11852
|
if (stream) console.log(`Stream: ${stream}`);
|
|
11520
11853
|
console.log(`Schema Version: ${state.schemaVersion}`);
|
|
@@ -11538,9 +11871,9 @@ function printStateProgress(progress) {
|
|
|
11538
11871
|
}
|
|
11539
11872
|
}
|
|
11540
11873
|
function createShowCommand() {
|
|
11541
|
-
return new
|
|
11874
|
+
return new Command100("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
|
|
11542
11875
|
const globalOpts = cmd.optsWithGlobals();
|
|
11543
|
-
const result = await loadState(
|
|
11876
|
+
const result = await loadState(path62.resolve(opts.path), opts.stream);
|
|
11544
11877
|
if (!result.ok) {
|
|
11545
11878
|
logger.error(result.error.message);
|
|
11546
11879
|
process.exit(ExitCode.ERROR);
|
|
@@ -11556,9 +11889,9 @@ function createShowCommand() {
|
|
|
11556
11889
|
|
|
11557
11890
|
// src/commands/state/reset.ts
|
|
11558
11891
|
init_dist3();
|
|
11559
|
-
import { Command as
|
|
11560
|
-
import * as
|
|
11561
|
-
import * as
|
|
11892
|
+
import { Command as Command101 } from "commander";
|
|
11893
|
+
import * as fs44 from "fs";
|
|
11894
|
+
import * as path63 from "path";
|
|
11562
11895
|
import * as readline3 from "readline";
|
|
11563
11896
|
async function resolveStatePath(projectPath, stream) {
|
|
11564
11897
|
if (stream) {
|
|
@@ -11567,9 +11900,9 @@ async function resolveStatePath(projectPath, stream) {
|
|
|
11567
11900
|
logger.error(streamResult.error.message);
|
|
11568
11901
|
process.exit(ExitCode.ERROR);
|
|
11569
11902
|
}
|
|
11570
|
-
return
|
|
11903
|
+
return path63.join(streamResult.value, "state.json");
|
|
11571
11904
|
}
|
|
11572
|
-
return
|
|
11905
|
+
return path63.join(projectPath, ".harness", "state.json");
|
|
11573
11906
|
}
|
|
11574
11907
|
async function confirmReset() {
|
|
11575
11908
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -11580,9 +11913,9 @@ async function confirmReset() {
|
|
|
11580
11913
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
11581
11914
|
}
|
|
11582
11915
|
async function runReset(opts) {
|
|
11583
|
-
const projectPath =
|
|
11916
|
+
const projectPath = path63.resolve(opts.path);
|
|
11584
11917
|
const statePath = await resolveStatePath(projectPath, opts.stream);
|
|
11585
|
-
if (!
|
|
11918
|
+
if (!fs44.existsSync(statePath)) {
|
|
11586
11919
|
logger.info("No state file found. Nothing to reset.");
|
|
11587
11920
|
process.exit(ExitCode.SUCCESS);
|
|
11588
11921
|
}
|
|
@@ -11591,7 +11924,7 @@ async function runReset(opts) {
|
|
|
11591
11924
|
process.exit(ExitCode.SUCCESS);
|
|
11592
11925
|
}
|
|
11593
11926
|
try {
|
|
11594
|
-
|
|
11927
|
+
fs44.unlinkSync(statePath);
|
|
11595
11928
|
logger.success("Project state reset.");
|
|
11596
11929
|
} catch (e) {
|
|
11597
11930
|
logger.error(`Failed to reset state: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -11600,16 +11933,16 @@ async function runReset(opts) {
|
|
|
11600
11933
|
process.exit(ExitCode.SUCCESS);
|
|
11601
11934
|
}
|
|
11602
11935
|
function createResetCommand() {
|
|
11603
|
-
return new
|
|
11936
|
+
return new Command101("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));
|
|
11604
11937
|
}
|
|
11605
11938
|
|
|
11606
11939
|
// src/commands/state/learn.ts
|
|
11607
11940
|
init_dist3();
|
|
11608
|
-
import { Command as
|
|
11609
|
-
import * as
|
|
11941
|
+
import { Command as Command102 } from "commander";
|
|
11942
|
+
import * as path64 from "path";
|
|
11610
11943
|
function createLearnCommand() {
|
|
11611
|
-
return new
|
|
11612
|
-
const projectPath =
|
|
11944
|
+
return new Command102("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) => {
|
|
11945
|
+
const projectPath = path64.resolve(opts.path);
|
|
11613
11946
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
11614
11947
|
if (!result.ok) {
|
|
11615
11948
|
logger.error(result.error.message);
|
|
@@ -11623,10 +11956,10 @@ function createLearnCommand() {
|
|
|
11623
11956
|
|
|
11624
11957
|
// src/commands/state/streams.ts
|
|
11625
11958
|
init_dist3();
|
|
11626
|
-
import { Command as
|
|
11627
|
-
import * as
|
|
11959
|
+
import { Command as Command103 } from "commander";
|
|
11960
|
+
import * as path65 from "path";
|
|
11628
11961
|
async function runListStreams(opts, globalOpts) {
|
|
11629
|
-
const projectPath =
|
|
11962
|
+
const projectPath = path65.resolve(opts.path);
|
|
11630
11963
|
const indexResult = await loadStreamIndex(projectPath);
|
|
11631
11964
|
const result = await listStreams(projectPath);
|
|
11632
11965
|
if (!result.ok) {
|
|
@@ -11647,7 +11980,7 @@ async function runListStreams(opts, globalOpts) {
|
|
|
11647
11980
|
process.exit(ExitCode.SUCCESS);
|
|
11648
11981
|
}
|
|
11649
11982
|
async function runCreateStream(name, opts) {
|
|
11650
|
-
const result = await createStream(
|
|
11983
|
+
const result = await createStream(path65.resolve(opts.path), name, opts.branch);
|
|
11651
11984
|
if (!result.ok) {
|
|
11652
11985
|
logger.error(result.error.message);
|
|
11653
11986
|
process.exit(ExitCode.ERROR);
|
|
@@ -11656,7 +11989,7 @@ async function runCreateStream(name, opts) {
|
|
|
11656
11989
|
process.exit(ExitCode.SUCCESS);
|
|
11657
11990
|
}
|
|
11658
11991
|
async function runArchiveStream(name, opts) {
|
|
11659
|
-
const result = await archiveStream(
|
|
11992
|
+
const result = await archiveStream(path65.resolve(opts.path), name);
|
|
11660
11993
|
if (!result.ok) {
|
|
11661
11994
|
logger.error(result.error.message);
|
|
11662
11995
|
process.exit(ExitCode.ERROR);
|
|
@@ -11665,7 +11998,7 @@ async function runArchiveStream(name, opts) {
|
|
|
11665
11998
|
process.exit(ExitCode.SUCCESS);
|
|
11666
11999
|
}
|
|
11667
12000
|
async function runActivateStream(name, opts) {
|
|
11668
|
-
const result = await setActiveStream(
|
|
12001
|
+
const result = await setActiveStream(path65.resolve(opts.path), name);
|
|
11669
12002
|
if (!result.ok) {
|
|
11670
12003
|
logger.error(result.error.message);
|
|
11671
12004
|
process.exit(ExitCode.ERROR);
|
|
@@ -11674,7 +12007,7 @@ async function runActivateStream(name, opts) {
|
|
|
11674
12007
|
process.exit(ExitCode.SUCCESS);
|
|
11675
12008
|
}
|
|
11676
12009
|
function createStreamsCommand() {
|
|
11677
|
-
const command = new
|
|
12010
|
+
const command = new Command103("streams").description("Manage state streams");
|
|
11678
12011
|
command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
|
|
11679
12012
|
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));
|
|
11680
12013
|
command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
|
|
@@ -11684,7 +12017,7 @@ function createStreamsCommand() {
|
|
|
11684
12017
|
|
|
11685
12018
|
// src/commands/state/index.ts
|
|
11686
12019
|
function createStateCommand() {
|
|
11687
|
-
const command = new
|
|
12020
|
+
const command = new Command104("state").description("Project state management commands");
|
|
11688
12021
|
command.addCommand(createShowCommand());
|
|
11689
12022
|
command.addCommand(createResetCommand());
|
|
11690
12023
|
command.addCommand(createLearnCommand());
|
|
@@ -11693,9 +12026,9 @@ function createStateCommand() {
|
|
|
11693
12026
|
}
|
|
11694
12027
|
|
|
11695
12028
|
// src/commands/sync-analyses.ts
|
|
11696
|
-
import * as
|
|
11697
|
-
import * as
|
|
11698
|
-
import { Command as
|
|
12029
|
+
import * as fs45 from "fs";
|
|
12030
|
+
import * as path66 from "path";
|
|
12031
|
+
import { Command as Command105 } from "commander";
|
|
11699
12032
|
init_dist3();
|
|
11700
12033
|
function isValidRecord(record) {
|
|
11701
12034
|
return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
|
|
@@ -11721,7 +12054,7 @@ function extractAnalysisFromComments(comments) {
|
|
|
11721
12054
|
return null;
|
|
11722
12055
|
}
|
|
11723
12056
|
function bootstrapTrackerCommand2(opts, verb) {
|
|
11724
|
-
const projectPath =
|
|
12057
|
+
const projectPath = path66.resolve(opts.dir);
|
|
11725
12058
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
11726
12059
|
if (!trackerConfig) {
|
|
11727
12060
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -11733,8 +12066,8 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
11733
12066
|
);
|
|
11734
12067
|
return null;
|
|
11735
12068
|
}
|
|
11736
|
-
const projectEnvPath =
|
|
11737
|
-
if (
|
|
12069
|
+
const projectEnvPath = path66.join(projectPath, ".env");
|
|
12070
|
+
if (fs45.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
11738
12071
|
__require("dotenv").config({ path: projectEnvPath });
|
|
11739
12072
|
}
|
|
11740
12073
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -11764,13 +12097,13 @@ async function loadRoadmapFeatures(projectPath) {
|
|
|
11764
12097
|
}
|
|
11765
12098
|
return r.value.features.filter((f) => !!f.externalId).map((f) => ({ name: f.name, externalId: f.externalId }));
|
|
11766
12099
|
}
|
|
11767
|
-
const roadmapFile =
|
|
11768
|
-
if (!
|
|
12100
|
+
const roadmapFile = path66.join(projectPath, "docs", "roadmap.md");
|
|
12101
|
+
if (!fs45.existsSync(roadmapFile)) {
|
|
11769
12102
|
logger.error("No docs/roadmap.md found. Cannot discover features with externalIds.");
|
|
11770
12103
|
return null;
|
|
11771
12104
|
}
|
|
11772
12105
|
const { parseRoadmap: parseRoadmap2 } = (init_dist3(), __toCommonJS(dist_exports));
|
|
11773
|
-
const roadmapParsed = parseRoadmap2(
|
|
12106
|
+
const roadmapParsed = parseRoadmap2(fs45.readFileSync(roadmapFile, "utf-8"));
|
|
11774
12107
|
if (!roadmapParsed.ok) {
|
|
11775
12108
|
logger.error("Failed to parse docs/roadmap.md");
|
|
11776
12109
|
return null;
|
|
@@ -11821,9 +12154,9 @@ async function runSyncAnalyses(opts) {
|
|
|
11821
12154
|
return;
|
|
11822
12155
|
}
|
|
11823
12156
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
11824
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
12157
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-JCFK263L.js");
|
|
11825
12158
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
11826
|
-
const archive = new AnalysisArchive(
|
|
12159
|
+
const archive = new AnalysisArchive(path66.join(projectPath, ".harness", "analyses"));
|
|
11827
12160
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
11828
12161
|
const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
|
|
11829
12162
|
features,
|
|
@@ -11843,7 +12176,7 @@ async function runSyncAnalyses(opts) {
|
|
|
11843
12176
|
}
|
|
11844
12177
|
}
|
|
11845
12178
|
function createSyncAnalysesCommand() {
|
|
11846
|
-
const command = new
|
|
12179
|
+
const command = new Command105("sync-analyses").description(
|
|
11847
12180
|
"Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
|
|
11848
12181
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
11849
12182
|
try {
|
|
@@ -11857,8 +12190,8 @@ function createSyncAnalysesCommand() {
|
|
|
11857
12190
|
}
|
|
11858
12191
|
|
|
11859
12192
|
// src/commands/sync-main.ts
|
|
11860
|
-
import { Command as
|
|
11861
|
-
import * as
|
|
12193
|
+
import { Command as Command106 } from "commander";
|
|
12194
|
+
import * as path67 from "path";
|
|
11862
12195
|
import {
|
|
11863
12196
|
syncMain as defaultSyncMain
|
|
11864
12197
|
} from "@harness-engineering/orchestrator";
|
|
@@ -11888,8 +12221,8 @@ function formatHuman2(r) {
|
|
|
11888
12221
|
}
|
|
11889
12222
|
}
|
|
11890
12223
|
function createSyncMainCommand() {
|
|
11891
|
-
return new
|
|
11892
|
-
const cwd =
|
|
12224
|
+
return new Command106("sync-main").description("Fast-forward the local default branch from origin (no-op on conflict)").option("--json", "Emit a SyncMainResult JSON object", false).option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
12225
|
+
const cwd = path67.resolve(opts.path ?? ".");
|
|
11893
12226
|
const exitCode = await runSyncMain({ cwd, json: Boolean(opts.json) });
|
|
11894
12227
|
process.exit(exitCode);
|
|
11895
12228
|
});
|
|
@@ -11897,7 +12230,7 @@ function createSyncMainCommand() {
|
|
|
11897
12230
|
|
|
11898
12231
|
// src/commands/taint.ts
|
|
11899
12232
|
init_dist3();
|
|
11900
|
-
import { Command as
|
|
12233
|
+
import { Command as Command107 } from "commander";
|
|
11901
12234
|
function getProjectRoot() {
|
|
11902
12235
|
return process.cwd();
|
|
11903
12236
|
}
|
|
@@ -11969,28 +12302,28 @@ function registerStatusCommand(taint) {
|
|
|
11969
12302
|
});
|
|
11970
12303
|
}
|
|
11971
12304
|
function createTaintCommand() {
|
|
11972
|
-
const taint = new
|
|
12305
|
+
const taint = new Command107("taint").description("Manage sentinel session taint state");
|
|
11973
12306
|
registerClearCommand(taint);
|
|
11974
12307
|
registerStatusCommand(taint);
|
|
11975
12308
|
return taint;
|
|
11976
12309
|
}
|
|
11977
12310
|
|
|
11978
12311
|
// src/commands/telemetry/index.ts
|
|
11979
|
-
import { Command as
|
|
12312
|
+
import { Command as Command111 } from "commander";
|
|
11980
12313
|
|
|
11981
12314
|
// src/commands/telemetry/identify.ts
|
|
11982
12315
|
init_dist3();
|
|
11983
|
-
import { Command as
|
|
11984
|
-
import * as
|
|
11985
|
-
import * as
|
|
12316
|
+
import { Command as Command108 } from "commander";
|
|
12317
|
+
import * as fs46 from "fs";
|
|
12318
|
+
import * as path68 from "path";
|
|
11986
12319
|
function telemetryFilePath(cwd) {
|
|
11987
|
-
return
|
|
12320
|
+
return path68.join(cwd, ".harness", "telemetry.json");
|
|
11988
12321
|
}
|
|
11989
12322
|
function writeTelemetryFile(cwd, data) {
|
|
11990
12323
|
const filePath = telemetryFilePath(cwd);
|
|
11991
|
-
const dir =
|
|
11992
|
-
|
|
11993
|
-
|
|
12324
|
+
const dir = path68.dirname(filePath);
|
|
12325
|
+
fs46.mkdirSync(dir, { recursive: true });
|
|
12326
|
+
fs46.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
11994
12327
|
}
|
|
11995
12328
|
var IDENTITY_FIELDS = ["project", "team", "alias"];
|
|
11996
12329
|
function applyIdentityFields(opts, identity) {
|
|
@@ -12004,7 +12337,7 @@ function printIdentity(identity) {
|
|
|
12004
12337
|
}
|
|
12005
12338
|
}
|
|
12006
12339
|
function createIdentifyCommand() {
|
|
12007
|
-
const cmd = new
|
|
12340
|
+
const cmd = new Command108("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) => {
|
|
12008
12341
|
const cwd = process.cwd();
|
|
12009
12342
|
if (opts.clear) {
|
|
12010
12343
|
writeTelemetryFile(cwd, { identity: {} });
|
|
@@ -12029,7 +12362,7 @@ function createIdentifyCommand() {
|
|
|
12029
12362
|
|
|
12030
12363
|
// src/commands/telemetry/status.ts
|
|
12031
12364
|
init_dist3();
|
|
12032
|
-
import { Command as
|
|
12365
|
+
import { Command as Command109 } from "commander";
|
|
12033
12366
|
function gatherEnvOverrides() {
|
|
12034
12367
|
const overrides = {};
|
|
12035
12368
|
if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
|
|
@@ -12071,7 +12404,7 @@ function printHumanStatus(result) {
|
|
|
12071
12404
|
printEnvOverrides(result.envOverrides);
|
|
12072
12405
|
}
|
|
12073
12406
|
function createStatusCommand() {
|
|
12074
|
-
const cmd = new
|
|
12407
|
+
const cmd = new Command109("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
|
|
12075
12408
|
const cwd = process.cwd();
|
|
12076
12409
|
const envOverrides = gatherEnvOverrides();
|
|
12077
12410
|
const consent = resolveConsent(cwd, void 0);
|
|
@@ -12104,11 +12437,11 @@ function createStatusCommand() {
|
|
|
12104
12437
|
|
|
12105
12438
|
// src/commands/telemetry/test.ts
|
|
12106
12439
|
init_dist3();
|
|
12107
|
-
import { Command as
|
|
12440
|
+
import { Command as Command110 } from "commander";
|
|
12108
12441
|
|
|
12109
12442
|
// src/bin/command-telemetry.ts
|
|
12110
|
-
import { existsSync as
|
|
12111
|
-
import { join as
|
|
12443
|
+
import { existsSync as existsSync43, readFileSync as readFileSync33, mkdirSync as mkdirSync19, appendFileSync, writeFileSync as writeFileSync25 } from "fs";
|
|
12444
|
+
import { join as join57, parse as parsePath, resolve as resolve43 } from "path";
|
|
12112
12445
|
import { spawn as spawn2 } from "child_process";
|
|
12113
12446
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
12114
12447
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
@@ -12116,7 +12449,7 @@ function findProjectRoot(cwd) {
|
|
|
12116
12449
|
let dir = resolve43(cwd);
|
|
12117
12450
|
const { root } = parsePath(dir);
|
|
12118
12451
|
while (dir !== root) {
|
|
12119
|
-
if (
|
|
12452
|
+
if (existsSync43(join57(dir, "harness.config.json"))) return dir;
|
|
12120
12453
|
dir = resolve43(dir, "..");
|
|
12121
12454
|
}
|
|
12122
12455
|
return cwd;
|
|
@@ -12154,7 +12487,7 @@ function resolveCommandName(cmd) {
|
|
|
12154
12487
|
}
|
|
12155
12488
|
function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
12156
12489
|
try {
|
|
12157
|
-
const metricsDir =
|
|
12490
|
+
const metricsDir = join57(cwd, ".harness", "metrics");
|
|
12158
12491
|
mkdirSync19(metricsDir, { recursive: true });
|
|
12159
12492
|
const record = {
|
|
12160
12493
|
skill: command,
|
|
@@ -12164,16 +12497,16 @@ function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
|
12164
12497
|
outcome,
|
|
12165
12498
|
phasesReached: []
|
|
12166
12499
|
};
|
|
12167
|
-
const adoptionFile =
|
|
12500
|
+
const adoptionFile = join57(metricsDir, "adoption.jsonl");
|
|
12168
12501
|
appendFileSync(adoptionFile, JSON.stringify(record) + "\n");
|
|
12169
12502
|
} catch {
|
|
12170
12503
|
}
|
|
12171
12504
|
}
|
|
12172
12505
|
function isTelemetryDisabledByConfig(cwd) {
|
|
12173
|
-
const configPath =
|
|
12174
|
-
if (!
|
|
12506
|
+
const configPath = join57(cwd, "harness.config.json");
|
|
12507
|
+
if (!existsSync43(configPath)) return false;
|
|
12175
12508
|
try {
|
|
12176
|
-
const config = JSON.parse(
|
|
12509
|
+
const config = JSON.parse(readFileSync33(configPath, "utf-8"));
|
|
12177
12510
|
return config?.telemetry?.enabled === false;
|
|
12178
12511
|
} catch {
|
|
12179
12512
|
return false;
|
|
@@ -12185,11 +12518,11 @@ function isTelemetryOptedOut(cwd) {
|
|
|
12185
12518
|
}
|
|
12186
12519
|
function flushTelemetryBackground(cwd) {
|
|
12187
12520
|
try {
|
|
12188
|
-
const adoptionFile =
|
|
12189
|
-
if (!
|
|
12521
|
+
const adoptionFile = join57(cwd, ".harness", "metrics", "adoption.jsonl");
|
|
12522
|
+
if (!existsSync43(adoptionFile)) return;
|
|
12190
12523
|
if (isTelemetryOptedOut(cwd)) return;
|
|
12191
|
-
const reporterPath =
|
|
12192
|
-
if (!
|
|
12524
|
+
const reporterPath = join57(cwd, ".harness", "hooks", "telemetry-reporter.js");
|
|
12525
|
+
if (!existsSync43(reporterPath)) return;
|
|
12193
12526
|
const child = spawn2(process.execPath, [reporterPath], {
|
|
12194
12527
|
cwd,
|
|
12195
12528
|
stdio: ["pipe", "ignore", "ignore"],
|
|
@@ -12226,7 +12559,7 @@ function reportTestSuccess(status, distinctId, identity) {
|
|
|
12226
12559
|
if (identity.team) logger.info(` team: ${identity.team}`);
|
|
12227
12560
|
}
|
|
12228
12561
|
function createTestCommand() {
|
|
12229
|
-
return new
|
|
12562
|
+
return new Command110("test").description("Send a test event to PostHog and verify connectivity").action(async () => {
|
|
12230
12563
|
const cwd = process.cwd();
|
|
12231
12564
|
const consent = resolveConsent(cwd, void 0);
|
|
12232
12565
|
if (!consent.allowed) {
|
|
@@ -12263,7 +12596,7 @@ function createTestCommand() {
|
|
|
12263
12596
|
|
|
12264
12597
|
// src/commands/telemetry/index.ts
|
|
12265
12598
|
function createTelemetryCommand() {
|
|
12266
|
-
const command = new
|
|
12599
|
+
const command = new Command111("telemetry").description("Telemetry identity and status management");
|
|
12267
12600
|
command.addCommand(createIdentifyCommand());
|
|
12268
12601
|
command.addCommand(createStatusCommand());
|
|
12269
12602
|
command.addCommand(createTestCommand());
|
|
@@ -12271,9 +12604,9 @@ function createTelemetryCommand() {
|
|
|
12271
12604
|
}
|
|
12272
12605
|
|
|
12273
12606
|
// src/commands/test-craft.ts
|
|
12274
|
-
import { Command as
|
|
12607
|
+
import { Command as Command112 } from "commander";
|
|
12275
12608
|
function createTestCraftCommand() {
|
|
12276
|
-
return new
|
|
12609
|
+
return new Command112("test-craft").description(
|
|
12277
12610
|
"LLM-judgment critique of test quality across vitest/jest/mocha/playwright. Fourth craft-pipeline ceiling skill. Per-test critique with best-effort source pairing."
|
|
12278
12611
|
).option("-f, --files <files...>", "Optional test file/glob scope").option("--frameworks <names...>", "Restrict to: vitest / jest / mocha / playwright").option("--max-files <n>", "Cap test file count (default: 100)").option("--max-tests-per-file <n>", "Cap per-file test critique (default: 20)").option("--no-source-pair", "Skip source-pairing resolution").action(async (opts, cmd) => {
|
|
12279
12612
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -12321,10 +12654,10 @@ function printResult7(result, mode, _formatter) {
|
|
|
12321
12654
|
list.push(f);
|
|
12322
12655
|
byFile.set(f.target.file, list);
|
|
12323
12656
|
}
|
|
12324
|
-
for (const [file,
|
|
12657
|
+
for (const [file, fs48] of byFile) {
|
|
12325
12658
|
console.log(`
|
|
12326
12659
|
${file}`);
|
|
12327
|
-
for (const f of
|
|
12660
|
+
for (const f of fs48) {
|
|
12328
12661
|
const nestingStr = f.target.nesting.length > 0 ? f.target.nesting.join(" > ") + " > " : "";
|
|
12329
12662
|
console.log(
|
|
12330
12663
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.framework}:${f.target.line}`
|
|
@@ -12343,8 +12676,8 @@ ${file}`);
|
|
|
12343
12676
|
}
|
|
12344
12677
|
|
|
12345
12678
|
// src/commands/traceability.ts
|
|
12346
|
-
import { Command as
|
|
12347
|
-
import
|
|
12679
|
+
import { Command as Command113 } from "commander";
|
|
12680
|
+
import chalk17 from "chalk";
|
|
12348
12681
|
function confidenceLabel(maxConfidence) {
|
|
12349
12682
|
if (maxConfidence >= 0.8) return "explicit";
|
|
12350
12683
|
if (maxConfidence > 0) return "inferred";
|
|
@@ -12353,13 +12686,13 @@ function confidenceLabel(maxConfidence) {
|
|
|
12353
12686
|
function statusIcon(status) {
|
|
12354
12687
|
switch (status) {
|
|
12355
12688
|
case "full":
|
|
12356
|
-
return
|
|
12689
|
+
return chalk17.green("\u2713 full");
|
|
12357
12690
|
case "code-only":
|
|
12358
|
-
return
|
|
12691
|
+
return chalk17.yellow("\u25D0 code-only");
|
|
12359
12692
|
case "test-only":
|
|
12360
|
-
return
|
|
12693
|
+
return chalk17.yellow("\u25D0 test-only");
|
|
12361
12694
|
case "none":
|
|
12362
|
-
return
|
|
12695
|
+
return chalk17.red("\u2717 none");
|
|
12363
12696
|
default:
|
|
12364
12697
|
return status;
|
|
12365
12698
|
}
|
|
@@ -12406,14 +12739,14 @@ function handleEmptyResults(mode) {
|
|
|
12406
12739
|
}
|
|
12407
12740
|
function printResultTable(result, mode) {
|
|
12408
12741
|
const specLabel = result.specPath || result.featureName || "Unknown";
|
|
12409
|
-
console.log(` ${
|
|
12742
|
+
console.log(` ${chalk17.cyan(specLabel)} ${chalk17.dim(`(${result.summary.total} requirements)`)}`);
|
|
12410
12743
|
console.log("");
|
|
12411
12744
|
const numWidth = 4;
|
|
12412
12745
|
const nameWidth = mode === OutputMode.VERBOSE ? 44 : 36;
|
|
12413
12746
|
const codeWidth = 6;
|
|
12414
12747
|
const testWidth = 7;
|
|
12415
12748
|
const confWidth = 12;
|
|
12416
|
-
const header =
|
|
12749
|
+
const header = chalk17.dim(
|
|
12417
12750
|
` ${pad("#", numWidth)}${pad("Requirement", nameWidth)}${pad("Code", codeWidth)}${pad("Tests", testWidth)}${pad("Confidence", confWidth)}Status`
|
|
12418
12751
|
);
|
|
12419
12752
|
console.log(header);
|
|
@@ -12442,10 +12775,10 @@ function printRequirementRows(requirements, mode, cols) {
|
|
|
12442
12775
|
);
|
|
12443
12776
|
if (mode === OutputMode.VERBOSE) {
|
|
12444
12777
|
for (const f of req.codeFiles) {
|
|
12445
|
-
console.log(
|
|
12778
|
+
console.log(chalk17.dim(` code: ${f.path} (${f.method})`));
|
|
12446
12779
|
}
|
|
12447
12780
|
for (const f of req.testFiles) {
|
|
12448
|
-
console.log(
|
|
12781
|
+
console.log(chalk17.dim(` test: ${f.path} (${f.method})`));
|
|
12449
12782
|
}
|
|
12450
12783
|
}
|
|
12451
12784
|
}
|
|
@@ -12456,7 +12789,7 @@ function printCoverageSummary(summary) {
|
|
|
12456
12789
|
const codePct = s.total > 0 ? Math.round(s.withCode / s.total * 100) : 0;
|
|
12457
12790
|
const testPct = s.total > 0 ? Math.round(s.withTests / s.total * 100) : 0;
|
|
12458
12791
|
console.log(
|
|
12459
|
-
` ${
|
|
12792
|
+
` ${chalk17.bold("Coverage:")} ${fullyPct}% fully traced (${s.fullyTraced}/${s.total}), ${codePct}% with code (${s.withCode}/${s.total}), ${testPct}% with tests (${s.withTests}/${s.total})`
|
|
12460
12793
|
);
|
|
12461
12794
|
}
|
|
12462
12795
|
async function runTraceability(options) {
|
|
@@ -12464,7 +12797,7 @@ async function runTraceability(options) {
|
|
|
12464
12797
|
const projectPath = process.cwd();
|
|
12465
12798
|
const store = await loadGraphStore(projectPath);
|
|
12466
12799
|
if (!store) handleNoStore(mode);
|
|
12467
|
-
const graphModule = await import("./dist-
|
|
12800
|
+
const graphModule = await import("./dist-OWHMNL4W.js");
|
|
12468
12801
|
const filterOptions = buildFilterOptions(options);
|
|
12469
12802
|
const results = graphModule.queryTraceability(store, filterOptions);
|
|
12470
12803
|
if (results.length === 0) handleEmptyResults(mode);
|
|
@@ -12472,14 +12805,14 @@ async function runTraceability(options) {
|
|
|
12472
12805
|
console.log(JSON.stringify(results, null, 2));
|
|
12473
12806
|
} else {
|
|
12474
12807
|
console.log("");
|
|
12475
|
-
console.log(
|
|
12808
|
+
console.log(chalk17.bold("Spec-to-Implementation Traceability"));
|
|
12476
12809
|
console.log("");
|
|
12477
12810
|
for (const result of results) printResultTable(result, mode);
|
|
12478
12811
|
}
|
|
12479
12812
|
process.exit(ExitCode.SUCCESS);
|
|
12480
12813
|
}
|
|
12481
12814
|
function createTraceabilityCommand() {
|
|
12482
|
-
return new
|
|
12815
|
+
return new Command113("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) => {
|
|
12483
12816
|
const globalOpts = cmd.optsWithGlobals();
|
|
12484
12817
|
await runTraceability({
|
|
12485
12818
|
spec: opts.spec,
|
|
@@ -12492,15 +12825,15 @@ function createTraceabilityCommand() {
|
|
|
12492
12825
|
}
|
|
12493
12826
|
|
|
12494
12827
|
// src/commands/uninstall.ts
|
|
12495
|
-
import * as
|
|
12496
|
-
import { Command as
|
|
12828
|
+
import * as path69 from "path";
|
|
12829
|
+
import { Command as Command114 } from "commander";
|
|
12497
12830
|
async function runUninstall(skillName, options) {
|
|
12498
12831
|
const packageName = resolvePackageName(skillName);
|
|
12499
12832
|
const shortName = extractSkillName(packageName);
|
|
12500
12833
|
const globalDir = resolveGlobalSkillsDir();
|
|
12501
|
-
const skillsDir =
|
|
12502
|
-
const communityBase =
|
|
12503
|
-
const lockfilePath =
|
|
12834
|
+
const skillsDir = path69.dirname(globalDir);
|
|
12835
|
+
const communityBase = path69.join(skillsDir, "community");
|
|
12836
|
+
const lockfilePath = path69.join(communityBase, "skills-lock.json");
|
|
12504
12837
|
const lockfile = readLockfile2(lockfilePath);
|
|
12505
12838
|
const entry = lockfile.skills[packageName];
|
|
12506
12839
|
if (!entry) {
|
|
@@ -12530,7 +12863,7 @@ async function runUninstall(skillName, options) {
|
|
|
12530
12863
|
return result;
|
|
12531
12864
|
}
|
|
12532
12865
|
function createUninstallCommand() {
|
|
12533
|
-
const cmd = new
|
|
12866
|
+
const cmd = new Command114("uninstall");
|
|
12534
12867
|
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) => {
|
|
12535
12868
|
try {
|
|
12536
12869
|
const result = await runUninstall(skill, opts);
|
|
@@ -12550,9 +12883,9 @@ function createUninstallCommand() {
|
|
|
12550
12883
|
|
|
12551
12884
|
// src/commands/uninstall-constraints.ts
|
|
12552
12885
|
init_dist3();
|
|
12553
|
-
import * as
|
|
12554
|
-
import * as
|
|
12555
|
-
import { Command as
|
|
12886
|
+
import * as fs47 from "fs/promises";
|
|
12887
|
+
import * as path70 from "path";
|
|
12888
|
+
import { Command as Command115 } from "commander";
|
|
12556
12889
|
async function runUninstallConstraints(options) {
|
|
12557
12890
|
const { packageName, configPath, lockfilePath } = options;
|
|
12558
12891
|
const lockfileResult = await readLockfile(lockfilePath);
|
|
@@ -12572,7 +12905,7 @@ async function runUninstallConstraints(options) {
|
|
|
12572
12905
|
}
|
|
12573
12906
|
let localConfig;
|
|
12574
12907
|
try {
|
|
12575
|
-
const raw = await
|
|
12908
|
+
const raw = await fs47.readFile(configPath, "utf-8");
|
|
12576
12909
|
localConfig = JSON.parse(raw);
|
|
12577
12910
|
} catch (err) {
|
|
12578
12911
|
return {
|
|
@@ -12609,11 +12942,11 @@ async function runUninstallConstraints(options) {
|
|
|
12609
12942
|
};
|
|
12610
12943
|
}
|
|
12611
12944
|
function createUninstallConstraintsCommand() {
|
|
12612
|
-
const cmd = new
|
|
12945
|
+
const cmd = new Command115("uninstall-constraints");
|
|
12613
12946
|
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) => {
|
|
12614
12947
|
let configPath;
|
|
12615
12948
|
if (opts.config) {
|
|
12616
|
-
configPath =
|
|
12949
|
+
configPath = path70.resolve(opts.config);
|
|
12617
12950
|
} else {
|
|
12618
12951
|
const found = findConfigFile();
|
|
12619
12952
|
if (!found.ok) {
|
|
@@ -12622,8 +12955,8 @@ function createUninstallConstraintsCommand() {
|
|
|
12622
12955
|
}
|
|
12623
12956
|
configPath = found.value;
|
|
12624
12957
|
}
|
|
12625
|
-
const projectRoot2 =
|
|
12626
|
-
const lockfilePath =
|
|
12958
|
+
const projectRoot2 = path70.dirname(configPath);
|
|
12959
|
+
const lockfilePath = path70.join(projectRoot2, ".harness", "constraints.lock.json");
|
|
12627
12960
|
const result = await runUninstallConstraints({
|
|
12628
12961
|
packageName: name,
|
|
12629
12962
|
configPath,
|
|
@@ -12649,13 +12982,13 @@ function createUninstallConstraintsCommand() {
|
|
|
12649
12982
|
|
|
12650
12983
|
// src/commands/update.ts
|
|
12651
12984
|
init_dist3();
|
|
12652
|
-
import { Command as
|
|
12985
|
+
import { Command as Command116 } from "commander";
|
|
12653
12986
|
import { execFile, execFileSync as execFileSync7 } from "child_process";
|
|
12654
|
-
import { realpathSync, existsSync as
|
|
12655
|
-
import { dirname as
|
|
12987
|
+
import { realpathSync, existsSync as existsSync44, readFileSync as readFileSync34 } from "fs";
|
|
12988
|
+
import { dirname as dirname23, join as join60 } from "path";
|
|
12656
12989
|
import { promisify } from "util";
|
|
12657
12990
|
import readline4 from "readline";
|
|
12658
|
-
import
|
|
12991
|
+
import chalk18 from "chalk";
|
|
12659
12992
|
var CLI_PACKAGE = "@harness-engineering/cli";
|
|
12660
12993
|
function detectPackageManagerFromPath(resolvedPath) {
|
|
12661
12994
|
const normalizedBin = resolvedPath.replace(/\\/g, "/");
|
|
@@ -12741,16 +13074,16 @@ function prompt3(question) {
|
|
|
12741
13074
|
});
|
|
12742
13075
|
}
|
|
12743
13076
|
function findPackageDir(resolvedBin) {
|
|
12744
|
-
let dir =
|
|
13077
|
+
let dir = dirname23(resolvedBin);
|
|
12745
13078
|
for (let i = 0; i < 6; i++) {
|
|
12746
|
-
if (
|
|
13079
|
+
if (existsSync44(join60(dir, "package.json"))) {
|
|
12747
13080
|
try {
|
|
12748
|
-
const pkg = JSON.parse(
|
|
13081
|
+
const pkg = JSON.parse(readFileSync34(join60(dir, "package.json"), "utf-8"));
|
|
12749
13082
|
if (pkg?.name === CLI_PACKAGE) return dir;
|
|
12750
13083
|
} catch {
|
|
12751
13084
|
}
|
|
12752
13085
|
}
|
|
12753
|
-
const parent =
|
|
13086
|
+
const parent = dirname23(dir);
|
|
12754
13087
|
if (parent === dir) return null;
|
|
12755
13088
|
dir = parent;
|
|
12756
13089
|
}
|
|
@@ -12790,7 +13123,7 @@ function findAllInstalls() {
|
|
|
12790
13123
|
if (!packageDir) continue;
|
|
12791
13124
|
let version = null;
|
|
12792
13125
|
try {
|
|
12793
|
-
const pkg = JSON.parse(
|
|
13126
|
+
const pkg = JSON.parse(readFileSync34(join60(packageDir, "package.json"), "utf-8"));
|
|
12794
13127
|
if (typeof pkg?.version === "string") version = pkg.version;
|
|
12795
13128
|
} catch {
|
|
12796
13129
|
}
|
|
@@ -12824,7 +13157,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
12824
13157
|
`Found ${installs.length} harness installs on PATH. Only the active one was updated.`
|
|
12825
13158
|
);
|
|
12826
13159
|
for (const install of installs) {
|
|
12827
|
-
const tag = install.packageDir === activeDir ?
|
|
13160
|
+
const tag = install.packageDir === activeDir ? chalk18.green(" (active, just updated)") : chalk18.yellow(" (stale)");
|
|
12828
13161
|
const versionStr = install.version ? `v${install.version}` : "unknown";
|
|
12829
13162
|
console.log(` ${install.binPath} -> ${versionStr}${tag}`);
|
|
12830
13163
|
}
|
|
@@ -12832,7 +13165,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
12832
13165
|
const commands = others.map((install) => buildUninstallCommand(install));
|
|
12833
13166
|
console.log("Cleanup commands:");
|
|
12834
13167
|
for (const { display } of commands) {
|
|
12835
|
-
console.log(` ${
|
|
13168
|
+
console.log(` ${chalk18.cyan(display)}`);
|
|
12836
13169
|
}
|
|
12837
13170
|
console.log("");
|
|
12838
13171
|
const answer = await prompt3(`Run these now to remove stale installs? (y/N) `);
|
|
@@ -12840,7 +13173,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
12840
13173
|
for (const cmd of commands) {
|
|
12841
13174
|
if (!cmd.runnable) {
|
|
12842
13175
|
logger.warn(`Skipped ${cmd.install.binPath} (no automatic uninstall for this layout).`);
|
|
12843
|
-
console.log(` Run manually: ${
|
|
13176
|
+
console.log(` Run manually: ${chalk18.cyan(cmd.display)}`);
|
|
12844
13177
|
continue;
|
|
12845
13178
|
}
|
|
12846
13179
|
try {
|
|
@@ -12848,7 +13181,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
12848
13181
|
execFileSync7(cmd.bin, cmd.args, { stdio: "inherit", timeout: 12e4 });
|
|
12849
13182
|
} catch {
|
|
12850
13183
|
logger.warn(`Uninstall failed for ${cmd.install.binPath}.`);
|
|
12851
|
-
console.log(` Run manually: ${
|
|
13184
|
+
console.log(` Run manually: ${chalk18.cyan(cmd.display)}`);
|
|
12852
13185
|
}
|
|
12853
13186
|
}
|
|
12854
13187
|
}
|
|
@@ -12883,12 +13216,12 @@ async function ensureTelemetryIfNeeded() {
|
|
|
12883
13216
|
}
|
|
12884
13217
|
function refreshHooks() {
|
|
12885
13218
|
const cwd = process.cwd();
|
|
12886
|
-
const configPath =
|
|
12887
|
-
if (!
|
|
13219
|
+
const configPath = join60(cwd, "harness.config.json");
|
|
13220
|
+
if (!existsSync44(configPath)) return;
|
|
12888
13221
|
let profile = "standard";
|
|
12889
|
-
const profilePath =
|
|
13222
|
+
const profilePath = join60(cwd, ".harness", "hooks", "profile.json");
|
|
12890
13223
|
try {
|
|
12891
|
-
const data = JSON.parse(
|
|
13224
|
+
const data = JSON.parse(readFileSync34(profilePath, "utf-8"));
|
|
12892
13225
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
12893
13226
|
profile = data.profile;
|
|
12894
13227
|
}
|
|
@@ -12908,7 +13241,7 @@ function runLocalGraphScan() {
|
|
|
12908
13241
|
execFileSync7("harness", ["graph", "scan", "."], { stdio: "inherit" });
|
|
12909
13242
|
} catch {
|
|
12910
13243
|
logger.warn("Graph scan failed. Run manually:");
|
|
12911
|
-
console.log(` ${
|
|
13244
|
+
console.log(` ${chalk18.cyan("harness graph scan .")}`);
|
|
12912
13245
|
}
|
|
12913
13246
|
}
|
|
12914
13247
|
async function offerRegeneration() {
|
|
@@ -12925,7 +13258,7 @@ async function offerRegeneration() {
|
|
|
12925
13258
|
});
|
|
12926
13259
|
} catch {
|
|
12927
13260
|
logger.warn("Generation failed. Run manually:");
|
|
12928
|
-
console.log(` ${
|
|
13261
|
+
console.log(` ${chalk18.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
|
|
12929
13262
|
}
|
|
12930
13263
|
if (!isGlobal) {
|
|
12931
13264
|
runLocalGraphScan();
|
|
@@ -12989,8 +13322,8 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
12989
13322
|
console.log("");
|
|
12990
13323
|
for (const { pkg, current, latest } of outdated) {
|
|
12991
13324
|
const shortName = pkg.replace("@harness-engineering/", "");
|
|
12992
|
-
const currentStr = current ?
|
|
12993
|
-
logger.info(`${shortName}: ${currentStr} \u2192 ${
|
|
13325
|
+
const currentStr = current ? chalk18.dim(`v${current}`) : chalk18.dim("not installed");
|
|
13326
|
+
logger.info(`${shortName}: ${currentStr} \u2192 ${chalk18.green(`v${latest}`)}`);
|
|
12994
13327
|
}
|
|
12995
13328
|
console.log("");
|
|
12996
13329
|
}
|
|
@@ -13007,7 +13340,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
13007
13340
|
} catch {
|
|
13008
13341
|
console.log("");
|
|
13009
13342
|
logger.error("Update failed. You can try manually:");
|
|
13010
|
-
console.log(` ${
|
|
13343
|
+
console.log(` ${chalk18.cyan(installCmd)}`);
|
|
13011
13344
|
process.exit(ExitCode.ERROR);
|
|
13012
13345
|
}
|
|
13013
13346
|
refreshHooks();
|
|
@@ -13017,7 +13350,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
13017
13350
|
process.exit(ExitCode.SUCCESS);
|
|
13018
13351
|
}
|
|
13019
13352
|
function createUpdateCommand() {
|
|
13020
|
-
return new
|
|
13353
|
+
return new Command116("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(
|
|
13021
13354
|
"--regenerate",
|
|
13022
13355
|
"Only regenerate slash commands and agent definitions (skip package updates)"
|
|
13023
13356
|
).action(async (opts, cmd) => {
|
|
@@ -13027,9 +13360,9 @@ function createUpdateCommand() {
|
|
|
13027
13360
|
}
|
|
13028
13361
|
|
|
13029
13362
|
// src/commands/usage.ts
|
|
13030
|
-
import { Command as
|
|
13363
|
+
import { Command as Command117 } from "commander";
|
|
13031
13364
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
13032
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
13365
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-JCFK263L.js");
|
|
13033
13366
|
const records = readCostRecords(cwd);
|
|
13034
13367
|
if (includeClaudeSessions) {
|
|
13035
13368
|
const ccRecords = parseCCRecords();
|
|
@@ -13122,7 +13455,7 @@ function registerDailyCommand(usage) {
|
|
|
13122
13455
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
13123
13456
|
return;
|
|
13124
13457
|
}
|
|
13125
|
-
const { aggregateByDay } = await import("./dist-
|
|
13458
|
+
const { aggregateByDay } = await import("./dist-JCFK263L.js");
|
|
13126
13459
|
const limited = aggregateByDay(records).slice(0, days);
|
|
13127
13460
|
if (globalOpts.json) {
|
|
13128
13461
|
console.log(
|
|
@@ -13151,7 +13484,7 @@ function registerSessionsCommand(usage) {
|
|
|
13151
13484
|
}
|
|
13152
13485
|
return;
|
|
13153
13486
|
}
|
|
13154
|
-
const { aggregateBySession } = await import("./dist-
|
|
13487
|
+
const { aggregateBySession } = await import("./dist-JCFK263L.js");
|
|
13155
13488
|
const sessionData = aggregateBySession(records);
|
|
13156
13489
|
const limited = sessionData.slice(0, limit);
|
|
13157
13490
|
if (globalOpts.json) {
|
|
@@ -13231,7 +13564,7 @@ function registerSessionCommand(usage) {
|
|
|
13231
13564
|
cwd,
|
|
13232
13565
|
globalOpts.includeClaudeSessions
|
|
13233
13566
|
);
|
|
13234
|
-
const { aggregateBySession } = await import("./dist-
|
|
13567
|
+
const { aggregateBySession } = await import("./dist-JCFK263L.js");
|
|
13235
13568
|
const sessionData = aggregateBySession(records);
|
|
13236
13569
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
13237
13570
|
if (!match) {
|
|
@@ -13266,7 +13599,7 @@ function registerLatestCommand(usage) {
|
|
|
13266
13599
|
}
|
|
13267
13600
|
return;
|
|
13268
13601
|
}
|
|
13269
|
-
const { aggregateBySession } = await import("./dist-
|
|
13602
|
+
const { aggregateBySession } = await import("./dist-JCFK263L.js");
|
|
13270
13603
|
const sessionData = aggregateBySession(records);
|
|
13271
13604
|
const latest = sessionData[0];
|
|
13272
13605
|
if (!latest) {
|
|
@@ -13292,7 +13625,7 @@ function registerLatestCommand(usage) {
|
|
|
13292
13625
|
});
|
|
13293
13626
|
}
|
|
13294
13627
|
function createUsageCommand() {
|
|
13295
|
-
const usage = new
|
|
13628
|
+
const usage = new Command117("usage").description("Token usage and cost tracking");
|
|
13296
13629
|
usage.option(
|
|
13297
13630
|
"--include-claude-sessions",
|
|
13298
13631
|
"Include Claude Code session data from ~/.claude/projects/"
|
|
@@ -13307,15 +13640,15 @@ function createUsageCommand() {
|
|
|
13307
13640
|
// src/commands/validate.ts
|
|
13308
13641
|
init_dist3();
|
|
13309
13642
|
init_dist3();
|
|
13310
|
-
import { Command as
|
|
13311
|
-
import * as
|
|
13643
|
+
import { Command as Command118 } from "commander";
|
|
13644
|
+
import * as path71 from "path";
|
|
13312
13645
|
async function runValidate(options) {
|
|
13313
13646
|
const configResult = resolveConfig(options.configPath);
|
|
13314
13647
|
if (!configResult.ok) {
|
|
13315
13648
|
return configResult;
|
|
13316
13649
|
}
|
|
13317
13650
|
const config = configResult.value;
|
|
13318
|
-
const cwd = options.cwd ?? (options.configPath ?
|
|
13651
|
+
const cwd = options.cwd ?? (options.configPath ? path71.dirname(path71.resolve(options.configPath)) : process.cwd());
|
|
13319
13652
|
const result = {
|
|
13320
13653
|
valid: true,
|
|
13321
13654
|
checks: {
|
|
@@ -13325,7 +13658,7 @@ async function runValidate(options) {
|
|
|
13325
13658
|
},
|
|
13326
13659
|
issues: []
|
|
13327
13660
|
};
|
|
13328
|
-
const agentsMapPath =
|
|
13661
|
+
const agentsMapPath = path71.resolve(cwd, config.agentsMapPath);
|
|
13329
13662
|
const agentsResult = await validateAgentsMap(agentsMapPath);
|
|
13330
13663
|
if (agentsResult.ok) {
|
|
13331
13664
|
result.checks.agentsMap = true;
|
|
@@ -13376,6 +13709,22 @@ async function runValidate(options) {
|
|
|
13376
13709
|
}
|
|
13377
13710
|
});
|
|
13378
13711
|
}
|
|
13712
|
+
const strategyResult = await validateStrategy(cwd);
|
|
13713
|
+
if (strategyResult.ok) {
|
|
13714
|
+
result.checks.strategyConfig = true;
|
|
13715
|
+
} else {
|
|
13716
|
+
result.valid = false;
|
|
13717
|
+
result.checks.strategyConfig = false;
|
|
13718
|
+
result.issues.push({
|
|
13719
|
+
check: "strategyConfig",
|
|
13720
|
+
file: "STRATEGY.md",
|
|
13721
|
+
severity: "error",
|
|
13722
|
+
message: strategyResult.error.message,
|
|
13723
|
+
...strategyResult.error.suggestions?.[0] !== void 0 && {
|
|
13724
|
+
suggestion: strategyResult.error.suggestions[0]
|
|
13725
|
+
}
|
|
13726
|
+
});
|
|
13727
|
+
}
|
|
13379
13728
|
const solutionsResult = await validateSolutionsDir(cwd);
|
|
13380
13729
|
if (solutionsResult.ok) {
|
|
13381
13730
|
result.checks.solutionsDir = true;
|
|
@@ -13531,11 +13880,11 @@ function resolveValidateMode(globalOpts) {
|
|
|
13531
13880
|
return OutputMode.TEXT;
|
|
13532
13881
|
}
|
|
13533
13882
|
async function printCrossCheckWarnings(mode) {
|
|
13534
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
13883
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-F7GEBRDH.js");
|
|
13535
13884
|
const cwd = process.cwd();
|
|
13536
13885
|
const crossResult = await runCrossCheck2({
|
|
13537
|
-
specsDir:
|
|
13538
|
-
plansDir:
|
|
13886
|
+
specsDir: path71.join(cwd, "docs", "specs"),
|
|
13887
|
+
plansDir: path71.join(cwd, "docs", "plans"),
|
|
13539
13888
|
projectPath: cwd
|
|
13540
13889
|
});
|
|
13541
13890
|
if (!crossResult.ok || crossResult.value.warnings === 0) return;
|
|
@@ -13547,7 +13896,7 @@ async function printCrossCheckWarnings(mode) {
|
|
|
13547
13896
|
${crossResult.value.warnings} warnings`);
|
|
13548
13897
|
}
|
|
13549
13898
|
function createValidateCommand3() {
|
|
13550
|
-
const command = new
|
|
13899
|
+
const command = new Command118("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
|
|
13551
13900
|
"--agent-configs",
|
|
13552
13901
|
"Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
|
|
13553
13902
|
).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()));
|
|
@@ -13596,7 +13945,7 @@ Agent configs checked via ${engineLabel}${note}`);
|
|
|
13596
13945
|
|
|
13597
13946
|
// src/commands/verify.ts
|
|
13598
13947
|
init_dist3();
|
|
13599
|
-
import { Command as
|
|
13948
|
+
import { Command as Command119 } from "commander";
|
|
13600
13949
|
import { execSync as execSync6 } from "child_process";
|
|
13601
13950
|
function loadBranchingConfig(configPath) {
|
|
13602
13951
|
const shouldLoad = configPath !== void 0 || findConfigFile().ok;
|
|
@@ -13659,7 +14008,7 @@ async function runVerify(options) {
|
|
|
13659
14008
|
process.exit(ExitCode.VALIDATION_FAILED);
|
|
13660
14009
|
}
|
|
13661
14010
|
function createVerifyCommand() {
|
|
13662
|
-
return new
|
|
14011
|
+
return new Command119("verify").description(
|
|
13663
14012
|
"Verify project conventions (currently: branch naming). Works with or without a harness.config.json."
|
|
13664
14013
|
).option(
|
|
13665
14014
|
"--branch <name>",
|
|
@@ -13722,6 +14071,7 @@ var commandCreators = [
|
|
|
13722
14071
|
createMcpCommand,
|
|
13723
14072
|
createMcpGuardCommand,
|
|
13724
14073
|
createMigrateCommand,
|
|
14074
|
+
createModelsCommand,
|
|
13725
14075
|
createNamingCraftCommand,
|
|
13726
14076
|
createNotificationsCommand,
|
|
13727
14077
|
createOrchestratorCommand,
|
|
@@ -13762,7 +14112,7 @@ var commandCreators = [
|
|
|
13762
14112
|
|
|
13763
14113
|
// src/index.ts
|
|
13764
14114
|
function createProgram() {
|
|
13765
|
-
const program = new
|
|
14115
|
+
const program = new Command120();
|
|
13766
14116
|
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");
|
|
13767
14117
|
for (const creator of commandCreators) {
|
|
13768
14118
|
program.addCommand(creator());
|