@harness-engineering/cli 2.7.1 → 3.0.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-audit-harness-strength/SKILL.md +188 -0
- package/dist/agents/skills/claude-code/harness-audit-harness-strength/skill.yaml +54 -0
- package/dist/agents/skills/claude-code/harness-autopilot/SKILL.md +2 -0
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +58 -12
- 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-execution/SKILL.md +1 -1
- package/dist/agents/skills/claude-code/harness-git-workflow/SKILL.md +2 -0
- package/dist/agents/skills/claude-code/harness-ideate/SKILL.md +279 -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-knowledge-pipeline/SKILL.md +2 -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/SKILL.md +62 -4
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +36 -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/harness-test-advisor/SKILL.md +80 -2
- package/dist/agents/skills/claude-code/harness-test-advisor/skill.yaml +4 -1
- package/dist/agents/skills/claude-code/initialize-harness-project/SKILL.md +86 -18
- package/dist/agents/skills/claude-code/initialize-harness-project/skill.yaml +1 -0
- package/dist/agents/skills/claude-code/outcome-eval/SKILL.md +97 -0
- package/dist/agents/skills/claude-code/outcome-eval/skill.yaml +51 -0
- 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-audit-harness-strength/SKILL.md +188 -0
- package/dist/agents/skills/codex/harness-audit-harness-strength/skill.yaml +54 -0
- package/dist/agents/skills/codex/harness-autopilot/SKILL.md +2 -0
- package/dist/agents/skills/codex/harness-brainstorming/SKILL.md +58 -12
- 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-execution/SKILL.md +1 -1
- package/dist/agents/skills/codex/harness-git-workflow/SKILL.md +2 -0
- package/dist/agents/skills/codex/harness-ideate/SKILL.md +279 -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-knowledge-pipeline/SKILL.md +2 -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/SKILL.md +62 -4
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +36 -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/harness-test-advisor/SKILL.md +80 -2
- package/dist/agents/skills/codex/harness-test-advisor/skill.yaml +4 -1
- package/dist/agents/skills/codex/initialize-harness-project/SKILL.md +86 -18
- package/dist/agents/skills/codex/initialize-harness-project/skill.yaml +1 -0
- package/dist/agents/skills/codex/outcome-eval/SKILL.md +97 -0
- package/dist/agents/skills/codex/outcome-eval/skill.yaml +51 -0
- 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-audit-harness-strength/SKILL.md +188 -0
- package/dist/agents/skills/cursor/harness-audit-harness-strength/skill.yaml +54 -0
- package/dist/agents/skills/cursor/harness-autopilot/SKILL.md +2 -0
- package/dist/agents/skills/cursor/harness-brainstorming/SKILL.md +58 -12
- 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-execution/SKILL.md +1 -1
- package/dist/agents/skills/cursor/harness-git-workflow/SKILL.md +2 -0
- package/dist/agents/skills/cursor/harness-ideate/SKILL.md +279 -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-knowledge-pipeline/SKILL.md +2 -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/SKILL.md +62 -4
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +36 -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/harness-test-advisor/SKILL.md +80 -2
- package/dist/agents/skills/cursor/harness-test-advisor/skill.yaml +4 -1
- package/dist/agents/skills/cursor/initialize-harness-project/SKILL.md +86 -18
- package/dist/agents/skills/cursor/initialize-harness-project/skill.yaml +1 -0
- package/dist/agents/skills/cursor/outcome-eval/SKILL.md +97 -0
- package/dist/agents/skills/cursor/outcome-eval/skill.yaml +51 -0
- 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-audit-harness-strength/SKILL.md +188 -0
- package/dist/agents/skills/gemini-cli/harness-audit-harness-strength/skill.yaml +54 -0
- package/dist/agents/skills/gemini-cli/harness-autopilot/SKILL.md +2 -0
- package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +58 -12
- 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-execution/SKILL.md +1 -1
- package/dist/agents/skills/gemini-cli/harness-git-workflow/SKILL.md +2 -0
- package/dist/agents/skills/gemini-cli/harness-ideate/SKILL.md +279 -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-knowledge-pipeline/SKILL.md +2 -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/SKILL.md +62 -4
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +36 -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/harness-test-advisor/SKILL.md +80 -2
- package/dist/agents/skills/gemini-cli/harness-test-advisor/skill.yaml +4 -1
- package/dist/agents/skills/gemini-cli/initialize-harness-project/SKILL.md +86 -18
- package/dist/agents/skills/gemini-cli/initialize-harness-project/skill.yaml +1 -0
- package/dist/agents/skills/gemini-cli/outcome-eval/SKILL.md +97 -0
- package/dist/agents/skills/gemini-cli/outcome-eval/skill.yaml +51 -0
- package/dist/agents/skills/package.json +1 -1
- package/dist/agents/skills/tests/harness-strategy.test.ts +145 -0
- package/dist/agents/skills/tests/harness-test-advisor.test.ts +94 -0
- package/dist/agents-md-XOJE5ETA.js +13 -0
- package/dist/analyzer-V633GUHY-MBCQWKDU.js +26 -0
- package/dist/{architecture-LUR2I67H.js → architecture-JEZPJ725.js} +6 -6
- package/dist/{assess-project-OJWECOP2.js → assess-project-RHXOQNMM.js} +1 -1
- package/dist/bin/harness-mcp.js +17 -17
- package/dist/bin/harness.js +27 -27
- package/dist/{check-phase-gate-HS5KYLRO.js → check-phase-gate-GNDOMS35.js} +6 -6
- package/dist/{chunk-6UHDQP2N.js → chunk-2N4OENGE.js} +1 -1
- package/dist/{chunk-O2ASYKA6.js → chunk-3ISHDWO7.js} +2 -2
- package/dist/{chunk-GGSNWH7P.js → chunk-527G27VI.js} +5 -5
- package/dist/{chunk-N5NSSLMU.js → chunk-6WQCLCBO.js} +2 -2
- package/dist/{chunk-OH3NXXUY.js → chunk-BDGTZRZ6.js} +11 -1
- package/dist/{chunk-Z3TMCCZB.js → chunk-BYVT5LVO.js} +9 -9
- package/dist/{chunk-TIH53QXY.js → chunk-CLI4K2CH.js} +1 -1
- package/dist/{chunk-VSIRUZQP.js → chunk-DE5U6KOL.js} +2 -2
- package/dist/{chunk-FXZI6NJ2.js → chunk-GGKRA7A7.js} +51 -4
- package/dist/{chunk-RJ2WEES5.js → chunk-GSP2XIVS.js} +2 -2
- package/dist/{chunk-CZPOPMMI.js → chunk-HEIMJJI4.js} +3 -3
- package/dist/{chunk-7AF6C5GE.js → chunk-HWTUUQOW.js} +3 -3
- package/dist/{chunk-FC4JPM6W.js → chunk-KAJOS3BA.js} +3 -3
- package/dist/{chunk-NIVWM7OW.js → chunk-MGUPSE6D.js} +10 -10
- package/dist/{chunk-WZY5U6NS.js → chunk-N24HCQA4.js} +3351 -887
- package/dist/{chunk-Y4T6ENKQ.js → chunk-N55WOGN3.js} +86 -17
- package/dist/{chunk-INBTDBV2.js → chunk-NWLGL3IR.js} +1 -1
- package/dist/{chunk-FEKBXX2J.js → chunk-ONCPJGHY.js} +9 -9
- package/dist/{chunk-PTHX545Q.js → chunk-PC2R5RUJ.js} +2 -2
- package/dist/{chunk-H2ZJOJ7A.js → chunk-PP6ZRL5T.js} +316 -42
- package/dist/{chunk-LECXPXS3.js → chunk-QBC7DI4Q.js} +2 -2
- package/dist/{chunk-B2VAYLYI.js → chunk-UX3PQKVZ.js} +8 -3
- package/dist/{chunk-7PLIWP7U.js → chunk-WWEVS7RO.js} +4409 -743
- package/dist/{chunk-LGYXR2KZ.js → chunk-XFU6SB2Q.js} +1122 -689
- package/dist/{chunk-KN3EMDZ5.js → chunk-XX4OLNWQ.js} +23 -3
- package/dist/{chunk-VI4IS55S.js → chunk-YQSYCBAH.js} +6 -6
- package/dist/{chunk-LYIPI2SJ.js → chunk-Z4AWEIBY.js} +6 -6
- package/dist/{ci-workflow-MDSTHJOY.js → ci-workflow-ORZ4F45F.js} +5 -5
- package/dist/{dist-E3SAAHCM.js → dist-ICW4QUEW.js} +5 -1
- package/dist/{dist-72ID44UE.js → dist-LHINSVK4.js} +136 -4
- package/dist/{dist-R3TOOPJ7.js → dist-OWHMNL4W.js} +1 -1
- package/dist/{docs-2RFOJ6XY.js → docs-AU3DXFFO.js} +6 -6
- package/dist/engine-6GPWOYQH.js +13 -0
- package/dist/{entropy-3UX6644G.js → entropy-VLKDRMRT.js} +5 -5
- package/dist/{feedback-CVCFYND4.js → feedback-YXYY44ZC.js} +1 -1
- package/dist/{generate-agent-definitions-HZ3XENWO.js → generate-agent-definitions-XIHQPKG3.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/hooks/cost-tracker.js +29 -6
- package/dist/index.d.ts +202 -0
- package/dist/index.js +27 -27
- package/dist/{loader-6RHWU5FH.js → loader-YAN56MT3.js} +5 -5
- package/dist/{mcp-OYR26JDI.js → mcp-RK3SFVYB.js} +17 -17
- package/dist/{performance-JJMIRXCH.js → performance-64DPTLLQ.js} +6 -6
- package/dist/{review-pipeline-BHQZIXWZ.js → review-pipeline-NENG6LW7.js} +5 -5
- package/dist/{runtime-DALTAVTN.js → runtime-FFIIRT2T.js} +5 -5
- package/dist/{scan-LYKLM4EQ.js → scan-P7YFKB77.js} +1 -1
- package/dist/{security-RMLMYFPB.js → security-3PIBN35B.js} +1 -1
- package/dist/templates/basic/harness.config.json.hbs +34 -0
- package/dist/templates/ci/README.md +86 -0
- package/dist/templates/ci/required-review.ruleset.json +20 -0
- package/dist/templates/ci/required-review.yml.hbs +57 -0
- package/dist/templates/ci/template.json +5 -0
- package/dist/templates/intermediate/harness.config.json.hbs +36 -0
- package/dist/templates/orchestrator/harness.orchestrator.md +9 -0
- package/dist/{tool-tiers-B7JC2XC3.js → tool-tiers-OKK5FE57.js} +2 -0
- package/dist/{validate-R7DZRDFK.js → validate-7IAF2ES3.js} +6 -6
- package/dist/validate-cross-check-QPSAOZKD.js +13 -0
- package/package.json +8 -8
- package/dist/agents-md-FKMKH6ZF.js +0 -13
- package/dist/analyzer-H3AHBFSL-KPOPJYRB.js +0 -26
- package/dist/engine-BSFKOGPN.js +0 -13
- package/dist/validate-cross-check-AUUZFNVL.js +0 -13
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6WQCLCBO.js";
|
|
4
4
|
import {
|
|
5
5
|
generateAgentsMd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-QBC7DI4Q.js";
|
|
7
7
|
import {
|
|
8
8
|
generateCIWorkflow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-GSP2XIVS.js";
|
|
10
10
|
import {
|
|
11
11
|
createGenerateAgentDefinitionsCommand,
|
|
12
12
|
generateAgentDefinitions
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-NWLGL3IR.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-UX3PQKVZ.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-PC2R5RUJ.js";
|
|
35
35
|
import {
|
|
36
36
|
runPersona
|
|
37
37
|
} from "./chunk-GISMXMVL.js";
|
|
@@ -46,7 +46,7 @@ import {
|
|
|
46
46
|
OutputMode,
|
|
47
47
|
createCheckPhaseGateCommand,
|
|
48
48
|
findFiles
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-KAJOS3BA.js";
|
|
50
50
|
import {
|
|
51
51
|
appendFrameworkAgents,
|
|
52
52
|
captureHealthSnapshot,
|
|
@@ -74,13 +74,13 @@ import {
|
|
|
74
74
|
runSecurityCraft,
|
|
75
75
|
runSpecCraft,
|
|
76
76
|
runTestCraft
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-WWEVS7RO.js";
|
|
78
78
|
import {
|
|
79
79
|
VALID_PLATFORMS
|
|
80
80
|
} from "./chunk-3ISINLYT.js";
|
|
81
81
|
import {
|
|
82
82
|
loadGraphStore
|
|
83
|
-
} from "./chunk-
|
|
83
|
+
} from "./chunk-CLI4K2CH.js";
|
|
84
84
|
import {
|
|
85
85
|
loadOrRebuildIndex
|
|
86
86
|
} from "./chunk-27AJKSQY.js";
|
|
@@ -99,7 +99,7 @@ import {
|
|
|
99
99
|
BranchingConfigSchema,
|
|
100
100
|
findConfigFile,
|
|
101
101
|
resolveConfig
|
|
102
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-GGKRA7A7.js";
|
|
103
103
|
import {
|
|
104
104
|
logger
|
|
105
105
|
} from "./chunk-EBJQ6N4M.js";
|
|
@@ -114,10 +114,13 @@ import {
|
|
|
114
114
|
BlueprintGenerator,
|
|
115
115
|
BundleSchema,
|
|
116
116
|
CINotifier,
|
|
117
|
+
CI_ASSESSMENTS,
|
|
117
118
|
CriticalPathResolver,
|
|
118
119
|
GitHubIssuesSyncAdapter,
|
|
120
|
+
HarnessStrengthAuditor,
|
|
119
121
|
PredictionEngine,
|
|
120
122
|
ProjectScanner,
|
|
123
|
+
RUNNER_PRESETS,
|
|
121
124
|
SecurityScanner,
|
|
122
125
|
SecurityTimelineManager,
|
|
123
126
|
SpecImpactEstimator,
|
|
@@ -128,6 +131,7 @@ import {
|
|
|
128
131
|
assembleCandidateReport,
|
|
129
132
|
assembleReport,
|
|
130
133
|
checkDocCoverage,
|
|
134
|
+
checkRoadmapHealth,
|
|
131
135
|
checkTaint,
|
|
132
136
|
clearTaint,
|
|
133
137
|
computeHotspots,
|
|
@@ -179,6 +183,7 @@ import {
|
|
|
179
183
|
resolveStreamPath,
|
|
180
184
|
runAll,
|
|
181
185
|
runCIChecks,
|
|
186
|
+
runCiReview,
|
|
182
187
|
runPulse,
|
|
183
188
|
runReviewPipeline,
|
|
184
189
|
scanForInjection,
|
|
@@ -191,9 +196,10 @@ import {
|
|
|
191
196
|
validatePulseConfig,
|
|
192
197
|
validateRoadmapMode,
|
|
193
198
|
validateSolutionsDir,
|
|
199
|
+
validateStrategy,
|
|
194
200
|
writeConfig,
|
|
195
201
|
writeLockfile
|
|
196
|
-
} from "./chunk-
|
|
202
|
+
} from "./chunk-N24HCQA4.js";
|
|
197
203
|
import {
|
|
198
204
|
CLI_VERSION
|
|
199
205
|
} from "./chunk-BM3PWGXQ.js";
|
|
@@ -206,20 +212,20 @@ import {
|
|
|
206
212
|
detectDocDrift,
|
|
207
213
|
generateSuggestions,
|
|
208
214
|
validateDependencies
|
|
209
|
-
} from "./chunk-
|
|
215
|
+
} from "./chunk-N55WOGN3.js";
|
|
210
216
|
import {
|
|
211
217
|
Err,
|
|
212
218
|
INDEXED_FILE_KINDS,
|
|
213
219
|
INSIGHTS_KEYS,
|
|
214
220
|
Ok,
|
|
215
221
|
init_dist
|
|
216
|
-
} from "./chunk-
|
|
222
|
+
} from "./chunk-BDGTZRZ6.js";
|
|
217
223
|
import {
|
|
218
224
|
DEFAULT_SKIP_DIRS,
|
|
219
225
|
DesignConstraintAdapter,
|
|
220
226
|
GraphStore,
|
|
221
227
|
init_dist as init_dist2
|
|
222
|
-
} from "./chunk-
|
|
228
|
+
} from "./chunk-PP6ZRL5T.js";
|
|
223
229
|
import {
|
|
224
230
|
ArchConfigSchema,
|
|
225
231
|
TimelineManager
|
|
@@ -230,7 +236,7 @@ import {
|
|
|
230
236
|
} from "./chunk-KFQGP6VL.js";
|
|
231
237
|
|
|
232
238
|
// src/index.ts
|
|
233
|
-
import { Command as
|
|
239
|
+
import { Command as Command122 } from "commander";
|
|
234
240
|
|
|
235
241
|
// src/commands/add.ts
|
|
236
242
|
init_dist3();
|
|
@@ -419,7 +425,7 @@ function registerSkillsCommand(adoption) {
|
|
|
419
425
|
const globalOpts = cmd.optsWithGlobals();
|
|
420
426
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
421
427
|
const cwd = process.cwd();
|
|
422
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
428
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-LHINSVK4.js");
|
|
423
429
|
const records = readAdoptionRecords(cwd);
|
|
424
430
|
if (records.length === 0) {
|
|
425
431
|
if (globalOpts.json) {
|
|
@@ -455,7 +461,7 @@ function registerRecentCommand(adoption) {
|
|
|
455
461
|
const globalOpts = cmd.optsWithGlobals();
|
|
456
462
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
457
463
|
const cwd = process.cwd();
|
|
458
|
-
const { readAdoptionRecords } = await import("./dist-
|
|
464
|
+
const { readAdoptionRecords } = await import("./dist-LHINSVK4.js");
|
|
459
465
|
const records = readAdoptionRecords(cwd);
|
|
460
466
|
if (records.length === 0) {
|
|
461
467
|
if (globalOpts.json) {
|
|
@@ -533,7 +539,7 @@ function registerSkillCommand(adoption) {
|
|
|
533
539
|
adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
|
|
534
540
|
const globalOpts = cmd.optsWithGlobals();
|
|
535
541
|
const cwd = process.cwd();
|
|
536
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
542
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-LHINSVK4.js");
|
|
537
543
|
const records = readAdoptionRecords(cwd);
|
|
538
544
|
const skillRecords = records.filter((r) => r.skill === name);
|
|
539
545
|
if (skillRecords.length === 0) {
|
|
@@ -848,6 +854,9 @@ function createAlignDesignSystemCommand() {
|
|
|
848
854
|
).option(
|
|
849
855
|
"--design-strictness <level>",
|
|
850
856
|
"Override design.strictness: strict | standard | permissive"
|
|
857
|
+
).option(
|
|
858
|
+
"--revert",
|
|
859
|
+
"Inverse-apply the most-recent batch recorded at .harness/align/last-batch.json. Skips files edited externally since the apply."
|
|
851
860
|
).action(async (opts, cmd) => {
|
|
852
861
|
const globalOpts = cmd.optsWithGlobals();
|
|
853
862
|
const outputMode = resolveOutputMode(globalOpts);
|
|
@@ -858,6 +867,7 @@ function createAlignDesignSystemCommand() {
|
|
|
858
867
|
if (opts.files !== void 0) input.files = opts.files;
|
|
859
868
|
if (opts.mode !== void 0) input.mode = opts.mode;
|
|
860
869
|
if (opts.designStrictness !== void 0) input.designStrictness = opts.designStrictness;
|
|
870
|
+
if (opts.revert === true) input.revert = true;
|
|
861
871
|
let result;
|
|
862
872
|
try {
|
|
863
873
|
result = await runAlignDesignSystem(input);
|
|
@@ -884,7 +894,11 @@ function printAlignResult(result, mode, _formatter) {
|
|
|
884
894
|
const verbose = mode === OutputMode.VERBOSE;
|
|
885
895
|
const { summary, outcomes, meta } = result;
|
|
886
896
|
if (outcomes.length === 0) {
|
|
887
|
-
|
|
897
|
+
if (meta.revert === true) {
|
|
898
|
+
console.log("No batch to revert (.harness/align/last-batch.json missing or empty).");
|
|
899
|
+
} else {
|
|
900
|
+
console.log("No drift findings to align.");
|
|
901
|
+
}
|
|
888
902
|
return;
|
|
889
903
|
}
|
|
890
904
|
const byFile = /* @__PURE__ */ new Map();
|
|
@@ -914,8 +928,9 @@ ${file}`);
|
|
|
914
928
|
}
|
|
915
929
|
}
|
|
916
930
|
console.log("");
|
|
931
|
+
const action = meta.revert === true ? "reverted" : "applied";
|
|
917
932
|
console.log(
|
|
918
|
-
`Summary: ${summary.applied}
|
|
933
|
+
`Summary: ${summary.applied} ${action}, ${summary.suggestions} suggestions, ${summary.skipped} skipped, ${summary.failed} failed (${summary.filesModified} files modified, ${summary.durationMs}ms)`
|
|
919
934
|
);
|
|
920
935
|
if (meta.dryRun) {
|
|
921
936
|
console.log("(dry-run \u2014 no files written)");
|
|
@@ -1654,9 +1669,9 @@ ${name} (${count} finding${count === 1 ? "" : "s"})`);
|
|
|
1654
1669
|
}
|
|
1655
1670
|
function printAnatomyFindings(findings, verbose) {
|
|
1656
1671
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1657
|
-
for (const [file,
|
|
1672
|
+
for (const [file, fs49] of byFile) {
|
|
1658
1673
|
console.log(` ${file}`);
|
|
1659
|
-
for (const f of
|
|
1674
|
+
for (const f of fs49) {
|
|
1660
1675
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1661
1676
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1662
1677
|
if (verbose) {
|
|
@@ -1667,9 +1682,9 @@ function printAnatomyFindings(findings, verbose) {
|
|
|
1667
1682
|
}
|
|
1668
1683
|
function printCraftFindings(findings, verbose) {
|
|
1669
1684
|
const byFile = groupByFile(findings, (f) => f.target.file);
|
|
1670
|
-
for (const [file,
|
|
1685
|
+
for (const [file, fs49] of byFile) {
|
|
1671
1686
|
console.log(` ${file}`);
|
|
1672
|
-
for (const f of
|
|
1687
|
+
for (const f of fs49) {
|
|
1673
1688
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
1674
1689
|
const sev = craftTierToSeverity(f.tier);
|
|
1675
1690
|
console.log(
|
|
@@ -1683,9 +1698,9 @@ function printCraftFindings(findings, verbose) {
|
|
|
1683
1698
|
}
|
|
1684
1699
|
function printDriftFindings(findings, verbose) {
|
|
1685
1700
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1686
|
-
for (const [file,
|
|
1701
|
+
for (const [file, fs49] of byFile) {
|
|
1687
1702
|
console.log(` ${file}`);
|
|
1688
|
-
for (const f of
|
|
1703
|
+
for (const f of fs49) {
|
|
1689
1704
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1690
1705
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1691
1706
|
if (verbose) {
|
|
@@ -1696,9 +1711,9 @@ function printDriftFindings(findings, verbose) {
|
|
|
1696
1711
|
}
|
|
1697
1712
|
function printBrandFindings(findings, verbose) {
|
|
1698
1713
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1699
|
-
for (const [file,
|
|
1714
|
+
for (const [file, fs49] of byFile) {
|
|
1700
1715
|
console.log(` ${file}`);
|
|
1701
|
-
for (const f of
|
|
1716
|
+
for (const f of fs49) {
|
|
1702
1717
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1703
1718
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1704
1719
|
if (verbose) {
|
|
@@ -1867,14 +1882,90 @@ function createCheckDocsCommand() {
|
|
|
1867
1882
|
return command;
|
|
1868
1883
|
}
|
|
1869
1884
|
|
|
1870
|
-
// src/commands/check-
|
|
1885
|
+
// src/commands/check-harness-strength.ts
|
|
1871
1886
|
init_dist3();
|
|
1872
1887
|
import { Command as Command14 } from "commander";
|
|
1873
1888
|
import * as path8 from "path";
|
|
1889
|
+
var SEVERITY_RANK = { error: 3, warning: 2, info: 1 };
|
|
1890
|
+
function runCheckHarnessStrength(cwd, options) {
|
|
1891
|
+
const projectRoot2 = path8.resolve(cwd);
|
|
1892
|
+
const auditor = new HarnessStrengthAuditor();
|
|
1893
|
+
const result = auditor.audit(projectRoot2, options.mode ? { mode: options.mode } : {});
|
|
1894
|
+
if (!result.ok) return result;
|
|
1895
|
+
const audit = result.value;
|
|
1896
|
+
const threshold = options.severity ?? "warning";
|
|
1897
|
+
const thresholdRank = SEVERITY_RANK[threshold];
|
|
1898
|
+
const filtered = audit.findings.filter((f) => SEVERITY_RANK[f.severity] >= thresholdRank);
|
|
1899
|
+
const hasErrors = filtered.some((f) => f.severity === "error");
|
|
1900
|
+
return Ok({ valid: !hasErrors, audit, filtered });
|
|
1901
|
+
}
|
|
1902
|
+
async function runCheckHarnessStrengthAction(opts, globalOpts) {
|
|
1903
|
+
const outMode = globalOpts.json ? OutputMode.JSON : globalOpts.quiet ? OutputMode.QUIET : globalOpts.verbose ? OutputMode.VERBOSE : OutputMode.TEXT;
|
|
1904
|
+
const formatter = new OutputFormatter(outMode);
|
|
1905
|
+
const resolvedMode = opts.mode ?? (opts.toolkit ? "toolkit" : opts.adopter ? "adopter" : void 0);
|
|
1906
|
+
const result = runCheckHarnessStrength(process.cwd(), {
|
|
1907
|
+
severity: opts.severity,
|
|
1908
|
+
...resolvedMode !== void 0 && { mode: resolvedMode },
|
|
1909
|
+
...opts.reportOnly !== void 0 && { reportOnly: opts.reportOnly }
|
|
1910
|
+
});
|
|
1911
|
+
if (!result.ok) {
|
|
1912
|
+
if (outMode === OutputMode.JSON) {
|
|
1913
|
+
console.log(JSON.stringify({ error: result.error.message }));
|
|
1914
|
+
} else {
|
|
1915
|
+
logger.error(result.error.message);
|
|
1916
|
+
}
|
|
1917
|
+
process.exit(ExitCode.ERROR);
|
|
1918
|
+
}
|
|
1919
|
+
const { valid, audit, filtered } = result.value;
|
|
1920
|
+
if (outMode === OutputMode.JSON) {
|
|
1921
|
+
console.log(JSON.stringify(audit, null, 2));
|
|
1922
|
+
process.exit(opts.reportOnly || valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
|
|
1923
|
+
}
|
|
1924
|
+
const issues = filtered.map((f) => ({
|
|
1925
|
+
file: f.line !== void 0 ? `${f.file}:${f.line}` : f.file,
|
|
1926
|
+
message: `[${f.id}] ${f.severity.toUpperCase()} ${f.message} -> ${f.remediation}`
|
|
1927
|
+
}));
|
|
1928
|
+
const header = formatter.formatSummary(
|
|
1929
|
+
`harness strength (${audit.mode})`,
|
|
1930
|
+
`${audit.score}/100 (${audit.tier})`,
|
|
1931
|
+
valid
|
|
1932
|
+
);
|
|
1933
|
+
if (header) console.log(header);
|
|
1934
|
+
const output = formatter.formatValidation({ valid, issues });
|
|
1935
|
+
if (output) console.log(output);
|
|
1936
|
+
const summaryLine = formatter.formatSummary(
|
|
1937
|
+
"findings",
|
|
1938
|
+
`${audit.summary.errors} error / ${audit.summary.warnings} warning / ${audit.summary.info} info`,
|
|
1939
|
+
valid
|
|
1940
|
+
);
|
|
1941
|
+
if (summaryLine) console.log(summaryLine);
|
|
1942
|
+
process.exit(opts.reportOnly || valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
|
|
1943
|
+
}
|
|
1944
|
+
function createCheckHarnessStrengthCommand() {
|
|
1945
|
+
const command = new Command14("check-harness-strength").description("Mechanically audit this project's harness setup against the 7 strength patterns").option("--severity <level>", "Minimum severity threshold to display and gate on", "warning").option("--mode <mode>", "Audit mode: adopter | toolkit (default: auto-detect)").option("--toolkit", "Force toolkit mode").option("--adopter", "Force adopter mode").option("--report-only", "Always exit 0 regardless of findings").hook("preAction", (thisCommand) => {
|
|
1946
|
+
const { severity, mode } = thisCommand.opts();
|
|
1947
|
+
if (!["error", "warning", "info"].includes(severity)) {
|
|
1948
|
+
logger.error(`Invalid severity: "${severity}". Must be one of: error, warning, info`);
|
|
1949
|
+
process.exit(ExitCode.ERROR);
|
|
1950
|
+
}
|
|
1951
|
+
if (mode !== void 0 && !["adopter", "toolkit"].includes(mode)) {
|
|
1952
|
+
logger.error(`Invalid mode: "${mode}". Must be one of: adopter, toolkit`);
|
|
1953
|
+
process.exit(ExitCode.ERROR);
|
|
1954
|
+
}
|
|
1955
|
+
}).action(async (opts, cmd) => {
|
|
1956
|
+
await runCheckHarnessStrengthAction(opts, cmd.optsWithGlobals());
|
|
1957
|
+
});
|
|
1958
|
+
return command;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
// src/commands/check-perf.ts
|
|
1962
|
+
init_dist3();
|
|
1963
|
+
import { Command as Command15 } from "commander";
|
|
1964
|
+
import * as path9 from "path";
|
|
1874
1965
|
async function runCheckPerf(cwd, options) {
|
|
1875
1966
|
const runAll2 = !options.structural && !options.size && !options.coupling;
|
|
1876
1967
|
const analyzer = new EntropyAnalyzer({
|
|
1877
|
-
rootDir:
|
|
1968
|
+
rootDir: path9.resolve(cwd),
|
|
1878
1969
|
analyze: {
|
|
1879
1970
|
complexity: runAll2 || !!options.structural,
|
|
1880
1971
|
coupling: runAll2 || !!options.coupling,
|
|
@@ -1986,7 +2077,7 @@ async function runCheckPerfAction(opts, globalOpts) {
|
|
|
1986
2077
|
process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
|
|
1987
2078
|
}
|
|
1988
2079
|
function createCheckPerfCommand() {
|
|
1989
|
-
const command = new
|
|
2080
|
+
const command = new Command15("check-perf").description("Run performance checks: structural complexity, coupling, and size budgets").option("--structural", "Run structural complexity checks only").option("--coupling", "Run coupling metric checks only").option("--size", "Run size budget checks only").action(async (opts, cmd) => {
|
|
1990
2081
|
await runCheckPerfAction(opts, cmd.optsWithGlobals());
|
|
1991
2082
|
});
|
|
1992
2083
|
return command;
|
|
@@ -1994,10 +2085,10 @@ function createCheckPerfCommand() {
|
|
|
1994
2085
|
|
|
1995
2086
|
// src/commands/check-security.ts
|
|
1996
2087
|
init_dist3();
|
|
1997
|
-
import { Command as
|
|
1998
|
-
import * as
|
|
2088
|
+
import { Command as Command16 } from "commander";
|
|
2089
|
+
import * as path10 from "path";
|
|
1999
2090
|
import { execSync as execSync3 } from "child_process";
|
|
2000
|
-
var
|
|
2091
|
+
var SEVERITY_RANK2 = {
|
|
2001
2092
|
error: 3,
|
|
2002
2093
|
warning: 2,
|
|
2003
2094
|
info: 1
|
|
@@ -2008,19 +2099,19 @@ function getChangedFiles(cwd) {
|
|
|
2008
2099
|
cwd,
|
|
2009
2100
|
encoding: "utf-8"
|
|
2010
2101
|
});
|
|
2011
|
-
return output.trim().split("\n").filter((f) => f.length > 0).map((f) =>
|
|
2102
|
+
return output.trim().split("\n").filter((f) => f.length > 0).map((f) => path10.resolve(cwd, f));
|
|
2012
2103
|
} catch {
|
|
2013
2104
|
return [];
|
|
2014
2105
|
}
|
|
2015
2106
|
}
|
|
2016
2107
|
async function runCheckSecurity(cwd, options) {
|
|
2017
|
-
const projectRoot2 =
|
|
2108
|
+
const projectRoot2 = path10.resolve(cwd);
|
|
2018
2109
|
let configData = {};
|
|
2019
2110
|
try {
|
|
2020
|
-
const
|
|
2021
|
-
const configPath =
|
|
2022
|
-
if (
|
|
2023
|
-
const raw =
|
|
2111
|
+
const fs49 = await import("fs");
|
|
2112
|
+
const configPath = path10.join(projectRoot2, "harness.config.json");
|
|
2113
|
+
if (fs49.existsSync(configPath)) {
|
|
2114
|
+
const raw = fs49.readFileSync(configPath, "utf-8");
|
|
2024
2115
|
const parsed = JSON.parse(raw);
|
|
2025
2116
|
configData = parsed.security ?? {};
|
|
2026
2117
|
}
|
|
@@ -2055,8 +2146,8 @@ async function runCheckSecurity(cwd, options) {
|
|
|
2055
2146
|
} catch {
|
|
2056
2147
|
}
|
|
2057
2148
|
const threshold = options.severity ?? "warning";
|
|
2058
|
-
const thresholdRank =
|
|
2059
|
-
const filtered = result.findings.filter((f) =>
|
|
2149
|
+
const thresholdRank = SEVERITY_RANK2[threshold];
|
|
2150
|
+
const filtered = result.findings.filter((f) => SEVERITY_RANK2[f.severity] >= thresholdRank);
|
|
2060
2151
|
const hasErrors = filtered.some((f) => f.severity === "error");
|
|
2061
2152
|
return Ok({
|
|
2062
2153
|
valid: !hasErrors,
|
|
@@ -2099,7 +2190,7 @@ async function runCheckSecurityAction(opts, globalOpts) {
|
|
|
2099
2190
|
process.exit(result.value.valid ? ExitCode.SUCCESS : ExitCode.VALIDATION_FAILED);
|
|
2100
2191
|
}
|
|
2101
2192
|
function createCheckSecurityCommand() {
|
|
2102
|
-
const command = new
|
|
2193
|
+
const command = new Command16("check-security").description("Run lightweight security scan: secrets, injection, XSS, weak crypto").option("--severity <level>", "Minimum severity threshold", "warning").hook("preAction", (thisCommand) => {
|
|
2103
2194
|
const severity = thisCommand.opts().severity;
|
|
2104
2195
|
if (!["error", "warning", "info"].includes(severity)) {
|
|
2105
2196
|
logger.error(`Invalid severity: "${severity}". Must be one of: error, warning, info`);
|
|
@@ -2112,11 +2203,11 @@ function createCheckSecurityCommand() {
|
|
|
2112
2203
|
}
|
|
2113
2204
|
|
|
2114
2205
|
// src/commands/ci/index.ts
|
|
2115
|
-
import { Command as
|
|
2206
|
+
import { Command as Command20 } from "commander";
|
|
2116
2207
|
|
|
2117
2208
|
// src/commands/ci/check.ts
|
|
2118
2209
|
init_dist3();
|
|
2119
|
-
import { Command as
|
|
2210
|
+
import { Command as Command17 } from "commander";
|
|
2120
2211
|
var VALID_CHECKS = [
|
|
2121
2212
|
"validate",
|
|
2122
2213
|
"deps",
|
|
@@ -2206,16 +2297,16 @@ async function runCheckAction(opts, globalOpts) {
|
|
|
2206
2297
|
process.exit(report.exitCode);
|
|
2207
2298
|
}
|
|
2208
2299
|
function createCheckCommand() {
|
|
2209
|
-
return new
|
|
2300
|
+
return new Command17("check").description("Run all harness checks for CI (validate, deps, docs, entropy, phase-gate, arch)").option("--skip <checks>", "Comma-separated checks to skip (e.g., entropy,docs)").option("--fail-on <severity>", "Fail on severity level: error (default) or warning", "error").action(async (opts, cmd) => {
|
|
2210
2301
|
await runCheckAction(opts, cmd.optsWithGlobals());
|
|
2211
2302
|
});
|
|
2212
2303
|
}
|
|
2213
2304
|
|
|
2214
2305
|
// src/commands/ci/init.ts
|
|
2215
2306
|
init_dist3();
|
|
2216
|
-
import { Command as
|
|
2307
|
+
import { Command as Command18 } from "commander";
|
|
2217
2308
|
import * as fs4 from "fs";
|
|
2218
|
-
import * as
|
|
2309
|
+
import * as path11 from "path";
|
|
2219
2310
|
var ALL_CHECKS = [
|
|
2220
2311
|
"validate",
|
|
2221
2312
|
"deps",
|
|
@@ -2233,8 +2324,74 @@ function buildSkipFlag(checks) {
|
|
|
2233
2324
|
if (skipChecks.length === 0) return "";
|
|
2234
2325
|
return ` --skip ${skipChecks.join(",")}`;
|
|
2235
2326
|
}
|
|
2236
|
-
function
|
|
2237
|
-
|
|
2327
|
+
function stepsForLanguage(language) {
|
|
2328
|
+
switch (language) {
|
|
2329
|
+
case "python":
|
|
2330
|
+
return {
|
|
2331
|
+
setup: ` - uses: actions/setup-python@v5
|
|
2332
|
+
with:
|
|
2333
|
+
python-version: '3.12'`,
|
|
2334
|
+
install: "pip install -e .",
|
|
2335
|
+
lint: "ruff check .",
|
|
2336
|
+
test: "pytest"
|
|
2337
|
+
};
|
|
2338
|
+
case "go":
|
|
2339
|
+
return {
|
|
2340
|
+
setup: ` - uses: actions/setup-go@v5
|
|
2341
|
+
with:
|
|
2342
|
+
go-version: 'stable'`,
|
|
2343
|
+
install: "go mod download",
|
|
2344
|
+
build: "go build ./...",
|
|
2345
|
+
lint: "golangci-lint run",
|
|
2346
|
+
test: "go test ./..."
|
|
2347
|
+
};
|
|
2348
|
+
case "rust":
|
|
2349
|
+
return {
|
|
2350
|
+
setup: ` - uses: dtolnay/rust-toolchain@stable`,
|
|
2351
|
+
install: "cargo fetch",
|
|
2352
|
+
build: "cargo build",
|
|
2353
|
+
lint: "cargo clippy",
|
|
2354
|
+
test: "cargo test"
|
|
2355
|
+
};
|
|
2356
|
+
case "java":
|
|
2357
|
+
return {
|
|
2358
|
+
setup: ` - uses: actions/setup-java@v4
|
|
2359
|
+
with:
|
|
2360
|
+
distribution: 'temurin'
|
|
2361
|
+
java-version: '21'`,
|
|
2362
|
+
install: "mvn -B -q install -DskipTests",
|
|
2363
|
+
test: "mvn -B verify"
|
|
2364
|
+
};
|
|
2365
|
+
case "typescript":
|
|
2366
|
+
default:
|
|
2367
|
+
return {
|
|
2368
|
+
setup: ` - uses: pnpm/action-setup@v4
|
|
2369
|
+
- uses: actions/setup-node@v4
|
|
2370
|
+
with:
|
|
2371
|
+
node-version: '22'`,
|
|
2372
|
+
install: "pnpm i --frozen-lockfile",
|
|
2373
|
+
build: "pnpm build",
|
|
2374
|
+
lint: "pnpm lint",
|
|
2375
|
+
test: "pnpm test"
|
|
2376
|
+
};
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
function generateGitHubActions(skipFlag, language) {
|
|
2380
|
+
const steps = stepsForLanguage(language);
|
|
2381
|
+
const commandSteps = [];
|
|
2382
|
+
commandSteps.push(` - name: Install dependencies
|
|
2383
|
+
run: ${steps.install}`);
|
|
2384
|
+
if (steps.build) {
|
|
2385
|
+
commandSteps.push(` - name: Build
|
|
2386
|
+
run: ${steps.build}`);
|
|
2387
|
+
}
|
|
2388
|
+
if (steps.lint) {
|
|
2389
|
+
commandSteps.push(` - name: Lint
|
|
2390
|
+
run: ${steps.lint}`);
|
|
2391
|
+
}
|
|
2392
|
+
commandSteps.push(` - name: Test
|
|
2393
|
+
run: ${steps.test}`);
|
|
2394
|
+
return `name: CI
|
|
2238
2395
|
|
|
2239
2396
|
on:
|
|
2240
2397
|
push:
|
|
@@ -2247,15 +2404,15 @@ concurrency:
|
|
|
2247
2404
|
cancel-in-progress: true
|
|
2248
2405
|
|
|
2249
2406
|
jobs:
|
|
2250
|
-
|
|
2407
|
+
ci:
|
|
2251
2408
|
runs-on: ubuntu-latest
|
|
2252
2409
|
steps:
|
|
2253
2410
|
- uses: actions/checkout@v4
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
- name:
|
|
2411
|
+
${steps.setup}
|
|
2412
|
+
${commandSteps.join("\n")}
|
|
2413
|
+
- name: Install harness CLI
|
|
2414
|
+
run: npm install -g @harness-engineering/cli
|
|
2415
|
+
- name: Harness gate
|
|
2259
2416
|
run: harness ci check --json${skipFlag}
|
|
2260
2417
|
`;
|
|
2261
2418
|
}
|
|
@@ -2300,10 +2457,13 @@ exit $EXIT_CODE
|
|
|
2300
2457
|
`;
|
|
2301
2458
|
}
|
|
2302
2459
|
function generateCIConfig(options) {
|
|
2303
|
-
const { platform, checks } = options;
|
|
2460
|
+
const { platform, checks, language } = options;
|
|
2304
2461
|
const skipFlag = buildSkipFlag(checks);
|
|
2305
2462
|
const generators = {
|
|
2306
|
-
github: {
|
|
2463
|
+
github: {
|
|
2464
|
+
filename: ".github/workflows/ci.yml",
|
|
2465
|
+
generate: (skip) => generateGitHubActions(skip, language)
|
|
2466
|
+
},
|
|
2307
2467
|
gitlab: { filename: ".gitlab-ci-harness.yml", generate: generateGitLabCI },
|
|
2308
2468
|
generic: { filename: "harness-ci.sh", generate: generateGenericScript }
|
|
2309
2469
|
};
|
|
@@ -2322,8 +2482,8 @@ function detectPlatform() {
|
|
|
2322
2482
|
return null;
|
|
2323
2483
|
}
|
|
2324
2484
|
function writeGeneratedConfig(filename, content, platform) {
|
|
2325
|
-
const targetPath =
|
|
2326
|
-
const dir =
|
|
2485
|
+
const targetPath = path11.resolve(filename);
|
|
2486
|
+
const dir = path11.dirname(targetPath);
|
|
2327
2487
|
fs4.mkdirSync(dir, { recursive: true });
|
|
2328
2488
|
fs4.writeFileSync(targetPath, content);
|
|
2329
2489
|
if (platform === "generic" && process.platform !== "win32") {
|
|
@@ -2335,6 +2495,7 @@ async function runInitAction(opts, globalOpts) {
|
|
|
2335
2495
|
const checks = opts.checks ? opts.checks.split(",").map((s) => s.trim()) : void 0;
|
|
2336
2496
|
const configOpts = { platform };
|
|
2337
2497
|
if (checks) configOpts.checks = checks;
|
|
2498
|
+
if (opts.language) configOpts.language = opts.language;
|
|
2338
2499
|
const result = generateCIConfig(configOpts);
|
|
2339
2500
|
if (!result.ok) {
|
|
2340
2501
|
logger.error(result.error.message);
|
|
@@ -2350,14 +2511,14 @@ async function runInitAction(opts, globalOpts) {
|
|
|
2350
2511
|
}
|
|
2351
2512
|
}
|
|
2352
2513
|
function createInitCommand() {
|
|
2353
|
-
return new
|
|
2514
|
+
return new Command18("init").description("Generate CI configuration for harness checks").option("--platform <platform>", "CI platform: github, gitlab, or generic").option("--language <language>", "Project language for build/lint/test steps").option("--checks <list>", "Comma-separated list of checks to include").action(async (opts, cmd) => {
|
|
2354
2515
|
await runInitAction(opts, cmd.optsWithGlobals());
|
|
2355
2516
|
});
|
|
2356
2517
|
}
|
|
2357
2518
|
|
|
2358
2519
|
// src/commands/ci/notify.ts
|
|
2359
2520
|
init_dist3();
|
|
2360
|
-
import { Command as
|
|
2521
|
+
import { Command as Command19 } from "commander";
|
|
2361
2522
|
import * as fs5 from "fs";
|
|
2362
2523
|
function loadReport(reportPath) {
|
|
2363
2524
|
try {
|
|
@@ -2454,14 +2615,14 @@ async function runNotifyAction(reportPath, opts, globalOpts) {
|
|
|
2454
2615
|
}
|
|
2455
2616
|
}
|
|
2456
2617
|
function createNotifyCommand() {
|
|
2457
|
-
return new
|
|
2618
|
+
return new Command19("notify").description("Post CI check results to GitHub (PR comment or issue)").argument("<report>", "Path to CI check report JSON file (from harness ci check --json)").requiredOption("--target <target>", "Notification target: pr-comment or issue").option("--pr <number>", "PR number (required for pr-comment target)").option("--title <title>", "Custom issue title (for issue target)").option("--labels <labels>", "Comma-separated labels for created issues").action(async (reportPath, opts, cmd) => {
|
|
2458
2619
|
await runNotifyAction(reportPath, opts, cmd.optsWithGlobals());
|
|
2459
2620
|
});
|
|
2460
2621
|
}
|
|
2461
2622
|
|
|
2462
2623
|
// src/commands/ci/index.ts
|
|
2463
2624
|
function createCICommand() {
|
|
2464
|
-
const command = new
|
|
2625
|
+
const command = new Command20("ci").description("CI/CD integration commands");
|
|
2465
2626
|
command.addCommand(createCheckCommand());
|
|
2466
2627
|
command.addCommand(createInitCommand());
|
|
2467
2628
|
command.addCommand(createNotifyCommand());
|
|
@@ -2470,8 +2631,8 @@ function createCICommand() {
|
|
|
2470
2631
|
|
|
2471
2632
|
// src/commands/cleanup.ts
|
|
2472
2633
|
init_dist3();
|
|
2473
|
-
import { Command as
|
|
2474
|
-
import * as
|
|
2634
|
+
import { Command as Command21 } from "commander";
|
|
2635
|
+
import * as path12 from "path";
|
|
2475
2636
|
async function runCleanup(options) {
|
|
2476
2637
|
const cwd = options.cwd ?? process.cwd();
|
|
2477
2638
|
const type = options.type ?? "all";
|
|
@@ -2486,12 +2647,12 @@ async function runCleanup(options) {
|
|
|
2486
2647
|
patternViolations: [],
|
|
2487
2648
|
totalIssues: 0
|
|
2488
2649
|
};
|
|
2489
|
-
const rootDir =
|
|
2490
|
-
const docsDir =
|
|
2650
|
+
const rootDir = path12.resolve(cwd, config.rootDir);
|
|
2651
|
+
const docsDir = path12.resolve(cwd, config.docsDir);
|
|
2491
2652
|
const entropyConfig = {
|
|
2492
2653
|
rootDir,
|
|
2493
2654
|
...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
|
|
2494
|
-
docPaths: [
|
|
2655
|
+
docPaths: [path12.join(docsDir, "**/*.md")],
|
|
2495
2656
|
analyze: {
|
|
2496
2657
|
drift: type === "all" || type === "drift",
|
|
2497
2658
|
deadCode: type === "all" || type === "dead-code",
|
|
@@ -2559,7 +2720,7 @@ function printCleanupResult(value, formatter) {
|
|
|
2559
2720
|
}
|
|
2560
2721
|
}
|
|
2561
2722
|
function createCleanupCommand() {
|
|
2562
|
-
const command = new
|
|
2723
|
+
const command = new Command21("cleanup").description("Detect entropy issues (doc drift, dead code, patterns)").option("-t, --type <type>", "Issue type: drift, dead-code, patterns, all", "all").action(async (opts, cmd) => {
|
|
2563
2724
|
const globalOpts = cmd.optsWithGlobals();
|
|
2564
2725
|
const mode = resolveOutputMode(globalOpts);
|
|
2565
2726
|
const formatter = new OutputFormatter(mode);
|
|
@@ -2590,9 +2751,9 @@ function createCleanupCommand() {
|
|
|
2590
2751
|
|
|
2591
2752
|
// src/commands/cleanup-sessions.ts
|
|
2592
2753
|
init_dist3();
|
|
2593
|
-
import { Command as
|
|
2754
|
+
import { Command as Command22 } from "commander";
|
|
2594
2755
|
import * as fs6 from "fs";
|
|
2595
|
-
import * as
|
|
2756
|
+
import * as path13 from "path";
|
|
2596
2757
|
var STALE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
2597
2758
|
var ONE_HOUR_MS = 60 * 60 * 1e3;
|
|
2598
2759
|
var ONE_DAY_MS = 24 * ONE_HOUR_MS;
|
|
@@ -2609,7 +2770,7 @@ function getMostRecentMtime(dirPath) {
|
|
|
2609
2770
|
try {
|
|
2610
2771
|
const entries = fs6.readdirSync(dirPath, { withFileTypes: true });
|
|
2611
2772
|
for (const entry of entries) {
|
|
2612
|
-
const fullPath =
|
|
2773
|
+
const fullPath = path13.join(dirPath, entry.name);
|
|
2613
2774
|
const stat = fs6.statSync(fullPath);
|
|
2614
2775
|
if (stat.mtimeMs > latest) latest = stat.mtimeMs;
|
|
2615
2776
|
}
|
|
@@ -2622,7 +2783,7 @@ function getMostRecentMtime(dirPath) {
|
|
|
2622
2783
|
async function runCleanupSessions(options) {
|
|
2623
2784
|
const cwd = options.cwd ?? process.cwd();
|
|
2624
2785
|
const dryRun = options.dryRun ?? false;
|
|
2625
|
-
const sessionsDir =
|
|
2786
|
+
const sessionsDir = path13.join(cwd, ".harness", "sessions");
|
|
2626
2787
|
const result = { removed: [], kept: [] };
|
|
2627
2788
|
if (!fs6.existsSync(sessionsDir)) {
|
|
2628
2789
|
return Ok(result);
|
|
@@ -2641,7 +2802,7 @@ async function runCleanupSessions(options) {
|
|
|
2641
2802
|
const now = Date.now();
|
|
2642
2803
|
for (const entry of entries) {
|
|
2643
2804
|
if (!entry.isDirectory()) continue;
|
|
2644
|
-
const sessionPath =
|
|
2805
|
+
const sessionPath = path13.join(sessionsDir, entry.name);
|
|
2645
2806
|
const mostRecent = getMostRecentMtime(sessionPath);
|
|
2646
2807
|
const ageMs = now - mostRecent;
|
|
2647
2808
|
if (ageMs > STALE_TTL_MS) {
|
|
@@ -2705,7 +2866,7 @@ async function runCleanupAll(opts) {
|
|
|
2705
2866
|
const results = [];
|
|
2706
2867
|
for (const target of targets) {
|
|
2707
2868
|
const ttlMs = (ttlOverrides[target.name] ?? 0) > 0 ? ttlOverrides[target.name] * ONE_HOUR_MS : target.defaultTtlMs;
|
|
2708
|
-
const dir =
|
|
2869
|
+
const dir = path13.join(cwd, ".harness", target.relativeDir);
|
|
2709
2870
|
const sweepResult = await sweepDirectory(dir, ttlMs, dryRun);
|
|
2710
2871
|
if (!sweepResult.ok) return Err(sweepResult.error);
|
|
2711
2872
|
results.push({ target: target.name, ...sweepResult.value });
|
|
@@ -2729,7 +2890,7 @@ async function sweepDirectory(dir, ttlMs, dryRun) {
|
|
|
2729
2890
|
}
|
|
2730
2891
|
const now = Date.now();
|
|
2731
2892
|
for (const entry of entries) {
|
|
2732
|
-
const entryPath =
|
|
2893
|
+
const entryPath = path13.join(dir, entry.name);
|
|
2733
2894
|
const ageMs = now - mtimeOf(entryPath);
|
|
2734
2895
|
if (ageMs > ttlMs) {
|
|
2735
2896
|
removed.push(entry.name);
|
|
@@ -2786,14 +2947,14 @@ ${dryRun ? "Would remove" : "Removed"} ${totalRemoved} stale entry(ies) across t
|
|
|
2786
2947
|
}
|
|
2787
2948
|
}
|
|
2788
2949
|
function createCleanupSessionsCommand() {
|
|
2789
|
-
const command = new
|
|
2950
|
+
const command = new Command22("cleanup-sessions").description(
|
|
2790
2951
|
"Remove stale entries from .harness/. Default: only .harness/sessions/ (no write in 24h)."
|
|
2791
2952
|
).option("--dry-run", "List stale entries without deleting them", false).option("--path <path>", "Project root path", ".").option("--all", "Sweep every registered .harness/ target").option(
|
|
2792
2953
|
"--include <list>",
|
|
2793
2954
|
"Comma-separated target names (mutually-exclusive with --exclude/--all)"
|
|
2794
2955
|
).option("--exclude <list>", "Comma-separated target names to skip").action(async (opts, cmd) => {
|
|
2795
2956
|
const globalOpts = cmd.optsWithGlobals();
|
|
2796
|
-
const cwd =
|
|
2957
|
+
const cwd = path13.resolve(opts.path);
|
|
2797
2958
|
const asJson = Boolean(globalOpts.json);
|
|
2798
2959
|
if (opts.all || opts.include || opts.exclude) {
|
|
2799
2960
|
await runExtendedSweep(cwd, opts, asJson);
|
|
@@ -2835,13 +2996,13 @@ async function runSessionsOnlySweep(cwd, opts, asJson) {
|
|
|
2835
2996
|
}
|
|
2836
2997
|
|
|
2837
2998
|
// src/commands/compound/index.ts
|
|
2838
|
-
import { Command as
|
|
2999
|
+
import { Command as Command24 } from "commander";
|
|
2839
3000
|
|
|
2840
3001
|
// src/commands/compound/scan-candidates.ts
|
|
2841
3002
|
init_dist3();
|
|
2842
3003
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync4 } from "fs";
|
|
2843
|
-
import { dirname as dirname2, join as join8, resolve as
|
|
2844
|
-
import { Command as
|
|
3004
|
+
import { dirname as dirname2, join as join8, resolve as resolve12 } from "path";
|
|
3005
|
+
import { Command as Command23 } from "commander";
|
|
2845
3006
|
var HOTSPOT_THRESHOLD = 7;
|
|
2846
3007
|
var DEFAULT_LOOKBACK = "7d";
|
|
2847
3008
|
async function runCompoundScanCandidatesCommand(opts) {
|
|
@@ -2896,7 +3057,7 @@ function emit(status, nonInteractive) {
|
|
|
2896
3057
|
return status;
|
|
2897
3058
|
}
|
|
2898
3059
|
function createScanCandidatesCommand() {
|
|
2899
|
-
return new
|
|
3060
|
+
return new Command23("scan-candidates").description(
|
|
2900
3061
|
"Scan recent fixes and hotspots for undocumented learnings; write candidate prompts"
|
|
2901
3062
|
).option("--lookback <window>", "Lookback window (e.g. 7d, 14d).", DEFAULT_LOOKBACK).option(
|
|
2902
3063
|
"--non-interactive",
|
|
@@ -2909,9 +3070,9 @@ function createScanCandidatesCommand() {
|
|
|
2909
3070
|
const nonInteractive = options.nonInteractive === true || !process.stdout.isTTY;
|
|
2910
3071
|
const status = await runCompoundScanCandidatesCommand({
|
|
2911
3072
|
lookback: options.lookback,
|
|
2912
|
-
configPath:
|
|
2913
|
-
outputPath: options.outputPath ?
|
|
2914
|
-
solutionsDir:
|
|
3073
|
+
configPath: resolve12(process.cwd(), options.config),
|
|
3074
|
+
outputPath: options.outputPath ? resolve12(process.cwd(), options.outputPath) : void 0,
|
|
3075
|
+
solutionsDir: resolve12(process.cwd(), options.solutionsDir),
|
|
2915
3076
|
nonInteractive
|
|
2916
3077
|
});
|
|
2917
3078
|
if (status.status === "failure") process.exitCode = 1;
|
|
@@ -2921,15 +3082,15 @@ function createScanCandidatesCommand() {
|
|
|
2921
3082
|
|
|
2922
3083
|
// src/commands/compound/index.ts
|
|
2923
3084
|
function createCompoundCommand() {
|
|
2924
|
-
const command = new
|
|
3085
|
+
const command = new Command24("compound").description("Compound (post-mortem playbook) commands");
|
|
2925
3086
|
command.addCommand(createScanCandidatesCommand());
|
|
2926
3087
|
return command;
|
|
2927
3088
|
}
|
|
2928
3089
|
|
|
2929
3090
|
// src/commands/copy-craft.ts
|
|
2930
|
-
import { Command as
|
|
3091
|
+
import { Command as Command25 } from "commander";
|
|
2931
3092
|
function createCopyCraftCommand() {
|
|
2932
|
-
return new
|
|
3093
|
+
return new Command25("copy-craft").description(
|
|
2933
3094
|
"LLM-judgment critique of prose-in-code across six surfaces: error messages, log lines, CLI output, commit subjects, PR descriptions, code comments. Third craft-pipeline ceiling skill. Graceful degradation when git/gh prereqs absent."
|
|
2934
3095
|
).option("-f, --files <files...>", "Optional source file/glob scope").option(
|
|
2935
3096
|
"-s, --surfaces <surfaces...>",
|
|
@@ -2981,10 +3142,10 @@ function printResult2(result, mode, _formatter) {
|
|
|
2981
3142
|
list.push(f);
|
|
2982
3143
|
bySurface.set(f.target.surface, list);
|
|
2983
3144
|
}
|
|
2984
|
-
for (const [surface,
|
|
3145
|
+
for (const [surface, fs49] of bySurface) {
|
|
2985
3146
|
console.log(`
|
|
2986
3147
|
[${surface}]`);
|
|
2987
|
-
for (const f of
|
|
3148
|
+
for (const f of fs49) {
|
|
2988
3149
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
2989
3150
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.file}${line}`);
|
|
2990
3151
|
console.log(` "${truncate(f.target.snippet, 80)}"`);
|
|
@@ -3010,11 +3171,11 @@ function truncate(s, n) {
|
|
|
3010
3171
|
}
|
|
3011
3172
|
|
|
3012
3173
|
// src/commands/dashboard.ts
|
|
3013
|
-
import { Command as
|
|
3174
|
+
import { Command as Command26 } from "commander";
|
|
3014
3175
|
import { spawn } from "child_process";
|
|
3015
3176
|
import { existsSync as existsSync5 } from "fs";
|
|
3016
3177
|
import { createRequire } from "module";
|
|
3017
|
-
import { dirname as dirname3, join as join9, resolve as
|
|
3178
|
+
import { dirname as dirname3, join as join9, resolve as resolve13 } from "path";
|
|
3018
3179
|
import { setTimeout } from "timers";
|
|
3019
3180
|
import { fileURLToPath } from "url";
|
|
3020
3181
|
var __filename = fileURLToPath(import.meta.url);
|
|
@@ -3078,7 +3239,7 @@ function buildDashboardEnv(opts, clientPort, apiPort, projectPath) {
|
|
|
3078
3239
|
function runDashboard(opts) {
|
|
3079
3240
|
const clientPort = Number(opts.port ?? DEFAULT_CLIENT_PORT);
|
|
3080
3241
|
const apiPort = Number(opts.apiPort ?? DEFAULT_API_PORT);
|
|
3081
|
-
const projectPath =
|
|
3242
|
+
const projectPath = resolve13(opts.cwd ?? process.cwd());
|
|
3082
3243
|
const url = `http://localhost:${apiPort}`;
|
|
3083
3244
|
const server = resolveServerScript();
|
|
3084
3245
|
if (!server) {
|
|
@@ -3094,13 +3255,13 @@ function runDashboard(opts) {
|
|
|
3094
3255
|
}
|
|
3095
3256
|
}
|
|
3096
3257
|
function createDashboardCommand() {
|
|
3097
|
-
return new
|
|
3258
|
+
return new Command26("dashboard").description("Start the Harness local web dashboard").option("--port <port>", "Client dev server port", String(DEFAULT_CLIENT_PORT)).option("--api-port <port>", "API server port", String(DEFAULT_API_PORT)).option("--orchestrator-url <url>", "Orchestrator URL (default: http://localhost:8080)").option("--no-open", "Do not automatically open browser").option("--cwd <path>", "Project directory (defaults to cwd)").action((opts) => runDashboard(opts));
|
|
3098
3259
|
}
|
|
3099
3260
|
|
|
3100
3261
|
// src/commands/design-pipeline.ts
|
|
3101
|
-
import { Command as
|
|
3262
|
+
import { Command as Command27 } from "commander";
|
|
3102
3263
|
function createDesignPipelineCommand() {
|
|
3103
|
-
return new
|
|
3264
|
+
return new Command27("design-pipeline").description(
|
|
3104
3265
|
"Run the design-pipeline orchestrator: FRESHEN \u2192 DETECT \u2192 FIX \u2192 AUDIT \u2192 FILL \u2192 REPORT. Composes detect-design-drift, align-design-system, audit-component-anatomy, audit-brand-compliance, and design-craft-elevator into a single sequential pipeline with convergence-based remediation."
|
|
3105
3266
|
).option("--fix", "Enable convergence-based remediation (default: detect + report only)").option("--no-freshen", "Skip the FRESHEN phase").option("--no-fill", "Skip the FILL phase (input bootstrap + craft polish)").option("--ci", "Non-interactive: safe fixes only, no prompts").option("-f, --files <files...>", "Optional file/glob scope passed to each verifier").option("-m, --mode <mode>", "Verifier mode: fast | full", "fast").option(
|
|
3106
3267
|
"--design-strictness <level>",
|
|
@@ -3186,10 +3347,10 @@ function verdictBadge(verdict) {
|
|
|
3186
3347
|
}
|
|
3187
3348
|
|
|
3188
3349
|
// src/commands/doctor.ts
|
|
3189
|
-
import { Command as
|
|
3350
|
+
import { Command as Command28 } from "commander";
|
|
3190
3351
|
import * as fs8 from "fs";
|
|
3191
3352
|
import * as os from "os";
|
|
3192
|
-
import * as
|
|
3353
|
+
import * as path15 from "path";
|
|
3193
3354
|
import chalk from "chalk";
|
|
3194
3355
|
|
|
3195
3356
|
// src/utils/node-version.ts
|
|
@@ -3273,7 +3434,7 @@ var INTEGRATION_REGISTRY = [
|
|
|
3273
3434
|
|
|
3274
3435
|
// src/integrations/config.ts
|
|
3275
3436
|
import * as fs7 from "fs";
|
|
3276
|
-
import * as
|
|
3437
|
+
import * as path14 from "path";
|
|
3277
3438
|
function readJsonSafe(filePath) {
|
|
3278
3439
|
if (!fs7.existsSync(filePath)) return null;
|
|
3279
3440
|
try {
|
|
@@ -3283,7 +3444,7 @@ function readJsonSafe(filePath) {
|
|
|
3283
3444
|
}
|
|
3284
3445
|
}
|
|
3285
3446
|
function writeJson(filePath, data) {
|
|
3286
|
-
const dir =
|
|
3447
|
+
const dir = path14.dirname(filePath);
|
|
3287
3448
|
if (!fs7.existsSync(dir)) {
|
|
3288
3449
|
fs7.mkdirSync(dir, { recursive: true });
|
|
3289
3450
|
}
|
|
@@ -3367,13 +3528,13 @@ function checkSlashCommands() {
|
|
|
3367
3528
|
const platforms = [
|
|
3368
3529
|
{
|
|
3369
3530
|
name: "Claude Code",
|
|
3370
|
-
dir:
|
|
3531
|
+
dir: path15.join(os.homedir(), ".claude", "commands", "harness"),
|
|
3371
3532
|
ext: ".md",
|
|
3372
3533
|
client: "claude-code"
|
|
3373
3534
|
},
|
|
3374
3535
|
{
|
|
3375
3536
|
name: "Gemini CLI",
|
|
3376
|
-
dir:
|
|
3537
|
+
dir: path15.join(os.homedir(), ".gemini", "commands", "harness"),
|
|
3377
3538
|
ext: ".toml",
|
|
3378
3539
|
client: "gemini-cli"
|
|
3379
3540
|
}
|
|
@@ -3397,7 +3558,7 @@ function checkSlashCommands() {
|
|
|
3397
3558
|
}
|
|
3398
3559
|
function checkMcpConfig(cwd) {
|
|
3399
3560
|
const results = [];
|
|
3400
|
-
const claudeConfig = readMcpConfig(
|
|
3561
|
+
const claudeConfig = readMcpConfig(path15.join(cwd, ".mcp.json"));
|
|
3401
3562
|
if (claudeConfig.mcpServers?.["harness"]) {
|
|
3402
3563
|
results.push({
|
|
3403
3564
|
name: "mcp-claude",
|
|
@@ -3412,9 +3573,9 @@ function checkMcpConfig(cwd) {
|
|
|
3412
3573
|
fix: "Run: harness setup-mcp --client claude"
|
|
3413
3574
|
});
|
|
3414
3575
|
}
|
|
3415
|
-
const geminiDir =
|
|
3576
|
+
const geminiDir = path15.join(cwd, ".gemini");
|
|
3416
3577
|
if (fs8.existsSync(geminiDir)) {
|
|
3417
|
-
const geminiConfig = readMcpConfig(
|
|
3578
|
+
const geminiConfig = readMcpConfig(path15.join(geminiDir, "settings.json"));
|
|
3418
3579
|
if (geminiConfig.mcpServers?.["harness"]) {
|
|
3419
3580
|
results.push({
|
|
3420
3581
|
name: "mcp-gemini",
|
|
@@ -3433,12 +3594,12 @@ function checkMcpConfig(cwd) {
|
|
|
3433
3594
|
return results;
|
|
3434
3595
|
}
|
|
3435
3596
|
function loadMcpPresence(cwd) {
|
|
3436
|
-
const mcpPath =
|
|
3437
|
-
const geminiDir =
|
|
3597
|
+
const mcpPath = path15.join(cwd, ".mcp.json");
|
|
3598
|
+
const geminiDir = path15.join(cwd, ".gemini");
|
|
3438
3599
|
const hasGemini = fs8.existsSync(geminiDir);
|
|
3439
3600
|
return {
|
|
3440
3601
|
mcpConfig: readMcpConfig(mcpPath),
|
|
3441
|
-
geminiConfig: hasGemini ? readMcpConfig(
|
|
3602
|
+
geminiConfig: hasGemini ? readMcpConfig(path15.join(geminiDir, "settings.json")) : null,
|
|
3442
3603
|
hasGemini
|
|
3443
3604
|
};
|
|
3444
3605
|
}
|
|
@@ -3470,7 +3631,7 @@ function checkTier0Presence(def, presence) {
|
|
|
3470
3631
|
function checkIntegrations(cwd) {
|
|
3471
3632
|
const results = [];
|
|
3472
3633
|
const presence = loadMcpPresence(cwd);
|
|
3473
|
-
const configPath =
|
|
3634
|
+
const configPath = path15.join(cwd, "harness.config.json");
|
|
3474
3635
|
const integrationsConfig = readIntegrationsConfig(configPath);
|
|
3475
3636
|
for (const def of INTEGRATION_REGISTRY.filter((d) => d.tier === 0)) {
|
|
3476
3637
|
results.push(checkTier0Presence(def, presence));
|
|
@@ -3538,12 +3699,12 @@ function checkLivePings(env = process.env) {
|
|
|
3538
3699
|
return results;
|
|
3539
3700
|
}
|
|
3540
3701
|
function listHookFiles(cwd) {
|
|
3541
|
-
const hooksDir =
|
|
3702
|
+
const hooksDir = path15.join(cwd, ".harness", "hooks");
|
|
3542
3703
|
if (!fs8.existsSync(hooksDir)) return [];
|
|
3543
3704
|
try {
|
|
3544
3705
|
return fs8.readdirSync(hooksDir).filter((f) => !f.startsWith(".")).map((f) => ({
|
|
3545
|
-
relativePath:
|
|
3546
|
-
absolutePath:
|
|
3706
|
+
relativePath: path15.join(".harness", "hooks", f),
|
|
3707
|
+
absolutePath: path15.join(hooksDir, f)
|
|
3547
3708
|
}));
|
|
3548
3709
|
} catch {
|
|
3549
3710
|
return [];
|
|
@@ -3640,7 +3801,7 @@ var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
|
3640
3801
|
function checkBaselineFreshness(cwd, now = Date.now()) {
|
|
3641
3802
|
const results = [];
|
|
3642
3803
|
for (const def of BASELINE_FILES) {
|
|
3643
|
-
const absolutePath =
|
|
3804
|
+
const absolutePath = path15.join(cwd, def.relativePath);
|
|
3644
3805
|
if (!fs8.existsSync(absolutePath)) {
|
|
3645
3806
|
results.push({
|
|
3646
3807
|
name: `baseline-freshness-${def.relativePath}`,
|
|
@@ -3688,7 +3849,7 @@ function checkBaselineFreshness(cwd, now = Date.now()) {
|
|
|
3688
3849
|
}
|
|
3689
3850
|
var SESSIONS_SAMPLE_SIZE = 5;
|
|
3690
3851
|
function listRecentSessions(cwd) {
|
|
3691
|
-
const sessionsDir =
|
|
3852
|
+
const sessionsDir = path15.join(cwd, ".harness", "sessions");
|
|
3692
3853
|
if (!fs8.existsSync(sessionsDir)) return [];
|
|
3693
3854
|
let entries;
|
|
3694
3855
|
try {
|
|
@@ -3699,7 +3860,7 @@ function listRecentSessions(cwd) {
|
|
|
3699
3860
|
const dirs = [];
|
|
3700
3861
|
for (const name of entries) {
|
|
3701
3862
|
if (name.startsWith(".")) continue;
|
|
3702
|
-
const absolutePath =
|
|
3863
|
+
const absolutePath = path15.join(sessionsDir, name);
|
|
3703
3864
|
try {
|
|
3704
3865
|
const stat = fs8.statSync(absolutePath);
|
|
3705
3866
|
if (!stat.isDirectory()) continue;
|
|
@@ -3716,7 +3877,7 @@ function scanSessionSummaries(sessions) {
|
|
|
3716
3877
|
let missing = 0;
|
|
3717
3878
|
const corrupt = [];
|
|
3718
3879
|
for (const session of sessions) {
|
|
3719
|
-
const summaryPath =
|
|
3880
|
+
const summaryPath = path15.join(session.absolutePath, "session-summary.json");
|
|
3720
3881
|
if (!fs8.existsSync(summaryPath)) {
|
|
3721
3882
|
missing += 1;
|
|
3722
3883
|
continue;
|
|
@@ -3790,7 +3951,7 @@ function formatCheck(check) {
|
|
|
3790
3951
|
return line;
|
|
3791
3952
|
}
|
|
3792
3953
|
function createDoctorCommand() {
|
|
3793
|
-
return new
|
|
3954
|
+
return new Command28("doctor").description(
|
|
3794
3955
|
"Check environment health: Node, slash commands, MCP, integrations, integration credentials, hooks, baselines, sessions"
|
|
3795
3956
|
).action((_opts, cmd) => {
|
|
3796
3957
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -3818,8 +3979,8 @@ function createDoctorCommand() {
|
|
|
3818
3979
|
|
|
3819
3980
|
// src/commands/fix-drift.ts
|
|
3820
3981
|
init_dist3();
|
|
3821
|
-
import { Command as
|
|
3822
|
-
import * as
|
|
3982
|
+
import { Command as Command29 } from "commander";
|
|
3983
|
+
import * as path16 from "path";
|
|
3823
3984
|
async function runFixDrift(options) {
|
|
3824
3985
|
const cwd = options.cwd ?? process.cwd();
|
|
3825
3986
|
const dryRun = options.dryRun !== false;
|
|
@@ -3828,12 +3989,12 @@ async function runFixDrift(options) {
|
|
|
3828
3989
|
return Err(configResult.error);
|
|
3829
3990
|
}
|
|
3830
3991
|
const config = configResult.value;
|
|
3831
|
-
const rootDir =
|
|
3832
|
-
const docsDir =
|
|
3992
|
+
const rootDir = path16.resolve(cwd, config.rootDir);
|
|
3993
|
+
const docsDir = path16.resolve(cwd, config.docsDir);
|
|
3833
3994
|
const entropyConfig = {
|
|
3834
3995
|
rootDir,
|
|
3835
3996
|
...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
|
|
3836
|
-
docPaths: [
|
|
3997
|
+
docPaths: [path16.join(docsDir, "**/*.md")],
|
|
3837
3998
|
analyze: {
|
|
3838
3999
|
drift: true,
|
|
3839
4000
|
deadCode: true,
|
|
@@ -3951,7 +4112,7 @@ function printFixDriftResult(value, mode, formatter) {
|
|
|
3951
4112
|
}
|
|
3952
4113
|
}
|
|
3953
4114
|
function createFixDriftCommand() {
|
|
3954
|
-
const command = new
|
|
4115
|
+
const command = new Command29("fix-drift").description("Auto-fix entropy issues (doc drift, dead code)").option("--no-dry-run", "Actually apply fixes (default is dry-run mode)").action(async (opts, cmd) => {
|
|
3955
4116
|
const globalOpts = cmd.optsWithGlobals();
|
|
3956
4117
|
const mode = resolveOutputMode(globalOpts);
|
|
3957
4118
|
const formatter = new OutputFormatter(mode);
|
|
@@ -3981,14 +4142,14 @@ function createFixDriftCommand() {
|
|
|
3981
4142
|
}
|
|
3982
4143
|
|
|
3983
4144
|
// src/commands/gateway/index.ts
|
|
3984
|
-
import { Command as
|
|
4145
|
+
import { Command as Command32 } from "commander";
|
|
3985
4146
|
|
|
3986
4147
|
// src/commands/gateway/token.ts
|
|
3987
|
-
import { Command as
|
|
3988
|
-
import { resolve as
|
|
4148
|
+
import { Command as Command30 } from "commander";
|
|
4149
|
+
import { resolve as resolve15 } from "path";
|
|
3989
4150
|
import { TokenStore } from "@harness-engineering/orchestrator";
|
|
3990
4151
|
function getStore() {
|
|
3991
|
-
const p = process.env["HARNESS_TOKENS_PATH"] ??
|
|
4152
|
+
const p = process.env["HARNESS_TOKENS_PATH"] ?? resolve15(".harness", "tokens.json");
|
|
3992
4153
|
return new TokenStore(p);
|
|
3993
4154
|
}
|
|
3994
4155
|
async function runTokenCreate(opts) {
|
|
@@ -4005,7 +4166,7 @@ async function runTokenRevoke(id) {
|
|
|
4005
4166
|
return store.revoke(id);
|
|
4006
4167
|
}
|
|
4007
4168
|
function createTokenCommand() {
|
|
4008
|
-
const cmd = new
|
|
4169
|
+
const cmd = new Command30("token").description("Manage Gateway API tokens");
|
|
4009
4170
|
cmd.command("create").description("Create a new auth token. Secret is shown ONCE.").requiredOption("--name <name>", "Human label").requiredOption("--scopes <scopes>", "Comma-separated scopes").option("--bridge <kind>", "slack | discord | github-app | custom").option("--tenant <id>", "Tenant identifier").option("--expires <iso>", "Expiry ISO-8601").action(
|
|
4010
4171
|
async (opts) => {
|
|
4011
4172
|
const scopes = opts.scopes.split(",").map((s) => s.trim());
|
|
@@ -4034,12 +4195,12 @@ function createTokenCommand() {
|
|
|
4034
4195
|
}
|
|
4035
4196
|
|
|
4036
4197
|
// src/commands/gateway/deliveries.ts
|
|
4037
|
-
import { Command as
|
|
4198
|
+
import { Command as Command31 } from "commander";
|
|
4038
4199
|
import { createInterface } from "readline";
|
|
4039
|
-
import { resolve as
|
|
4200
|
+
import { resolve as resolve16 } from "path";
|
|
4040
4201
|
import { WebhookQueue } from "@harness-engineering/orchestrator";
|
|
4041
4202
|
function resolveQueuePath() {
|
|
4042
|
-
return process.env["HARNESS_WEBHOOK_QUEUE_PATH"] ??
|
|
4203
|
+
return process.env["HARNESS_WEBHOOK_QUEUE_PATH"] ?? resolve16(".harness", "webhook-queue.sqlite");
|
|
4043
4204
|
}
|
|
4044
4205
|
function getQueue() {
|
|
4045
4206
|
return new WebhookQueue(resolveQueuePath());
|
|
@@ -4074,7 +4235,7 @@ function promptYesNo(message) {
|
|
|
4074
4235
|
});
|
|
4075
4236
|
}
|
|
4076
4237
|
function createDeliveriesCommand() {
|
|
4077
|
-
const cmd = new
|
|
4238
|
+
const cmd = new Command31("deliveries").description("Inspect and manage webhook delivery queue");
|
|
4078
4239
|
cmd.command("list").description("List delivery queue entries (newest first; capped at 200 rows)").option("--status <status>", "Filter by status: pending|failed|delivered|dead").option("--subscription <id>", "Filter by subscription ID").action((opts) => {
|
|
4079
4240
|
const queue = getQueue();
|
|
4080
4241
|
try {
|
|
@@ -4134,14 +4295,14 @@ function createDeliveriesCommand() {
|
|
|
4134
4295
|
|
|
4135
4296
|
// src/commands/gateway/index.ts
|
|
4136
4297
|
function createGatewayCommand() {
|
|
4137
|
-
const cmd = new
|
|
4298
|
+
const cmd = new Command32("gateway").description("Gateway API administration");
|
|
4138
4299
|
cmd.addCommand(createTokenCommand());
|
|
4139
4300
|
cmd.addCommand(createDeliveriesCommand());
|
|
4140
4301
|
return cmd;
|
|
4141
4302
|
}
|
|
4142
4303
|
|
|
4143
4304
|
// src/commands/generate.ts
|
|
4144
|
-
import { Command as
|
|
4305
|
+
import { Command as Command33 } from "commander";
|
|
4145
4306
|
function validatePlatforms(platforms) {
|
|
4146
4307
|
for (const p of platforms) {
|
|
4147
4308
|
if (!VALID_PLATFORMS.includes(p)) {
|
|
@@ -4201,22 +4362,22 @@ async function runGenerateAction(opts, globalOpts) {
|
|
|
4201
4362
|
}
|
|
4202
4363
|
}
|
|
4203
4364
|
function createGenerateCommand() {
|
|
4204
|
-
return new
|
|
4365
|
+
return new Command33("generate").description("Generate all platform integrations (slash commands + agent definitions)").option("--platforms <list>", "Target platforms (comma-separated)", "claude-code,gemini-cli").option("--global", "Write to global directories", false).option("--include-global", "Include built-in global skills", false).option("--output <dir>", "Custom output directory").option("--dry-run", "Show what would change without writing", false).option("--yes", "Skip deletion confirmation prompts", false).action(async (opts, cmd) => {
|
|
4205
4366
|
await runGenerateAction(opts, cmd.optsWithGlobals());
|
|
4206
4367
|
});
|
|
4207
4368
|
}
|
|
4208
4369
|
|
|
4209
4370
|
// src/commands/graph/index.ts
|
|
4210
|
-
import { Command as
|
|
4371
|
+
import { Command as Command36 } from "commander";
|
|
4211
4372
|
|
|
4212
4373
|
// src/commands/graph/status.ts
|
|
4213
|
-
import * as
|
|
4374
|
+
import * as path17 from "path";
|
|
4214
4375
|
async function readConnectorSyncStatus(graphDir) {
|
|
4215
|
-
const
|
|
4376
|
+
const fs49 = await import("fs/promises");
|
|
4216
4377
|
const result = {};
|
|
4217
4378
|
try {
|
|
4218
|
-
const syncMetaPath =
|
|
4219
|
-
const syncMeta = JSON.parse(await
|
|
4379
|
+
const syncMetaPath = path17.join(graphDir, "sync-metadata.json");
|
|
4380
|
+
const syncMeta = JSON.parse(await fs49.readFile(syncMetaPath, "utf-8"));
|
|
4220
4381
|
for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
|
|
4221
4382
|
result[name] = data.lastSyncTimestamp;
|
|
4222
4383
|
}
|
|
@@ -4225,8 +4386,8 @@ async function readConnectorSyncStatus(graphDir) {
|
|
|
4225
4386
|
return result;
|
|
4226
4387
|
}
|
|
4227
4388
|
async function runGraphStatus(projectPath) {
|
|
4228
|
-
const { GraphStore: GraphStore2, loadGraphMetadata } = await import("./dist-
|
|
4229
|
-
const graphDir =
|
|
4389
|
+
const { GraphStore: GraphStore2, loadGraphMetadata } = await import("./dist-OWHMNL4W.js");
|
|
4390
|
+
const graphDir = path17.join(projectPath, ".harness", "graph");
|
|
4230
4391
|
const metaResult = await loadGraphMetadata(graphDir);
|
|
4231
4392
|
if (metaResult.status === "not_found") {
|
|
4232
4393
|
return { status: "no_graph", message: "No graph found. Run `harness scan` first." };
|
|
@@ -4275,10 +4436,10 @@ async function runGraphStatus(projectPath) {
|
|
|
4275
4436
|
}
|
|
4276
4437
|
|
|
4277
4438
|
// src/commands/graph/export.ts
|
|
4278
|
-
import * as
|
|
4439
|
+
import * as path18 from "path";
|
|
4279
4440
|
async function runGraphExport(projectPath, format) {
|
|
4280
|
-
const { GraphStore: GraphStore2 } = await import("./dist-
|
|
4281
|
-
const graphDir =
|
|
4441
|
+
const { GraphStore: GraphStore2 } = await import("./dist-OWHMNL4W.js");
|
|
4442
|
+
const graphDir = path18.join(projectPath, ".harness", "graph");
|
|
4282
4443
|
const store = new GraphStore2();
|
|
4283
4444
|
const loaded = await store.load(graphDir);
|
|
4284
4445
|
if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
|
|
@@ -4307,15 +4468,15 @@ async function runGraphExport(projectPath, format) {
|
|
|
4307
4468
|
}
|
|
4308
4469
|
|
|
4309
4470
|
// src/commands/graph/index.ts
|
|
4310
|
-
import * as
|
|
4471
|
+
import * as path21 from "path";
|
|
4311
4472
|
|
|
4312
4473
|
// src/commands/graph/query.ts
|
|
4313
|
-
import { Command as
|
|
4314
|
-
import * as
|
|
4474
|
+
import { Command as Command34 } from "commander";
|
|
4475
|
+
import * as path19 from "path";
|
|
4315
4476
|
async function runQuery(projectPath, rootNodeId, opts) {
|
|
4316
|
-
const { GraphStore: GraphStore2, ContextQL } = await import("./dist-
|
|
4477
|
+
const { GraphStore: GraphStore2, ContextQL } = await import("./dist-OWHMNL4W.js");
|
|
4317
4478
|
const store = new GraphStore2();
|
|
4318
|
-
const graphDir =
|
|
4479
|
+
const graphDir = path19.join(projectPath, ".harness", "graph");
|
|
4319
4480
|
const loaded = await store.load(graphDir);
|
|
4320
4481
|
if (!loaded) throw new Error("No graph found. Run `harness scan` first.");
|
|
4321
4482
|
const params = {
|
|
@@ -4337,7 +4498,7 @@ function printQueryResult(result) {
|
|
|
4337
4498
|
}
|
|
4338
4499
|
}
|
|
4339
4500
|
async function runQueryAction(rootNodeId, opts, globalOpts) {
|
|
4340
|
-
const projectPath =
|
|
4501
|
+
const projectPath = path19.resolve(globalOpts.config ? path19.dirname(globalOpts.config) : ".");
|
|
4341
4502
|
try {
|
|
4342
4503
|
const result = await runQuery(projectPath, rootNodeId, {
|
|
4343
4504
|
depth: parseInt(opts.depth),
|
|
@@ -4356,25 +4517,32 @@ async function runQueryAction(rootNodeId, opts, globalOpts) {
|
|
|
4356
4517
|
}
|
|
4357
4518
|
}
|
|
4358
4519
|
function createQueryCommand() {
|
|
4359
|
-
return new
|
|
4520
|
+
return new Command34("query").description("Query the knowledge graph").argument("<rootNodeId>", "Starting node ID").option("--depth <n>", "Max traversal depth", "3").option("--types <types>", "Comma-separated node types to include").option("--edges <edges>", "Comma-separated edge types to include").option("--bidirectional", "Traverse both directions").action(async (rootNodeId, opts, cmd) => {
|
|
4360
4521
|
await runQueryAction(rootNodeId, opts, cmd.optsWithGlobals());
|
|
4361
4522
|
});
|
|
4362
4523
|
}
|
|
4363
4524
|
|
|
4364
4525
|
// src/commands/graph/ingest.ts
|
|
4365
|
-
import { Command as
|
|
4366
|
-
import * as
|
|
4526
|
+
import { Command as Command35 } from "commander";
|
|
4527
|
+
import * as path20 from "path";
|
|
4367
4528
|
async function loadConnectorConfig(projectPath, source) {
|
|
4368
4529
|
try {
|
|
4369
|
-
const
|
|
4370
|
-
const configPath =
|
|
4371
|
-
const config = JSON.parse(await
|
|
4530
|
+
const fs49 = await import("fs/promises");
|
|
4531
|
+
const configPath = path20.join(projectPath, "harness.config.json");
|
|
4532
|
+
const config = JSON.parse(await fs49.readFile(configPath, "utf-8"));
|
|
4372
4533
|
const connector = config.graph?.connectors?.[source];
|
|
4373
4534
|
return connector ?? {};
|
|
4374
4535
|
} catch {
|
|
4375
4536
|
return {};
|
|
4376
4537
|
}
|
|
4377
4538
|
}
|
|
4539
|
+
async function ingestBusinessKnowledge(bk, projectPath) {
|
|
4540
|
+
return mergeResults(
|
|
4541
|
+
await bk.ingest(path20.join(projectPath, "docs", "knowledge")),
|
|
4542
|
+
await bk.ingestSolutions(path20.join(projectPath, "docs", "solutions")),
|
|
4543
|
+
await bk.ingestStrategy(path20.join(projectPath, "STRATEGY.md"))
|
|
4544
|
+
);
|
|
4545
|
+
}
|
|
4378
4546
|
function mergeResults(...results) {
|
|
4379
4547
|
return results.reduce(
|
|
4380
4548
|
(acc, r) => ({
|
|
@@ -4401,6 +4569,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4401
4569
|
CodeIngestor,
|
|
4402
4570
|
TopologicalLinker,
|
|
4403
4571
|
KnowledgeIngestor,
|
|
4572
|
+
BusinessKnowledgeIngestor,
|
|
4404
4573
|
GitIngestor,
|
|
4405
4574
|
RequirementIngestor,
|
|
4406
4575
|
SyncManager,
|
|
@@ -4410,8 +4579,8 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4410
4579
|
ConfluenceConnector,
|
|
4411
4580
|
FigmaConnector,
|
|
4412
4581
|
MiroConnector
|
|
4413
|
-
} = await import("./dist-
|
|
4414
|
-
const graphDir =
|
|
4582
|
+
} = await import("./dist-OWHMNL4W.js");
|
|
4583
|
+
const graphDir = path20.join(projectPath, ".harness", "graph");
|
|
4415
4584
|
const store = new GraphStore2();
|
|
4416
4585
|
await store.load(graphDir);
|
|
4417
4586
|
const ingestOptions = loadIngestOptions(projectPath);
|
|
@@ -4420,12 +4589,14 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4420
4589
|
const codeResult = await new CodeIngestor(store, ingestOptions).ingest(projectPath);
|
|
4421
4590
|
new TopologicalLinker(store).link();
|
|
4422
4591
|
const knowledgeResult = await new KnowledgeIngestor(store).ingestAll(projectPath);
|
|
4592
|
+
const bkInst = new BusinessKnowledgeIngestor(store);
|
|
4593
|
+
const bkResult = await ingestBusinessKnowledge(bkInst, projectPath);
|
|
4423
4594
|
const reqResult = await new RequirementIngestor(store).ingestSpecs(
|
|
4424
|
-
|
|
4595
|
+
path20.join(projectPath, "docs", "changes")
|
|
4425
4596
|
);
|
|
4426
4597
|
const gitResult = await new GitIngestor(store).ingest(projectPath);
|
|
4427
|
-
const { createExtractionRunner } = await import("./dist-
|
|
4428
|
-
const extractedDir =
|
|
4598
|
+
const { createExtractionRunner } = await import("./dist-OWHMNL4W.js");
|
|
4599
|
+
const extractedDir = path20.join(projectPath, ".harness", "knowledge", "extracted");
|
|
4429
4600
|
const signalsResult = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
4430
4601
|
const syncManager = new SyncManager(store, graphDir);
|
|
4431
4602
|
const connectorMap = {
|
|
@@ -4445,6 +4616,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4445
4616
|
const merged = mergeResults(
|
|
4446
4617
|
codeResult,
|
|
4447
4618
|
knowledgeResult,
|
|
4619
|
+
bkResult,
|
|
4448
4620
|
reqResult,
|
|
4449
4621
|
gitResult,
|
|
4450
4622
|
signalsResult,
|
|
@@ -4458,20 +4630,23 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4458
4630
|
result = await new CodeIngestor(store, ingestOptions).ingest(projectPath);
|
|
4459
4631
|
new TopologicalLinker(store).link();
|
|
4460
4632
|
break;
|
|
4461
|
-
case "knowledge":
|
|
4462
|
-
|
|
4633
|
+
case "knowledge": {
|
|
4634
|
+
const knowledge = await new KnowledgeIngestor(store).ingestAll(projectPath);
|
|
4635
|
+
const bk = await ingestBusinessKnowledge(new BusinessKnowledgeIngestor(store), projectPath);
|
|
4636
|
+
result = mergeResults(knowledge, bk);
|
|
4463
4637
|
break;
|
|
4638
|
+
}
|
|
4464
4639
|
case "git":
|
|
4465
4640
|
result = await new GitIngestor(store).ingest(projectPath);
|
|
4466
4641
|
break;
|
|
4467
4642
|
case "requirements":
|
|
4468
4643
|
result = await new RequirementIngestor(store).ingestSpecs(
|
|
4469
|
-
|
|
4644
|
+
path20.join(projectPath, "docs", "changes")
|
|
4470
4645
|
);
|
|
4471
4646
|
break;
|
|
4472
4647
|
case "business-signals": {
|
|
4473
|
-
const { createExtractionRunner } = await import("./dist-
|
|
4474
|
-
const extractedDir =
|
|
4648
|
+
const { createExtractionRunner } = await import("./dist-OWHMNL4W.js");
|
|
4649
|
+
const extractedDir = path20.join(projectPath, ".harness", "knowledge", "extracted");
|
|
4475
4650
|
result = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
4476
4651
|
break;
|
|
4477
4652
|
}
|
|
@@ -4506,40 +4681,46 @@ async function runIngest(projectPath, source, opts) {
|
|
|
4506
4681
|
await store.save(graphDir);
|
|
4507
4682
|
return result;
|
|
4508
4683
|
}
|
|
4684
|
+
function printIngestSummary(result, label) {
|
|
4685
|
+
console.log(
|
|
4686
|
+
`Ingested (${label}): +${result.nodesAdded} nodes, +${result.edgesAdded} edges (${result.durationMs}ms)`
|
|
4687
|
+
);
|
|
4688
|
+
if (result.errors.length === 0) return;
|
|
4689
|
+
console.warn(` ${result.errors.length} parse/skip warning(s):`);
|
|
4690
|
+
for (const err of result.errors) console.warn(` - ${err}`);
|
|
4691
|
+
}
|
|
4692
|
+
async function handleIngestAction(opts, cmd) {
|
|
4693
|
+
if (!opts.source && !opts.all) {
|
|
4694
|
+
console.error("Error: --source or --all is required");
|
|
4695
|
+
process.exit(1);
|
|
4696
|
+
}
|
|
4697
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
4698
|
+
const projectPath = path20.resolve(globalOpts.config ? path20.dirname(globalOpts.config) : ".");
|
|
4699
|
+
try {
|
|
4700
|
+
const runOpts = {};
|
|
4701
|
+
if (opts.full !== void 0) runOpts.full = opts.full;
|
|
4702
|
+
if (opts.all !== void 0) runOpts.all = opts.all;
|
|
4703
|
+
const result = await runIngest(projectPath, opts.source ?? "", runOpts);
|
|
4704
|
+
if (globalOpts.json) {
|
|
4705
|
+
console.log(JSON.stringify(result));
|
|
4706
|
+
} else {
|
|
4707
|
+
printIngestSummary(result, opts.all ? "all" : opts.source ?? "");
|
|
4708
|
+
}
|
|
4709
|
+
} catch (err) {
|
|
4710
|
+
console.error("Ingest failed:", err instanceof Error ? err.message : err);
|
|
4711
|
+
process.exit(2);
|
|
4712
|
+
}
|
|
4713
|
+
}
|
|
4509
4714
|
function createIngestCommand() {
|
|
4510
|
-
return new
|
|
4715
|
+
return new Command35("ingest").description("Ingest data into the knowledge graph").option(
|
|
4511
4716
|
"--source <name>",
|
|
4512
4717
|
"Source to ingest (code, knowledge, git, requirements, business-signals, jira, slack, ci, confluence, figma, miro)"
|
|
4513
|
-
).option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(
|
|
4514
|
-
if (!opts.source && !opts.all) {
|
|
4515
|
-
console.error("Error: --source or --all is required");
|
|
4516
|
-
process.exit(1);
|
|
4517
|
-
}
|
|
4518
|
-
const globalOpts = cmd.optsWithGlobals();
|
|
4519
|
-
const projectPath = path19.resolve(globalOpts.config ? path19.dirname(globalOpts.config) : ".");
|
|
4520
|
-
try {
|
|
4521
|
-
const result = await runIngest(projectPath, opts.source ?? "", {
|
|
4522
|
-
full: opts.full,
|
|
4523
|
-
all: opts.all
|
|
4524
|
-
});
|
|
4525
|
-
if (globalOpts.json) {
|
|
4526
|
-
console.log(JSON.stringify(result));
|
|
4527
|
-
} else {
|
|
4528
|
-
const label = opts.all ? "all" : opts.source;
|
|
4529
|
-
console.log(
|
|
4530
|
-
`Ingested (${label}): +${result.nodesAdded} nodes, +${result.edgesAdded} edges (${result.durationMs}ms)`
|
|
4531
|
-
);
|
|
4532
|
-
}
|
|
4533
|
-
} catch (err) {
|
|
4534
|
-
console.error("Ingest failed:", err instanceof Error ? err.message : err);
|
|
4535
|
-
process.exit(2);
|
|
4536
|
-
}
|
|
4537
|
-
});
|
|
4718
|
+
).option("--all", "Run all sources (code, knowledge, git, and configured connectors)").option("--full", "Force full re-ingestion").action(handleIngestAction);
|
|
4538
4719
|
}
|
|
4539
4720
|
|
|
4540
4721
|
// src/commands/graph/index.ts
|
|
4541
4722
|
function resolveProjectPath(globalOpts) {
|
|
4542
|
-
return
|
|
4723
|
+
return path21.resolve(globalOpts.config ? path21.dirname(globalOpts.config) : ".");
|
|
4543
4724
|
}
|
|
4544
4725
|
function printGraphStatus(result) {
|
|
4545
4726
|
if (result.status === "no_graph" || result.status === "schema_mismatch") {
|
|
@@ -4585,19 +4766,19 @@ async function runExportAction(opts, cmd) {
|
|
|
4585
4766
|
}
|
|
4586
4767
|
}
|
|
4587
4768
|
function createGraphCommand() {
|
|
4588
|
-
const graph = new
|
|
4769
|
+
const graph = new Command36("graph").description("Knowledge graph management");
|
|
4589
4770
|
graph.command("status").description("Show graph statistics").action(runStatusAction);
|
|
4590
4771
|
graph.command("export").description("Export graph").requiredOption("--format <format>", "Output format (json, mermaid)").action(runExportAction);
|
|
4591
4772
|
return graph;
|
|
4592
4773
|
}
|
|
4593
4774
|
|
|
4594
4775
|
// src/commands/hooks/index.ts
|
|
4595
|
-
import { Command as
|
|
4776
|
+
import { Command as Command41 } from "commander";
|
|
4596
4777
|
|
|
4597
4778
|
// src/commands/hooks/init.ts
|
|
4598
|
-
import { Command as
|
|
4779
|
+
import { Command as Command37 } from "commander";
|
|
4599
4780
|
import * as fs9 from "fs";
|
|
4600
|
-
import * as
|
|
4781
|
+
import * as path22 from "path";
|
|
4601
4782
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4602
4783
|
|
|
4603
4784
|
// src/hooks/profiles.ts
|
|
@@ -4627,14 +4808,14 @@ var PROFILES = {
|
|
|
4627
4808
|
|
|
4628
4809
|
// src/commands/hooks/init.ts
|
|
4629
4810
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
4630
|
-
var __dirname2 =
|
|
4811
|
+
var __dirname2 = path22.dirname(__filename2);
|
|
4631
4812
|
var VALID_PROFILES = ["minimal", "standard", "strict"];
|
|
4632
4813
|
function resolveHookSourceDir() {
|
|
4633
4814
|
const candidates = [
|
|
4634
4815
|
// Dev layout: src/commands/hooks/ → ../../hooks/
|
|
4635
|
-
|
|
4816
|
+
path22.resolve(__dirname2, "..", "..", "hooks"),
|
|
4636
4817
|
// Bundled layout: dist/ → ./hooks/ (copied by copy-assets.mjs)
|
|
4637
|
-
|
|
4818
|
+
path22.resolve(__dirname2, "hooks")
|
|
4638
4819
|
];
|
|
4639
4820
|
for (const candidate of candidates) {
|
|
4640
4821
|
if (fs9.existsSync(candidate)) {
|
|
@@ -4674,12 +4855,12 @@ function mergeSettings(existing, hooksConfig) {
|
|
|
4674
4855
|
}
|
|
4675
4856
|
function initHooks(options) {
|
|
4676
4857
|
const { profile, projectDir } = options;
|
|
4677
|
-
const hooksDestDir =
|
|
4858
|
+
const hooksDestDir = path22.join(projectDir, ".harness", "hooks");
|
|
4678
4859
|
fs9.mkdirSync(hooksDestDir, { recursive: true });
|
|
4679
4860
|
if (fs9.existsSync(hooksDestDir)) {
|
|
4680
4861
|
for (const entry of fs9.readdirSync(hooksDestDir)) {
|
|
4681
4862
|
if (entry.endsWith(".js")) {
|
|
4682
|
-
fs9.unlinkSync(
|
|
4863
|
+
fs9.unlinkSync(path22.join(hooksDestDir, entry));
|
|
4683
4864
|
}
|
|
4684
4865
|
}
|
|
4685
4866
|
}
|
|
@@ -4688,18 +4869,18 @@ function initHooks(options) {
|
|
|
4688
4869
|
const activeNames = PROFILES[profile];
|
|
4689
4870
|
const activeScripts = HOOK_SCRIPTS.filter((h) => activeNames.includes(h.name));
|
|
4690
4871
|
for (const script of activeScripts) {
|
|
4691
|
-
const srcFile =
|
|
4692
|
-
const destFile =
|
|
4872
|
+
const srcFile = path22.join(sourceDir, `${script.name}.js`);
|
|
4873
|
+
const destFile = path22.join(hooksDestDir, `${script.name}.js`);
|
|
4693
4874
|
if (fs9.existsSync(srcFile)) {
|
|
4694
4875
|
fs9.copyFileSync(srcFile, destFile);
|
|
4695
4876
|
copiedScripts.push(script.name);
|
|
4696
4877
|
}
|
|
4697
4878
|
}
|
|
4698
|
-
const profilePath =
|
|
4879
|
+
const profilePath = path22.join(hooksDestDir, "profile.json");
|
|
4699
4880
|
fs9.writeFileSync(profilePath, JSON.stringify({ profile }, null, 2) + "\n");
|
|
4700
|
-
const claudeDir =
|
|
4881
|
+
const claudeDir = path22.join(projectDir, ".claude");
|
|
4701
4882
|
fs9.mkdirSync(claudeDir, { recursive: true });
|
|
4702
|
-
const settingsPath =
|
|
4883
|
+
const settingsPath = path22.join(claudeDir, "settings.json");
|
|
4703
4884
|
let existing = {};
|
|
4704
4885
|
if (fs9.existsSync(settingsPath)) {
|
|
4705
4886
|
try {
|
|
@@ -4717,7 +4898,7 @@ function initHooks(options) {
|
|
|
4717
4898
|
return { copiedScripts, settingsPath, profilePath };
|
|
4718
4899
|
}
|
|
4719
4900
|
function createInitCommand2() {
|
|
4720
|
-
return new
|
|
4901
|
+
return new Command37("init").description("Install Claude Code hook configurations into the current project").option("--profile <profile>", "Hook profile: minimal, standard, or strict", "standard").action(async (opts, cmd) => {
|
|
4721
4902
|
const globalOpts = cmd.optsWithGlobals();
|
|
4722
4903
|
const profile = opts.profile;
|
|
4723
4904
|
if (!VALID_PROFILES.includes(profile)) {
|
|
@@ -4742,7 +4923,7 @@ function createInitCommand2() {
|
|
|
4742
4923
|
);
|
|
4743
4924
|
logger.info(`Profile: ${profile}`);
|
|
4744
4925
|
logger.info(
|
|
4745
|
-
`Settings: ${
|
|
4926
|
+
`Settings: ${path22.relative(projectDir, result.settingsPath).replaceAll("\\", "/")}`
|
|
4746
4927
|
);
|
|
4747
4928
|
logger.dim("Run 'harness hooks list' to see installed hooks");
|
|
4748
4929
|
}
|
|
@@ -4755,12 +4936,12 @@ function createInitCommand2() {
|
|
|
4755
4936
|
}
|
|
4756
4937
|
|
|
4757
4938
|
// src/commands/hooks/list.ts
|
|
4758
|
-
import { Command as
|
|
4939
|
+
import { Command as Command38 } from "commander";
|
|
4759
4940
|
import * as fs10 from "fs";
|
|
4760
|
-
import * as
|
|
4941
|
+
import * as path23 from "path";
|
|
4761
4942
|
function listHooks(projectDir) {
|
|
4762
|
-
const hooksDir =
|
|
4763
|
-
const profilePath =
|
|
4943
|
+
const hooksDir = path23.join(projectDir, ".harness", "hooks");
|
|
4944
|
+
const profilePath = path23.join(hooksDir, "profile.json");
|
|
4764
4945
|
if (!fs10.existsSync(profilePath)) {
|
|
4765
4946
|
return { installed: false, profile: null, hooks: [] };
|
|
4766
4947
|
}
|
|
@@ -4779,7 +4960,7 @@ function listHooks(projectDir) {
|
|
|
4779
4960
|
name: h.name,
|
|
4780
4961
|
event: h.event,
|
|
4781
4962
|
matcher: h.matcher,
|
|
4782
|
-
scriptPath:
|
|
4963
|
+
scriptPath: path23.join(".harness", "hooks", `${h.name}.js`)
|
|
4783
4964
|
}));
|
|
4784
4965
|
const result = { installed: true, profile, hooks };
|
|
4785
4966
|
if (warning) {
|
|
@@ -4788,7 +4969,7 @@ function listHooks(projectDir) {
|
|
|
4788
4969
|
return result;
|
|
4789
4970
|
}
|
|
4790
4971
|
function createListCommand() {
|
|
4791
|
-
return new
|
|
4972
|
+
return new Command38("list").description("Show installed hooks and active profile").action(async (_opts, cmd) => {
|
|
4792
4973
|
const globalOpts = cmd.optsWithGlobals();
|
|
4793
4974
|
const projectDir = process.cwd();
|
|
4794
4975
|
const result = listHooks(projectDir);
|
|
@@ -4809,12 +4990,12 @@ function createListCommand() {
|
|
|
4809
4990
|
}
|
|
4810
4991
|
|
|
4811
4992
|
// src/commands/hooks/remove.ts
|
|
4812
|
-
import { Command as
|
|
4993
|
+
import { Command as Command39 } from "commander";
|
|
4813
4994
|
import * as fs11 from "fs";
|
|
4814
|
-
import * as
|
|
4995
|
+
import * as path24 from "path";
|
|
4815
4996
|
function removeHooks(projectDir) {
|
|
4816
|
-
const hooksDir =
|
|
4817
|
-
const settingsPath =
|
|
4997
|
+
const hooksDir = path24.join(projectDir, ".harness", "hooks");
|
|
4998
|
+
const settingsPath = path24.join(projectDir, ".claude", "settings.json");
|
|
4818
4999
|
let removed = false;
|
|
4819
5000
|
let settingsCleaned = false;
|
|
4820
5001
|
if (fs11.existsSync(hooksDir)) {
|
|
@@ -4839,7 +5020,7 @@ function removeHooks(projectDir) {
|
|
|
4839
5020
|
return { removed, hooksDir, settingsCleaned };
|
|
4840
5021
|
}
|
|
4841
5022
|
function createRemoveCommand() {
|
|
4842
|
-
return new
|
|
5023
|
+
return new Command39("remove").description("Remove harness-managed hooks from the current project").action(async (_opts, cmd) => {
|
|
4843
5024
|
const globalOpts = cmd.optsWithGlobals();
|
|
4844
5025
|
const projectDir = process.cwd();
|
|
4845
5026
|
const result = removeHooks(projectDir);
|
|
@@ -4861,16 +5042,16 @@ function createRemoveCommand() {
|
|
|
4861
5042
|
}
|
|
4862
5043
|
|
|
4863
5044
|
// src/commands/hooks/add.ts
|
|
4864
|
-
import { Command as
|
|
5045
|
+
import { Command as Command40 } from "commander";
|
|
4865
5046
|
import * as fs12 from "fs";
|
|
4866
|
-
import * as
|
|
5047
|
+
import * as path25 from "path";
|
|
4867
5048
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4868
|
-
var __dirname3 =
|
|
5049
|
+
var __dirname3 = path25.dirname(fileURLToPath3(import.meta.url));
|
|
4869
5050
|
var ALIASES = {
|
|
4870
5051
|
sentinel: ["sentinel-pre", "sentinel-post"]
|
|
4871
5052
|
};
|
|
4872
5053
|
function hookSourceDir() {
|
|
4873
|
-
const d =
|
|
5054
|
+
const d = path25.resolve(__dirname3, "..", "..", "hooks");
|
|
4874
5055
|
if (fs12.existsSync(d)) return d;
|
|
4875
5056
|
throw new Error(`Hook scripts not found: ${d}`);
|
|
4876
5057
|
}
|
|
@@ -4888,11 +5069,11 @@ function addHooks(hookName, projectDir) {
|
|
|
4888
5069
|
const names = ALIASES[hookName] ?? [hookName];
|
|
4889
5070
|
const result = { added: [], alreadyInstalled: [], notFound: [] };
|
|
4890
5071
|
const srcDir = hookSourceDir();
|
|
4891
|
-
const destDir =
|
|
5072
|
+
const destDir = path25.join(projectDir, ".harness", "hooks");
|
|
4892
5073
|
fs12.mkdirSync(destDir, { recursive: true });
|
|
4893
|
-
const claudeDir =
|
|
5074
|
+
const claudeDir = path25.join(projectDir, ".claude");
|
|
4894
5075
|
fs12.mkdirSync(claudeDir, { recursive: true });
|
|
4895
|
-
const settingsPath =
|
|
5076
|
+
const settingsPath = path25.join(claudeDir, "settings.json");
|
|
4896
5077
|
const settings = readJson(settingsPath);
|
|
4897
5078
|
if (!settings.hooks) settings.hooks = {};
|
|
4898
5079
|
for (const name of names) {
|
|
@@ -4901,8 +5082,8 @@ function addHooks(hookName, projectDir) {
|
|
|
4901
5082
|
result.notFound.push(name);
|
|
4902
5083
|
continue;
|
|
4903
5084
|
}
|
|
4904
|
-
const src =
|
|
4905
|
-
const dest =
|
|
5085
|
+
const src = path25.join(srcDir, `${name}.js`);
|
|
5086
|
+
const dest = path25.join(destDir, `${name}.js`);
|
|
4906
5087
|
if (fs12.existsSync(dest)) {
|
|
4907
5088
|
result.alreadyInstalled.push(name);
|
|
4908
5089
|
} else if (!fs12.existsSync(src)) {
|
|
@@ -4918,7 +5099,7 @@ function addHooks(hookName, projectDir) {
|
|
|
4918
5099
|
return result;
|
|
4919
5100
|
}
|
|
4920
5101
|
function createAddCommand2() {
|
|
4921
|
-
return new
|
|
5102
|
+
return new Command40("add").argument("<hook-name>", "Hook name or alias (e.g., sentinel)").description("Add a hook without changing the profile").action(async (hookName, _opts, cmd) => {
|
|
4922
5103
|
const projectDir = process.cwd();
|
|
4923
5104
|
try {
|
|
4924
5105
|
const res = addHooks(hookName, projectDir);
|
|
@@ -4943,7 +5124,7 @@ function createAddCommand2() {
|
|
|
4943
5124
|
|
|
4944
5125
|
// src/commands/hooks/index.ts
|
|
4945
5126
|
function createHooksCommand() {
|
|
4946
|
-
const command = new
|
|
5127
|
+
const command = new Command41("hooks").description("Manage Claude Code hook configurations");
|
|
4947
5128
|
command.addCommand(createInitCommand2());
|
|
4948
5129
|
command.addCommand(createListCommand());
|
|
4949
5130
|
command.addCommand(createRemoveCommand());
|
|
@@ -4952,9 +5133,9 @@ function createHooksCommand() {
|
|
|
4952
5133
|
}
|
|
4953
5134
|
|
|
4954
5135
|
// src/commands/impact-preview.ts
|
|
4955
|
-
import { Command as
|
|
5136
|
+
import { Command as Command42 } from "commander";
|
|
4956
5137
|
import { execSync as execSync4 } from "child_process";
|
|
4957
|
-
import * as
|
|
5138
|
+
import * as path26 from "path";
|
|
4958
5139
|
import * as fs13 from "fs";
|
|
4959
5140
|
function getStagedFiles(cwd) {
|
|
4960
5141
|
try {
|
|
@@ -4969,7 +5150,7 @@ function getStagedFiles(cwd) {
|
|
|
4969
5150
|
}
|
|
4970
5151
|
function graphExists(projectPath) {
|
|
4971
5152
|
try {
|
|
4972
|
-
return fs13.existsSync(
|
|
5153
|
+
return fs13.existsSync(path26.join(projectPath, ".harness", "graph", "graph.json"));
|
|
4973
5154
|
} catch {
|
|
4974
5155
|
return false;
|
|
4975
5156
|
}
|
|
@@ -4978,7 +5159,7 @@ function extractNodeName(id) {
|
|
|
4978
5159
|
const parts = id.split(":");
|
|
4979
5160
|
if (parts.length > 1) {
|
|
4980
5161
|
const fullPath = parts.slice(1).join(":");
|
|
4981
|
-
return
|
|
5162
|
+
return path26.basename(fullPath);
|
|
4982
5163
|
}
|
|
4983
5164
|
return id;
|
|
4984
5165
|
}
|
|
@@ -5133,7 +5314,7 @@ function formatImpactOutput(stagedFiles, acc, options) {
|
|
|
5133
5314
|
return options.detailed ? formatDetailed(stagedFiles.length, merged, acc.aggregateCounts) : formatCompact(stagedFiles.length, merged, acc.aggregateCounts);
|
|
5134
5315
|
}
|
|
5135
5316
|
async function runImpactPreview(options) {
|
|
5136
|
-
const projectPath =
|
|
5317
|
+
const projectPath = path26.resolve(options.path ?? process.cwd());
|
|
5137
5318
|
const stagedFiles = getStagedFiles(projectPath);
|
|
5138
5319
|
if (stagedFiles.length === 0) return "Impact Preview: no staged changes";
|
|
5139
5320
|
if (!graphExists(projectPath)) {
|
|
@@ -5151,7 +5332,7 @@ async function runImpactPreview(options) {
|
|
|
5151
5332
|
return formatImpactOutput(stagedFiles, acc, options);
|
|
5152
5333
|
}
|
|
5153
5334
|
function createImpactPreviewCommand() {
|
|
5154
|
-
const command = new
|
|
5335
|
+
const command = new Command42("impact-preview").description("Show blast radius of staged changes using the knowledge graph").option("--detailed", "Show all affected files instead of top items").option("--per-file", "Show impact per staged file instead of aggregate").option("--path <dir>", "Project root (default: cwd)").action(async (opts) => {
|
|
5155
5336
|
const output = await runImpactPreview({
|
|
5156
5337
|
detailed: opts.detailed,
|
|
5157
5338
|
perFile: opts.perFile,
|
|
@@ -5165,24 +5346,24 @@ function createImpactPreviewCommand() {
|
|
|
5165
5346
|
|
|
5166
5347
|
// src/commands/init.ts
|
|
5167
5348
|
init_dist3();
|
|
5168
|
-
import { Command as
|
|
5349
|
+
import { Command as Command44 } from "commander";
|
|
5169
5350
|
import chalk3 from "chalk";
|
|
5170
5351
|
import * as fs16 from "fs";
|
|
5171
|
-
import * as
|
|
5352
|
+
import * as path29 from "path";
|
|
5172
5353
|
|
|
5173
5354
|
// src/commands/setup-mcp.ts
|
|
5174
|
-
import { Command as
|
|
5355
|
+
import { Command as Command43 } from "commander";
|
|
5175
5356
|
import * as fs15 from "fs";
|
|
5176
|
-
import * as
|
|
5357
|
+
import * as path28 from "path";
|
|
5177
5358
|
import * as os2 from "os";
|
|
5178
5359
|
import chalk2 from "chalk";
|
|
5179
5360
|
import * as clack from "@clack/prompts";
|
|
5180
5361
|
|
|
5181
5362
|
// src/integrations/toml.ts
|
|
5182
5363
|
import * as fs14 from "fs";
|
|
5183
|
-
import * as
|
|
5364
|
+
import * as path27 from "path";
|
|
5184
5365
|
function writeTomlMcpEntry(filePath, name, entry) {
|
|
5185
|
-
const dir =
|
|
5366
|
+
const dir = path27.dirname(filePath);
|
|
5186
5367
|
if (!fs14.existsSync(dir)) {
|
|
5187
5368
|
fs14.mkdirSync(dir, { recursive: true });
|
|
5188
5369
|
}
|
|
@@ -5312,6 +5493,8 @@ var ALL_MCP_TOOLS = [
|
|
|
5312
5493
|
"run_code_review",
|
|
5313
5494
|
"gather_context",
|
|
5314
5495
|
"assess_project",
|
|
5496
|
+
"canary_probe",
|
|
5497
|
+
"canary_recommend_framework",
|
|
5315
5498
|
"review_changes",
|
|
5316
5499
|
"detect_anomalies",
|
|
5317
5500
|
"ask_graph",
|
|
@@ -5368,7 +5551,21 @@ var ALL_MCP_TOOLS = [
|
|
|
5368
5551
|
// craft-pipeline #9 — knowledge-craft LLM-judgment ceiling skill (docs/knowledge/ entries)
|
|
5369
5552
|
"knowledge_craft",
|
|
5370
5553
|
// craft-pipeline #10 — security-craft LLM-judgment ceiling skill (AST-driven, conservative-confidence)
|
|
5371
|
-
"security_craft"
|
|
5554
|
+
"security_craft",
|
|
5555
|
+
// outcome-eval — post-execution spec-satisfaction verdict (tiered confidence→authority)
|
|
5556
|
+
"outcome_eval",
|
|
5557
|
+
// strategic-anchor — STRATEGY.md read/validate/write so projects can call
|
|
5558
|
+
// these via MCP without needing @harness-engineering/core in node_modules
|
|
5559
|
+
"validate_strategy",
|
|
5560
|
+
"read_strategy",
|
|
5561
|
+
"write_strategy",
|
|
5562
|
+
// strategic-anchor — pulse config writer + STRATEGY.md seed extractor
|
|
5563
|
+
"write_pulse_config",
|
|
5564
|
+
"seed_pulse_from_strategy",
|
|
5565
|
+
// strategic-anchor — compound lock acquire/release (server holds the handle;
|
|
5566
|
+
// skill threads a token between calls)
|
|
5567
|
+
"acquire_compound_lock",
|
|
5568
|
+
"release_compound_lock"
|
|
5372
5569
|
];
|
|
5373
5570
|
async function runCursorToolPicker() {
|
|
5374
5571
|
try {
|
|
@@ -5405,7 +5602,7 @@ function readJsonFile(filePath) {
|
|
|
5405
5602
|
}
|
|
5406
5603
|
}
|
|
5407
5604
|
function writeJsonFile(filePath, data) {
|
|
5408
|
-
const dir =
|
|
5605
|
+
const dir = path28.dirname(filePath);
|
|
5409
5606
|
if (!fs15.existsSync(dir)) {
|
|
5410
5607
|
fs15.mkdirSync(dir, { recursive: true });
|
|
5411
5608
|
}
|
|
@@ -5424,7 +5621,7 @@ function configureMcpServer(configPath) {
|
|
|
5424
5621
|
return true;
|
|
5425
5622
|
}
|
|
5426
5623
|
function addGeminiTrustedFolder(cwd) {
|
|
5427
|
-
const trustedPath =
|
|
5624
|
+
const trustedPath = path28.join(os2.homedir(), ".gemini", "trustedFolders.json");
|
|
5428
5625
|
const folders = readJsonFile(trustedPath) ?? {};
|
|
5429
5626
|
if (folders[cwd] === "TRUST_FOLDER") {
|
|
5430
5627
|
return false;
|
|
@@ -5438,7 +5635,7 @@ function setupMcp(cwd, client) {
|
|
|
5438
5635
|
const skipped = [];
|
|
5439
5636
|
let trustedFolder = false;
|
|
5440
5637
|
if (client === "all" || client === "claude") {
|
|
5441
|
-
const configPath =
|
|
5638
|
+
const configPath = path28.join(cwd, ".mcp.json");
|
|
5442
5639
|
if (configureMcpServer(configPath)) {
|
|
5443
5640
|
configured.push("Claude Code");
|
|
5444
5641
|
} else {
|
|
@@ -5446,7 +5643,7 @@ function setupMcp(cwd, client) {
|
|
|
5446
5643
|
}
|
|
5447
5644
|
}
|
|
5448
5645
|
if (client === "all" || client === "gemini") {
|
|
5449
|
-
const configPath =
|
|
5646
|
+
const configPath = path28.join(cwd, ".gemini", "settings.json");
|
|
5450
5647
|
if (configureMcpServer(configPath)) {
|
|
5451
5648
|
configured.push("Gemini CLI");
|
|
5452
5649
|
} else {
|
|
@@ -5455,7 +5652,7 @@ function setupMcp(cwd, client) {
|
|
|
5455
5652
|
trustedFolder = addGeminiTrustedFolder(cwd);
|
|
5456
5653
|
}
|
|
5457
5654
|
if (client === "all" || client === "codex") {
|
|
5458
|
-
const configPath =
|
|
5655
|
+
const configPath = path28.join(cwd, ".codex", "config.toml");
|
|
5459
5656
|
const alreadyConfigured = (() => {
|
|
5460
5657
|
if (!fs15.existsSync(configPath)) return false;
|
|
5461
5658
|
const content = fs15.readFileSync(configPath, "utf-8");
|
|
@@ -5473,7 +5670,7 @@ function setupMcp(cwd, client) {
|
|
|
5473
5670
|
}
|
|
5474
5671
|
}
|
|
5475
5672
|
if (client === "all" || client === "cursor") {
|
|
5476
|
-
const configPath =
|
|
5673
|
+
const configPath = path28.join(cwd, ".cursor", "mcp.json");
|
|
5477
5674
|
const existing = readJsonFile(configPath);
|
|
5478
5675
|
if (existing?.mcpServers?.["harness"]) {
|
|
5479
5676
|
skipped.push("Cursor");
|
|
@@ -5483,7 +5680,7 @@ function setupMcp(cwd, client) {
|
|
|
5483
5680
|
}
|
|
5484
5681
|
}
|
|
5485
5682
|
if (client === "all" || client === "opencode") {
|
|
5486
|
-
const configPath =
|
|
5683
|
+
const configPath = path28.join(cwd, "opencode.json");
|
|
5487
5684
|
const existing = readJsonFile(configPath);
|
|
5488
5685
|
if (existing?.mcp?.["harness"]) {
|
|
5489
5686
|
skipped.push("OpenCode");
|
|
@@ -5497,7 +5694,7 @@ function setupMcp(cwd, client) {
|
|
|
5497
5694
|
async function resolveCursorWithPicker(cwd, pick) {
|
|
5498
5695
|
const configured = [];
|
|
5499
5696
|
const skipped = [];
|
|
5500
|
-
const cursorConfigPath =
|
|
5697
|
+
const cursorConfigPath = path28.join(cwd, ".cursor", "mcp.json");
|
|
5501
5698
|
const existing = readJsonFile(cursorConfigPath);
|
|
5502
5699
|
if (existing?.mcpServers?.["harness"] && !pick) {
|
|
5503
5700
|
skipped.push("Cursor");
|
|
@@ -5541,7 +5738,7 @@ function printMcpResult(configured, skipped, trustedFolder) {
|
|
|
5541
5738
|
console.log("");
|
|
5542
5739
|
}
|
|
5543
5740
|
function createSetupMcpCommand() {
|
|
5544
|
-
return new
|
|
5741
|
+
return new Command43("setup-mcp").description("Configure MCP server for AI agent integration").option(
|
|
5545
5742
|
"--client <client>",
|
|
5546
5743
|
"Client to configure (claude, gemini, codex, cursor, opencode, all)",
|
|
5547
5744
|
"all"
|
|
@@ -5572,11 +5769,11 @@ function loadEngineAndTemplates(_options) {
|
|
|
5572
5769
|
}
|
|
5573
5770
|
function resolveInitDefaults(options) {
|
|
5574
5771
|
const cwd = options.cwd ?? process.cwd();
|
|
5575
|
-
return { cwd, name: options.name ??
|
|
5772
|
+
return { cwd, name: options.name ?? path29.basename(cwd), force: options.force ?? false };
|
|
5576
5773
|
}
|
|
5577
5774
|
async function runInit(options) {
|
|
5578
5775
|
const { cwd, name, force } = resolveInitDefaults(options);
|
|
5579
|
-
const configPath =
|
|
5776
|
+
const configPath = path29.join(cwd, "harness.config.json");
|
|
5580
5777
|
if (!force && fs16.existsSync(configPath)) {
|
|
5581
5778
|
return Err(
|
|
5582
5779
|
new CLIError("Project already initialized. Use --force to overwrite.", ExitCode.ERROR)
|
|
@@ -5630,6 +5827,18 @@ function scaffoldProject(engine, ctx) {
|
|
|
5630
5827
|
...language !== void 0 && { language }
|
|
5631
5828
|
});
|
|
5632
5829
|
if (!renderResult.ok) return Err(new CLIError(renderResult.error.message, ExitCode.ERROR));
|
|
5830
|
+
const ciResult = generateCIConfig({
|
|
5831
|
+
platform: "github",
|
|
5832
|
+
...language !== void 0 && { language }
|
|
5833
|
+
});
|
|
5834
|
+
if (ciResult.ok) {
|
|
5835
|
+
renderResult.value.files.push({
|
|
5836
|
+
relativePath: ciResult.value.filename,
|
|
5837
|
+
content: ciResult.value.content
|
|
5838
|
+
});
|
|
5839
|
+
} else {
|
|
5840
|
+
logger.warn(`CI workflow was not generated: ${ciResult.error.message}`);
|
|
5841
|
+
}
|
|
5633
5842
|
const existingProject = !force && engine.isExistingProject(cwd);
|
|
5634
5843
|
const writeResult = engine.write(renderResult.value, cwd, {
|
|
5635
5844
|
overwrite: force,
|
|
@@ -5687,7 +5896,7 @@ async function runInitAction2(opts) {
|
|
|
5687
5896
|
process.exit(ExitCode.SUCCESS);
|
|
5688
5897
|
}
|
|
5689
5898
|
function createInitCommand3() {
|
|
5690
|
-
const command = new
|
|
5899
|
+
const command = new Command44("init").description("Initialize a new harness-engineering project").option("-n, --name <name>", "Project name").option("-l, --level <level>", "Adoption level (basic, intermediate, advanced)", "basic").option("-t, --template <template>", "Specific template name (e.g. orchestrator)").option("--framework <framework>", "Framework overlay (nextjs)").option("--language <language>", "Target language (typescript, python, go, rust, java)").option("-f, --force", "Overwrite existing files").option("-y, --yes", "Use defaults without prompting").action(async (opts, cmd) => {
|
|
5691
5900
|
const globalOpts = cmd.optsWithGlobals();
|
|
5692
5901
|
await runInitAction2({
|
|
5693
5902
|
name: opts.name,
|
|
@@ -5703,7 +5912,7 @@ function createInitCommand3() {
|
|
|
5703
5912
|
|
|
5704
5913
|
// src/commands/insights.ts
|
|
5705
5914
|
init_dist();
|
|
5706
|
-
import { Command as
|
|
5915
|
+
import { Command as Command45 } from "commander";
|
|
5707
5916
|
function parseSkip2(raw) {
|
|
5708
5917
|
if (!raw) return [];
|
|
5709
5918
|
const valid = new Set(INSIGHTS_KEYS);
|
|
@@ -5741,10 +5950,10 @@ function renderPretty(report) {
|
|
|
5741
5950
|
}
|
|
5742
5951
|
}
|
|
5743
5952
|
function createInsightsCommand() {
|
|
5744
|
-
return new
|
|
5953
|
+
return new Command45("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) => {
|
|
5745
5954
|
const cwd = process.cwd();
|
|
5746
5955
|
const skip = parseSkip2(opts.skip);
|
|
5747
|
-
const { composeInsights } = await import("./dist-
|
|
5956
|
+
const { composeInsights } = await import("./dist-LHINSVK4.js");
|
|
5748
5957
|
const report = await composeInsights(cwd, { skip });
|
|
5749
5958
|
if (opts.json) {
|
|
5750
5959
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -5757,14 +5966,14 @@ function createInsightsCommand() {
|
|
|
5757
5966
|
// src/commands/install.ts
|
|
5758
5967
|
import * as fs21 from "fs";
|
|
5759
5968
|
import * as os5 from "os";
|
|
5760
|
-
import * as
|
|
5969
|
+
import * as path34 from "path";
|
|
5761
5970
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
5762
|
-
import { Command as
|
|
5971
|
+
import { Command as Command46 } from "commander";
|
|
5763
5972
|
import { parse as yamlParse } from "yaml";
|
|
5764
5973
|
|
|
5765
5974
|
// src/registry/npm-client.ts
|
|
5766
5975
|
import * as fs17 from "fs";
|
|
5767
|
-
import * as
|
|
5976
|
+
import * as path30 from "path";
|
|
5768
5977
|
import * as os3 from "os";
|
|
5769
5978
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
5770
5979
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
@@ -5787,7 +5996,7 @@ function extractSkillName(packageName) {
|
|
|
5787
5996
|
function readNpmrcToken(registryUrl) {
|
|
5788
5997
|
const { hostname: hostname2, pathname } = new URL(registryUrl);
|
|
5789
5998
|
const registryPath = `//${hostname2}${pathname.replace(/\/$/, "")}/:_authToken=`;
|
|
5790
|
-
const candidates = [
|
|
5999
|
+
const candidates = [path30.join(process.cwd(), ".npmrc"), path30.join(os3.homedir(), ".npmrc")];
|
|
5791
6000
|
for (const npmrcPath of candidates) {
|
|
5792
6001
|
try {
|
|
5793
6002
|
const content = fs17.readFileSync(npmrcPath, "utf-8");
|
|
@@ -5886,12 +6095,12 @@ async function searchNpmRegistry(query, registryUrl) {
|
|
|
5886
6095
|
|
|
5887
6096
|
// src/registry/tarball.ts
|
|
5888
6097
|
import * as fs18 from "fs";
|
|
5889
|
-
import * as
|
|
6098
|
+
import * as path31 from "path";
|
|
5890
6099
|
import * as os4 from "os";
|
|
5891
6100
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
5892
6101
|
function extractTarball(tarballBuffer) {
|
|
5893
|
-
const tmpDir = fs18.mkdtempSync(
|
|
5894
|
-
const tarballPath =
|
|
6102
|
+
const tmpDir = fs18.mkdtempSync(path31.join(os4.tmpdir(), "harness-skill-install-"));
|
|
6103
|
+
const tarballPath = path31.join(tmpDir, "package.tgz");
|
|
5895
6104
|
try {
|
|
5896
6105
|
fs18.writeFileSync(tarballPath, tarballBuffer);
|
|
5897
6106
|
execFileSync2("tar", ["-xzf", tarballPath, "-C", tmpDir], {
|
|
@@ -5910,15 +6119,15 @@ function extractTarball(tarballBuffer) {
|
|
|
5910
6119
|
function placeSkillContent(extractedPkgDir, communityBaseDir, skillName, platforms) {
|
|
5911
6120
|
const files = fs18.readdirSync(extractedPkgDir);
|
|
5912
6121
|
for (const platform of platforms) {
|
|
5913
|
-
const targetDir =
|
|
6122
|
+
const targetDir = path31.join(communityBaseDir, platform, skillName);
|
|
5914
6123
|
if (fs18.existsSync(targetDir)) {
|
|
5915
6124
|
fs18.rmSync(targetDir, { recursive: true, force: true });
|
|
5916
6125
|
}
|
|
5917
6126
|
fs18.mkdirSync(targetDir, { recursive: true });
|
|
5918
6127
|
for (const file of files) {
|
|
5919
6128
|
if (file === "package.json" || file === "node_modules") continue;
|
|
5920
|
-
const srcPath =
|
|
5921
|
-
const destPath =
|
|
6129
|
+
const srcPath = path31.join(extractedPkgDir, file);
|
|
6130
|
+
const destPath = path31.join(targetDir, file);
|
|
5922
6131
|
const stat = fs18.statSync(srcPath);
|
|
5923
6132
|
if (stat.isDirectory()) {
|
|
5924
6133
|
fs18.cpSync(srcPath, destPath, { recursive: true });
|
|
@@ -5930,7 +6139,7 @@ function placeSkillContent(extractedPkgDir, communityBaseDir, skillName, platfor
|
|
|
5930
6139
|
}
|
|
5931
6140
|
function removeSkillContent(communityBaseDir, skillName, platforms) {
|
|
5932
6141
|
for (const platform of platforms) {
|
|
5933
|
-
const targetDir =
|
|
6142
|
+
const targetDir = path31.join(communityBaseDir, platform, skillName);
|
|
5934
6143
|
if (fs18.existsSync(targetDir)) {
|
|
5935
6144
|
fs18.rmSync(targetDir, { recursive: true, force: true });
|
|
5936
6145
|
}
|
|
@@ -5981,7 +6190,7 @@ function findDependentsOf(lockfile, targetPackageName) {
|
|
|
5981
6190
|
|
|
5982
6191
|
// src/registry/lockfile.ts
|
|
5983
6192
|
import * as fs19 from "fs";
|
|
5984
|
-
import * as
|
|
6193
|
+
import * as path32 from "path";
|
|
5985
6194
|
function createEmptyLockfile() {
|
|
5986
6195
|
return { version: 1, skills: {} };
|
|
5987
6196
|
}
|
|
@@ -6021,7 +6230,7 @@ function readLockfile2(filePath) {
|
|
|
6021
6230
|
return parsed;
|
|
6022
6231
|
}
|
|
6023
6232
|
function writeLockfile2(filePath, lockfile) {
|
|
6024
|
-
const dir =
|
|
6233
|
+
const dir = path32.dirname(filePath);
|
|
6025
6234
|
fs19.mkdirSync(dir, { recursive: true });
|
|
6026
6235
|
fs19.writeFileSync(filePath, sortedStringify(lockfile) + "\n", "utf-8");
|
|
6027
6236
|
}
|
|
@@ -6047,7 +6256,7 @@ function removeLockfileEntry(lockfile, name) {
|
|
|
6047
6256
|
|
|
6048
6257
|
// src/registry/bundled-skills.ts
|
|
6049
6258
|
import * as fs20 from "fs";
|
|
6050
|
-
import * as
|
|
6259
|
+
import * as path33 from "path";
|
|
6051
6260
|
function getBundledSkillNames(bundledSkillsDir) {
|
|
6052
6261
|
if (!fs20.existsSync(bundledSkillsDir)) {
|
|
6053
6262
|
return /* @__PURE__ */ new Set();
|
|
@@ -6056,7 +6265,7 @@ function getBundledSkillNames(bundledSkillsDir) {
|
|
|
6056
6265
|
const names = /* @__PURE__ */ new Set();
|
|
6057
6266
|
for (const entry of entries) {
|
|
6058
6267
|
try {
|
|
6059
|
-
const stat = fs20.statSync(
|
|
6268
|
+
const stat = fs20.statSync(path33.join(bundledSkillsDir, String(entry)));
|
|
6060
6269
|
if (stat.isDirectory()) {
|
|
6061
6270
|
names.add(String(entry));
|
|
6062
6271
|
}
|
|
@@ -6084,12 +6293,12 @@ function validateSkillYaml(parsed) {
|
|
|
6084
6293
|
function resolveCommunityBase(global) {
|
|
6085
6294
|
if (global) {
|
|
6086
6295
|
const communityBase2 = resolveGlobalCommunityBaseDir();
|
|
6087
|
-
return { communityBase: communityBase2, lockfilePath:
|
|
6296
|
+
return { communityBase: communityBase2, lockfilePath: path34.join(communityBase2, "skills-lock.json") };
|
|
6088
6297
|
}
|
|
6089
6298
|
const globalDir = resolveGlobalSkillsDir();
|
|
6090
|
-
const skillsDir =
|
|
6091
|
-
const communityBase =
|
|
6092
|
-
return { communityBase, lockfilePath:
|
|
6299
|
+
const skillsDir = path34.dirname(globalDir);
|
|
6300
|
+
const communityBase = path34.join(skillsDir, "community");
|
|
6301
|
+
return { communityBase, lockfilePath: path34.join(communityBase, "skills-lock.json") };
|
|
6093
6302
|
}
|
|
6094
6303
|
function parseGitHubRef(from) {
|
|
6095
6304
|
const ghPrefix = from.match(/^github:([^/]+)\/([^#]+?)(?:#(.+))?$/);
|
|
@@ -6105,7 +6314,7 @@ function parseGitHubRef(from) {
|
|
|
6105
6314
|
return null;
|
|
6106
6315
|
}
|
|
6107
6316
|
function cloneGitHubRepo(owner, repo, ref) {
|
|
6108
|
-
const tmpDir = fs21.mkdtempSync(
|
|
6317
|
+
const tmpDir = fs21.mkdtempSync(path34.join(os5.tmpdir(), "harness-gh-install-"));
|
|
6109
6318
|
const url = `https://github.com/${owner}/${repo}.git`;
|
|
6110
6319
|
try {
|
|
6111
6320
|
const cloneArgs = ["clone", "--depth", "1"];
|
|
@@ -6127,7 +6336,7 @@ function discoverSkillDirs(rootDir) {
|
|
|
6127
6336
|
function scan(dir, depth) {
|
|
6128
6337
|
if (depth > 3) return;
|
|
6129
6338
|
if (!fs21.existsSync(dir)) return;
|
|
6130
|
-
if (fs21.existsSync(
|
|
6339
|
+
if (fs21.existsSync(path34.join(dir, "skill.yaml"))) {
|
|
6131
6340
|
skillDirs.push(dir);
|
|
6132
6341
|
return;
|
|
6133
6342
|
}
|
|
@@ -6135,14 +6344,14 @@ function discoverSkillDirs(rootDir) {
|
|
|
6135
6344
|
for (const entry of entries) {
|
|
6136
6345
|
if (!entry.isDirectory()) continue;
|
|
6137
6346
|
if (entry.name.startsWith(".") || DEFAULT_SKIP_DIRS.has(entry.name)) continue;
|
|
6138
|
-
scan(
|
|
6347
|
+
scan(path34.join(dir, entry.name), depth + 1);
|
|
6139
6348
|
}
|
|
6140
6349
|
}
|
|
6141
6350
|
scan(rootDir, 0);
|
|
6142
6351
|
return skillDirs;
|
|
6143
6352
|
}
|
|
6144
6353
|
function resolveLocalPkgDir(fromPath) {
|
|
6145
|
-
const resolvedPath =
|
|
6354
|
+
const resolvedPath = path34.resolve(fromPath);
|
|
6146
6355
|
if (!fs21.existsSync(resolvedPath)) {
|
|
6147
6356
|
throw new Error(`--from path does not exist: ${resolvedPath}`);
|
|
6148
6357
|
}
|
|
@@ -6153,12 +6362,12 @@ function resolveLocalPkgDir(fromPath) {
|
|
|
6153
6362
|
if (resolvedPath.endsWith(".tgz") || resolvedPath.endsWith(".tar.gz")) {
|
|
6154
6363
|
const tarballBuffer = fs21.readFileSync(resolvedPath);
|
|
6155
6364
|
const extractDir = extractTarball(tarballBuffer);
|
|
6156
|
-
return { pkgDir:
|
|
6365
|
+
return { pkgDir: path34.join(extractDir, "package"), extractDir };
|
|
6157
6366
|
}
|
|
6158
6367
|
throw new Error(`--from path must be a directory or .tgz file. Got: ${resolvedPath}`);
|
|
6159
6368
|
}
|
|
6160
6369
|
function installSkillDir(pkgDir, resolvedPath, options) {
|
|
6161
|
-
const skillYamlPath =
|
|
6370
|
+
const skillYamlPath = path34.join(pkgDir, "skill.yaml");
|
|
6162
6371
|
if (!fs21.existsSync(skillYamlPath)) {
|
|
6163
6372
|
throw new Error(`No skill.yaml found at ${skillYamlPath}`);
|
|
6164
6373
|
}
|
|
@@ -6190,7 +6399,7 @@ function installSkillDir(pkgDir, resolvedPath, options) {
|
|
|
6190
6399
|
async function runLocalInstall(fromPath, options) {
|
|
6191
6400
|
const { pkgDir, extractDir } = resolveLocalPkgDir(fromPath);
|
|
6192
6401
|
try {
|
|
6193
|
-
return installSkillDir(pkgDir,
|
|
6402
|
+
return installSkillDir(pkgDir, path34.resolve(fromPath), options);
|
|
6194
6403
|
} finally {
|
|
6195
6404
|
if (extractDir) cleanupTempDir(extractDir);
|
|
6196
6405
|
}
|
|
@@ -6233,8 +6442,8 @@ async function runInstall(skillName, options) {
|
|
|
6233
6442
|
version: installed.map((r) => r.version).join(", ")
|
|
6234
6443
|
};
|
|
6235
6444
|
}
|
|
6236
|
-
const resolvedFrom =
|
|
6237
|
-
if (fs21.existsSync(resolvedFrom) && fs21.statSync(resolvedFrom).isDirectory() && !fs21.existsSync(
|
|
6445
|
+
const resolvedFrom = path34.resolve(options.from);
|
|
6446
|
+
if (fs21.existsSync(resolvedFrom) && fs21.statSync(resolvedFrom).isDirectory() && !fs21.existsSync(path34.join(resolvedFrom, "skill.yaml"))) {
|
|
6238
6447
|
const results = await runBulkInstall(resolvedFrom, options);
|
|
6239
6448
|
const installed = results.filter((r) => r.installed);
|
|
6240
6449
|
return {
|
|
@@ -6275,8 +6484,8 @@ async function runInstall(skillName, options) {
|
|
|
6275
6484
|
const extractDir = extractTarball(tarballBuffer);
|
|
6276
6485
|
let skillYaml;
|
|
6277
6486
|
try {
|
|
6278
|
-
const extractedPkgDir =
|
|
6279
|
-
const skillYamlPath =
|
|
6487
|
+
const extractedPkgDir = path34.join(extractDir, "package");
|
|
6488
|
+
const skillYamlPath = path34.join(extractedPkgDir, "skill.yaml");
|
|
6280
6489
|
if (!fs21.existsSync(skillYamlPath)) {
|
|
6281
6490
|
throw new Error(`contains invalid skill.yaml: file not found in package`);
|
|
6282
6491
|
}
|
|
@@ -6320,7 +6529,7 @@ async function runInstall(skillName, options) {
|
|
|
6320
6529
|
return result;
|
|
6321
6530
|
}
|
|
6322
6531
|
function createInstallCommand() {
|
|
6323
|
-
const cmd = new
|
|
6532
|
+
const cmd = new Command46("install");
|
|
6324
6533
|
cmd.description("Install skills from npm registry, local directory, or GitHub repository").argument("<skill>", 'Skill name, @harness-skills/scoped package, or "." for bulk install').option("--version <range>", "Semver range or exact version to install").option("--force", "Force reinstall even if same version is already installed").option(
|
|
6325
6534
|
"--from <source>",
|
|
6326
6535
|
"Install from local path, directory, or GitHub (github:owner/repo, https://github.com/owner/repo)"
|
|
@@ -6355,8 +6564,8 @@ function createInstallCommand() {
|
|
|
6355
6564
|
// src/commands/install-constraints.ts
|
|
6356
6565
|
init_dist3();
|
|
6357
6566
|
import * as fs22 from "fs/promises";
|
|
6358
|
-
import * as
|
|
6359
|
-
import { Command as
|
|
6567
|
+
import * as path35 from "path";
|
|
6568
|
+
import { Command as Command47 } from "commander";
|
|
6360
6569
|
import semver3 from "semver";
|
|
6361
6570
|
async function runInstallConstraints(options) {
|
|
6362
6571
|
const { source, configPath, lockfilePath } = options;
|
|
@@ -6553,7 +6762,7 @@ function isNodeError(err) {
|
|
|
6553
6762
|
return err instanceof Error && "code" in err;
|
|
6554
6763
|
}
|
|
6555
6764
|
function resolveConfigPath(opts) {
|
|
6556
|
-
if (opts.config) return
|
|
6765
|
+
if (opts.config) return path35.resolve(opts.config);
|
|
6557
6766
|
const found = findConfigFile();
|
|
6558
6767
|
if (!found.ok) {
|
|
6559
6768
|
logger.error(found.error.message);
|
|
@@ -6588,9 +6797,9 @@ function logInstallResult(val, opts) {
|
|
|
6588
6797
|
}
|
|
6589
6798
|
async function handleInstallConstraints(source, opts) {
|
|
6590
6799
|
const configPath = resolveConfigPath(opts);
|
|
6591
|
-
const projectRoot2 =
|
|
6592
|
-
const lockfilePath =
|
|
6593
|
-
const resolvedSource =
|
|
6800
|
+
const projectRoot2 = path35.dirname(configPath);
|
|
6801
|
+
const lockfilePath = path35.join(projectRoot2, ".harness", "constraints.lock.json");
|
|
6802
|
+
const resolvedSource = path35.resolve(source);
|
|
6594
6803
|
if (opts.forceLocal && opts.forcePackage) {
|
|
6595
6804
|
logger.error("Cannot use both --force-local and --force-package.");
|
|
6596
6805
|
process.exit(1);
|
|
@@ -6610,19 +6819,19 @@ async function handleInstallConstraints(source, opts) {
|
|
|
6610
6819
|
logInstallResult(result.value, opts);
|
|
6611
6820
|
}
|
|
6612
6821
|
function createInstallConstraintsCommand() {
|
|
6613
|
-
const cmd = new
|
|
6822
|
+
const cmd = new Command47("install-constraints");
|
|
6614
6823
|
cmd.description("Install a constraints bundle into the local harness config").argument("<source>", "Path to a .harness-constraints.json bundle file").option("--force-local", "Resolve all conflicts by keeping local values").option("--force-package", "Resolve all conflicts by using package values").option("--dry-run", "Show what would change without writing files").option("-c, --config <path>", "Path to harness.config.json").action(handleInstallConstraints);
|
|
6615
6824
|
return cmd;
|
|
6616
6825
|
}
|
|
6617
6826
|
|
|
6618
6827
|
// src/commands/integrations/index.ts
|
|
6619
|
-
import { Command as
|
|
6828
|
+
import { Command as Command52 } from "commander";
|
|
6620
6829
|
|
|
6621
6830
|
// src/commands/integrations/add.ts
|
|
6622
6831
|
init_dist3();
|
|
6623
|
-
import { Command as
|
|
6832
|
+
import { Command as Command48 } from "commander";
|
|
6624
6833
|
import * as fs23 from "fs";
|
|
6625
|
-
import * as
|
|
6834
|
+
import * as path36 from "path";
|
|
6626
6835
|
import chalk4 from "chalk";
|
|
6627
6836
|
function buildMcpEntry(def) {
|
|
6628
6837
|
const entry = { command: def.mcpConfig.command };
|
|
@@ -6631,14 +6840,14 @@ function buildMcpEntry(def) {
|
|
|
6631
6840
|
return entry;
|
|
6632
6841
|
}
|
|
6633
6842
|
function writeMcpEntries(cwd, defName, mcpEntry) {
|
|
6634
|
-
writeMcpEntry(
|
|
6635
|
-
const geminiDir =
|
|
6843
|
+
writeMcpEntry(path36.join(cwd, ".mcp.json"), defName, mcpEntry);
|
|
6844
|
+
const geminiDir = path36.join(cwd, ".gemini");
|
|
6636
6845
|
if (fs23.existsSync(geminiDir)) {
|
|
6637
|
-
writeMcpEntry(
|
|
6846
|
+
writeMcpEntry(path36.join(geminiDir, "settings.json"), defName, mcpEntry);
|
|
6638
6847
|
}
|
|
6639
6848
|
}
|
|
6640
6849
|
function updateIntegrationsConfig(cwd, defName) {
|
|
6641
|
-
const configPath =
|
|
6850
|
+
const configPath = path36.join(cwd, "harness.config.json");
|
|
6642
6851
|
const integConfig = readIntegrationsConfig(configPath);
|
|
6643
6852
|
if (!integConfig.enabled.includes(defName)) integConfig.enabled.push(defName);
|
|
6644
6853
|
integConfig.dismissed = integConfig.dismissed.filter((d) => d !== defName);
|
|
@@ -6684,7 +6893,7 @@ function printAddSuccess(value) {
|
|
|
6684
6893
|
}
|
|
6685
6894
|
}
|
|
6686
6895
|
function createAddIntegrationCommand() {
|
|
6687
|
-
return new
|
|
6896
|
+
return new Command48("add").description("Enable an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
|
|
6688
6897
|
const globalOpts = cmd.optsWithGlobals();
|
|
6689
6898
|
const result = addIntegration(process.cwd(), name);
|
|
6690
6899
|
if (!result.ok) {
|
|
@@ -6698,8 +6907,8 @@ function createAddIntegrationCommand() {
|
|
|
6698
6907
|
}
|
|
6699
6908
|
|
|
6700
6909
|
// src/commands/integrations/list.ts
|
|
6701
|
-
import { Command as
|
|
6702
|
-
import * as
|
|
6910
|
+
import { Command as Command49 } from "commander";
|
|
6911
|
+
import * as path37 from "path";
|
|
6703
6912
|
import chalk5 from "chalk";
|
|
6704
6913
|
function printTier0Integrations(tier0, mcpServers) {
|
|
6705
6914
|
console.log(" Tier 0 (zero-config):");
|
|
@@ -6723,8 +6932,8 @@ function printTier1Integrations(tier1, mcpServers, dismissed) {
|
|
|
6723
6932
|
}
|
|
6724
6933
|
async function runListIntegrations(globalOpts) {
|
|
6725
6934
|
const cwd = process.cwd();
|
|
6726
|
-
const mcpConfig = readMcpConfig(
|
|
6727
|
-
const integConfig = readIntegrationsConfig(
|
|
6935
|
+
const mcpConfig = readMcpConfig(path37.join(cwd, ".mcp.json"));
|
|
6936
|
+
const integConfig = readIntegrationsConfig(path37.join(cwd, "harness.config.json"));
|
|
6728
6937
|
const mcpServers = mcpConfig.mcpServers ?? {};
|
|
6729
6938
|
if (globalOpts.json) {
|
|
6730
6939
|
const entries = INTEGRATION_REGISTRY.map((i) => ({
|
|
@@ -6755,14 +6964,14 @@ async function runListIntegrations(globalOpts) {
|
|
|
6755
6964
|
process.exit(ExitCode.SUCCESS);
|
|
6756
6965
|
}
|
|
6757
6966
|
function createListIntegrationsCommand() {
|
|
6758
|
-
return new
|
|
6967
|
+
return new Command49("list").description("Show all MCP integrations with status").action(async (_opts, cmd) => runListIntegrations(cmd.optsWithGlobals()));
|
|
6759
6968
|
}
|
|
6760
6969
|
|
|
6761
6970
|
// src/commands/integrations/remove.ts
|
|
6762
6971
|
init_dist3();
|
|
6763
|
-
import { Command as
|
|
6972
|
+
import { Command as Command50 } from "commander";
|
|
6764
6973
|
import * as fs24 from "fs";
|
|
6765
|
-
import * as
|
|
6974
|
+
import * as path38 from "path";
|
|
6766
6975
|
function removeIntegration(cwd, name) {
|
|
6767
6976
|
const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
|
|
6768
6977
|
if (!def) {
|
|
@@ -6773,21 +6982,21 @@ function removeIntegration(cwd, name) {
|
|
|
6773
6982
|
)
|
|
6774
6983
|
);
|
|
6775
6984
|
}
|
|
6776
|
-
const mcpPath =
|
|
6985
|
+
const mcpPath = path38.join(cwd, ".mcp.json");
|
|
6777
6986
|
removeMcpEntry(mcpPath, def.name);
|
|
6778
|
-
const geminiDir =
|
|
6987
|
+
const geminiDir = path38.join(cwd, ".gemini");
|
|
6779
6988
|
if (fs24.existsSync(geminiDir)) {
|
|
6780
|
-
const geminiPath =
|
|
6989
|
+
const geminiPath = path38.join(geminiDir, "settings.json");
|
|
6781
6990
|
removeMcpEntry(geminiPath, def.name);
|
|
6782
6991
|
}
|
|
6783
|
-
const configPath =
|
|
6992
|
+
const configPath = path38.join(cwd, "harness.config.json");
|
|
6784
6993
|
const integConfig = readIntegrationsConfig(configPath);
|
|
6785
6994
|
integConfig.enabled = integConfig.enabled.filter((e) => e !== def.name);
|
|
6786
6995
|
writeIntegrationsConfig(configPath, integConfig);
|
|
6787
6996
|
return Ok(def.displayName);
|
|
6788
6997
|
}
|
|
6789
6998
|
function createRemoveIntegrationCommand() {
|
|
6790
|
-
return new
|
|
6999
|
+
return new Command50("remove").description("Remove an MCP integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
|
|
6791
7000
|
const globalOpts = cmd.optsWithGlobals();
|
|
6792
7001
|
const cwd = process.cwd();
|
|
6793
7002
|
const result = removeIntegration(cwd, name);
|
|
@@ -6809,8 +7018,8 @@ function createRemoveIntegrationCommand() {
|
|
|
6809
7018
|
|
|
6810
7019
|
// src/commands/integrations/dismiss.ts
|
|
6811
7020
|
init_dist3();
|
|
6812
|
-
import { Command as
|
|
6813
|
-
import * as
|
|
7021
|
+
import { Command as Command51 } from "commander";
|
|
7022
|
+
import * as path39 from "path";
|
|
6814
7023
|
function dismissIntegration(cwd, name) {
|
|
6815
7024
|
const def = INTEGRATION_REGISTRY.find((i) => i.name === name);
|
|
6816
7025
|
if (!def) {
|
|
@@ -6821,7 +7030,7 @@ function dismissIntegration(cwd, name) {
|
|
|
6821
7030
|
)
|
|
6822
7031
|
);
|
|
6823
7032
|
}
|
|
6824
|
-
const configPath =
|
|
7033
|
+
const configPath = path39.join(cwd, "harness.config.json");
|
|
6825
7034
|
const integConfig = readIntegrationsConfig(configPath);
|
|
6826
7035
|
if (!integConfig.dismissed.includes(def.name)) {
|
|
6827
7036
|
integConfig.dismissed.push(def.name);
|
|
@@ -6831,7 +7040,7 @@ function dismissIntegration(cwd, name) {
|
|
|
6831
7040
|
return Ok(def.displayName);
|
|
6832
7041
|
}
|
|
6833
7042
|
function createDismissIntegrationCommand() {
|
|
6834
|
-
return new
|
|
7043
|
+
return new Command51("dismiss").description("Suppress doctor recommendations for an integration").argument("<name>", "Integration name (e.g. perplexity, augment-code)").action(async (name, _opts, cmd) => {
|
|
6835
7044
|
const globalOpts = cmd.optsWithGlobals();
|
|
6836
7045
|
const cwd = process.cwd();
|
|
6837
7046
|
const result = dismissIntegration(cwd, name);
|
|
@@ -6855,7 +7064,7 @@ function createDismissIntegrationCommand() {
|
|
|
6855
7064
|
|
|
6856
7065
|
// src/commands/integrations/index.ts
|
|
6857
7066
|
function createIntegrationsCommand() {
|
|
6858
|
-
const command = new
|
|
7067
|
+
const command = new Command52("integrations").description(
|
|
6859
7068
|
"Manage MCP peer integrations (add, list, remove, dismiss)"
|
|
6860
7069
|
);
|
|
6861
7070
|
command.addCommand(createListIntegrationsCommand());
|
|
@@ -6866,9 +7075,9 @@ function createIntegrationsCommand() {
|
|
|
6866
7075
|
}
|
|
6867
7076
|
|
|
6868
7077
|
// src/commands/knowledge-craft.ts
|
|
6869
|
-
import { Command as
|
|
7078
|
+
import { Command as Command53 } from "commander";
|
|
6870
7079
|
function createKnowledgeCraftCommand() {
|
|
6871
|
-
return new
|
|
7080
|
+
return new Command53("knowledge-craft").description(
|
|
6872
7081
|
"LLM-judgment critique of knowledge-entry quality (docs/knowledge/, excluding decisions/). Fifth non-design craft-pipeline ceiling skill; 7 seed rubrics (load-bearing-fact, earns-graph-place, carries-forward-decision, \u2026). Per-file critique."
|
|
6873
7082
|
).option("-f, --files <files...>", "Optional file scope (overrides discovery)").option(
|
|
6874
7083
|
"--exclude-dirs <dirs...>",
|
|
@@ -6916,10 +7125,10 @@ function printResult3(result, mode, _formatter) {
|
|
|
6916
7125
|
list.push(f);
|
|
6917
7126
|
byFile.set(f.target.file, list);
|
|
6918
7127
|
}
|
|
6919
|
-
for (const [file,
|
|
7128
|
+
for (const [file, fs49] of byFile) {
|
|
6920
7129
|
console.log(`
|
|
6921
7130
|
${file}`);
|
|
6922
|
-
for (const f of
|
|
7131
|
+
for (const f of fs49) {
|
|
6923
7132
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.relative}`);
|
|
6924
7133
|
console.log(` ${f.message}`);
|
|
6925
7134
|
if (verbose) console.log(` source: ${f.cite.source}`);
|
|
@@ -6933,18 +7142,18 @@ ${file}`);
|
|
|
6933
7142
|
}
|
|
6934
7143
|
|
|
6935
7144
|
// src/commands/knowledge-pipeline.ts
|
|
6936
|
-
import { Command as
|
|
6937
|
-
import * as
|
|
7145
|
+
import { Command as Command54 } from "commander";
|
|
7146
|
+
import * as path40 from "path";
|
|
6938
7147
|
import chalk6 from "chalk";
|
|
6939
7148
|
function createKnowledgePipelineCommand() {
|
|
6940
|
-
return new
|
|
7149
|
+
return new Command54("knowledge-pipeline").description("Run knowledge extraction, drift detection, and gap analysis").option("--fix", "Enable convergence-based auto-remediation (default: detect-only)").option("--ci", "Non-interactive mode \u2014 apply safe fixes only, report everything else").option("--domain <name>", "Limit pipeline to a specific knowledge domain").option("--drift-check", "Exit 1 if unresolved drift exists (CI gate mode)").option("--analyze-images", "Enable vision model analysis of image files").option("--image-paths <paths>", "Comma-separated image file paths for analysis").option("--coverage", "Display per-domain coverage report").option("--check-contradictions", "Display cross-source contradiction report").action(async (opts, cmd) => {
|
|
6941
7150
|
const globalOpts = cmd.optsWithGlobals();
|
|
6942
7151
|
const projectDir = process.cwd();
|
|
6943
7152
|
try {
|
|
6944
|
-
const graphDir =
|
|
6945
|
-
const
|
|
6946
|
-
await
|
|
6947
|
-
const { GraphStore: GraphStore2, KnowledgePipelineRunner } = await import("./dist-
|
|
7153
|
+
const graphDir = path40.join(projectDir, ".harness", "graph");
|
|
7154
|
+
const fs49 = await import("fs/promises");
|
|
7155
|
+
await fs49.mkdir(graphDir, { recursive: true });
|
|
7156
|
+
const { GraphStore: GraphStore2, KnowledgePipelineRunner } = await import("./dist-OWHMNL4W.js");
|
|
6948
7157
|
const store = new GraphStore2();
|
|
6949
7158
|
try {
|
|
6950
7159
|
await store.load(graphDir);
|
|
@@ -7000,6 +7209,7 @@ function createKnowledgePipelineCommand() {
|
|
|
7000
7209
|
iterations: result.iterations,
|
|
7001
7210
|
findings: result.findings,
|
|
7002
7211
|
extraction: result.extraction,
|
|
7212
|
+
errors: result.errors,
|
|
7003
7213
|
gaps: {
|
|
7004
7214
|
domains: result.gaps.domains.length,
|
|
7005
7215
|
totalEntries: result.gaps.totalEntries,
|
|
@@ -7040,7 +7250,7 @@ function createKnowledgePipelineCommand() {
|
|
|
7040
7250
|
` Findings: ${result.findings.new} new, ${result.findings.stale} stale, ${result.findings.drifted} drifted, ${result.findings.contradicting} contradicting`
|
|
7041
7251
|
);
|
|
7042
7252
|
console.log(
|
|
7043
|
-
` Extraction: ${result.extraction.codeSignals} code signals, ${result.extraction.diagrams} diagrams, ${result.extraction.linkerFacts} linker facts, ${result.extraction.businessKnowledge} business knowledge, ${result.extraction.images} images`
|
|
7253
|
+
` 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`
|
|
7044
7254
|
);
|
|
7045
7255
|
console.log(
|
|
7046
7256
|
` Gaps: ${result.gaps.domains.length} domains \u2014 ${result.gaps.totalEntries} documented / ${result.gaps.totalExtracted} extracted / ${result.gaps.totalGaps} undocumented`
|
|
@@ -7051,6 +7261,13 @@ function createKnowledgePipelineCommand() {
|
|
|
7051
7261
|
if (result.remediations.length > 0) {
|
|
7052
7262
|
console.log(` Remediations: ${result.remediations.length} applied`);
|
|
7053
7263
|
}
|
|
7264
|
+
if (result.errors.length > 0) {
|
|
7265
|
+
console.warn("");
|
|
7266
|
+
console.warn(` ${result.errors.length} ingestion warning(s):`);
|
|
7267
|
+
for (const err of result.errors) {
|
|
7268
|
+
console.warn(` - ${err}`);
|
|
7269
|
+
}
|
|
7270
|
+
}
|
|
7054
7271
|
if (result.materialization) {
|
|
7055
7272
|
const mat = result.materialization;
|
|
7056
7273
|
console.log(
|
|
@@ -7101,14 +7318,14 @@ function createKnowledgePipelineCommand() {
|
|
|
7101
7318
|
}
|
|
7102
7319
|
|
|
7103
7320
|
// src/commands/learnings/index.ts
|
|
7104
|
-
import { Command as
|
|
7321
|
+
import { Command as Command56 } from "commander";
|
|
7105
7322
|
|
|
7106
7323
|
// src/commands/learnings/prune.ts
|
|
7107
7324
|
init_dist3();
|
|
7108
|
-
import { Command as
|
|
7109
|
-
import * as
|
|
7325
|
+
import { Command as Command55 } from "commander";
|
|
7326
|
+
import * as path41 from "path";
|
|
7110
7327
|
async function handlePrune(opts) {
|
|
7111
|
-
const projectPath =
|
|
7328
|
+
const projectPath = path41.resolve(opts.path);
|
|
7112
7329
|
const result = await pruneLearnings(projectPath, opts.stream);
|
|
7113
7330
|
if (!result.ok) {
|
|
7114
7331
|
logger.error(result.error.message);
|
|
@@ -7147,23 +7364,23 @@ function printPatternProposals(patterns) {
|
|
|
7147
7364
|
);
|
|
7148
7365
|
}
|
|
7149
7366
|
function createPruneCommand() {
|
|
7150
|
-
return new
|
|
7367
|
+
return new Command55("prune").description(
|
|
7151
7368
|
"Analyze global learnings for patterns, present improvement proposals, and archive old entries"
|
|
7152
7369
|
).option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(handlePrune);
|
|
7153
7370
|
}
|
|
7154
7371
|
|
|
7155
7372
|
// src/commands/learnings/index.ts
|
|
7156
7373
|
function createLearningsCommand() {
|
|
7157
|
-
const command = new
|
|
7374
|
+
const command = new Command56("learnings").description("Learnings management commands");
|
|
7158
7375
|
command.addCommand(createPruneCommand());
|
|
7159
7376
|
return command;
|
|
7160
7377
|
}
|
|
7161
7378
|
|
|
7162
7379
|
// src/commands/linter/index.ts
|
|
7163
|
-
import { Command as
|
|
7380
|
+
import { Command as Command59 } from "commander";
|
|
7164
7381
|
|
|
7165
7382
|
// src/commands/linter/generate.ts
|
|
7166
|
-
import { Command as
|
|
7383
|
+
import { Command as Command57 } from "commander";
|
|
7167
7384
|
function formatGenerateError(e) {
|
|
7168
7385
|
switch (e.type) {
|
|
7169
7386
|
case "parse":
|
|
@@ -7207,7 +7424,7 @@ function handleSuccess(result, useJson) {
|
|
|
7207
7424
|
Generated ${result.rulesGenerated.length} rules to ${result.outputDir}`);
|
|
7208
7425
|
}
|
|
7209
7426
|
function createGenerateCommand2() {
|
|
7210
|
-
return new
|
|
7427
|
+
return new Command57("generate").description("Generate ESLint rules from harness-linter.yml").option("-c, --config <path>", "Path to harness-linter.yml", "./harness-linter.yml").option("-o, --output <dir>", "Override output directory").option("--clean", "Remove existing files before generating").option("--dry-run", "Preview without writing files").option("--json", "Output as JSON").option("--verbose", "Show detailed output").action(async (options) => {
|
|
7211
7428
|
try {
|
|
7212
7429
|
if (options.verbose) logger.info(`Parsing config: ${options.config}`);
|
|
7213
7430
|
const result = await generate({
|
|
@@ -7229,9 +7446,9 @@ function createGenerateCommand2() {
|
|
|
7229
7446
|
}
|
|
7230
7447
|
|
|
7231
7448
|
// src/commands/linter/validate.ts
|
|
7232
|
-
import { Command as
|
|
7449
|
+
import { Command as Command58 } from "commander";
|
|
7233
7450
|
function createValidateCommand() {
|
|
7234
|
-
return new
|
|
7451
|
+
return new Command58("validate").description("Validate harness-linter.yml config").option("-c, --config <path>", "Path to harness-linter.yml", "./harness-linter.yml").option("--json", "Output as JSON").action(async (options) => {
|
|
7235
7452
|
try {
|
|
7236
7453
|
const result = await validate({ configPath: options.config });
|
|
7237
7454
|
if (options.json) {
|
|
@@ -7250,7 +7467,7 @@ function createValidateCommand() {
|
|
|
7250
7467
|
|
|
7251
7468
|
// src/commands/linter/index.ts
|
|
7252
7469
|
function createLinterCommand() {
|
|
7253
|
-
const linter = new
|
|
7470
|
+
const linter = new Command59("linter").description(
|
|
7254
7471
|
"Generate and validate ESLint rules from YAML config"
|
|
7255
7472
|
);
|
|
7256
7473
|
linter.addCommand(createGenerateCommand2());
|
|
@@ -7259,8 +7476,8 @@ function createLinterCommand() {
|
|
|
7259
7476
|
}
|
|
7260
7477
|
|
|
7261
7478
|
// src/commands/maintenance.ts
|
|
7262
|
-
import { Command as
|
|
7263
|
-
import * as
|
|
7479
|
+
import { Command as Command60 } from "commander";
|
|
7480
|
+
import * as path42 from "path";
|
|
7264
7481
|
import * as fs25 from "fs";
|
|
7265
7482
|
import chalk7 from "chalk";
|
|
7266
7483
|
import {
|
|
@@ -7269,7 +7486,7 @@ import {
|
|
|
7269
7486
|
WorkflowLoader
|
|
7270
7487
|
} from "@harness-engineering/orchestrator";
|
|
7271
7488
|
async function loadMaintenanceConfig(cwd) {
|
|
7272
|
-
const workflowPath =
|
|
7489
|
+
const workflowPath = path42.join(cwd, "harness.orchestrator.md");
|
|
7273
7490
|
if (!fs25.existsSync(workflowPath)) return null;
|
|
7274
7491
|
const loader = new WorkflowLoader();
|
|
7275
7492
|
const result = await loader.loadWorkflow(workflowPath);
|
|
@@ -7351,11 +7568,11 @@ function printListTable(rows) {
|
|
|
7351
7568
|
}
|
|
7352
7569
|
}
|
|
7353
7570
|
function createMaintenanceCommand() {
|
|
7354
|
-
const command = new
|
|
7571
|
+
const command = new Command60("maintenance").description(
|
|
7355
7572
|
"Inspect built-in + custom maintenance tasks and their persisted outputs"
|
|
7356
7573
|
);
|
|
7357
7574
|
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) => {
|
|
7358
|
-
const cwd =
|
|
7575
|
+
const cwd = path42.resolve(opts.path);
|
|
7359
7576
|
const config = await loadMaintenanceConfig(cwd);
|
|
7360
7577
|
const tasks = mergeResolvedTasks(config);
|
|
7361
7578
|
const rows = tasks.map((t) => ({
|
|
@@ -7382,10 +7599,10 @@ async function runShow(taskId, opts) {
|
|
|
7382
7599
|
console.error(`Invalid task id '${taskId}' (must match ^[a-z0-9][a-z0-9-]*$).`);
|
|
7383
7600
|
process.exit(ExitCode.ERROR);
|
|
7384
7601
|
}
|
|
7385
|
-
const cwd =
|
|
7602
|
+
const cwd = path42.resolve(opts.path ?? ".");
|
|
7386
7603
|
const limit = Math.max(1, parseInt(opts.limit ?? "5", 10) || 5);
|
|
7387
7604
|
const store = new TaskOutputStore({
|
|
7388
|
-
rootDir:
|
|
7605
|
+
rootDir: path42.join(cwd, ".harness", "maintenance")
|
|
7389
7606
|
});
|
|
7390
7607
|
const entries = await store.list(taskId, limit, 0);
|
|
7391
7608
|
renderShow(taskId, entries, Boolean(opts.json));
|
|
@@ -7419,7 +7636,7 @@ function describeOrigin(origin) {
|
|
|
7419
7636
|
}
|
|
7420
7637
|
|
|
7421
7638
|
// src/commands/mcp.ts
|
|
7422
|
-
import { Command as
|
|
7639
|
+
import { Command as Command61 } from "commander";
|
|
7423
7640
|
var VALID_TIERS = /* @__PURE__ */ new Set(["core", "standard", "full"]);
|
|
7424
7641
|
function parseTier(value) {
|
|
7425
7642
|
const lower = value.toLowerCase();
|
|
@@ -7436,7 +7653,7 @@ function parseBudget(value) {
|
|
|
7436
7653
|
return n;
|
|
7437
7654
|
}
|
|
7438
7655
|
function createMcpCommand() {
|
|
7439
|
-
return new
|
|
7656
|
+
return new Command61("mcp").description("Start the MCP (Model Context Protocol) server on stdio").option("--tools <tools...>", "Only register the specified tools (used by Cursor integration)").option(
|
|
7440
7657
|
"--tier <core|standard|full>",
|
|
7441
7658
|
"Load a preset tool tier instead of all tools",
|
|
7442
7659
|
parseTier
|
|
@@ -7446,8 +7663,8 @@ function createMcpCommand() {
|
|
|
7446
7663
|
parseBudget
|
|
7447
7664
|
).action(async (opts) => {
|
|
7448
7665
|
const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
|
|
7449
|
-
import("./mcp-
|
|
7450
|
-
import("./tool-tiers-
|
|
7666
|
+
import("./mcp-RK3SFVYB.js"),
|
|
7667
|
+
import("./tool-tiers-OKK5FE57.js")
|
|
7451
7668
|
]);
|
|
7452
7669
|
if (opts.tools && opts.tools.length > 0) {
|
|
7453
7670
|
await startServer2(opts.tools);
|
|
@@ -7468,9 +7685,9 @@ function createMcpCommand() {
|
|
|
7468
7685
|
|
|
7469
7686
|
// src/commands/mcp-guard.ts
|
|
7470
7687
|
init_dist3();
|
|
7471
|
-
import { Command as
|
|
7688
|
+
import { Command as Command62 } from "commander";
|
|
7472
7689
|
import * as fs26 from "fs";
|
|
7473
|
-
import * as
|
|
7690
|
+
import * as path43 from "path";
|
|
7474
7691
|
import chalk8 from "chalk";
|
|
7475
7692
|
function extractNpmPackages(config) {
|
|
7476
7693
|
const out = [];
|
|
@@ -7509,7 +7726,7 @@ function parseNpmSpec(spec) {
|
|
|
7509
7726
|
}
|
|
7510
7727
|
async function runMcpGuardCheck(options = {}) {
|
|
7511
7728
|
const cwd = options.cwd ?? process.cwd();
|
|
7512
|
-
const configPath = options.configPath ??
|
|
7729
|
+
const configPath = options.configPath ?? path43.join(cwd, ".mcp.json");
|
|
7513
7730
|
if (!fs26.existsSync(configPath)) {
|
|
7514
7731
|
return { ok: true, checked: [] };
|
|
7515
7732
|
}
|
|
@@ -7520,7 +7737,7 @@ async function runMcpGuardCheck(options = {}) {
|
|
|
7520
7737
|
return { ok: true, checked: [] };
|
|
7521
7738
|
}
|
|
7522
7739
|
const pkgs = extractNpmPackages(config);
|
|
7523
|
-
const cacheDir =
|
|
7740
|
+
const cacheDir = path43.join(cwd, ".harness", "cache", "osv");
|
|
7524
7741
|
const warnLogger = {
|
|
7525
7742
|
warn: (m, ctx) => logger.warn(ctx ? `${m} ${JSON.stringify(ctx)}` : m)
|
|
7526
7743
|
};
|
|
@@ -7576,12 +7793,12 @@ function formatHuman(result) {
|
|
|
7576
7793
|
}
|
|
7577
7794
|
}
|
|
7578
7795
|
function createMcpGuardCommand() {
|
|
7579
|
-
const command = new
|
|
7796
|
+
const command = new Command62("mcp-guard").description("Pre-launch OSV malware guard for MCP/npx packages").addHelpText(
|
|
7580
7797
|
"after",
|
|
7581
7798
|
'\nUse "harness mcp-guard check" before launching MCP servers to block known malicious packages.'
|
|
7582
7799
|
);
|
|
7583
7800
|
command.command("check").description("Check every MCP/npx package in .mcp.json against OSV.dev advisories").option("--strict", "Fail closed on network errors (default: fail-open)", false).option("--json", "Emit machine-readable JSON", false).option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
7584
|
-
const cwd =
|
|
7801
|
+
const cwd = path43.resolve(opts.path);
|
|
7585
7802
|
const result = await runMcpGuardCheck({ cwd, strict: opts.strict, json: opts.json });
|
|
7586
7803
|
if (opts.json) {
|
|
7587
7804
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -7592,8 +7809,8 @@ function createMcpGuardCommand() {
|
|
|
7592
7809
|
process.exit(result.ok ? ExitCode.SUCCESS : 2);
|
|
7593
7810
|
});
|
|
7594
7811
|
command.command("cache").description("Manage the on-disk OSV advisory cache").command("clear").option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
7595
|
-
const cwd =
|
|
7596
|
-
const cacheDir =
|
|
7812
|
+
const cwd = path43.resolve(opts.path);
|
|
7813
|
+
const cacheDir = path43.join(cwd, ".harness", "cache", "osv");
|
|
7597
7814
|
try {
|
|
7598
7815
|
fs26.rmSync(cacheDir, { recursive: true, force: true });
|
|
7599
7816
|
console.log(`Cleared ${cacheDir}`);
|
|
@@ -7607,12 +7824,12 @@ function createMcpGuardCommand() {
|
|
|
7607
7824
|
}
|
|
7608
7825
|
|
|
7609
7826
|
// src/commands/migrate.ts
|
|
7610
|
-
import { Command as
|
|
7827
|
+
import { Command as Command64 } from "commander";
|
|
7611
7828
|
|
|
7612
7829
|
// src/commands/migrate-backends.ts
|
|
7613
|
-
import { Command as
|
|
7830
|
+
import { Command as Command63 } from "commander";
|
|
7614
7831
|
import * as fs27 from "fs";
|
|
7615
|
-
import * as
|
|
7832
|
+
import * as path44 from "path";
|
|
7616
7833
|
import chalk9 from "chalk";
|
|
7617
7834
|
import { parse as parseYaml2 } from "yaml";
|
|
7618
7835
|
async function runMigrateBackends(opts = {}) {
|
|
@@ -7634,7 +7851,7 @@ async function runMigrateBackends(opts = {}) {
|
|
|
7634
7851
|
};
|
|
7635
7852
|
}
|
|
7636
7853
|
const routing = readRoutingFromOrchestratorMd(mdPath);
|
|
7637
|
-
const jsonPath =
|
|
7854
|
+
const jsonPath = path44.join(path44.dirname(mdPath), "harness.config.json");
|
|
7638
7855
|
if (!fs27.existsSync(jsonPath)) {
|
|
7639
7856
|
return {
|
|
7640
7857
|
status: "error",
|
|
@@ -7727,7 +7944,7 @@ function readRoutingFromOrchestratorMd(mdPath) {
|
|
|
7727
7944
|
return routing ?? null;
|
|
7728
7945
|
}
|
|
7729
7946
|
function createBackendsSubcommand() {
|
|
7730
|
-
return new
|
|
7947
|
+
return new Command63("backends").description(
|
|
7731
7948
|
"Copy agent.backends (and routing) from harness.orchestrator.md into harness.config.json."
|
|
7732
7949
|
).option("--dry-run", "Show what would change without writing", false).option("--force", "Overwrite existing agent.backends in harness.config.json", false).action(async (options) => {
|
|
7733
7950
|
const result = await runMigrateBackends({
|
|
@@ -7747,7 +7964,7 @@ function createBackendsSubcommand() {
|
|
|
7747
7964
|
init_dist3();
|
|
7748
7965
|
init_dist2();
|
|
7749
7966
|
import * as fs28 from "fs";
|
|
7750
|
-
import * as
|
|
7967
|
+
import * as path45 from "path";
|
|
7751
7968
|
import * as readline from "readline";
|
|
7752
7969
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
7753
7970
|
import chalk10 from "chalk";
|
|
@@ -7779,7 +7996,7 @@ function walk(dir, predicate) {
|
|
|
7779
7996
|
const out = [];
|
|
7780
7997
|
for (const entry of fs28.readdirSync(dir, { withFileTypes: true })) {
|
|
7781
7998
|
if (DEFAULT_SKIP_DIRS.has(entry.name)) continue;
|
|
7782
|
-
const full =
|
|
7999
|
+
const full = path45.join(dir, entry.name);
|
|
7783
8000
|
if (entry.isDirectory()) out.push(...walk(full, predicate));
|
|
7784
8001
|
else if (predicate(full)) out.push(full);
|
|
7785
8002
|
}
|
|
@@ -7791,11 +8008,11 @@ function extractAutopilotTopic(state) {
|
|
|
7791
8008
|
}
|
|
7792
8009
|
function buildAutopilotMap(cwd) {
|
|
7793
8010
|
const map = /* @__PURE__ */ new Map();
|
|
7794
|
-
const sessionsDir =
|
|
8011
|
+
const sessionsDir = path45.join(cwd, ".harness", "sessions");
|
|
7795
8012
|
if (!fs28.existsSync(sessionsDir)) return map;
|
|
7796
8013
|
for (const session of listDirs(sessionsDir)) {
|
|
7797
8014
|
if (!session.startsWith("changes--")) continue;
|
|
7798
|
-
const state = readJson2(
|
|
8015
|
+
const state = readJson2(path45.join(sessionsDir, session, "autopilot-state.json"));
|
|
7799
8016
|
if (!state) continue;
|
|
7800
8017
|
const topic = extractAutopilotTopic(state);
|
|
7801
8018
|
if (!topic) continue;
|
|
@@ -7833,7 +8050,7 @@ function extractTopicFromFilename(filename, proposalTopics) {
|
|
|
7833
8050
|
return best;
|
|
7834
8051
|
}
|
|
7835
8052
|
function resolveDocsDir(cwd) {
|
|
7836
|
-
const configPath =
|
|
8053
|
+
const configPath = path45.join(cwd, "harness.config.json");
|
|
7837
8054
|
const configResult = fs28.existsSync(configPath) ? resolveConfig(configPath) : null;
|
|
7838
8055
|
let docsDir;
|
|
7839
8056
|
if (configResult?.ok) {
|
|
@@ -7846,16 +8063,16 @@ function resolveDocsDir(cwd) {
|
|
|
7846
8063
|
} else {
|
|
7847
8064
|
docsDir = "./docs";
|
|
7848
8065
|
}
|
|
7849
|
-
const docsAbs =
|
|
7850
|
-
const relRaw =
|
|
8066
|
+
const docsAbs = path45.resolve(cwd, docsDir);
|
|
8067
|
+
const relRaw = path45.relative(cwd, docsAbs).replaceAll("\\", "/");
|
|
7851
8068
|
return relRaw === "" ? "docs" : relRaw;
|
|
7852
8069
|
}
|
|
7853
8070
|
function collectAdrMoves(cwd, docsRel) {
|
|
7854
8071
|
const moves = [];
|
|
7855
|
-
const legacyAdrDir =
|
|
8072
|
+
const legacyAdrDir = path45.join(cwd, ".harness", "architecture");
|
|
7856
8073
|
if (!fs28.existsSync(legacyAdrDir)) return moves;
|
|
7857
8074
|
for (const topic of listDirs(legacyAdrDir)) {
|
|
7858
|
-
const topicSrc =
|
|
8075
|
+
const topicSrc = path45.join(legacyAdrDir, topic);
|
|
7859
8076
|
for (const file of listFiles(topicSrc, (n) => n.endsWith(".md"))) {
|
|
7860
8077
|
moves.push({
|
|
7861
8078
|
src: `.harness/architecture/${topic}/${file}`,
|
|
@@ -7867,9 +8084,9 @@ function collectAdrMoves(cwd, docsRel) {
|
|
|
7867
8084
|
}
|
|
7868
8085
|
function collectProposalTopics(cwd) {
|
|
7869
8086
|
const topics = /* @__PURE__ */ new Set();
|
|
7870
|
-
const changesDir =
|
|
8087
|
+
const changesDir = path45.join(cwd, "docs", "changes");
|
|
7871
8088
|
for (const topic of listDirs(changesDir)) {
|
|
7872
|
-
if (fs28.existsSync(
|
|
8089
|
+
if (fs28.existsSync(path45.join(changesDir, topic, "proposal.md"))) {
|
|
7873
8090
|
topics.add(topic);
|
|
7874
8091
|
}
|
|
7875
8092
|
}
|
|
@@ -7896,12 +8113,12 @@ async function buildPlan(opts) {
|
|
|
7896
8113
|
orphanPlans: [],
|
|
7897
8114
|
proposalTopics: collectProposalTopics(cwd)
|
|
7898
8115
|
};
|
|
7899
|
-
const legacyPlanDir =
|
|
8116
|
+
const legacyPlanDir = path45.join(cwd, "docs", "plans");
|
|
7900
8117
|
if (!fs28.existsSync(legacyPlanDir)) return plan;
|
|
7901
8118
|
const autopilotMap = buildAutopilotMap(cwd);
|
|
7902
8119
|
for (const file of listFiles(legacyPlanDir, (n) => n.endsWith(".md") && n !== "index.md")) {
|
|
7903
8120
|
const planRel = `docs/plans/${file}`;
|
|
7904
|
-
const planAbs =
|
|
8121
|
+
const planAbs = path45.join(legacyPlanDir, file);
|
|
7905
8122
|
const subdir = file.includes("VERIFICATION") ? "verifications" : "plans";
|
|
7906
8123
|
const classified = classifyPlan(planRel, planAbs, file, autopilotMap, plan.proposalTopics);
|
|
7907
8124
|
if (classified) {
|
|
@@ -7919,8 +8136,8 @@ async function buildPlan(opts) {
|
|
|
7919
8136
|
return plan;
|
|
7920
8137
|
}
|
|
7921
8138
|
function gitMv(cwd, src, dest, useGit) {
|
|
7922
|
-
const destAbs =
|
|
7923
|
-
fs28.mkdirSync(
|
|
8139
|
+
const destAbs = path45.join(cwd, dest);
|
|
8140
|
+
fs28.mkdirSync(path45.dirname(destAbs), { recursive: true });
|
|
7924
8141
|
if (useGit) {
|
|
7925
8142
|
try {
|
|
7926
8143
|
execFileSync4("git", ["mv", src, dest], { cwd, stdio: "pipe" });
|
|
@@ -7928,7 +8145,7 @@ function gitMv(cwd, src, dest, useGit) {
|
|
|
7928
8145
|
} catch {
|
|
7929
8146
|
}
|
|
7930
8147
|
}
|
|
7931
|
-
fs28.renameSync(
|
|
8148
|
+
fs28.renameSync(path45.join(cwd, src), destAbs);
|
|
7932
8149
|
}
|
|
7933
8150
|
function executeMigration(plan) {
|
|
7934
8151
|
let moved = 0;
|
|
@@ -7960,8 +8177,8 @@ function updateReferences(plan) {
|
|
|
7960
8177
|
let files = 0;
|
|
7961
8178
|
let replacements = 0;
|
|
7962
8179
|
const targets = [
|
|
7963
|
-
...walk(
|
|
7964
|
-
...walk(
|
|
8180
|
+
...walk(path45.join(plan.cwd, "docs"), (p) => p.endsWith(".md")),
|
|
8181
|
+
...walk(path45.join(plan.cwd, ".harness", "sessions"), (p) => p.endsWith(".json"))
|
|
7965
8182
|
];
|
|
7966
8183
|
for (const target of targets) {
|
|
7967
8184
|
let text;
|
|
@@ -7988,7 +8205,7 @@ function updateReferences(plan) {
|
|
|
7988
8205
|
}
|
|
7989
8206
|
async function prompt(question, def = "n") {
|
|
7990
8207
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
7991
|
-
const answer = await new Promise((
|
|
8208
|
+
const answer = await new Promise((resolve47) => rl.question(question, resolve47));
|
|
7992
8209
|
rl.close();
|
|
7993
8210
|
return (answer.trim() || def).toLowerCase();
|
|
7994
8211
|
}
|
|
@@ -8038,7 +8255,7 @@ async function resolveOrphans(plan, opts) {
|
|
|
8038
8255
|
}
|
|
8039
8256
|
function bucketOrphans(plan, topic) {
|
|
8040
8257
|
for (const src of plan.orphanPlans) {
|
|
8041
|
-
const file =
|
|
8258
|
+
const file = path45.basename(src);
|
|
8042
8259
|
const isVerification = file.includes("VERIFICATION");
|
|
8043
8260
|
const subdir = isVerification ? "verifications" : "plans";
|
|
8044
8261
|
plan.planMoves.push({
|
|
@@ -8080,9 +8297,9 @@ function summarize(plan) {
|
|
|
8080
8297
|
console.log("");
|
|
8081
8298
|
}
|
|
8082
8299
|
async function detectLegacyArtifacts(cwd) {
|
|
8083
|
-
const adrLegacyDir =
|
|
8300
|
+
const adrLegacyDir = path45.join(cwd, ".harness", "architecture");
|
|
8084
8301
|
const adrLegacy = fs28.existsSync(adrLegacyDir) && listDirs(adrLegacyDir).length > 0;
|
|
8085
|
-
const planLegacyDir =
|
|
8302
|
+
const planLegacyDir = path45.join(cwd, "docs", "plans");
|
|
8086
8303
|
const planLegacy = fs28.existsSync(planLegacyDir) && listFiles(planLegacyDir, (n) => n.endsWith(".md") && n !== "index.md").length > 0;
|
|
8087
8304
|
return { adrLegacy, planLegacy };
|
|
8088
8305
|
}
|
|
@@ -8151,7 +8368,7 @@ async function runMigrate(opts) {
|
|
|
8151
8368
|
});
|
|
8152
8369
|
}
|
|
8153
8370
|
function createMigrateCommand() {
|
|
8154
|
-
const cmd = new
|
|
8371
|
+
const cmd = new Command64("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(
|
|
8155
8372
|
"--skip-references",
|
|
8156
8373
|
"Do not update path references in docs/sessions after moves",
|
|
8157
8374
|
false
|
|
@@ -8175,7 +8392,7 @@ function createMigrateCommand() {
|
|
|
8175
8392
|
}
|
|
8176
8393
|
|
|
8177
8394
|
// src/commands/models.ts
|
|
8178
|
-
import { Command as
|
|
8395
|
+
import { Command as Command65 } from "commander";
|
|
8179
8396
|
import { defaultFetchModels } from "@harness-engineering/orchestrator";
|
|
8180
8397
|
function errorResult(message, partial) {
|
|
8181
8398
|
return {
|
|
@@ -8293,7 +8510,7 @@ function formatProbe(result) {
|
|
|
8293
8510
|
return lines.join("\n");
|
|
8294
8511
|
}
|
|
8295
8512
|
function createModelsCommand() {
|
|
8296
|
-
const cmd = new
|
|
8513
|
+
const cmd = new Command65("models").description(
|
|
8297
8514
|
"Inspect and manage local LLM backends. Currently ships `probe`; LMLM phases add status/suggest/pool/proposals."
|
|
8298
8515
|
);
|
|
8299
8516
|
cmd.command("probe").description(
|
|
@@ -8319,9 +8536,9 @@ function createModelsCommand() {
|
|
|
8319
8536
|
}
|
|
8320
8537
|
|
|
8321
8538
|
// src/commands/naming-craft.ts
|
|
8322
|
-
import { Command as
|
|
8539
|
+
import { Command as Command66 } from "commander";
|
|
8323
8540
|
function createNamingCraftCommand() {
|
|
8324
|
-
return new
|
|
8541
|
+
return new Command66("naming-craft").description(
|
|
8325
8542
|
"LLM-judgment critique of identifier names (variables, functions, types, files). First craft-pipeline ceiling skill; uses curated rubric catalog from Martin/Beck/Karlton."
|
|
8326
8543
|
).option("-f, --files <files...>", "Optional file/glob scope").option(
|
|
8327
8544
|
"-k, --kinds <kinds...>",
|
|
@@ -8371,10 +8588,10 @@ function printResult4(result, mode, _formatter) {
|
|
|
8371
8588
|
list.push(f);
|
|
8372
8589
|
byFile.set(f.target.file, list);
|
|
8373
8590
|
}
|
|
8374
|
-
for (const [file,
|
|
8591
|
+
for (const [file, fs49] of byFile) {
|
|
8375
8592
|
console.log(`
|
|
8376
8593
|
${file}`);
|
|
8377
|
-
for (const f of
|
|
8594
|
+
for (const f of fs49) {
|
|
8378
8595
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
8379
8596
|
console.log(
|
|
8380
8597
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.kind} ${f.target.identifier}${line}`
|
|
@@ -8394,13 +8611,13 @@ ${file}`);
|
|
|
8394
8611
|
}
|
|
8395
8612
|
|
|
8396
8613
|
// src/commands/notifications/index.ts
|
|
8397
|
-
import { Command as
|
|
8614
|
+
import { Command as Command68 } from "commander";
|
|
8398
8615
|
|
|
8399
8616
|
// src/commands/notifications/test.ts
|
|
8400
8617
|
init_dist3();
|
|
8401
|
-
import { Command as
|
|
8618
|
+
import { Command as Command67 } from "commander";
|
|
8402
8619
|
import { randomBytes } from "crypto";
|
|
8403
|
-
import { resolve as
|
|
8620
|
+
import { resolve as resolve29 } from "path";
|
|
8404
8621
|
import { SinkRegistry, SinkConfigError, wrapAsEnvelope } from "@harness-engineering/orchestrator";
|
|
8405
8622
|
function makeTestEvent(message) {
|
|
8406
8623
|
return {
|
|
@@ -8450,9 +8667,9 @@ async function runNotificationsTest(sinkId, opts, projectRoot2 = process.cwd())
|
|
|
8450
8667
|
return { ok: false, error: result.error };
|
|
8451
8668
|
}
|
|
8452
8669
|
function createNotificationsTestSubcommand() {
|
|
8453
|
-
return new
|
|
8670
|
+
return new Command67("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) => {
|
|
8454
8671
|
const globalOpts = cmd.optsWithGlobals();
|
|
8455
|
-
const projectRoot2 = typeof globalOpts["cwd"] === "string" ?
|
|
8672
|
+
const projectRoot2 = typeof globalOpts["cwd"] === "string" ? resolve29(globalOpts["cwd"]) : process.cwd();
|
|
8456
8673
|
const result = await runNotificationsTest(sinkId, opts, projectRoot2);
|
|
8457
8674
|
if (globalOpts["json"]) {
|
|
8458
8675
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -8467,7 +8684,7 @@ function createNotificationsTestSubcommand() {
|
|
|
8467
8684
|
|
|
8468
8685
|
// src/commands/notifications/index.ts
|
|
8469
8686
|
function createNotificationsCommand() {
|
|
8470
|
-
const cmd = new
|
|
8687
|
+
const cmd = new Command68("notifications").description(
|
|
8471
8688
|
"Manage notification sinks (Slack and others)"
|
|
8472
8689
|
);
|
|
8473
8690
|
cmd.addCommand(createNotificationsTestSubcommand());
|
|
@@ -8475,13 +8692,13 @@ function createNotificationsCommand() {
|
|
|
8475
8692
|
}
|
|
8476
8693
|
|
|
8477
8694
|
// src/commands/orchestrator.ts
|
|
8478
|
-
import { Command as
|
|
8479
|
-
import * as
|
|
8695
|
+
import { Command as Command69 } from "commander";
|
|
8696
|
+
import * as path46 from "path";
|
|
8480
8697
|
import { Orchestrator, WorkflowLoader as WorkflowLoader2, launchTUI } from "@harness-engineering/orchestrator";
|
|
8481
8698
|
function createOrchestratorCommand() {
|
|
8482
|
-
const orchestrator = new
|
|
8699
|
+
const orchestrator = new Command69("orchestrator");
|
|
8483
8700
|
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) => {
|
|
8484
|
-
const workflowPath =
|
|
8701
|
+
const workflowPath = path46.resolve(process.cwd(), opts.workflow);
|
|
8485
8702
|
const loader = new WorkflowLoader2();
|
|
8486
8703
|
const result = await loader.loadWorkflow(workflowPath);
|
|
8487
8704
|
if (!result.ok) {
|
|
@@ -8515,13 +8732,13 @@ function createOrchestratorCommand() {
|
|
|
8515
8732
|
|
|
8516
8733
|
// src/commands/perf.ts
|
|
8517
8734
|
init_dist3();
|
|
8518
|
-
import { Command as
|
|
8519
|
-
import * as
|
|
8735
|
+
import { Command as Command70 } from "commander";
|
|
8736
|
+
import * as path47 from "path";
|
|
8520
8737
|
function registerBenchCommand(perf) {
|
|
8521
8738
|
perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
|
|
8522
8739
|
const globalOpts = cmd.optsWithGlobals();
|
|
8523
8740
|
const cwd = process.cwd();
|
|
8524
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
8741
|
+
const { BenchmarkRunner } = await import("./dist-LHINSVK4.js");
|
|
8525
8742
|
const runner = new BenchmarkRunner();
|
|
8526
8743
|
const benchFiles = runner.discover(cwd, glob2);
|
|
8527
8744
|
if (benchFiles.length === 0) {
|
|
@@ -8585,7 +8802,7 @@ async function runBaselinesShow(globalOpts) {
|
|
|
8585
8802
|
}
|
|
8586
8803
|
async function runBaselinesUpdate(globalOpts) {
|
|
8587
8804
|
const cwd = process.cwd();
|
|
8588
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
8805
|
+
const { BenchmarkRunner } = await import("./dist-LHINSVK4.js");
|
|
8589
8806
|
const runner = new BenchmarkRunner();
|
|
8590
8807
|
const manager = new BaselineManager(cwd);
|
|
8591
8808
|
logger.info("Running benchmarks to update baselines...");
|
|
@@ -8626,9 +8843,9 @@ function registerReportCommand(perf) {
|
|
|
8626
8843
|
perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
|
|
8627
8844
|
const globalOpts = cmd.optsWithGlobals();
|
|
8628
8845
|
const cwd = process.cwd();
|
|
8629
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-
|
|
8846
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-LHINSVK4.js");
|
|
8630
8847
|
const analyzer = new EntropyAnalyzer2({
|
|
8631
|
-
rootDir:
|
|
8848
|
+
rootDir: path47.resolve(cwd),
|
|
8632
8849
|
analyze: { complexity: true, coupling: true }
|
|
8633
8850
|
});
|
|
8634
8851
|
const result = await analyzer.analyze();
|
|
@@ -8687,7 +8904,7 @@ function registerCriticalPathsCommand(perf) {
|
|
|
8687
8904
|
});
|
|
8688
8905
|
}
|
|
8689
8906
|
function createPerfCommand() {
|
|
8690
|
-
const perf = new
|
|
8907
|
+
const perf = new Command70("perf").description("Performance benchmark and baseline management");
|
|
8691
8908
|
registerBenchCommand(perf);
|
|
8692
8909
|
registerBaselinesCommands(perf);
|
|
8693
8910
|
registerReportCommand(perf);
|
|
@@ -8696,10 +8913,10 @@ function createPerfCommand() {
|
|
|
8696
8913
|
}
|
|
8697
8914
|
|
|
8698
8915
|
// src/commands/persona/index.ts
|
|
8699
|
-
import { Command as
|
|
8916
|
+
import { Command as Command73 } from "commander";
|
|
8700
8917
|
|
|
8701
8918
|
// src/commands/persona/list.ts
|
|
8702
|
-
import { Command as
|
|
8919
|
+
import { Command as Command71 } from "commander";
|
|
8703
8920
|
function printPersonaList(personas) {
|
|
8704
8921
|
if (personas.length === 0) {
|
|
8705
8922
|
logger.info("No personas found.");
|
|
@@ -8713,7 +8930,7 @@ function printPersonaList(personas) {
|
|
|
8713
8930
|
}
|
|
8714
8931
|
}
|
|
8715
8932
|
function createListCommand2() {
|
|
8716
|
-
return new
|
|
8933
|
+
return new Command71("list").description("List available agent personas").action(async (_opts, cmd) => {
|
|
8717
8934
|
const globalOpts = cmd.optsWithGlobals();
|
|
8718
8935
|
const personasDir = resolvePersonasDir();
|
|
8719
8936
|
const result = listPersonas(personasDir);
|
|
@@ -8733,17 +8950,17 @@ function createListCommand2() {
|
|
|
8733
8950
|
}
|
|
8734
8951
|
|
|
8735
8952
|
// src/commands/persona/generate.ts
|
|
8736
|
-
import { Command as
|
|
8953
|
+
import { Command as Command72 } from "commander";
|
|
8737
8954
|
import * as fs29 from "fs";
|
|
8738
|
-
import * as
|
|
8955
|
+
import * as path48 from "path";
|
|
8739
8956
|
function generatePersonaArtifacts(persona, outputDir, only) {
|
|
8740
8957
|
const slug = toKebabCase(persona.name);
|
|
8741
8958
|
const generated = [];
|
|
8742
8959
|
if (!only || only === "runtime") {
|
|
8743
8960
|
const result = generateRuntime(persona);
|
|
8744
8961
|
if (result.ok) {
|
|
8745
|
-
const outPath =
|
|
8746
|
-
fs29.mkdirSync(
|
|
8962
|
+
const outPath = path48.join(outputDir, `${slug}.runtime.json`);
|
|
8963
|
+
fs29.mkdirSync(path48.dirname(outPath), { recursive: true });
|
|
8747
8964
|
fs29.writeFileSync(outPath, result.value);
|
|
8748
8965
|
generated.push(outPath);
|
|
8749
8966
|
}
|
|
@@ -8751,7 +8968,7 @@ function generatePersonaArtifacts(persona, outputDir, only) {
|
|
|
8751
8968
|
if (!only || only === "agents-md") {
|
|
8752
8969
|
const result = generateAgentsMd(persona);
|
|
8753
8970
|
if (result.ok) {
|
|
8754
|
-
const outPath =
|
|
8971
|
+
const outPath = path48.join(outputDir, `${slug}.agents.md`);
|
|
8755
8972
|
fs29.writeFileSync(outPath, result.value);
|
|
8756
8973
|
generated.push(outPath);
|
|
8757
8974
|
}
|
|
@@ -8759,8 +8976,8 @@ function generatePersonaArtifacts(persona, outputDir, only) {
|
|
|
8759
8976
|
if (!only || only === "ci") {
|
|
8760
8977
|
const result = generateCIWorkflow(persona, "github");
|
|
8761
8978
|
if (result.ok) {
|
|
8762
|
-
const outPath =
|
|
8763
|
-
fs29.mkdirSync(
|
|
8979
|
+
const outPath = path48.join(outputDir, ".github", "workflows", `${slug}.yml`);
|
|
8980
|
+
fs29.mkdirSync(path48.dirname(outPath), { recursive: true });
|
|
8764
8981
|
fs29.writeFileSync(outPath, result.value);
|
|
8765
8982
|
generated.push(outPath);
|
|
8766
8983
|
}
|
|
@@ -8768,16 +8985,16 @@ function generatePersonaArtifacts(persona, outputDir, only) {
|
|
|
8768
8985
|
return generated;
|
|
8769
8986
|
}
|
|
8770
8987
|
function createGenerateCommand3() {
|
|
8771
|
-
return new
|
|
8988
|
+
return new Command72("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) => {
|
|
8772
8989
|
const globalOpts = cmd.optsWithGlobals();
|
|
8773
|
-
const personaResult = loadPersona(
|
|
8990
|
+
const personaResult = loadPersona(path48.join(resolvePersonasDir(), `${name}.yaml`));
|
|
8774
8991
|
if (!personaResult.ok) {
|
|
8775
8992
|
logger.error(personaResult.error.message);
|
|
8776
8993
|
process.exit(ExitCode.ERROR);
|
|
8777
8994
|
}
|
|
8778
8995
|
const generated = generatePersonaArtifacts(
|
|
8779
8996
|
personaResult.value,
|
|
8780
|
-
|
|
8997
|
+
path48.resolve(opts.outputDir),
|
|
8781
8998
|
opts.only
|
|
8782
8999
|
);
|
|
8783
9000
|
if (!globalOpts.quiet) {
|
|
@@ -8790,7 +9007,7 @@ function createGenerateCommand3() {
|
|
|
8790
9007
|
|
|
8791
9008
|
// src/commands/persona/index.ts
|
|
8792
9009
|
function createPersonaCommand() {
|
|
8793
|
-
const command = new
|
|
9010
|
+
const command = new Command73("persona").description("Agent persona management commands");
|
|
8794
9011
|
command.addCommand(createListCommand2());
|
|
8795
9012
|
command.addCommand(createGenerateCommand3());
|
|
8796
9013
|
return command;
|
|
@@ -8798,7 +9015,7 @@ function createPersonaCommand() {
|
|
|
8798
9015
|
|
|
8799
9016
|
// src/commands/predict.ts
|
|
8800
9017
|
init_dist3();
|
|
8801
|
-
import { Command as
|
|
9018
|
+
import { Command as Command74 } from "commander";
|
|
8802
9019
|
import chalk11 from "chalk";
|
|
8803
9020
|
var CATEGORY_ORDER = [
|
|
8804
9021
|
"circular-deps",
|
|
@@ -8904,7 +9121,7 @@ function handlePredictError(err, mode) {
|
|
|
8904
9121
|
process.exit(ExitCode.ERROR);
|
|
8905
9122
|
}
|
|
8906
9123
|
function createPredictCommand() {
|
|
8907
|
-
const command = new
|
|
9124
|
+
const command = new Command74("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) => {
|
|
8908
9125
|
const globalOpts = cmd.optsWithGlobals();
|
|
8909
9126
|
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
8910
9127
|
try {
|
|
@@ -8928,10 +9145,10 @@ function createPredictCommand() {
|
|
|
8928
9145
|
|
|
8929
9146
|
// src/commands/proposals.ts
|
|
8930
9147
|
init_dist3();
|
|
8931
|
-
import { Command as
|
|
8932
|
-
import { resolve as
|
|
9148
|
+
import { Command as Command75 } from "commander";
|
|
9149
|
+
import { resolve as resolve33 } from "path";
|
|
8933
9150
|
function projectRoot() {
|
|
8934
|
-
return
|
|
9151
|
+
return resolve33(process.env["HARNESS_PROJECT_ROOT"] ?? process.cwd());
|
|
8935
9152
|
}
|
|
8936
9153
|
function summarizeProposal(p) {
|
|
8937
9154
|
return {
|
|
@@ -9023,7 +9240,7 @@ async function actApproveCommand(id) {
|
|
|
9023
9240
|
}
|
|
9024
9241
|
}
|
|
9025
9242
|
function createProposalsCommand() {
|
|
9026
|
-
const cmd = new
|
|
9243
|
+
const cmd = new Command75("proposals").description("Skill-proposal review queue");
|
|
9027
9244
|
cmd.command("list").description("List skill proposals in the local queue").option(
|
|
9028
9245
|
"--status <status>",
|
|
9029
9246
|
`Filter by status \u2014 one of ${ALLOWED_STATUSES.join(" | ")}`,
|
|
@@ -9039,8 +9256,8 @@ function createProposalsCommand() {
|
|
|
9039
9256
|
|
|
9040
9257
|
// src/commands/publish-analyses.ts
|
|
9041
9258
|
import * as fs30 from "fs";
|
|
9042
|
-
import * as
|
|
9043
|
-
import { Command as
|
|
9259
|
+
import * as path49 from "path";
|
|
9260
|
+
import { Command as Command76 } from "commander";
|
|
9044
9261
|
init_dist3();
|
|
9045
9262
|
import {
|
|
9046
9263
|
renderAnalysisComment,
|
|
@@ -9048,7 +9265,7 @@ import {
|
|
|
9048
9265
|
savePublishedIndex
|
|
9049
9266
|
} from "@harness-engineering/orchestrator";
|
|
9050
9267
|
function bootstrapTrackerCommand(opts, verb) {
|
|
9051
|
-
const projectPath =
|
|
9268
|
+
const projectPath = path49.resolve(opts.dir);
|
|
9052
9269
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
9053
9270
|
if (!trackerConfig) {
|
|
9054
9271
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -9060,7 +9277,7 @@ function bootstrapTrackerCommand(opts, verb) {
|
|
|
9060
9277
|
);
|
|
9061
9278
|
return null;
|
|
9062
9279
|
}
|
|
9063
|
-
const projectEnvPath =
|
|
9280
|
+
const projectEnvPath = path49.join(projectPath, ".env");
|
|
9064
9281
|
if (fs30.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
9065
9282
|
__require("dotenv").config({ path: projectEnvPath });
|
|
9066
9283
|
}
|
|
@@ -9097,7 +9314,7 @@ async function buildNameToExternalIdMap(projectPath) {
|
|
|
9097
9314
|
}
|
|
9098
9315
|
return fileLessMap;
|
|
9099
9316
|
}
|
|
9100
|
-
const roadmapFile =
|
|
9317
|
+
const roadmapFile = path49.join(projectPath, "docs", "roadmap.md");
|
|
9101
9318
|
if (!fs30.existsSync(roadmapFile)) {
|
|
9102
9319
|
logger.error("No docs/roadmap.md found. Cannot map issue hashes to external IDs.");
|
|
9103
9320
|
return null;
|
|
@@ -9153,8 +9370,8 @@ async function runPublishAnalyses(opts) {
|
|
|
9153
9370
|
const nameToExternalId = await buildNameToExternalIdMap(projectPath);
|
|
9154
9371
|
if (!nameToExternalId) return process.exit(1);
|
|
9155
9372
|
const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
|
|
9156
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
9157
|
-
const archive = new AnalysisArchive(
|
|
9373
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-LHINSVK4.js");
|
|
9374
|
+
const archive = new AnalysisArchive(path49.join(projectPath, ".harness", "analyses"));
|
|
9158
9375
|
const analyses = await archive.list();
|
|
9159
9376
|
if (analyses.length === 0) {
|
|
9160
9377
|
logger.info("No analyses found to publish.");
|
|
@@ -9176,7 +9393,7 @@ async function runPublishAnalyses(opts) {
|
|
|
9176
9393
|
}
|
|
9177
9394
|
}
|
|
9178
9395
|
function createPublishAnalysesCommand() {
|
|
9179
|
-
const command = new
|
|
9396
|
+
const command = new Command76("publish-analyses").description(
|
|
9180
9397
|
"Publishes locally generated intelligence analyses to the external issue tracker (e.g., GitHub)"
|
|
9181
9398
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
9182
9399
|
try {
|
|
@@ -9192,13 +9409,13 @@ function createPublishAnalysesCommand() {
|
|
|
9192
9409
|
}
|
|
9193
9410
|
|
|
9194
9411
|
// src/commands/pulse/index.ts
|
|
9195
|
-
import { Command as
|
|
9412
|
+
import { Command as Command78 } from "commander";
|
|
9196
9413
|
|
|
9197
9414
|
// src/commands/pulse/run.ts
|
|
9198
9415
|
init_dist3();
|
|
9199
9416
|
import { readFileSync as readFileSync19, mkdirSync as mkdirSync13, writeFileSync as writeFileSync16 } from "fs";
|
|
9200
|
-
import { dirname as dirname18, join as join39, resolve as
|
|
9201
|
-
import { Command as
|
|
9417
|
+
import { dirname as dirname18, join as join39, resolve as resolve35 } from "path";
|
|
9418
|
+
import { Command as Command77 } from "commander";
|
|
9202
9419
|
async function runPulseRunCommand(opts) {
|
|
9203
9420
|
const startedAt = Date.now();
|
|
9204
9421
|
let raw;
|
|
@@ -9263,7 +9480,7 @@ function emit2(status, nonInteractive) {
|
|
|
9263
9480
|
return status;
|
|
9264
9481
|
}
|
|
9265
9482
|
function createRunCommand2() {
|
|
9266
|
-
return new
|
|
9483
|
+
return new Command77("run").description("Run a pulse: query configured adapters, sanitize, assemble single-page report").option(
|
|
9267
9484
|
"--lookback <window>",
|
|
9268
9485
|
"Lookback window (e.g. 24h, 7d). Defaults to pulse.lookbackDefault."
|
|
9269
9486
|
).option(
|
|
@@ -9273,8 +9490,8 @@ function createRunCommand2() {
|
|
|
9273
9490
|
async (options) => {
|
|
9274
9491
|
const nonInteractive = options.nonInteractive === true || !process.stdout.isTTY;
|
|
9275
9492
|
const status = await runPulseRunCommand({
|
|
9276
|
-
configPath:
|
|
9277
|
-
outputDir:
|
|
9493
|
+
configPath: resolve35(process.cwd(), options.config),
|
|
9494
|
+
outputDir: resolve35(process.cwd(), options.outputDir),
|
|
9278
9495
|
nonInteractive,
|
|
9279
9496
|
lookback: options.lookback
|
|
9280
9497
|
});
|
|
@@ -9287,13 +9504,13 @@ function createRunCommand2() {
|
|
|
9287
9504
|
|
|
9288
9505
|
// src/commands/pulse/index.ts
|
|
9289
9506
|
function createPulseCommand() {
|
|
9290
|
-
const command = new
|
|
9507
|
+
const command = new Command78("pulse").description("Pulse (read-side observability) commands");
|
|
9291
9508
|
command.addCommand(createRunCommand2());
|
|
9292
9509
|
return command;
|
|
9293
9510
|
}
|
|
9294
9511
|
|
|
9295
9512
|
// src/commands/recommend.ts
|
|
9296
|
-
import { Command as
|
|
9513
|
+
import { Command as Command79 } from "commander";
|
|
9297
9514
|
import chalk12 from "chalk";
|
|
9298
9515
|
async function resolveSnapshot(cwd, noCache) {
|
|
9299
9516
|
if (!noCache) {
|
|
@@ -9357,7 +9574,7 @@ function printRecommendations(result) {
|
|
|
9357
9574
|
console.log("");
|
|
9358
9575
|
}
|
|
9359
9576
|
function createRecommendCommand() {
|
|
9360
|
-
const command = new
|
|
9577
|
+
const command = new Command79("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) => {
|
|
9361
9578
|
const globalOpts = cmd.optsWithGlobals();
|
|
9362
9579
|
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
9363
9580
|
try {
|
|
@@ -9392,19 +9609,197 @@ function createRecommendCommand() {
|
|
|
9392
9609
|
return command;
|
|
9393
9610
|
}
|
|
9394
9611
|
|
|
9612
|
+
// src/commands/review-ci.ts
|
|
9613
|
+
init_dist3();
|
|
9614
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
9615
|
+
import { writeFileSync as writeFileSync17 } from "fs";
|
|
9616
|
+
import { Command as Command80, Option } from "commander";
|
|
9617
|
+
|
|
9618
|
+
// src/commands/review-ci-local-adapter.ts
|
|
9619
|
+
import { z } from "zod";
|
|
9620
|
+
import { OpenAICompatibleAnalysisProvider } from "@harness-engineering/intelligence";
|
|
9621
|
+
var FindingSchema = z.object({
|
|
9622
|
+
id: z.string(),
|
|
9623
|
+
file: z.string(),
|
|
9624
|
+
lineRange: z.tuple([z.number(), z.number()]),
|
|
9625
|
+
domain: z.enum(["compliance", "bug", "security", "architecture", "learnings"]),
|
|
9626
|
+
severity: z.enum(["critical", "important", "suggestion"]),
|
|
9627
|
+
title: z.string(),
|
|
9628
|
+
rationale: z.string(),
|
|
9629
|
+
suggestion: z.string().optional(),
|
|
9630
|
+
evidence: z.array(z.string())
|
|
9631
|
+
});
|
|
9632
|
+
var LocalVerdictSchema = z.object({
|
|
9633
|
+
assessment: z.enum(["approve", "comment", "request-changes"]),
|
|
9634
|
+
findings: z.array(FindingSchema)
|
|
9635
|
+
});
|
|
9636
|
+
var MAX_SURFACED_ERROR_LEN = 200;
|
|
9637
|
+
function sanitizeProviderError(err) {
|
|
9638
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
9639
|
+
const redacted = raw.replace(/https?:\/\/\S+/gi, "[endpoint]").replace(/\b(bearer|authorization|api[-_]?key|token)\b\s*[:=]?\s*\S+/gi, "[redacted]").replace(/\s+/g, " ").trim();
|
|
9640
|
+
const capped = redacted.length > MAX_SURFACED_ERROR_LEN ? redacted.slice(0, MAX_SURFACED_ERROR_LEN - 1) + "\u2026" : redacted;
|
|
9641
|
+
return `local runner provider error: ${capped}`;
|
|
9642
|
+
}
|
|
9643
|
+
function createLocalInvoke() {
|
|
9644
|
+
return async ({ endpoint, model, instruction, diff: diff2 }) => {
|
|
9645
|
+
if (!/^https?:\/\//i.test(endpoint)) {
|
|
9646
|
+
throw new Error(
|
|
9647
|
+
`HARNESS_LOCAL_ENDPOINT must be an http(s) URL (got '${endpoint}'); e.g. http://localhost:1234/v1`
|
|
9648
|
+
);
|
|
9649
|
+
}
|
|
9650
|
+
const provider = new OpenAICompatibleAnalysisProvider({
|
|
9651
|
+
apiKey: process.env.HARNESS_LOCAL_API_KEY ?? "local",
|
|
9652
|
+
baseUrl: endpoint,
|
|
9653
|
+
defaultModel: model
|
|
9654
|
+
});
|
|
9655
|
+
try {
|
|
9656
|
+
const { result } = await provider.analyze({
|
|
9657
|
+
prompt: `${instruction}
|
|
9658
|
+
|
|
9659
|
+
---
|
|
9660
|
+
DIFF UNDER REVIEW:
|
|
9661
|
+
${diff2}`,
|
|
9662
|
+
responseSchema: LocalVerdictSchema,
|
|
9663
|
+
model
|
|
9664
|
+
});
|
|
9665
|
+
return JSON.stringify(result);
|
|
9666
|
+
} catch (err) {
|
|
9667
|
+
throw new Error(sanitizeProviderError(err), { cause: err });
|
|
9668
|
+
}
|
|
9669
|
+
};
|
|
9670
|
+
}
|
|
9671
|
+
|
|
9672
|
+
// src/commands/review-ci.ts
|
|
9673
|
+
var KNOWN_RUNNERS = Object.keys(RUNNER_PRESETS);
|
|
9674
|
+
var BLOCK_ON_LEVELS = [...CI_ASSESSMENTS, "none"];
|
|
9675
|
+
function assertKnownRunner(runner) {
|
|
9676
|
+
if (runner === void 0) return;
|
|
9677
|
+
if (!KNOWN_RUNNERS.includes(runner)) {
|
|
9678
|
+
throw new Error(`unknown runner '${runner}' (expected: ${KNOWN_RUNNERS.join("|")})`);
|
|
9679
|
+
}
|
|
9680
|
+
}
|
|
9681
|
+
var defaultRunGit = (args) => execFileSync5("git", args, { encoding: "utf-8" }).toString().trim();
|
|
9682
|
+
function resolveDiffRange(opts) {
|
|
9683
|
+
if (opts.range) return opts.range;
|
|
9684
|
+
const runGit = opts.runGit ?? defaultRunGit;
|
|
9685
|
+
let base = "main";
|
|
9686
|
+
try {
|
|
9687
|
+
const ref = runGit(["symbolic-ref", "refs/remotes/origin/HEAD"]);
|
|
9688
|
+
const m = ref.match(/origin\/(.+)$/);
|
|
9689
|
+
if (m?.[1]) base = m[1];
|
|
9690
|
+
} catch {
|
|
9691
|
+
}
|
|
9692
|
+
return `origin/${base}...HEAD`;
|
|
9693
|
+
}
|
|
9694
|
+
function splitDiffByFile(rawDiff) {
|
|
9695
|
+
const sections = /* @__PURE__ */ new Map();
|
|
9696
|
+
const headerRe = /^diff --git a\/(.+?) b\/(.+?)$/;
|
|
9697
|
+
let currentPath = null;
|
|
9698
|
+
let buffer = [];
|
|
9699
|
+
const flush = () => {
|
|
9700
|
+
if (currentPath !== null) sections.set(currentPath, buffer.join("\n"));
|
|
9701
|
+
};
|
|
9702
|
+
for (const line of rawDiff.split("\n")) {
|
|
9703
|
+
const m = line.match(headerRe);
|
|
9704
|
+
if (m?.[2]) {
|
|
9705
|
+
flush();
|
|
9706
|
+
currentPath = m[2];
|
|
9707
|
+
buffer = [line];
|
|
9708
|
+
} else if (currentPath !== null) {
|
|
9709
|
+
buffer.push(line);
|
|
9710
|
+
}
|
|
9711
|
+
}
|
|
9712
|
+
flush();
|
|
9713
|
+
return sections;
|
|
9714
|
+
}
|
|
9715
|
+
function buildDiffInfo(rawDiff) {
|
|
9716
|
+
const parsed = parseDiff(rawDiff);
|
|
9717
|
+
if (!parsed.ok) throw new Error(`Failed to parse diff: ${parsed.error.message}`);
|
|
9718
|
+
const files = parsed.value.files;
|
|
9719
|
+
const perFile = splitDiffByFile(rawDiff);
|
|
9720
|
+
return {
|
|
9721
|
+
changedFiles: files.map((f) => f.path),
|
|
9722
|
+
newFiles: files.filter((f) => f.status === "added").map((f) => f.path),
|
|
9723
|
+
deletedFiles: files.filter((f) => f.status === "deleted").map((f) => f.path),
|
|
9724
|
+
totalDiffLines: rawDiff ? rawDiff.split("\n").length : 0,
|
|
9725
|
+
// On a path-key miss, fall back to '' (the file contributes no per-file diff)
|
|
9726
|
+
// rather than the ENTIRE raw diff — substituting the whole diff would duplicate
|
|
9727
|
+
// every file's content into one section, inflating the STDIN payload to the LLM
|
|
9728
|
+
// (and risking core's execMaxStdoutBytes). With splitDiffByFile aligned to core's
|
|
9729
|
+
// path extraction, ordinary paths never miss; this is the safe degenerate case.
|
|
9730
|
+
fileDiffs: new Map(files.map((f) => [f.path, perFile.get(f.path) ?? ""]))
|
|
9731
|
+
};
|
|
9732
|
+
}
|
|
9733
|
+
function defaultResolveRaw(range, _cwd, runGit) {
|
|
9734
|
+
return runGit(["diff", range]);
|
|
9735
|
+
}
|
|
9736
|
+
function buildCallOpts(opts, cwd, diff2) {
|
|
9737
|
+
const runner = opts.runner;
|
|
9738
|
+
return {
|
|
9739
|
+
projectRoot: cwd,
|
|
9740
|
+
diff: diff2,
|
|
9741
|
+
...runner ? { runner } : {},
|
|
9742
|
+
...opts.blockOn ? { blockOn: opts.blockOn } : {},
|
|
9743
|
+
...runner === "local" ? { localInvoke: opts.localInvoke ?? createLocalInvoke() } : {}
|
|
9744
|
+
};
|
|
9745
|
+
}
|
|
9746
|
+
async function runReviewCi(opts) {
|
|
9747
|
+
assertKnownRunner(opts.runner);
|
|
9748
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
9749
|
+
const runGit = opts.runGit ?? defaultRunGit;
|
|
9750
|
+
const range = resolveDiffRange({
|
|
9751
|
+
...opts.diffRange ? { range: opts.diffRange } : {},
|
|
9752
|
+
cwd,
|
|
9753
|
+
runGit
|
|
9754
|
+
});
|
|
9755
|
+
const rawDiff = (opts.resolveRaw ?? defaultResolveRaw)(range, cwd, runGit);
|
|
9756
|
+
const diff2 = buildDiffInfo(rawDiff);
|
|
9757
|
+
const callOpts = buildCallOpts(opts, cwd, diff2);
|
|
9758
|
+
return (opts.runCiReviewImpl ?? runCiReview)(callOpts);
|
|
9759
|
+
}
|
|
9760
|
+
function emitReviewCi(result, opts, writeFile = (p, d) => writeFileSync17(p, d), log = (m) => process.stdout.write(m + "\n")) {
|
|
9761
|
+
log(result.terminalOutput);
|
|
9762
|
+
if (opts.jsonPath) writeFile(opts.jsonPath, JSON.stringify(result.verdict, null, 2));
|
|
9763
|
+
if (opts.comment) {
|
|
9764
|
+
logger.warn(
|
|
9765
|
+
"review-ci: --comment posting is not yet wired (no gh PR poster; Phase 3 stub). The verdict above is authoritative; use --json to capture the artifact. PR-review posting lands in a later phase."
|
|
9766
|
+
);
|
|
9767
|
+
}
|
|
9768
|
+
}
|
|
9769
|
+
function createReviewCiCommand() {
|
|
9770
|
+
return new Command80("review-ci").description("Run the tiered code-review gate (floor + optional LLM runner) for CI").addOption(
|
|
9771
|
+
new Option("--runner <runner>", `${KNOWN_RUNNERS.join(" | ")} (omit = floor-only)`).choices(
|
|
9772
|
+
KNOWN_RUNNERS
|
|
9773
|
+
)
|
|
9774
|
+
).addOption(
|
|
9775
|
+
new Option("--block-on <level>", BLOCK_ON_LEVELS.join(" | ")).choices(BLOCK_ON_LEVELS).default("request-changes")
|
|
9776
|
+
).option("--diff <range>", "git range (default: origin/<base>...HEAD)").option("--comment", "post verdict as a PR review (stubbed in this phase)").option("--json <path>", "write the verdict artifact to this path").action(async (opts) => {
|
|
9777
|
+
const result = await runReviewCi({
|
|
9778
|
+
runner: opts.runner,
|
|
9779
|
+
blockOn: opts.blockOn,
|
|
9780
|
+
diffRange: opts.diff
|
|
9781
|
+
});
|
|
9782
|
+
emitReviewCi(result, {
|
|
9783
|
+
jsonPath: opts.json,
|
|
9784
|
+
comment: opts.comment
|
|
9785
|
+
});
|
|
9786
|
+
process.exit(result.exitCode);
|
|
9787
|
+
});
|
|
9788
|
+
}
|
|
9789
|
+
|
|
9395
9790
|
// src/commands/roadmap/index.ts
|
|
9396
|
-
import { Command as
|
|
9791
|
+
import { Command as Command82 } from "commander";
|
|
9397
9792
|
|
|
9398
9793
|
// src/commands/roadmap/migrate.ts
|
|
9399
9794
|
init_dist3();
|
|
9400
|
-
import { Command as
|
|
9795
|
+
import { Command as Command81 } from "commander";
|
|
9401
9796
|
import * as fs32 from "fs";
|
|
9402
|
-
import * as
|
|
9797
|
+
import * as path51 from "path";
|
|
9403
9798
|
import chalk13 from "chalk";
|
|
9404
9799
|
|
|
9405
9800
|
// src/commands/roadmap/migrate-lock.ts
|
|
9406
9801
|
import * as fs31 from "fs";
|
|
9407
|
-
import * as
|
|
9802
|
+
import * as path50 from "path";
|
|
9408
9803
|
import * as os6 from "os";
|
|
9409
9804
|
var LOCK_FILE_NAME = "migrate.lock";
|
|
9410
9805
|
var STALE_LOCK_MS = 30 * 60 * 1e3;
|
|
@@ -9418,8 +9813,8 @@ function isPidAlive(pid) {
|
|
|
9418
9813
|
}
|
|
9419
9814
|
}
|
|
9420
9815
|
function acquireMigrateLock(projectRoot2) {
|
|
9421
|
-
const harnessDir =
|
|
9422
|
-
const lockPath =
|
|
9816
|
+
const harnessDir = path50.join(projectRoot2, ".harness");
|
|
9817
|
+
const lockPath = path50.join(harnessDir, LOCK_FILE_NAME);
|
|
9423
9818
|
if (!fs31.existsSync(harnessDir)) fs31.mkdirSync(harnessDir, { recursive: true });
|
|
9424
9819
|
if (fs31.existsSync(lockPath)) {
|
|
9425
9820
|
const existingRaw = (() => {
|
|
@@ -9618,7 +10013,7 @@ async function runRoadmapMigrate(opts) {
|
|
|
9618
10013
|
if (!clientR.ok) return Err(new CLIError(clientR.error.message));
|
|
9619
10014
|
client = clientR.value;
|
|
9620
10015
|
}
|
|
9621
|
-
const roadmapPath =
|
|
10016
|
+
const roadmapPath = path51.join(cwd, "docs", "roadmap.md");
|
|
9622
10017
|
if (!fs32.existsSync(roadmapPath)) {
|
|
9623
10018
|
return Err(new CLIError(`docs/roadmap.md not found in ${cwd}`));
|
|
9624
10019
|
}
|
|
@@ -9667,7 +10062,7 @@ async function runRoadmapMigrate(opts) {
|
|
|
9667
10062
|
}
|
|
9668
10063
|
}
|
|
9669
10064
|
function createRoadmapMigrateCommand() {
|
|
9670
|
-
return new
|
|
10065
|
+
return new Command81("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(
|
|
9671
10066
|
"--format <fmt>",
|
|
9672
10067
|
'Output format: "human" (default) or "json" (single JSON object for CI consumers)',
|
|
9673
10068
|
"human"
|
|
@@ -9702,16 +10097,16 @@ function createRoadmapMigrateCommand() {
|
|
|
9702
10097
|
|
|
9703
10098
|
// src/commands/roadmap/index.ts
|
|
9704
10099
|
function createRoadmapCommand() {
|
|
9705
|
-
const roadmap = new
|
|
10100
|
+
const roadmap = new Command82("roadmap").description("Roadmap management");
|
|
9706
10101
|
roadmap.addCommand(createRoadmapMigrateCommand());
|
|
9707
10102
|
return roadmap;
|
|
9708
10103
|
}
|
|
9709
10104
|
|
|
9710
10105
|
// src/commands/routing/index.ts
|
|
9711
|
-
import { Command as
|
|
10106
|
+
import { Command as Command86 } from "commander";
|
|
9712
10107
|
|
|
9713
10108
|
// src/commands/routing/config.ts
|
|
9714
|
-
import { Command as
|
|
10109
|
+
import { Command as Command83 } from "commander";
|
|
9715
10110
|
|
|
9716
10111
|
// src/commands/routing/http-client.ts
|
|
9717
10112
|
function orchestratorBase() {
|
|
@@ -9721,9 +10116,9 @@ function authHeader() {
|
|
|
9721
10116
|
const tok = process.env["HARNESS_API_TOKEN"];
|
|
9722
10117
|
return tok ? { Authorization: `Bearer ${tok}` } : {};
|
|
9723
10118
|
}
|
|
9724
|
-
async function getJson(
|
|
10119
|
+
async function getJson(path73) {
|
|
9725
10120
|
try {
|
|
9726
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
10121
|
+
const res = await fetch(`${orchestratorBase()}${path73}`, {
|
|
9727
10122
|
headers: { ...authHeader() }
|
|
9728
10123
|
});
|
|
9729
10124
|
const text = await res.text();
|
|
@@ -9738,9 +10133,9 @@ async function getJson(path72) {
|
|
|
9738
10133
|
};
|
|
9739
10134
|
}
|
|
9740
10135
|
}
|
|
9741
|
-
async function postJson(
|
|
10136
|
+
async function postJson(path73, body) {
|
|
9742
10137
|
try {
|
|
9743
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
10138
|
+
const res = await fetch(`${orchestratorBase()}${path73}`, {
|
|
9744
10139
|
method: "POST",
|
|
9745
10140
|
headers: { "Content-Type": "application/json", ...authHeader() },
|
|
9746
10141
|
body: JSON.stringify(body)
|
|
@@ -9776,7 +10171,7 @@ function renderHuman(data) {
|
|
|
9776
10171
|
}
|
|
9777
10172
|
}
|
|
9778
10173
|
function createConfigCommand() {
|
|
9779
|
-
return new
|
|
10174
|
+
return new Command83("config").description("Print active routing config and resolved fallback chains").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
9780
10175
|
const r = await getJson("/api/v1/routing/config");
|
|
9781
10176
|
if (!r.ok) {
|
|
9782
10177
|
if (r.status === 0) {
|
|
@@ -9802,7 +10197,7 @@ function createConfigCommand() {
|
|
|
9802
10197
|
}
|
|
9803
10198
|
|
|
9804
10199
|
// src/commands/routing/trace.ts
|
|
9805
|
-
import { Command as
|
|
10200
|
+
import { Command as Command84 } from "commander";
|
|
9806
10201
|
function buildUseCase(opts) {
|
|
9807
10202
|
if (opts.skill) {
|
|
9808
10203
|
return opts.mode ? { kind: "skill", skillName: opts.skill, cognitiveMode: opts.mode } : { kind: "skill", skillName: opts.skill };
|
|
@@ -9823,7 +10218,7 @@ function renderHuman2(r) {
|
|
|
9823
10218
|
}
|
|
9824
10219
|
}
|
|
9825
10220
|
function createTraceCommand() {
|
|
9826
|
-
return new
|
|
10221
|
+
return new Command84("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) => {
|
|
9827
10222
|
const useCase = buildUseCase(opts);
|
|
9828
10223
|
if (!useCase) {
|
|
9829
10224
|
logger.error("Either --skill <name> or --mode <m> is required");
|
|
@@ -9855,7 +10250,7 @@ function createTraceCommand() {
|
|
|
9855
10250
|
}
|
|
9856
10251
|
|
|
9857
10252
|
// src/commands/routing/decisions.ts
|
|
9858
|
-
import { Command as
|
|
10253
|
+
import { Command as Command85 } from "commander";
|
|
9859
10254
|
function summarizeUseCase(uc) {
|
|
9860
10255
|
switch (uc.kind) {
|
|
9861
10256
|
case "skill":
|
|
@@ -9903,7 +10298,7 @@ function buildQuery(opts) {
|
|
|
9903
10298
|
return q ? `?${q}` : "";
|
|
9904
10299
|
}
|
|
9905
10300
|
function createDecisionsCommand() {
|
|
9906
|
-
return new
|
|
10301
|
+
return new Command85("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(
|
|
9907
10302
|
async (opts) => {
|
|
9908
10303
|
const query = buildQuery(opts);
|
|
9909
10304
|
const r = await getJson(`/api/v1/routing/decisions${query}`);
|
|
@@ -9933,7 +10328,7 @@ function createDecisionsCommand() {
|
|
|
9933
10328
|
|
|
9934
10329
|
// src/commands/routing/index.ts
|
|
9935
10330
|
function createRoutingCommand() {
|
|
9936
|
-
const cmd = new
|
|
10331
|
+
const cmd = new Command86("routing").description(
|
|
9937
10332
|
"Inspect routing config, trace decisions, and read recent dispatches"
|
|
9938
10333
|
);
|
|
9939
10334
|
cmd.addCommand(createConfigCommand());
|
|
@@ -9944,8 +10339,8 @@ function createRoutingCommand() {
|
|
|
9944
10339
|
|
|
9945
10340
|
// src/commands/scan-config.ts
|
|
9946
10341
|
init_dist3();
|
|
9947
|
-
import { Command as
|
|
9948
|
-
import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as
|
|
10342
|
+
import { Command as Command87 } from "commander";
|
|
10343
|
+
import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync20 } from "fs";
|
|
9949
10344
|
import { join as join42, relative as relative3 } from "path";
|
|
9950
10345
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
9951
10346
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
@@ -9972,7 +10367,7 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
|
|
|
9972
10367
|
if (!hasHighSeverity) return;
|
|
9973
10368
|
const { cleaned, linesStripped } = stripHighSeverityPatterns(content, injectionFindings);
|
|
9974
10369
|
if (linesStripped > 0) {
|
|
9975
|
-
|
|
10370
|
+
writeFileSync20(filePath, cleaned);
|
|
9976
10371
|
logger.info(
|
|
9977
10372
|
`scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${relative3(targetDir, filePath).replaceAll("\\", "/")}`
|
|
9978
10373
|
);
|
|
@@ -10033,7 +10428,7 @@ function formatTextOutput(result) {
|
|
|
10033
10428
|
}
|
|
10034
10429
|
}
|
|
10035
10430
|
function createScanConfigCommand() {
|
|
10036
|
-
const command = new
|
|
10431
|
+
const command = new Command87("scan-config").description(
|
|
10037
10432
|
"Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
|
|
10038
10433
|
).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
|
|
10039
10434
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -10051,7 +10446,7 @@ function createScanConfigCommand() {
|
|
|
10051
10446
|
|
|
10052
10447
|
// src/commands/search.ts
|
|
10053
10448
|
init_dist();
|
|
10054
|
-
import { Command as
|
|
10449
|
+
import { Command as Command88 } from "commander";
|
|
10055
10450
|
function parseFileKinds(raw) {
|
|
10056
10451
|
if (!raw) return void 0;
|
|
10057
10452
|
const set = /* @__PURE__ */ new Set();
|
|
@@ -10072,7 +10467,7 @@ function fmtBytes(n) {
|
|
|
10072
10467
|
return n.toFixed(0);
|
|
10073
10468
|
}
|
|
10074
10469
|
function createSearchCommand() {
|
|
10075
|
-
return new
|
|
10470
|
+
return new Command88("search").description("Full-text search over archived + live session content.").argument(
|
|
10076
10471
|
"<query>",
|
|
10077
10472
|
"FTS5 query (bare words AND-joined; quotes/AND/OR/NOT/column: for advanced syntax)"
|
|
10078
10473
|
).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(
|
|
@@ -10132,9 +10527,9 @@ function createSearchCommand() {
|
|
|
10132
10527
|
}
|
|
10133
10528
|
|
|
10134
10529
|
// src/commands/security-craft.ts
|
|
10135
|
-
import { Command as
|
|
10530
|
+
import { Command as Command89 } from "commander";
|
|
10136
10531
|
function createSecurityCraftCommand() {
|
|
10137
|
-
return new
|
|
10532
|
+
return new Command89("security-craft").description(
|
|
10138
10533
|
"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."
|
|
10139
10534
|
).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) => {
|
|
10140
10535
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -10181,10 +10576,10 @@ function printResult5(result, mode, _formatter) {
|
|
|
10181
10576
|
list.push(f);
|
|
10182
10577
|
byFile.set(f.target.file, list);
|
|
10183
10578
|
}
|
|
10184
|
-
for (const [file,
|
|
10579
|
+
for (const [file, fs49] of byFile) {
|
|
10185
10580
|
console.log(`
|
|
10186
10581
|
${file}`);
|
|
10187
|
-
for (const f of
|
|
10582
|
+
for (const f of fs49) {
|
|
10188
10583
|
console.log(
|
|
10189
10584
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.signal}:${f.target.line}`
|
|
10190
10585
|
);
|
|
@@ -10200,18 +10595,18 @@ ${file}`);
|
|
|
10200
10595
|
}
|
|
10201
10596
|
|
|
10202
10597
|
// src/commands/setup.ts
|
|
10203
|
-
import { Command as
|
|
10598
|
+
import { Command as Command90 } from "commander";
|
|
10204
10599
|
import * as fs35 from "fs";
|
|
10205
10600
|
import * as os8 from "os";
|
|
10206
|
-
import * as
|
|
10601
|
+
import * as path54 from "path";
|
|
10207
10602
|
import chalk15 from "chalk";
|
|
10208
10603
|
|
|
10209
10604
|
// src/utils/first-run.ts
|
|
10210
10605
|
import * as fs33 from "fs";
|
|
10211
10606
|
import * as os7 from "os";
|
|
10212
|
-
import * as
|
|
10213
|
-
var HARNESS_DIR =
|
|
10214
|
-
var MARKER_FILE =
|
|
10607
|
+
import * as path52 from "path";
|
|
10608
|
+
var HARNESS_DIR = path52.join(os7.homedir(), ".harness");
|
|
10609
|
+
var MARKER_FILE = path52.join(HARNESS_DIR, ".setup-complete");
|
|
10215
10610
|
function isFirstRun() {
|
|
10216
10611
|
return !fs33.existsSync(MARKER_FILE);
|
|
10217
10612
|
}
|
|
@@ -10231,7 +10626,7 @@ function printFirstRunWelcome() {
|
|
|
10231
10626
|
|
|
10232
10627
|
// src/commands/telemetry-wizard.ts
|
|
10233
10628
|
import * as fs34 from "fs";
|
|
10234
|
-
import * as
|
|
10629
|
+
import * as path53 from "path";
|
|
10235
10630
|
import readline2 from "readline";
|
|
10236
10631
|
import chalk14 from "chalk";
|
|
10237
10632
|
function prompt2(question) {
|
|
@@ -10239,10 +10634,10 @@ function prompt2(question) {
|
|
|
10239
10634
|
input: process.stdin,
|
|
10240
10635
|
output: process.stdout
|
|
10241
10636
|
});
|
|
10242
|
-
return new Promise((
|
|
10637
|
+
return new Promise((resolve47) => {
|
|
10243
10638
|
rl.question(question, (answer) => {
|
|
10244
10639
|
rl.close();
|
|
10245
|
-
|
|
10640
|
+
resolve47(answer.trim().toLowerCase());
|
|
10246
10641
|
});
|
|
10247
10642
|
});
|
|
10248
10643
|
}
|
|
@@ -10251,10 +10646,10 @@ function promptRaw(question) {
|
|
|
10251
10646
|
input: process.stdin,
|
|
10252
10647
|
output: process.stdout
|
|
10253
10648
|
});
|
|
10254
|
-
return new Promise((
|
|
10649
|
+
return new Promise((resolve47) => {
|
|
10255
10650
|
rl.question(question, (answer) => {
|
|
10256
10651
|
rl.close();
|
|
10257
|
-
|
|
10652
|
+
resolve47(answer.trim());
|
|
10258
10653
|
});
|
|
10259
10654
|
});
|
|
10260
10655
|
}
|
|
@@ -10262,7 +10657,7 @@ function isNo(answer) {
|
|
|
10262
10657
|
return answer === "n" || answer === "no";
|
|
10263
10658
|
}
|
|
10264
10659
|
function isTelemetryConfigured(cwd) {
|
|
10265
|
-
const configPath =
|
|
10660
|
+
const configPath = path53.join(cwd, "harness.config.json");
|
|
10266
10661
|
try {
|
|
10267
10662
|
const raw = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
|
|
10268
10663
|
return raw.telemetry !== void 0 && raw.adoption !== void 0;
|
|
@@ -10271,7 +10666,7 @@ function isTelemetryConfigured(cwd) {
|
|
|
10271
10666
|
}
|
|
10272
10667
|
}
|
|
10273
10668
|
function writeTelemetryConfig(cwd, result) {
|
|
10274
|
-
const configPath =
|
|
10669
|
+
const configPath = path53.join(cwd, "harness.config.json");
|
|
10275
10670
|
try {
|
|
10276
10671
|
const raw = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
|
|
10277
10672
|
raw.telemetry = { enabled: result.telemetryEnabled };
|
|
@@ -10284,9 +10679,9 @@ function writeTelemetryConfig(cwd, result) {
|
|
|
10284
10679
|
}
|
|
10285
10680
|
const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
|
|
10286
10681
|
if (hasIdentity) {
|
|
10287
|
-
const harnessDir =
|
|
10682
|
+
const harnessDir = path53.join(cwd, ".harness");
|
|
10288
10683
|
fs34.mkdirSync(harnessDir, { recursive: true });
|
|
10289
|
-
const telemetryFile =
|
|
10684
|
+
const telemetryFile = path53.join(harnessDir, "telemetry.json");
|
|
10290
10685
|
fs34.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
|
|
10291
10686
|
}
|
|
10292
10687
|
}
|
|
@@ -10333,7 +10728,7 @@ async function runTelemetryWizard() {
|
|
|
10333
10728
|
return { telemetryEnabled, adoptionEnabled, identity };
|
|
10334
10729
|
}
|
|
10335
10730
|
async function ensureTelemetryConfigured(cwd) {
|
|
10336
|
-
const configPath =
|
|
10731
|
+
const configPath = path53.join(cwd, "harness.config.json");
|
|
10337
10732
|
if (!fs34.existsSync(configPath)) {
|
|
10338
10733
|
return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
|
|
10339
10734
|
}
|
|
@@ -10380,7 +10775,7 @@ function runSlashCommandGeneration() {
|
|
|
10380
10775
|
}
|
|
10381
10776
|
}
|
|
10382
10777
|
function detectClient(dirName) {
|
|
10383
|
-
return fs35.existsSync(
|
|
10778
|
+
return fs35.existsSync(path54.join(os8.homedir(), dirName));
|
|
10384
10779
|
}
|
|
10385
10780
|
async function runMcpSetup(cwd) {
|
|
10386
10781
|
const results = [];
|
|
@@ -10396,7 +10791,7 @@ async function runMcpSetup(cwd) {
|
|
|
10396
10791
|
{ name: "Cursor", dir: ".cursor", client: "cursor", configTarget: ".cursor/mcp.json" },
|
|
10397
10792
|
{
|
|
10398
10793
|
name: "OpenCode",
|
|
10399
|
-
dir:
|
|
10794
|
+
dir: path54.join(".config", "opencode"),
|
|
10400
10795
|
client: "opencode",
|
|
10401
10796
|
configTarget: "opencode.json"
|
|
10402
10797
|
}
|
|
@@ -10428,7 +10823,7 @@ function formatStep(result) {
|
|
|
10428
10823
|
}
|
|
10429
10824
|
function configureTier0Integrations(cwd) {
|
|
10430
10825
|
try {
|
|
10431
|
-
const mcpPath =
|
|
10826
|
+
const mcpPath = path54.join(cwd, ".mcp.json");
|
|
10432
10827
|
const config = readMcpConfig(mcpPath);
|
|
10433
10828
|
const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
|
|
10434
10829
|
const added = [];
|
|
@@ -10437,9 +10832,9 @@ function configureTier0Integrations(cwd) {
|
|
|
10437
10832
|
writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
|
|
10438
10833
|
added.push(integration.displayName);
|
|
10439
10834
|
}
|
|
10440
|
-
const geminiDir =
|
|
10835
|
+
const geminiDir = path54.join(cwd, ".gemini");
|
|
10441
10836
|
if (fs35.existsSync(geminiDir)) {
|
|
10442
|
-
const geminiPath =
|
|
10837
|
+
const geminiPath = path54.join(geminiDir, "settings.json");
|
|
10443
10838
|
const geminiConfig = readMcpConfig(geminiPath);
|
|
10444
10839
|
for (const integration of tier0) {
|
|
10445
10840
|
if (!geminiConfig.mcpServers[integration.name]) {
|
|
@@ -10447,8 +10842,8 @@ function configureTier0Integrations(cwd) {
|
|
|
10447
10842
|
}
|
|
10448
10843
|
}
|
|
10449
10844
|
}
|
|
10450
|
-
const opencodePath =
|
|
10451
|
-
const opencodeGlobalDir =
|
|
10845
|
+
const opencodePath = path54.join(cwd, "opencode.json");
|
|
10846
|
+
const opencodeGlobalDir = path54.join(os8.homedir(), ".config", "opencode");
|
|
10452
10847
|
if (fs35.existsSync(opencodePath) || fs35.existsSync(opencodeGlobalDir)) {
|
|
10453
10848
|
for (const integration of tier0) {
|
|
10454
10849
|
writeOpencodeMcpEntry(opencodePath, integration.name, integration.mcpConfig);
|
|
@@ -10467,12 +10862,12 @@ function configureTier0Integrations(cwd) {
|
|
|
10467
10862
|
}
|
|
10468
10863
|
}
|
|
10469
10864
|
function ensureHooks(cwd) {
|
|
10470
|
-
const configPath =
|
|
10865
|
+
const configPath = path54.join(cwd, "harness.config.json");
|
|
10471
10866
|
if (!fs35.existsSync(configPath)) {
|
|
10472
10867
|
return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
|
|
10473
10868
|
}
|
|
10474
10869
|
let profile = "standard";
|
|
10475
|
-
const profilePath =
|
|
10870
|
+
const profilePath = path54.join(cwd, ".harness", "hooks", "profile.json");
|
|
10476
10871
|
try {
|
|
10477
10872
|
const data = JSON.parse(fs35.readFileSync(profilePath, "utf-8"));
|
|
10478
10873
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
@@ -10506,7 +10901,7 @@ async function detectLegacyLayout(cwd) {
|
|
|
10506
10901
|
}
|
|
10507
10902
|
async function runInitialGraphScan(cwd) {
|
|
10508
10903
|
try {
|
|
10509
|
-
const { runScan: runScan2 } = await import("./scan-
|
|
10904
|
+
const { runScan: runScan2 } = await import("./scan-P7YFKB77.js");
|
|
10510
10905
|
const result = await runScan2(cwd);
|
|
10511
10906
|
return {
|
|
10512
10907
|
status: "pass",
|
|
@@ -10545,7 +10940,7 @@ async function runSetup(cwd) {
|
|
|
10545
10940
|
return { steps, success };
|
|
10546
10941
|
}
|
|
10547
10942
|
function createSetupCommand() {
|
|
10548
|
-
return new
|
|
10943
|
+
return new Command90("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
|
|
10549
10944
|
const cwd = process.cwd();
|
|
10550
10945
|
console.log("");
|
|
10551
10946
|
console.log(` ${chalk15.bold("harness setup")}`);
|
|
@@ -10568,14 +10963,14 @@ function createSetupCommand() {
|
|
|
10568
10963
|
|
|
10569
10964
|
// src/commands/share.ts
|
|
10570
10965
|
init_dist3();
|
|
10571
|
-
import { Command as
|
|
10966
|
+
import { Command as Command91 } from "commander";
|
|
10572
10967
|
import * as fs36 from "fs";
|
|
10573
|
-
import * as
|
|
10968
|
+
import * as path55 from "path";
|
|
10574
10969
|
import { parse as parseYaml3 } from "yaml";
|
|
10575
10970
|
var MANIFEST_FILENAME = "constraints.yaml";
|
|
10576
10971
|
async function runShareAction(projectPath, options) {
|
|
10577
|
-
const rootDir =
|
|
10578
|
-
const manifestPath =
|
|
10972
|
+
const rootDir = path55.resolve(projectPath);
|
|
10973
|
+
const manifestPath = path55.join(rootDir, MANIFEST_FILENAME);
|
|
10579
10974
|
if (!fs36.existsSync(manifestPath)) {
|
|
10580
10975
|
logger.error(
|
|
10581
10976
|
`No ${MANIFEST_FILENAME} found at ${manifestPath}.
|
|
@@ -10599,7 +10994,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10599
10994
|
process.exit(1);
|
|
10600
10995
|
}
|
|
10601
10996
|
const manifest = manifestResult.value;
|
|
10602
|
-
const configResult = resolveConfig(
|
|
10997
|
+
const configResult = resolveConfig(path55.join(rootDir, "harness.config.json"));
|
|
10603
10998
|
if (!configResult.ok) {
|
|
10604
10999
|
logger.error(configResult.error.message);
|
|
10605
11000
|
process.exit(1);
|
|
@@ -10617,8 +11012,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10617
11012
|
);
|
|
10618
11013
|
process.exit(1);
|
|
10619
11014
|
}
|
|
10620
|
-
const outputDir =
|
|
10621
|
-
const outputPath =
|
|
11015
|
+
const outputDir = path55.resolve(options.output);
|
|
11016
|
+
const outputPath = path55.join(outputDir, `${manifest.name}.harness-constraints.json`);
|
|
10622
11017
|
const writeResult = await writeConfig(outputPath, bundle);
|
|
10623
11018
|
if (!writeResult.ok) {
|
|
10624
11019
|
logger.error(`Failed to write bundle: ${writeResult.error.message}`);
|
|
@@ -10627,25 +11022,25 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10627
11022
|
logger.success(`Bundle written to ${outputPath}`);
|
|
10628
11023
|
}
|
|
10629
11024
|
function createShareCommand() {
|
|
10630
|
-
return new
|
|
11025
|
+
return new Command91("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) => {
|
|
10631
11026
|
await runShareAction(projectPath, options);
|
|
10632
11027
|
});
|
|
10633
11028
|
}
|
|
10634
11029
|
|
|
10635
11030
|
// src/commands/skill/index.ts
|
|
10636
|
-
import { Command as
|
|
11031
|
+
import { Command as Command99 } from "commander";
|
|
10637
11032
|
|
|
10638
11033
|
// src/commands/skill/list.ts
|
|
10639
|
-
import { Command as
|
|
11034
|
+
import { Command as Command92 } from "commander";
|
|
10640
11035
|
import * as fs37 from "fs";
|
|
10641
|
-
import * as
|
|
11036
|
+
import * as path56 from "path";
|
|
10642
11037
|
import { parse } from "yaml";
|
|
10643
11038
|
function scanDirectory(dirPath, source) {
|
|
10644
11039
|
if (!fs37.existsSync(dirPath)) return [];
|
|
10645
11040
|
const entries = fs37.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
10646
11041
|
const skills = [];
|
|
10647
11042
|
for (const name of entries) {
|
|
10648
|
-
const yamlPath =
|
|
11043
|
+
const yamlPath = path56.join(dirPath, name, "skill.yaml");
|
|
10649
11044
|
if (!fs37.existsSync(yamlPath)) continue;
|
|
10650
11045
|
try {
|
|
10651
11046
|
const raw = fs37.readFileSync(yamlPath, "utf-8");
|
|
@@ -10666,10 +11061,10 @@ function scanDirectory(dirPath, source) {
|
|
|
10666
11061
|
}
|
|
10667
11062
|
function collectCommunitySkills(seen, allSkills) {
|
|
10668
11063
|
const globalDir = resolveGlobalSkillsDir();
|
|
10669
|
-
const skillsDir =
|
|
10670
|
-
const communityBase =
|
|
10671
|
-
const communityPlatformDir =
|
|
10672
|
-
const lockfilePath =
|
|
11064
|
+
const skillsDir = path56.dirname(globalDir);
|
|
11065
|
+
const communityBase = path56.join(skillsDir, "community");
|
|
11066
|
+
const communityPlatformDir = path56.join(communityBase, "claude-code");
|
|
11067
|
+
const lockfilePath = path56.join(communityBase, "skills-lock.json");
|
|
10673
11068
|
const lockfile = readLockfile2(lockfilePath);
|
|
10674
11069
|
const communitySkills = scanDirectory(communityPlatformDir, "community");
|
|
10675
11070
|
for (const skill of communitySkills) {
|
|
@@ -10739,7 +11134,7 @@ function printSkillsVerbose(skills) {
|
|
|
10739
11134
|
for (const s of skills) printSkillEntry(s);
|
|
10740
11135
|
}
|
|
10741
11136
|
function createListCommand3() {
|
|
10742
|
-
return new
|
|
11137
|
+
return new Command92("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) => {
|
|
10743
11138
|
const globalOpts = cmd.optsWithGlobals();
|
|
10744
11139
|
const skills = collectSkills({ filter: resolveFilter(opts) });
|
|
10745
11140
|
if (globalOpts.json) {
|
|
@@ -10754,13 +11149,13 @@ function createListCommand3() {
|
|
|
10754
11149
|
}
|
|
10755
11150
|
|
|
10756
11151
|
// src/commands/skill/run.ts
|
|
10757
|
-
import { Command as
|
|
11152
|
+
import { Command as Command93 } from "commander";
|
|
10758
11153
|
import * as fs38 from "fs";
|
|
10759
|
-
import * as
|
|
11154
|
+
import * as path57 from "path";
|
|
10760
11155
|
import { parse as parse2 } from "yaml";
|
|
10761
11156
|
|
|
10762
11157
|
// src/skill/complexity.ts
|
|
10763
|
-
import { execFileSync as
|
|
11158
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
10764
11159
|
function evaluateSignals(signals) {
|
|
10765
11160
|
if (signals.fileCount >= 3) return "thorough";
|
|
10766
11161
|
if (signals.newDir) return "thorough";
|
|
@@ -10772,17 +11167,17 @@ function evaluateSignals(signals) {
|
|
|
10772
11167
|
}
|
|
10773
11168
|
function detectComplexity(projectPath) {
|
|
10774
11169
|
try {
|
|
10775
|
-
const base =
|
|
11170
|
+
const base = execFileSync6("git", ["merge-base", "HEAD", "main"], {
|
|
10776
11171
|
cwd: projectPath,
|
|
10777
11172
|
encoding: "utf-8",
|
|
10778
11173
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10779
11174
|
}).trim();
|
|
10780
|
-
const diffFiles =
|
|
11175
|
+
const diffFiles = execFileSync6("git", ["diff", "--name-only", base], {
|
|
10781
11176
|
cwd: projectPath,
|
|
10782
11177
|
encoding: "utf-8",
|
|
10783
11178
|
stdio: ["pipe", "pipe", "pipe"]
|
|
10784
11179
|
}).trim().split("\n").filter(Boolean);
|
|
10785
|
-
const diffStat =
|
|
11180
|
+
const diffStat = execFileSync6("git", ["diff", "--stat", base], {
|
|
10786
11181
|
cwd: projectPath,
|
|
10787
11182
|
encoding: "utf-8",
|
|
10788
11183
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -10852,7 +11247,7 @@ ${options.principles}`);
|
|
|
10852
11247
|
|
|
10853
11248
|
// src/commands/skill/run.ts
|
|
10854
11249
|
function loadSkillMetadata(skillDir) {
|
|
10855
|
-
const yamlPath =
|
|
11250
|
+
const yamlPath = path57.join(skillDir, "skill.yaml");
|
|
10856
11251
|
if (!fs38.existsSync(yamlPath)) return null;
|
|
10857
11252
|
try {
|
|
10858
11253
|
const result = SkillMetadataSchema.safeParse(parse2(fs38.readFileSync(yamlPath, "utf-8")));
|
|
@@ -10867,18 +11262,18 @@ function resolveComplexity(metadata, requested, projectPath) {
|
|
|
10867
11262
|
return requested;
|
|
10868
11263
|
}
|
|
10869
11264
|
function loadPrinciples(projectPath) {
|
|
10870
|
-
const principlesPath =
|
|
11265
|
+
const principlesPath = path57.join(projectPath, "docs", "principles.md");
|
|
10871
11266
|
return fs38.existsSync(principlesPath) ? fs38.readFileSync(principlesPath, "utf-8") : void 0;
|
|
10872
11267
|
}
|
|
10873
11268
|
function readMostRecentFileInDir(dirPath) {
|
|
10874
|
-
const files = fs38.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs38.statSync(
|
|
10875
|
-
if (files.length > 0) return fs38.readFileSync(
|
|
11269
|
+
const files = fs38.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs38.statSync(path57.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
11270
|
+
if (files.length > 0) return fs38.readFileSync(path57.join(dirPath, files[0].name), "utf-8");
|
|
10876
11271
|
return void 0;
|
|
10877
11272
|
}
|
|
10878
11273
|
function loadPriorState(metadata, projectPath) {
|
|
10879
11274
|
if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
|
|
10880
11275
|
for (const stateFilePath of metadata.state.files) {
|
|
10881
|
-
const fullPath =
|
|
11276
|
+
const fullPath = path57.join(projectPath, stateFilePath);
|
|
10882
11277
|
if (!fs38.existsSync(fullPath)) continue;
|
|
10883
11278
|
const stat = fs38.statSync(fullPath);
|
|
10884
11279
|
if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
|
|
@@ -10902,7 +11297,7 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
10902
11297
|
}
|
|
10903
11298
|
function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
10904
11299
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
10905
|
-
const stateFile =
|
|
11300
|
+
const stateFile = path57.join(projectPath, ".harness", "state.json");
|
|
10906
11301
|
if (!fs38.existsSync(stateFile)) return content;
|
|
10907
11302
|
const stateContent = fs38.readFileSync(stateFile, "utf-8");
|
|
10908
11303
|
return content + `
|
|
@@ -10916,14 +11311,14 @@ ${stateContent}
|
|
|
10916
11311
|
}
|
|
10917
11312
|
async function runSkill(name, opts) {
|
|
10918
11313
|
const skillsDir = resolveSkillsDir();
|
|
10919
|
-
const skillDir =
|
|
11314
|
+
const skillDir = path57.join(skillsDir, name);
|
|
10920
11315
|
if (!fs38.existsSync(skillDir)) {
|
|
10921
11316
|
logger.error(`Skill not found: ${name}`);
|
|
10922
11317
|
process.exit(ExitCode.ERROR);
|
|
10923
11318
|
return;
|
|
10924
11319
|
}
|
|
10925
11320
|
const metadata = loadSkillMetadata(skillDir);
|
|
10926
|
-
const projectPath = opts.path ?
|
|
11321
|
+
const projectPath = opts.path ? path57.resolve(opts.path) : process.cwd();
|
|
10927
11322
|
const complexity = resolveComplexity(
|
|
10928
11323
|
metadata,
|
|
10929
11324
|
opts.complexity ?? "standard",
|
|
@@ -10950,7 +11345,7 @@ async function runSkill(name, opts) {
|
|
|
10950
11345
|
...stateWarning !== void 0 && { stateWarning },
|
|
10951
11346
|
...opts.party !== void 0 && { party: opts.party }
|
|
10952
11347
|
});
|
|
10953
|
-
const skillMdPath =
|
|
11348
|
+
const skillMdPath = path57.join(skillDir, "SKILL.md");
|
|
10954
11349
|
if (!fs38.existsSync(skillMdPath)) {
|
|
10955
11350
|
logger.error(`SKILL.md not found for skill: ${name}`);
|
|
10956
11351
|
process.exit(ExitCode.ERROR);
|
|
@@ -10968,16 +11363,16 @@ async function runSkill(name, opts) {
|
|
|
10968
11363
|
process.exit(ExitCode.SUCCESS);
|
|
10969
11364
|
}
|
|
10970
11365
|
function createRunCommand3() {
|
|
10971
|
-
return new
|
|
11366
|
+
return new Command93("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(
|
|
10972
11367
|
"--backend <name>",
|
|
10973
11368
|
"Spec B: one-shot routing override forwarded to the orchestrator as HARNESS_BACKEND_OVERRIDE"
|
|
10974
11369
|
).action(async (name, opts) => runSkill(name, opts));
|
|
10975
11370
|
}
|
|
10976
11371
|
|
|
10977
11372
|
// src/commands/skill/validate.ts
|
|
10978
|
-
import { Command as
|
|
11373
|
+
import { Command as Command94 } from "commander";
|
|
10979
11374
|
import * as fs39 from "fs";
|
|
10980
|
-
import * as
|
|
11375
|
+
import * as path58 from "path";
|
|
10981
11376
|
import { parse as parse3 } from "yaml";
|
|
10982
11377
|
var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
10983
11378
|
"## When to Use",
|
|
@@ -11018,8 +11413,8 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
|
11018
11413
|
}
|
|
11019
11414
|
}
|
|
11020
11415
|
function validateSkillEntry(name, skillsDir, errors) {
|
|
11021
|
-
const skillDir =
|
|
11022
|
-
const yamlPath =
|
|
11416
|
+
const skillDir = path58.join(skillsDir, name);
|
|
11417
|
+
const yamlPath = path58.join(skillDir, "skill.yaml");
|
|
11023
11418
|
if (!fs39.existsSync(yamlPath)) {
|
|
11024
11419
|
errors.push(`${name}: missing skill.yaml`);
|
|
11025
11420
|
return false;
|
|
@@ -11031,7 +11426,7 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
11031
11426
|
errors.push(`${name}/skill.yaml: ${result.error.message}`);
|
|
11032
11427
|
return false;
|
|
11033
11428
|
}
|
|
11034
|
-
validateSkillMd(name,
|
|
11429
|
+
validateSkillMd(name, path58.join(skillDir, "SKILL.md"), result.data.type, errors);
|
|
11035
11430
|
return true;
|
|
11036
11431
|
} catch (e) {
|
|
11037
11432
|
errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -11039,7 +11434,7 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
11039
11434
|
}
|
|
11040
11435
|
}
|
|
11041
11436
|
function createValidateCommand2() {
|
|
11042
|
-
return new
|
|
11437
|
+
return new Command94("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
11043
11438
|
const globalOpts = cmd.optsWithGlobals();
|
|
11044
11439
|
const skillsDir = resolveSkillsDir();
|
|
11045
11440
|
if (!fs39.existsSync(skillsDir)) {
|
|
@@ -11069,18 +11464,18 @@ function createValidateCommand2() {
|
|
|
11069
11464
|
}
|
|
11070
11465
|
|
|
11071
11466
|
// src/commands/skill/info.ts
|
|
11072
|
-
import { Command as
|
|
11467
|
+
import { Command as Command95 } from "commander";
|
|
11073
11468
|
import * as fs40 from "fs";
|
|
11074
|
-
import * as
|
|
11469
|
+
import * as path59 from "path";
|
|
11075
11470
|
import { parse as parse4 } from "yaml";
|
|
11076
11471
|
function loadSkillMetadata2(name) {
|
|
11077
11472
|
const skillsDir = resolveSkillsDir();
|
|
11078
|
-
const skillDir =
|
|
11473
|
+
const skillDir = path59.join(skillsDir, name);
|
|
11079
11474
|
if (!fs40.existsSync(skillDir)) {
|
|
11080
11475
|
logger.error(`Skill not found: ${name}`);
|
|
11081
11476
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
11082
11477
|
}
|
|
11083
|
-
const yamlPath =
|
|
11478
|
+
const yamlPath = path59.join(skillDir, "skill.yaml");
|
|
11084
11479
|
if (!fs40.existsSync(yamlPath)) {
|
|
11085
11480
|
logger.error(`skill.yaml not found for skill: ${name}`);
|
|
11086
11481
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
@@ -11115,7 +11510,7 @@ function printSkillInfo(skill) {
|
|
|
11115
11510
|
console.log(`Persistent: ${skill.state.persistent}`);
|
|
11116
11511
|
}
|
|
11117
11512
|
function createInfoCommand() {
|
|
11118
|
-
return new
|
|
11513
|
+
return new Command95("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
|
|
11119
11514
|
const globalOpts = cmd.optsWithGlobals();
|
|
11120
11515
|
const loaded = loadSkillMetadata2(name);
|
|
11121
11516
|
if (!loaded.ok) {
|
|
@@ -11132,7 +11527,7 @@ function createInfoCommand() {
|
|
|
11132
11527
|
}
|
|
11133
11528
|
|
|
11134
11529
|
// src/commands/skill/search.ts
|
|
11135
|
-
import { Command as
|
|
11530
|
+
import { Command as Command96 } from "commander";
|
|
11136
11531
|
async function runSearch(query, opts) {
|
|
11137
11532
|
const results = await searchNpmRegistry(query, opts.registry);
|
|
11138
11533
|
return results.filter((r) => {
|
|
@@ -11177,14 +11572,14 @@ async function runSearchAction(query, opts, globalOpts) {
|
|
|
11177
11572
|
}
|
|
11178
11573
|
}
|
|
11179
11574
|
function createSearchCommand2() {
|
|
11180
|
-
return new
|
|
11575
|
+
return new Command96("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) => {
|
|
11181
11576
|
await runSearchAction(query, opts, cmd.optsWithGlobals());
|
|
11182
11577
|
});
|
|
11183
11578
|
}
|
|
11184
11579
|
|
|
11185
11580
|
// src/commands/skill/create.ts
|
|
11186
|
-
import { Command as
|
|
11187
|
-
import * as
|
|
11581
|
+
import { Command as Command97 } from "commander";
|
|
11582
|
+
import * as path60 from "path";
|
|
11188
11583
|
import * as fs41 from "fs";
|
|
11189
11584
|
import YAML from "yaml";
|
|
11190
11585
|
var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
@@ -11269,21 +11664,21 @@ function runCreate(name, opts) {
|
|
|
11269
11664
|
if (!KEBAB_CASE_RE.test(name)) {
|
|
11270
11665
|
throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
|
|
11271
11666
|
}
|
|
11272
|
-
const baseDir = opts.outputDir ??
|
|
11273
|
-
const skillDir =
|
|
11667
|
+
const baseDir = opts.outputDir ?? path60.join(process.cwd(), "agents", "skills", "claude-code");
|
|
11668
|
+
const skillDir = path60.join(baseDir, name);
|
|
11274
11669
|
if (fs41.existsSync(skillDir)) {
|
|
11275
11670
|
throw new Error(`Skill directory already exists: ${skillDir}`);
|
|
11276
11671
|
}
|
|
11277
11672
|
fs41.mkdirSync(skillDir, { recursive: true });
|
|
11278
11673
|
const description = opts.description || `A community skill: ${name}`;
|
|
11279
11674
|
const skillYaml = buildSkillYaml(name, opts);
|
|
11280
|
-
const skillYamlPath =
|
|
11675
|
+
const skillYamlPath = path60.join(skillDir, "skill.yaml");
|
|
11281
11676
|
fs41.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
|
|
11282
11677
|
const skillMd = buildSkillMd(name, description);
|
|
11283
|
-
const skillMdPath =
|
|
11678
|
+
const skillMdPath = path60.join(skillDir, "SKILL.md");
|
|
11284
11679
|
fs41.writeFileSync(skillMdPath, skillMd);
|
|
11285
11680
|
const readme = buildReadme(name, description);
|
|
11286
|
-
const readmePath =
|
|
11681
|
+
const readmePath = path60.join(skillDir, "README.md");
|
|
11287
11682
|
fs41.writeFileSync(readmePath, readme);
|
|
11288
11683
|
return {
|
|
11289
11684
|
name,
|
|
@@ -11298,12 +11693,12 @@ function printCreateResult(name, result) {
|
|
|
11298
11693
|
}
|
|
11299
11694
|
logger.info(`
|
|
11300
11695
|
Next steps:`);
|
|
11301
|
-
logger.info(` 1. Edit ${
|
|
11696
|
+
logger.info(` 1. Edit ${path60.join(result.directory, "SKILL.md")} with your skill content`);
|
|
11302
11697
|
logger.info(` 2. Run: harness skill validate ${name}`);
|
|
11303
11698
|
logger.info(` 3. Run: harness skills publish`);
|
|
11304
11699
|
}
|
|
11305
11700
|
function createCreateCommand() {
|
|
11306
|
-
return new
|
|
11701
|
+
return new Command97("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) => {
|
|
11307
11702
|
const globalOpts = cmd.optsWithGlobals();
|
|
11308
11703
|
try {
|
|
11309
11704
|
const result = runCreate(name, {
|
|
@@ -11326,19 +11721,19 @@ function createCreateCommand() {
|
|
|
11326
11721
|
}
|
|
11327
11722
|
|
|
11328
11723
|
// src/commands/skill/publish.ts
|
|
11329
|
-
import { Command as
|
|
11724
|
+
import { Command as Command98 } from "commander";
|
|
11330
11725
|
import * as fs43 from "fs";
|
|
11331
|
-
import * as
|
|
11332
|
-
import { execFileSync as
|
|
11726
|
+
import * as path62 from "path";
|
|
11727
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
11333
11728
|
|
|
11334
11729
|
// src/registry/validator.ts
|
|
11335
11730
|
import * as fs42 from "fs";
|
|
11336
|
-
import * as
|
|
11731
|
+
import * as path61 from "path";
|
|
11337
11732
|
import { parse as parse5 } from "yaml";
|
|
11338
11733
|
import semver4 from "semver";
|
|
11339
11734
|
async function validateForPublish(skillDir, registryUrl) {
|
|
11340
11735
|
const errors = [];
|
|
11341
|
-
const skillYamlPath =
|
|
11736
|
+
const skillYamlPath = path61.join(skillDir, "skill.yaml");
|
|
11342
11737
|
if (!fs42.existsSync(skillYamlPath)) {
|
|
11343
11738
|
errors.push("skill.yaml not found. Create one with: harness skill create <name>");
|
|
11344
11739
|
return { valid: false, errors };
|
|
@@ -11367,7 +11762,7 @@ async function validateForPublish(skillDir, registryUrl) {
|
|
|
11367
11762
|
if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
|
|
11368
11763
|
errors.push("At least one trigger is required. Add triggers to skill.yaml.");
|
|
11369
11764
|
}
|
|
11370
|
-
const skillMdPath =
|
|
11765
|
+
const skillMdPath = path61.join(skillDir, "SKILL.md");
|
|
11371
11766
|
if (!fs42.existsSync(skillMdPath)) {
|
|
11372
11767
|
errors.push("SKILL.md not found. Create it with content describing your skill.");
|
|
11373
11768
|
} else {
|
|
@@ -11451,11 +11846,11 @@ ${errorList}`);
|
|
|
11451
11846
|
}
|
|
11452
11847
|
const meta = validation.skillMeta;
|
|
11453
11848
|
const pkg = derivePackageJson(meta);
|
|
11454
|
-
const pkgPath =
|
|
11849
|
+
const pkgPath = path62.join(skillDir, "package.json");
|
|
11455
11850
|
fs43.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
11456
|
-
const readmePath =
|
|
11851
|
+
const readmePath = path62.join(skillDir, "README.md");
|
|
11457
11852
|
if (!fs43.existsSync(readmePath)) {
|
|
11458
|
-
const skillMdContent = fs43.readFileSync(
|
|
11853
|
+
const skillMdContent = fs43.readFileSync(path62.join(skillDir, "SKILL.md"), "utf-8");
|
|
11459
11854
|
const readme = `# ${pkg.name}
|
|
11460
11855
|
|
|
11461
11856
|
${meta.description}
|
|
@@ -11483,7 +11878,7 @@ ${skillMdContent}`;
|
|
|
11483
11878
|
if (opts.registry) {
|
|
11484
11879
|
publishArgs.push("--registry", opts.registry);
|
|
11485
11880
|
}
|
|
11486
|
-
|
|
11881
|
+
execFileSync7("npm", publishArgs, {
|
|
11487
11882
|
cwd: skillDir,
|
|
11488
11883
|
stdio: "pipe",
|
|
11489
11884
|
timeout: 6e4
|
|
@@ -11495,7 +11890,7 @@ ${skillMdContent}`;
|
|
|
11495
11890
|
};
|
|
11496
11891
|
}
|
|
11497
11892
|
function createPublishCommand() {
|
|
11498
|
-
return new
|
|
11893
|
+
return new Command98("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) => {
|
|
11499
11894
|
const globalOpts = cmd.optsWithGlobals();
|
|
11500
11895
|
const skillDir = opts.dir || process.cwd();
|
|
11501
11896
|
try {
|
|
@@ -11520,7 +11915,7 @@ function createPublishCommand() {
|
|
|
11520
11915
|
|
|
11521
11916
|
// src/commands/skill/index.ts
|
|
11522
11917
|
function createSkillCommand() {
|
|
11523
|
-
const command = new
|
|
11918
|
+
const command = new Command99("skill").description("Skill management commands");
|
|
11524
11919
|
command.addCommand(createListCommand3());
|
|
11525
11920
|
command.addCommand(createRunCommand3());
|
|
11526
11921
|
command.addCommand(createValidateCommand2());
|
|
@@ -11533,7 +11928,7 @@ function createSkillCommand() {
|
|
|
11533
11928
|
|
|
11534
11929
|
// src/commands/snapshot.ts
|
|
11535
11930
|
init_dist3();
|
|
11536
|
-
import { Command as
|
|
11931
|
+
import { Command as Command100 } from "commander";
|
|
11537
11932
|
import { execSync as execSync5 } from "child_process";
|
|
11538
11933
|
import chalk16 from "chalk";
|
|
11539
11934
|
function getCommitHash3(cwd) {
|
|
@@ -11715,7 +12110,7 @@ function registerListCommand(parent) {
|
|
|
11715
12110
|
});
|
|
11716
12111
|
}
|
|
11717
12112
|
function createSnapshotCommand() {
|
|
11718
|
-
const command = new
|
|
12113
|
+
const command = new Command100("snapshot").description("Architecture timeline snapshot commands");
|
|
11719
12114
|
registerCaptureCommand(command);
|
|
11720
12115
|
registerTrendsCommand(command);
|
|
11721
12116
|
registerListCommand(command);
|
|
@@ -11723,9 +12118,9 @@ function createSnapshotCommand() {
|
|
|
11723
12118
|
}
|
|
11724
12119
|
|
|
11725
12120
|
// src/commands/spec-craft.ts
|
|
11726
|
-
import { Command as
|
|
12121
|
+
import { Command as Command101 } from "commander";
|
|
11727
12122
|
function createSpecCraftCommand() {
|
|
11728
|
-
return new
|
|
12123
|
+
return new Command101("spec-craft").description(
|
|
11729
12124
|
"LLM-judgment critique of spec quality (proposals + ADRs). Second craft-pipeline ceiling skill; 7 seed rubrics from the spec-quality canon. Per-section critique."
|
|
11730
12125
|
).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) => {
|
|
11731
12126
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -11773,10 +12168,10 @@ function printResult6(result, mode, _formatter) {
|
|
|
11773
12168
|
list.push(f);
|
|
11774
12169
|
byFile.set(f.target.file, list);
|
|
11775
12170
|
}
|
|
11776
|
-
for (const [file,
|
|
12171
|
+
for (const [file, fs49] of byFile) {
|
|
11777
12172
|
console.log(`
|
|
11778
12173
|
${file}`);
|
|
11779
|
-
for (const f of
|
|
12174
|
+
for (const f of fs49) {
|
|
11780
12175
|
console.log(
|
|
11781
12176
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ## ${f.target.section}:${f.target.line}`
|
|
11782
12177
|
);
|
|
@@ -11792,12 +12187,12 @@ ${file}`);
|
|
|
11792
12187
|
}
|
|
11793
12188
|
|
|
11794
12189
|
// src/commands/state/index.ts
|
|
11795
|
-
import { Command as
|
|
12190
|
+
import { Command as Command106 } from "commander";
|
|
11796
12191
|
|
|
11797
12192
|
// src/commands/state/show.ts
|
|
11798
12193
|
init_dist3();
|
|
11799
|
-
import { Command as
|
|
11800
|
-
import * as
|
|
12194
|
+
import { Command as Command102 } from "commander";
|
|
12195
|
+
import * as path63 from "path";
|
|
11801
12196
|
function printStateText(state, stream) {
|
|
11802
12197
|
if (stream) console.log(`Stream: ${stream}`);
|
|
11803
12198
|
console.log(`Schema Version: ${state.schemaVersion}`);
|
|
@@ -11821,9 +12216,9 @@ function printStateProgress(progress) {
|
|
|
11821
12216
|
}
|
|
11822
12217
|
}
|
|
11823
12218
|
function createShowCommand() {
|
|
11824
|
-
return new
|
|
12219
|
+
return new Command102("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
|
|
11825
12220
|
const globalOpts = cmd.optsWithGlobals();
|
|
11826
|
-
const result = await loadState(
|
|
12221
|
+
const result = await loadState(path63.resolve(opts.path), opts.stream);
|
|
11827
12222
|
if (!result.ok) {
|
|
11828
12223
|
logger.error(result.error.message);
|
|
11829
12224
|
process.exit(ExitCode.ERROR);
|
|
@@ -11839,9 +12234,9 @@ function createShowCommand() {
|
|
|
11839
12234
|
|
|
11840
12235
|
// src/commands/state/reset.ts
|
|
11841
12236
|
init_dist3();
|
|
11842
|
-
import { Command as
|
|
12237
|
+
import { Command as Command103 } from "commander";
|
|
11843
12238
|
import * as fs44 from "fs";
|
|
11844
|
-
import * as
|
|
12239
|
+
import * as path64 from "path";
|
|
11845
12240
|
import * as readline3 from "readline";
|
|
11846
12241
|
async function resolveStatePath(projectPath, stream) {
|
|
11847
12242
|
if (stream) {
|
|
@@ -11850,20 +12245,20 @@ async function resolveStatePath(projectPath, stream) {
|
|
|
11850
12245
|
logger.error(streamResult.error.message);
|
|
11851
12246
|
process.exit(ExitCode.ERROR);
|
|
11852
12247
|
}
|
|
11853
|
-
return
|
|
12248
|
+
return path64.join(streamResult.value, "state.json");
|
|
11854
12249
|
}
|
|
11855
|
-
return
|
|
12250
|
+
return path64.join(projectPath, ".harness", "state.json");
|
|
11856
12251
|
}
|
|
11857
12252
|
async function confirmReset() {
|
|
11858
12253
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
11859
|
-
const answer = await new Promise((
|
|
11860
|
-
rl.question("Reset project state? This cannot be undone. [y/N] ",
|
|
12254
|
+
const answer = await new Promise((resolve47) => {
|
|
12255
|
+
rl.question("Reset project state? This cannot be undone. [y/N] ", resolve47);
|
|
11861
12256
|
});
|
|
11862
12257
|
rl.close();
|
|
11863
12258
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
11864
12259
|
}
|
|
11865
12260
|
async function runReset(opts) {
|
|
11866
|
-
const projectPath =
|
|
12261
|
+
const projectPath = path64.resolve(opts.path);
|
|
11867
12262
|
const statePath = await resolveStatePath(projectPath, opts.stream);
|
|
11868
12263
|
if (!fs44.existsSync(statePath)) {
|
|
11869
12264
|
logger.info("No state file found. Nothing to reset.");
|
|
@@ -11883,16 +12278,16 @@ async function runReset(opts) {
|
|
|
11883
12278
|
process.exit(ExitCode.SUCCESS);
|
|
11884
12279
|
}
|
|
11885
12280
|
function createResetCommand() {
|
|
11886
|
-
return new
|
|
12281
|
+
return new Command103("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));
|
|
11887
12282
|
}
|
|
11888
12283
|
|
|
11889
12284
|
// src/commands/state/learn.ts
|
|
11890
12285
|
init_dist3();
|
|
11891
|
-
import { Command as
|
|
11892
|
-
import * as
|
|
12286
|
+
import { Command as Command104 } from "commander";
|
|
12287
|
+
import * as path65 from "path";
|
|
11893
12288
|
function createLearnCommand() {
|
|
11894
|
-
return new
|
|
11895
|
-
const projectPath =
|
|
12289
|
+
return new Command104("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) => {
|
|
12290
|
+
const projectPath = path65.resolve(opts.path);
|
|
11896
12291
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
11897
12292
|
if (!result.ok) {
|
|
11898
12293
|
logger.error(result.error.message);
|
|
@@ -11906,10 +12301,10 @@ function createLearnCommand() {
|
|
|
11906
12301
|
|
|
11907
12302
|
// src/commands/state/streams.ts
|
|
11908
12303
|
init_dist3();
|
|
11909
|
-
import { Command as
|
|
11910
|
-
import * as
|
|
12304
|
+
import { Command as Command105 } from "commander";
|
|
12305
|
+
import * as path66 from "path";
|
|
11911
12306
|
async function runListStreams(opts, globalOpts) {
|
|
11912
|
-
const projectPath =
|
|
12307
|
+
const projectPath = path66.resolve(opts.path);
|
|
11913
12308
|
const indexResult = await loadStreamIndex(projectPath);
|
|
11914
12309
|
const result = await listStreams(projectPath);
|
|
11915
12310
|
if (!result.ok) {
|
|
@@ -11930,7 +12325,7 @@ async function runListStreams(opts, globalOpts) {
|
|
|
11930
12325
|
process.exit(ExitCode.SUCCESS);
|
|
11931
12326
|
}
|
|
11932
12327
|
async function runCreateStream(name, opts) {
|
|
11933
|
-
const result = await createStream(
|
|
12328
|
+
const result = await createStream(path66.resolve(opts.path), name, opts.branch);
|
|
11934
12329
|
if (!result.ok) {
|
|
11935
12330
|
logger.error(result.error.message);
|
|
11936
12331
|
process.exit(ExitCode.ERROR);
|
|
@@ -11939,7 +12334,7 @@ async function runCreateStream(name, opts) {
|
|
|
11939
12334
|
process.exit(ExitCode.SUCCESS);
|
|
11940
12335
|
}
|
|
11941
12336
|
async function runArchiveStream(name, opts) {
|
|
11942
|
-
const result = await archiveStream(
|
|
12337
|
+
const result = await archiveStream(path66.resolve(opts.path), name);
|
|
11943
12338
|
if (!result.ok) {
|
|
11944
12339
|
logger.error(result.error.message);
|
|
11945
12340
|
process.exit(ExitCode.ERROR);
|
|
@@ -11948,7 +12343,7 @@ async function runArchiveStream(name, opts) {
|
|
|
11948
12343
|
process.exit(ExitCode.SUCCESS);
|
|
11949
12344
|
}
|
|
11950
12345
|
async function runActivateStream(name, opts) {
|
|
11951
|
-
const result = await setActiveStream(
|
|
12346
|
+
const result = await setActiveStream(path66.resolve(opts.path), name);
|
|
11952
12347
|
if (!result.ok) {
|
|
11953
12348
|
logger.error(result.error.message);
|
|
11954
12349
|
process.exit(ExitCode.ERROR);
|
|
@@ -11957,7 +12352,7 @@ async function runActivateStream(name, opts) {
|
|
|
11957
12352
|
process.exit(ExitCode.SUCCESS);
|
|
11958
12353
|
}
|
|
11959
12354
|
function createStreamsCommand() {
|
|
11960
|
-
const command = new
|
|
12355
|
+
const command = new Command105("streams").description("Manage state streams");
|
|
11961
12356
|
command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
|
|
11962
12357
|
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));
|
|
11963
12358
|
command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
|
|
@@ -11967,7 +12362,7 @@ function createStreamsCommand() {
|
|
|
11967
12362
|
|
|
11968
12363
|
// src/commands/state/index.ts
|
|
11969
12364
|
function createStateCommand() {
|
|
11970
|
-
const command = new
|
|
12365
|
+
const command = new Command106("state").description("Project state management commands");
|
|
11971
12366
|
command.addCommand(createShowCommand());
|
|
11972
12367
|
command.addCommand(createResetCommand());
|
|
11973
12368
|
command.addCommand(createLearnCommand());
|
|
@@ -11977,8 +12372,8 @@ function createStateCommand() {
|
|
|
11977
12372
|
|
|
11978
12373
|
// src/commands/sync-analyses.ts
|
|
11979
12374
|
import * as fs45 from "fs";
|
|
11980
|
-
import * as
|
|
11981
|
-
import { Command as
|
|
12375
|
+
import * as path67 from "path";
|
|
12376
|
+
import { Command as Command107 } from "commander";
|
|
11982
12377
|
init_dist3();
|
|
11983
12378
|
function isValidRecord(record) {
|
|
11984
12379
|
return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
|
|
@@ -12004,7 +12399,7 @@ function extractAnalysisFromComments(comments) {
|
|
|
12004
12399
|
return null;
|
|
12005
12400
|
}
|
|
12006
12401
|
function bootstrapTrackerCommand2(opts, verb) {
|
|
12007
|
-
const projectPath =
|
|
12402
|
+
const projectPath = path67.resolve(opts.dir);
|
|
12008
12403
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
12009
12404
|
if (!trackerConfig) {
|
|
12010
12405
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -12016,7 +12411,7 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
12016
12411
|
);
|
|
12017
12412
|
return null;
|
|
12018
12413
|
}
|
|
12019
|
-
const projectEnvPath =
|
|
12414
|
+
const projectEnvPath = path67.join(projectPath, ".env");
|
|
12020
12415
|
if (fs45.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
12021
12416
|
__require("dotenv").config({ path: projectEnvPath });
|
|
12022
12417
|
}
|
|
@@ -12047,7 +12442,7 @@ async function loadRoadmapFeatures(projectPath) {
|
|
|
12047
12442
|
}
|
|
12048
12443
|
return r.value.features.filter((f) => !!f.externalId).map((f) => ({ name: f.name, externalId: f.externalId }));
|
|
12049
12444
|
}
|
|
12050
|
-
const roadmapFile =
|
|
12445
|
+
const roadmapFile = path67.join(projectPath, "docs", "roadmap.md");
|
|
12051
12446
|
if (!fs45.existsSync(roadmapFile)) {
|
|
12052
12447
|
logger.error("No docs/roadmap.md found. Cannot discover features with externalIds.");
|
|
12053
12448
|
return null;
|
|
@@ -12104,9 +12499,9 @@ async function runSyncAnalyses(opts) {
|
|
|
12104
12499
|
return;
|
|
12105
12500
|
}
|
|
12106
12501
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
12107
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
12502
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-LHINSVK4.js");
|
|
12108
12503
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
12109
|
-
const archive = new AnalysisArchive(
|
|
12504
|
+
const archive = new AnalysisArchive(path67.join(projectPath, ".harness", "analyses"));
|
|
12110
12505
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
12111
12506
|
const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
|
|
12112
12507
|
features,
|
|
@@ -12126,7 +12521,7 @@ async function runSyncAnalyses(opts) {
|
|
|
12126
12521
|
}
|
|
12127
12522
|
}
|
|
12128
12523
|
function createSyncAnalysesCommand() {
|
|
12129
|
-
const command = new
|
|
12524
|
+
const command = new Command107("sync-analyses").description(
|
|
12130
12525
|
"Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
|
|
12131
12526
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
12132
12527
|
try {
|
|
@@ -12140,8 +12535,8 @@ function createSyncAnalysesCommand() {
|
|
|
12140
12535
|
}
|
|
12141
12536
|
|
|
12142
12537
|
// src/commands/sync-main.ts
|
|
12143
|
-
import { Command as
|
|
12144
|
-
import * as
|
|
12538
|
+
import { Command as Command108 } from "commander";
|
|
12539
|
+
import * as path68 from "path";
|
|
12145
12540
|
import {
|
|
12146
12541
|
syncMain as defaultSyncMain
|
|
12147
12542
|
} from "@harness-engineering/orchestrator";
|
|
@@ -12171,8 +12566,8 @@ function formatHuman2(r) {
|
|
|
12171
12566
|
}
|
|
12172
12567
|
}
|
|
12173
12568
|
function createSyncMainCommand() {
|
|
12174
|
-
return new
|
|
12175
|
-
const cwd =
|
|
12569
|
+
return new Command108("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) => {
|
|
12570
|
+
const cwd = path68.resolve(opts.path ?? ".");
|
|
12176
12571
|
const exitCode = await runSyncMain({ cwd, json: Boolean(opts.json) });
|
|
12177
12572
|
process.exit(exitCode);
|
|
12178
12573
|
});
|
|
@@ -12180,7 +12575,7 @@ function createSyncMainCommand() {
|
|
|
12180
12575
|
|
|
12181
12576
|
// src/commands/taint.ts
|
|
12182
12577
|
init_dist3();
|
|
12183
|
-
import { Command as
|
|
12578
|
+
import { Command as Command109 } from "commander";
|
|
12184
12579
|
function getProjectRoot() {
|
|
12185
12580
|
return process.cwd();
|
|
12186
12581
|
}
|
|
@@ -12252,26 +12647,26 @@ function registerStatusCommand(taint) {
|
|
|
12252
12647
|
});
|
|
12253
12648
|
}
|
|
12254
12649
|
function createTaintCommand() {
|
|
12255
|
-
const taint = new
|
|
12650
|
+
const taint = new Command109("taint").description("Manage sentinel session taint state");
|
|
12256
12651
|
registerClearCommand(taint);
|
|
12257
12652
|
registerStatusCommand(taint);
|
|
12258
12653
|
return taint;
|
|
12259
12654
|
}
|
|
12260
12655
|
|
|
12261
12656
|
// src/commands/telemetry/index.ts
|
|
12262
|
-
import { Command as
|
|
12657
|
+
import { Command as Command113 } from "commander";
|
|
12263
12658
|
|
|
12264
12659
|
// src/commands/telemetry/identify.ts
|
|
12265
12660
|
init_dist3();
|
|
12266
|
-
import { Command as
|
|
12661
|
+
import { Command as Command110 } from "commander";
|
|
12267
12662
|
import * as fs46 from "fs";
|
|
12268
|
-
import * as
|
|
12663
|
+
import * as path69 from "path";
|
|
12269
12664
|
function telemetryFilePath(cwd) {
|
|
12270
|
-
return
|
|
12665
|
+
return path69.join(cwd, ".harness", "telemetry.json");
|
|
12271
12666
|
}
|
|
12272
12667
|
function writeTelemetryFile(cwd, data) {
|
|
12273
12668
|
const filePath = telemetryFilePath(cwd);
|
|
12274
|
-
const dir =
|
|
12669
|
+
const dir = path69.dirname(filePath);
|
|
12275
12670
|
fs46.mkdirSync(dir, { recursive: true });
|
|
12276
12671
|
fs46.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
12277
12672
|
}
|
|
@@ -12287,7 +12682,7 @@ function printIdentity(identity) {
|
|
|
12287
12682
|
}
|
|
12288
12683
|
}
|
|
12289
12684
|
function createIdentifyCommand() {
|
|
12290
|
-
const cmd = new
|
|
12685
|
+
const cmd = new Command110("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) => {
|
|
12291
12686
|
const cwd = process.cwd();
|
|
12292
12687
|
if (opts.clear) {
|
|
12293
12688
|
writeTelemetryFile(cwd, { identity: {} });
|
|
@@ -12312,7 +12707,7 @@ function createIdentifyCommand() {
|
|
|
12312
12707
|
|
|
12313
12708
|
// src/commands/telemetry/status.ts
|
|
12314
12709
|
init_dist3();
|
|
12315
|
-
import { Command as
|
|
12710
|
+
import { Command as Command111 } from "commander";
|
|
12316
12711
|
function gatherEnvOverrides() {
|
|
12317
12712
|
const overrides = {};
|
|
12318
12713
|
if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
|
|
@@ -12354,7 +12749,7 @@ function printHumanStatus(result) {
|
|
|
12354
12749
|
printEnvOverrides(result.envOverrides);
|
|
12355
12750
|
}
|
|
12356
12751
|
function createStatusCommand() {
|
|
12357
|
-
const cmd = new
|
|
12752
|
+
const cmd = new Command111("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
|
|
12358
12753
|
const cwd = process.cwd();
|
|
12359
12754
|
const envOverrides = gatherEnvOverrides();
|
|
12360
12755
|
const consent = resolveConsent(cwd, void 0);
|
|
@@ -12387,20 +12782,20 @@ function createStatusCommand() {
|
|
|
12387
12782
|
|
|
12388
12783
|
// src/commands/telemetry/test.ts
|
|
12389
12784
|
init_dist3();
|
|
12390
|
-
import { Command as
|
|
12785
|
+
import { Command as Command112 } from "commander";
|
|
12391
12786
|
|
|
12392
12787
|
// src/bin/command-telemetry.ts
|
|
12393
|
-
import { existsSync as existsSync43, readFileSync as readFileSync33, mkdirSync as mkdirSync19, appendFileSync, writeFileSync as
|
|
12394
|
-
import { join as join57, parse as parsePath, resolve as
|
|
12788
|
+
import { existsSync as existsSync43, readFileSync as readFileSync33, mkdirSync as mkdirSync19, appendFileSync, writeFileSync as writeFileSync26 } from "fs";
|
|
12789
|
+
import { join as join57, parse as parsePath, resolve as resolve44 } from "path";
|
|
12395
12790
|
import { spawn as spawn2 } from "child_process";
|
|
12396
12791
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
12397
12792
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
12398
12793
|
function findProjectRoot(cwd) {
|
|
12399
|
-
let dir =
|
|
12794
|
+
let dir = resolve44(cwd);
|
|
12400
12795
|
const { root } = parsePath(dir);
|
|
12401
12796
|
while (dir !== root) {
|
|
12402
12797
|
if (existsSync43(join57(dir, "harness.config.json"))) return dir;
|
|
12403
|
-
dir =
|
|
12798
|
+
dir = resolve44(dir, "..");
|
|
12404
12799
|
}
|
|
12405
12800
|
return cwd;
|
|
12406
12801
|
}
|
|
@@ -12509,7 +12904,7 @@ function reportTestSuccess(status, distinctId, identity) {
|
|
|
12509
12904
|
if (identity.team) logger.info(` team: ${identity.team}`);
|
|
12510
12905
|
}
|
|
12511
12906
|
function createTestCommand() {
|
|
12512
|
-
return new
|
|
12907
|
+
return new Command112("test").description("Send a test event to PostHog and verify connectivity").action(async () => {
|
|
12513
12908
|
const cwd = process.cwd();
|
|
12514
12909
|
const consent = resolveConsent(cwd, void 0);
|
|
12515
12910
|
if (!consent.allowed) {
|
|
@@ -12546,7 +12941,7 @@ function createTestCommand() {
|
|
|
12546
12941
|
|
|
12547
12942
|
// src/commands/telemetry/index.ts
|
|
12548
12943
|
function createTelemetryCommand() {
|
|
12549
|
-
const command = new
|
|
12944
|
+
const command = new Command113("telemetry").description("Telemetry identity and status management");
|
|
12550
12945
|
command.addCommand(createIdentifyCommand());
|
|
12551
12946
|
command.addCommand(createStatusCommand());
|
|
12552
12947
|
command.addCommand(createTestCommand());
|
|
@@ -12554,9 +12949,9 @@ function createTelemetryCommand() {
|
|
|
12554
12949
|
}
|
|
12555
12950
|
|
|
12556
12951
|
// src/commands/test-craft.ts
|
|
12557
|
-
import { Command as
|
|
12952
|
+
import { Command as Command114 } from "commander";
|
|
12558
12953
|
function createTestCraftCommand() {
|
|
12559
|
-
return new
|
|
12954
|
+
return new Command114("test-craft").description(
|
|
12560
12955
|
"LLM-judgment critique of test quality across vitest/jest/mocha/playwright. Fourth craft-pipeline ceiling skill. Per-test critique with best-effort source pairing."
|
|
12561
12956
|
).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) => {
|
|
12562
12957
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -12604,10 +12999,10 @@ function printResult7(result, mode, _formatter) {
|
|
|
12604
12999
|
list.push(f);
|
|
12605
13000
|
byFile.set(f.target.file, list);
|
|
12606
13001
|
}
|
|
12607
|
-
for (const [file,
|
|
13002
|
+
for (const [file, fs49] of byFile) {
|
|
12608
13003
|
console.log(`
|
|
12609
13004
|
${file}`);
|
|
12610
|
-
for (const f of
|
|
13005
|
+
for (const f of fs49) {
|
|
12611
13006
|
const nestingStr = f.target.nesting.length > 0 ? f.target.nesting.join(" > ") + " > " : "";
|
|
12612
13007
|
console.log(
|
|
12613
13008
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.framework}:${f.target.line}`
|
|
@@ -12626,7 +13021,7 @@ ${file}`);
|
|
|
12626
13021
|
}
|
|
12627
13022
|
|
|
12628
13023
|
// src/commands/traceability.ts
|
|
12629
|
-
import { Command as
|
|
13024
|
+
import { Command as Command115 } from "commander";
|
|
12630
13025
|
import chalk17 from "chalk";
|
|
12631
13026
|
function confidenceLabel(maxConfidence) {
|
|
12632
13027
|
if (maxConfidence >= 0.8) return "explicit";
|
|
@@ -12747,7 +13142,7 @@ async function runTraceability(options) {
|
|
|
12747
13142
|
const projectPath = process.cwd();
|
|
12748
13143
|
const store = await loadGraphStore(projectPath);
|
|
12749
13144
|
if (!store) handleNoStore(mode);
|
|
12750
|
-
const graphModule = await import("./dist-
|
|
13145
|
+
const graphModule = await import("./dist-OWHMNL4W.js");
|
|
12751
13146
|
const filterOptions = buildFilterOptions(options);
|
|
12752
13147
|
const results = graphModule.queryTraceability(store, filterOptions);
|
|
12753
13148
|
if (results.length === 0) handleEmptyResults(mode);
|
|
@@ -12762,7 +13157,7 @@ async function runTraceability(options) {
|
|
|
12762
13157
|
process.exit(ExitCode.SUCCESS);
|
|
12763
13158
|
}
|
|
12764
13159
|
function createTraceabilityCommand() {
|
|
12765
|
-
return new
|
|
13160
|
+
return new Command115("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) => {
|
|
12766
13161
|
const globalOpts = cmd.optsWithGlobals();
|
|
12767
13162
|
await runTraceability({
|
|
12768
13163
|
spec: opts.spec,
|
|
@@ -12775,15 +13170,15 @@ function createTraceabilityCommand() {
|
|
|
12775
13170
|
}
|
|
12776
13171
|
|
|
12777
13172
|
// src/commands/uninstall.ts
|
|
12778
|
-
import * as
|
|
12779
|
-
import { Command as
|
|
13173
|
+
import * as path70 from "path";
|
|
13174
|
+
import { Command as Command116 } from "commander";
|
|
12780
13175
|
async function runUninstall(skillName, options) {
|
|
12781
13176
|
const packageName = resolvePackageName(skillName);
|
|
12782
13177
|
const shortName = extractSkillName(packageName);
|
|
12783
13178
|
const globalDir = resolveGlobalSkillsDir();
|
|
12784
|
-
const skillsDir =
|
|
12785
|
-
const communityBase =
|
|
12786
|
-
const lockfilePath =
|
|
13179
|
+
const skillsDir = path70.dirname(globalDir);
|
|
13180
|
+
const communityBase = path70.join(skillsDir, "community");
|
|
13181
|
+
const lockfilePath = path70.join(communityBase, "skills-lock.json");
|
|
12787
13182
|
const lockfile = readLockfile2(lockfilePath);
|
|
12788
13183
|
const entry = lockfile.skills[packageName];
|
|
12789
13184
|
if (!entry) {
|
|
@@ -12813,7 +13208,7 @@ async function runUninstall(skillName, options) {
|
|
|
12813
13208
|
return result;
|
|
12814
13209
|
}
|
|
12815
13210
|
function createUninstallCommand() {
|
|
12816
|
-
const cmd = new
|
|
13211
|
+
const cmd = new Command116("uninstall");
|
|
12817
13212
|
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) => {
|
|
12818
13213
|
try {
|
|
12819
13214
|
const result = await runUninstall(skill, opts);
|
|
@@ -12834,8 +13229,8 @@ function createUninstallCommand() {
|
|
|
12834
13229
|
// src/commands/uninstall-constraints.ts
|
|
12835
13230
|
init_dist3();
|
|
12836
13231
|
import * as fs47 from "fs/promises";
|
|
12837
|
-
import * as
|
|
12838
|
-
import { Command as
|
|
13232
|
+
import * as path71 from "path";
|
|
13233
|
+
import { Command as Command117 } from "commander";
|
|
12839
13234
|
async function runUninstallConstraints(options) {
|
|
12840
13235
|
const { packageName, configPath, lockfilePath } = options;
|
|
12841
13236
|
const lockfileResult = await readLockfile(lockfilePath);
|
|
@@ -12892,11 +13287,11 @@ async function runUninstallConstraints(options) {
|
|
|
12892
13287
|
};
|
|
12893
13288
|
}
|
|
12894
13289
|
function createUninstallConstraintsCommand() {
|
|
12895
|
-
const cmd = new
|
|
13290
|
+
const cmd = new Command117("uninstall-constraints");
|
|
12896
13291
|
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) => {
|
|
12897
13292
|
let configPath;
|
|
12898
13293
|
if (opts.config) {
|
|
12899
|
-
configPath =
|
|
13294
|
+
configPath = path71.resolve(opts.config);
|
|
12900
13295
|
} else {
|
|
12901
13296
|
const found = findConfigFile();
|
|
12902
13297
|
if (!found.ok) {
|
|
@@ -12905,8 +13300,8 @@ function createUninstallConstraintsCommand() {
|
|
|
12905
13300
|
}
|
|
12906
13301
|
configPath = found.value;
|
|
12907
13302
|
}
|
|
12908
|
-
const projectRoot2 =
|
|
12909
|
-
const lockfilePath =
|
|
13303
|
+
const projectRoot2 = path71.dirname(configPath);
|
|
13304
|
+
const lockfilePath = path71.join(projectRoot2, ".harness", "constraints.lock.json");
|
|
12910
13305
|
const result = await runUninstallConstraints({
|
|
12911
13306
|
packageName: name,
|
|
12912
13307
|
configPath,
|
|
@@ -12932,8 +13327,8 @@ function createUninstallConstraintsCommand() {
|
|
|
12932
13327
|
|
|
12933
13328
|
// src/commands/update.ts
|
|
12934
13329
|
init_dist3();
|
|
12935
|
-
import { Command as
|
|
12936
|
-
import { execFile, execFileSync as
|
|
13330
|
+
import { Command as Command118 } from "commander";
|
|
13331
|
+
import { execFile, execFileSync as execFileSync8 } from "child_process";
|
|
12937
13332
|
import { realpathSync, existsSync as existsSync44, readFileSync as readFileSync34 } from "fs";
|
|
12938
13333
|
import { dirname as dirname23, join as join60 } from "path";
|
|
12939
13334
|
import { promisify } from "util";
|
|
@@ -12975,7 +13370,7 @@ async function getLatestVersionAsync(pkg) {
|
|
|
12975
13370
|
function getInstalledVersions(pm, packages) {
|
|
12976
13371
|
const versions = {};
|
|
12977
13372
|
try {
|
|
12978
|
-
const output =
|
|
13373
|
+
const output = execFileSync8(pm, ["list", "-g", "--json"], {
|
|
12979
13374
|
encoding: "utf-8",
|
|
12980
13375
|
timeout: 15e3
|
|
12981
13376
|
});
|
|
@@ -12996,7 +13391,7 @@ function getInstalledVersions(pm, packages) {
|
|
|
12996
13391
|
}
|
|
12997
13392
|
function getInstalledPackages(pm) {
|
|
12998
13393
|
try {
|
|
12999
|
-
const output =
|
|
13394
|
+
const output = execFileSync8(pm, ["list", "-g", "--json"], {
|
|
13000
13395
|
encoding: "utf-8",
|
|
13001
13396
|
timeout: 15e3
|
|
13002
13397
|
});
|
|
@@ -13016,10 +13411,10 @@ function prompt3(question) {
|
|
|
13016
13411
|
input: process.stdin,
|
|
13017
13412
|
output: process.stdout
|
|
13018
13413
|
});
|
|
13019
|
-
return new Promise((
|
|
13414
|
+
return new Promise((resolve47) => {
|
|
13020
13415
|
rl.question(question, (answer) => {
|
|
13021
13416
|
rl.close();
|
|
13022
|
-
|
|
13417
|
+
resolve47(answer.trim().toLowerCase());
|
|
13023
13418
|
});
|
|
13024
13419
|
});
|
|
13025
13420
|
}
|
|
@@ -13051,7 +13446,7 @@ function findAllInstalls() {
|
|
|
13051
13446
|
const args = isWindows ? ["harness"] : ["-a", "harness"];
|
|
13052
13447
|
let output;
|
|
13053
13448
|
try {
|
|
13054
|
-
const raw =
|
|
13449
|
+
const raw = execFileSync8(cmd, args, { encoding: "utf-8", timeout: 5e3 });
|
|
13055
13450
|
if (typeof raw !== "string") return [];
|
|
13056
13451
|
output = raw;
|
|
13057
13452
|
} catch {
|
|
@@ -13128,7 +13523,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
13128
13523
|
}
|
|
13129
13524
|
try {
|
|
13130
13525
|
logger.info(`Uninstalling: ${cmd.display}`);
|
|
13131
|
-
|
|
13526
|
+
execFileSync8(cmd.bin, cmd.args, { stdio: "inherit", timeout: 12e4 });
|
|
13132
13527
|
} catch {
|
|
13133
13528
|
logger.warn(`Uninstall failed for ${cmd.install.binPath}.`);
|
|
13134
13529
|
console.log(` Run manually: ${chalk18.cyan(cmd.display)}`);
|
|
@@ -13188,7 +13583,7 @@ function refreshHooks() {
|
|
|
13188
13583
|
function runLocalGraphScan() {
|
|
13189
13584
|
try {
|
|
13190
13585
|
logger.info("Scanning codebase to rebuild knowledge graph...");
|
|
13191
|
-
|
|
13586
|
+
execFileSync8("harness", ["graph", "scan", "."], { stdio: "inherit" });
|
|
13192
13587
|
} catch {
|
|
13193
13588
|
logger.warn("Graph scan failed. Run manually:");
|
|
13194
13589
|
console.log(` ${chalk18.cyan("harness graph scan .")}`);
|
|
@@ -13203,7 +13598,7 @@ async function offerRegeneration() {
|
|
|
13203
13598
|
const scopeAnswer = await prompt3("Generate for (G)lobal or (l)ocal project? (G/l) ");
|
|
13204
13599
|
const isGlobal = scopeAnswer !== "l" && scopeAnswer !== "local";
|
|
13205
13600
|
try {
|
|
13206
|
-
|
|
13601
|
+
execFileSync8("harness", ["generate", ...isGlobal ? ["--global"] : []], {
|
|
13207
13602
|
stdio: "inherit"
|
|
13208
13603
|
});
|
|
13209
13604
|
} catch {
|
|
@@ -13283,7 +13678,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
13283
13678
|
}
|
|
13284
13679
|
try {
|
|
13285
13680
|
logger.info("Updating packages...");
|
|
13286
|
-
|
|
13681
|
+
execFileSync8(pm, ["install", "-g", ...installPkgs], { stdio: "inherit", timeout: 12e4 });
|
|
13287
13682
|
console.log("");
|
|
13288
13683
|
logger.success("Update complete");
|
|
13289
13684
|
invalidateCheckState();
|
|
@@ -13300,7 +13695,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
13300
13695
|
process.exit(ExitCode.SUCCESS);
|
|
13301
13696
|
}
|
|
13302
13697
|
function createUpdateCommand() {
|
|
13303
|
-
return new
|
|
13698
|
+
return new Command118("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(
|
|
13304
13699
|
"--regenerate",
|
|
13305
13700
|
"Only regenerate slash commands and agent definitions (skip package updates)"
|
|
13306
13701
|
).action(async (opts, cmd) => {
|
|
@@ -13310,9 +13705,9 @@ function createUpdateCommand() {
|
|
|
13310
13705
|
}
|
|
13311
13706
|
|
|
13312
13707
|
// src/commands/usage.ts
|
|
13313
|
-
import { Command as
|
|
13708
|
+
import { Command as Command119 } from "commander";
|
|
13314
13709
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
13315
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
13710
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-LHINSVK4.js");
|
|
13316
13711
|
const records = readCostRecords(cwd);
|
|
13317
13712
|
if (includeClaudeSessions) {
|
|
13318
13713
|
const ccRecords = parseCCRecords();
|
|
@@ -13405,7 +13800,7 @@ function registerDailyCommand(usage) {
|
|
|
13405
13800
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
13406
13801
|
return;
|
|
13407
13802
|
}
|
|
13408
|
-
const { aggregateByDay } = await import("./dist-
|
|
13803
|
+
const { aggregateByDay } = await import("./dist-LHINSVK4.js");
|
|
13409
13804
|
const limited = aggregateByDay(records).slice(0, days);
|
|
13410
13805
|
if (globalOpts.json) {
|
|
13411
13806
|
console.log(
|
|
@@ -13434,7 +13829,7 @@ function registerSessionsCommand(usage) {
|
|
|
13434
13829
|
}
|
|
13435
13830
|
return;
|
|
13436
13831
|
}
|
|
13437
|
-
const { aggregateBySession } = await import("./dist-
|
|
13832
|
+
const { aggregateBySession } = await import("./dist-LHINSVK4.js");
|
|
13438
13833
|
const sessionData = aggregateBySession(records);
|
|
13439
13834
|
const limited = sessionData.slice(0, limit);
|
|
13440
13835
|
if (globalOpts.json) {
|
|
@@ -13514,7 +13909,7 @@ function registerSessionCommand(usage) {
|
|
|
13514
13909
|
cwd,
|
|
13515
13910
|
globalOpts.includeClaudeSessions
|
|
13516
13911
|
);
|
|
13517
|
-
const { aggregateBySession } = await import("./dist-
|
|
13912
|
+
const { aggregateBySession } = await import("./dist-LHINSVK4.js");
|
|
13518
13913
|
const sessionData = aggregateBySession(records);
|
|
13519
13914
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
13520
13915
|
if (!match) {
|
|
@@ -13549,7 +13944,7 @@ function registerLatestCommand(usage) {
|
|
|
13549
13944
|
}
|
|
13550
13945
|
return;
|
|
13551
13946
|
}
|
|
13552
|
-
const { aggregateBySession } = await import("./dist-
|
|
13947
|
+
const { aggregateBySession } = await import("./dist-LHINSVK4.js");
|
|
13553
13948
|
const sessionData = aggregateBySession(records);
|
|
13554
13949
|
const latest = sessionData[0];
|
|
13555
13950
|
if (!latest) {
|
|
@@ -13575,7 +13970,7 @@ function registerLatestCommand(usage) {
|
|
|
13575
13970
|
});
|
|
13576
13971
|
}
|
|
13577
13972
|
function createUsageCommand() {
|
|
13578
|
-
const usage = new
|
|
13973
|
+
const usage = new Command119("usage").description("Token usage and cost tracking");
|
|
13579
13974
|
usage.option(
|
|
13580
13975
|
"--include-claude-sessions",
|
|
13581
13976
|
"Include Claude Code session data from ~/.claude/projects/"
|
|
@@ -13590,15 +13985,16 @@ function createUsageCommand() {
|
|
|
13590
13985
|
// src/commands/validate.ts
|
|
13591
13986
|
init_dist3();
|
|
13592
13987
|
init_dist3();
|
|
13593
|
-
import { Command as
|
|
13594
|
-
import * as
|
|
13988
|
+
import { Command as Command120 } from "commander";
|
|
13989
|
+
import * as fs48 from "fs";
|
|
13990
|
+
import * as path72 from "path";
|
|
13595
13991
|
async function runValidate(options) {
|
|
13596
13992
|
const configResult = resolveConfig(options.configPath);
|
|
13597
13993
|
if (!configResult.ok) {
|
|
13598
13994
|
return configResult;
|
|
13599
13995
|
}
|
|
13600
13996
|
const config = configResult.value;
|
|
13601
|
-
const cwd = options.cwd ?? (options.configPath ?
|
|
13997
|
+
const cwd = options.cwd ?? (options.configPath ? path72.dirname(path72.resolve(options.configPath)) : process.cwd());
|
|
13602
13998
|
const result = {
|
|
13603
13999
|
valid: true,
|
|
13604
14000
|
checks: {
|
|
@@ -13608,7 +14004,7 @@ async function runValidate(options) {
|
|
|
13608
14004
|
},
|
|
13609
14005
|
issues: []
|
|
13610
14006
|
};
|
|
13611
|
-
const agentsMapPath =
|
|
14007
|
+
const agentsMapPath = path72.resolve(cwd, config.agentsMapPath);
|
|
13612
14008
|
const agentsResult = await validateAgentsMap(agentsMapPath);
|
|
13613
14009
|
if (agentsResult.ok) {
|
|
13614
14010
|
result.checks.agentsMap = true;
|
|
@@ -13659,6 +14055,22 @@ async function runValidate(options) {
|
|
|
13659
14055
|
}
|
|
13660
14056
|
});
|
|
13661
14057
|
}
|
|
14058
|
+
const strategyResult = await validateStrategy(cwd);
|
|
14059
|
+
if (strategyResult.ok) {
|
|
14060
|
+
result.checks.strategyConfig = true;
|
|
14061
|
+
} else {
|
|
14062
|
+
result.valid = false;
|
|
14063
|
+
result.checks.strategyConfig = false;
|
|
14064
|
+
result.issues.push({
|
|
14065
|
+
check: "strategyConfig",
|
|
14066
|
+
file: "STRATEGY.md",
|
|
14067
|
+
severity: "error",
|
|
14068
|
+
message: strategyResult.error.message,
|
|
14069
|
+
...strategyResult.error.suggestions?.[0] !== void 0 && {
|
|
14070
|
+
suggestion: strategyResult.error.suggestions[0]
|
|
14071
|
+
}
|
|
14072
|
+
});
|
|
14073
|
+
}
|
|
13662
14074
|
const solutionsResult = await validateSolutionsDir(cwd);
|
|
13663
14075
|
if (solutionsResult.ok) {
|
|
13664
14076
|
result.checks.solutionsDir = true;
|
|
@@ -13689,6 +14101,25 @@ async function runValidate(options) {
|
|
|
13689
14101
|
}
|
|
13690
14102
|
});
|
|
13691
14103
|
}
|
|
14104
|
+
const roadmapPath = path72.join(cwd, "docs", "roadmap.md");
|
|
14105
|
+
if (fs48.existsSync(roadmapPath)) {
|
|
14106
|
+
const parsed = parseRoadmap(fs48.readFileSync(roadmapPath, "utf-8"));
|
|
14107
|
+
if (parsed.ok) {
|
|
14108
|
+
const findings = checkRoadmapHealth(parsed.value);
|
|
14109
|
+
result.checks.roadmapHealth = !findings.some((f) => f.severity === "error");
|
|
14110
|
+
if (!result.checks.roadmapHealth) result.valid = false;
|
|
14111
|
+
for (const finding of findings) {
|
|
14112
|
+
result.issues.push({
|
|
14113
|
+
check: "roadmapHealth",
|
|
14114
|
+
file: "docs/roadmap.md",
|
|
14115
|
+
ruleId: finding.ruleId,
|
|
14116
|
+
severity: finding.severity === "error" ? "error" : "warning",
|
|
14117
|
+
message: finding.feature ? `${finding.feature} (${finding.milestone}): ${finding.message}` : finding.message,
|
|
14118
|
+
...finding.suggestion !== void 0 && { suggestion: finding.suggestion }
|
|
14119
|
+
});
|
|
14120
|
+
}
|
|
14121
|
+
}
|
|
14122
|
+
}
|
|
13692
14123
|
const anatomyEnabled = config.design?.audit?.componentAnatomy?.enabled !== false;
|
|
13693
14124
|
if (anatomyEnabled) {
|
|
13694
14125
|
try {
|
|
@@ -13814,11 +14245,11 @@ function resolveValidateMode(globalOpts) {
|
|
|
13814
14245
|
return OutputMode.TEXT;
|
|
13815
14246
|
}
|
|
13816
14247
|
async function printCrossCheckWarnings(mode) {
|
|
13817
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
14248
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-QPSAOZKD.js");
|
|
13818
14249
|
const cwd = process.cwd();
|
|
13819
14250
|
const crossResult = await runCrossCheck2({
|
|
13820
|
-
specsDir:
|
|
13821
|
-
plansDir:
|
|
14251
|
+
specsDir: path72.join(cwd, "docs", "specs"),
|
|
14252
|
+
plansDir: path72.join(cwd, "docs", "plans"),
|
|
13822
14253
|
projectPath: cwd
|
|
13823
14254
|
});
|
|
13824
14255
|
if (!crossResult.ok || crossResult.value.warnings === 0) return;
|
|
@@ -13830,7 +14261,7 @@ async function printCrossCheckWarnings(mode) {
|
|
|
13830
14261
|
${crossResult.value.warnings} warnings`);
|
|
13831
14262
|
}
|
|
13832
14263
|
function createValidateCommand3() {
|
|
13833
|
-
const command = new
|
|
14264
|
+
const command = new Command120("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
|
|
13834
14265
|
"--agent-configs",
|
|
13835
14266
|
"Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
|
|
13836
14267
|
).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()));
|
|
@@ -13879,7 +14310,7 @@ Agent configs checked via ${engineLabel}${note}`);
|
|
|
13879
14310
|
|
|
13880
14311
|
// src/commands/verify.ts
|
|
13881
14312
|
init_dist3();
|
|
13882
|
-
import { Command as
|
|
14313
|
+
import { Command as Command121 } from "commander";
|
|
13883
14314
|
import { execSync as execSync6 } from "child_process";
|
|
13884
14315
|
function loadBranchingConfig(configPath) {
|
|
13885
14316
|
const shouldLoad = configPath !== void 0 || findConfigFile().ok;
|
|
@@ -13942,7 +14373,7 @@ async function runVerify(options) {
|
|
|
13942
14373
|
process.exit(ExitCode.VALIDATION_FAILED);
|
|
13943
14374
|
}
|
|
13944
14375
|
function createVerifyCommand() {
|
|
13945
|
-
return new
|
|
14376
|
+
return new Command121("verify").description(
|
|
13946
14377
|
"Verify project conventions (currently: branch naming). Works with or without a harness.config.json."
|
|
13947
14378
|
).option(
|
|
13948
14379
|
"--branch <name>",
|
|
@@ -13971,6 +14402,7 @@ var commandCreators = [
|
|
|
13971
14402
|
createCheckDepsCommand,
|
|
13972
14403
|
createCheckDesignCommand,
|
|
13973
14404
|
createCheckDocsCommand,
|
|
14405
|
+
createCheckHarnessStrengthCommand,
|
|
13974
14406
|
createCheckPerfCommand,
|
|
13975
14407
|
createCheckPhaseGateCommand,
|
|
13976
14408
|
createCheckSecurityCommand,
|
|
@@ -14017,6 +14449,7 @@ var commandCreators = [
|
|
|
14017
14449
|
createPulseCommand,
|
|
14018
14450
|
createQueryCommand,
|
|
14019
14451
|
createRecommendCommand,
|
|
14452
|
+
createReviewCiCommand,
|
|
14020
14453
|
createRoadmapCommand,
|
|
14021
14454
|
createRoutingCommand,
|
|
14022
14455
|
createScanCommand,
|
|
@@ -14046,7 +14479,7 @@ var commandCreators = [
|
|
|
14046
14479
|
|
|
14047
14480
|
// src/index.ts
|
|
14048
14481
|
function createProgram() {
|
|
14049
|
-
const program = new
|
|
14482
|
+
const program = new Command122();
|
|
14050
14483
|
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");
|
|
14051
14484
|
for (const creator of commandCreators) {
|
|
14052
14485
|
program.addCommand(creator());
|