@harness-engineering/cli 2.2.1 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +13 -7
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +13 -7
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +13 -7
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +13 -7
- package/dist/{agents-md-L4X4MOWT.js → agents-md-5JG3AE5A.js} +3 -3
- package/dist/{architecture-WJRIYQ45.js → architecture-544ISXDI.js} +4 -4
- package/dist/{assess-project-PHC2HO3V.js → assess-project-PBO6XFJC.js} +1 -1
- package/dist/bin/harness-mcp.js +15 -15
- package/dist/bin/harness.js +25 -25
- package/dist/{check-phase-gate-646KY67H.js → check-phase-gate-BZU4DGF3.js} +4 -4
- package/dist/{chunk-2QPHCR22.js → chunk-22OKI5QI.js} +9 -9
- package/dist/{chunk-CPYL6LJR.js → chunk-2564TBT6.js} +1 -1
- package/dist/{chunk-YG4ZBGEH.js → chunk-7BNK4M23.js} +1 -1
- package/dist/{chunk-L2IDE7WS.js → chunk-A36MGB3V.js} +277 -111
- package/dist/{chunk-LO7ZLV7S.js → chunk-DBJC4ULT.js} +12 -5
- package/dist/{chunk-JPWICLXQ.js → chunk-DHWFOHPX.js} +1 -1
- package/dist/{chunk-W4AFGFFB.js → chunk-EMJFHTPZ.js} +3 -3
- package/dist/{chunk-4HKVZO7R.js → chunk-IYA3NS5P.js} +1 -1
- package/dist/{chunk-YLOAQZLA.js → chunk-J4MX5DIA.js} +5 -5
- package/dist/{chunk-224XKVLP.js → chunk-JMACX5FN.js} +1 -1
- package/dist/{chunk-YLTRFZAT.js → chunk-KJR4Z2YC.js} +8 -8
- package/dist/{chunk-VJQLUS5I.js → chunk-KT5PWWWO.js} +1 -1
- package/dist/{chunk-OJMO4JBQ.js → chunk-LBYGGNEZ.js} +1 -1
- package/dist/{chunk-36X7TRUI.js → chunk-LYMPKO6N.js} +2 -2
- package/dist/{chunk-GOOTYTTV.js → chunk-N6DVSOLZ.js} +933 -500
- package/dist/{chunk-VKDBM3RX.js → chunk-QZ67TDLJ.js} +3 -3
- package/dist/{chunk-2ZURBWEV.js → chunk-R6GLLMRZ.js} +9 -9
- package/dist/{chunk-QTUPHTTK.js → chunk-T2ZMHXCX.js} +1 -1
- package/dist/{chunk-YWD6WXXM.js → chunk-TIJXG4UB.js} +6 -6
- package/dist/{chunk-KTWKW6KJ.js → chunk-TIQU5VY7.js} +1 -1
- package/dist/{chunk-E7W3YOXL.js → chunk-U2SF3KAQ.js} +2 -2
- package/dist/{chunk-TNZYQTEE.js → chunk-WMZKZCCE.js} +1 -1
- package/dist/{chunk-BMM3GXV6.js → chunk-XPRGWHBL.js} +80 -37
- package/dist/{chunk-KV7A5OZT.js → chunk-YI6NTL72.js} +5 -5
- package/dist/{chunk-K4WRQTEF.js → chunk-YNG7BWGB.js} +2034 -490
- package/dist/{ci-workflow-TOS5X527.js → ci-workflow-ZO6FPO3V.js} +3 -3
- package/dist/{dist-D27LJR2T.js → dist-SQAOKWXM.js} +5 -3
- package/dist/{dist-6RT2AVYU.js → dist-ZS3Y3YWP.js} +30 -2
- package/dist/{docs-HOJMQYDZ.js → docs-VW2KDPI3.js} +4 -4
- package/dist/{engine-D6UYH6DB.js → engine-LFQ2JSHY.js} +3 -3
- package/dist/{entropy-LZ4IDGZR.js → entropy-P62LCVJI.js} +3 -3
- package/dist/{feedback-WV7EQVK7.js → feedback-O3TZAPGX.js} +1 -1
- package/dist/{generate-agent-definitions-GFNXJCBY.js → generate-agent-definitions-WVCOZDKI.js} +4 -4
- package/dist/{glob-helper-FZPPTGJN.js → glob-helper-JE3MO4UD.js} +1 -1
- package/dist/{graph-loader-5MKHAJJF.js → graph-loader-TJU4E7KL.js} +1 -1
- package/dist/index.d.ts +15 -9
- package/dist/index.js +25 -25
- package/dist/{loader-Z23JTXIZ.js → loader-UB2A65DN.js} +3 -3
- package/dist/{mcp-JDJGELGD.js → mcp-ST26QMDK.js} +15 -15
- package/dist/{performance-7ATLHTQU.js → performance-5HQG3IU6.js} +4 -4
- package/dist/{review-pipeline-COBGGXHJ.js → review-pipeline-VRDL5A7J.js} +3 -3
- package/dist/{runtime-KCBNQUXK.js → runtime-TQQT2MWQ.js} +3 -3
- package/dist/{scan-BIJYHOE7.js → scan-SFX5U3TE.js} +1 -1
- package/dist/{security-MAPRIFQT.js → security-IMFAE6MW.js} +1 -1
- package/dist/{validate-74XTQR7Q.js → validate-GW2DG64P.js} +4 -4
- package/dist/{validate-cross-check-MI5MZ7MB.js → validate-cross-check-F5YQVXLT.js} +3 -3
- package/package.json +6 -6
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WMZKZCCE.js";
|
|
4
4
|
import {
|
|
5
5
|
generateAgentsMd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-IYA3NS5P.js";
|
|
7
7
|
import {
|
|
8
8
|
generateCIWorkflow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DHWFOHPX.js";
|
|
10
10
|
import {
|
|
11
11
|
TemplateEngine
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-JMACX5FN.js";
|
|
13
13
|
import {
|
|
14
14
|
generate,
|
|
15
15
|
validate
|
|
@@ -28,22 +28,22 @@ import {
|
|
|
28
28
|
OutputMode,
|
|
29
29
|
createCheckPhaseGateCommand,
|
|
30
30
|
findFiles
|
|
31
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-U2SF3KAQ.js";
|
|
32
32
|
import {
|
|
33
33
|
createGenerateAgentDefinitionsCommand,
|
|
34
34
|
generateAgentDefinitions
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-LBYGGNEZ.js";
|
|
36
36
|
import {
|
|
37
37
|
toKebabCase
|
|
38
38
|
} from "./chunk-KET4QQZB.js";
|
|
39
39
|
import {
|
|
40
40
|
listPersonas,
|
|
41
41
|
loadPersona
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-KT5PWWWO.js";
|
|
43
43
|
import {
|
|
44
44
|
createScanCommand,
|
|
45
45
|
loadIngestOptions
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-7BNK4M23.js";
|
|
47
47
|
import {
|
|
48
48
|
appendFrameworkAgents,
|
|
49
49
|
captureHealthSnapshot,
|
|
@@ -57,17 +57,17 @@ import {
|
|
|
57
57
|
loadCachedSnapshot,
|
|
58
58
|
persistToolingConfig,
|
|
59
59
|
recommend
|
|
60
|
-
} from "./chunk-
|
|
60
|
+
} from "./chunk-A36MGB3V.js";
|
|
61
61
|
import {
|
|
62
62
|
loadGraphStore
|
|
63
|
-
} from "./chunk-
|
|
63
|
+
} from "./chunk-TIQU5VY7.js";
|
|
64
64
|
import {
|
|
65
65
|
loadOrRebuildIndex
|
|
66
66
|
} from "./chunk-27AJKSQY.js";
|
|
67
67
|
import {
|
|
68
68
|
findConfigFile,
|
|
69
69
|
resolveConfig
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-DBJC4ULT.js";
|
|
71
71
|
import {
|
|
72
72
|
VALID_PLATFORMS
|
|
73
73
|
} from "./chunk-3ISINLYT.js";
|
|
@@ -115,6 +115,7 @@ import {
|
|
|
115
115
|
computeWindow,
|
|
116
116
|
createFixes,
|
|
117
117
|
createStream,
|
|
118
|
+
createTrackerClient,
|
|
118
119
|
crossReferenceUndocumentedFixes,
|
|
119
120
|
deepMergeConstraints,
|
|
120
121
|
defineLayer,
|
|
@@ -129,18 +130,22 @@ import {
|
|
|
129
130
|
generateSuggestions,
|
|
130
131
|
getOrCreateInstallId,
|
|
131
132
|
gitScan,
|
|
132
|
-
init_dist,
|
|
133
|
+
init_dist as init_dist2,
|
|
133
134
|
isoWeek,
|
|
134
135
|
listStreams,
|
|
135
136
|
listTaintedSessions,
|
|
137
|
+
loadProjectRoadmapMode,
|
|
136
138
|
loadState,
|
|
137
139
|
loadStreamIndex,
|
|
140
|
+
loadTrackerClientConfigFromProject,
|
|
138
141
|
loadTrackerSyncConfig,
|
|
139
142
|
mapInjectionFindings,
|
|
140
143
|
mapSecurityFindings,
|
|
144
|
+
migrate_exports,
|
|
141
145
|
parseDiff,
|
|
142
146
|
parseFileRegions,
|
|
143
147
|
parseManifest,
|
|
148
|
+
parseRoadmap,
|
|
144
149
|
parseSecurityConfig,
|
|
145
150
|
pruneLearnings,
|
|
146
151
|
readIdentity,
|
|
@@ -161,14 +166,19 @@ import {
|
|
|
161
166
|
validateDependencies,
|
|
162
167
|
validateKnowledgeMap,
|
|
163
168
|
validatePulseConfig,
|
|
169
|
+
validateRoadmapMode,
|
|
164
170
|
validateSolutionsDir,
|
|
165
171
|
writeConfig,
|
|
166
172
|
writeLockfile
|
|
167
|
-
} from "./chunk-
|
|
173
|
+
} from "./chunk-YNG7BWGB.js";
|
|
168
174
|
import {
|
|
169
175
|
Err,
|
|
170
176
|
Ok
|
|
171
177
|
} from "./chunk-RL4VMEXL.js";
|
|
178
|
+
import {
|
|
179
|
+
DEFAULT_SKIP_DIRS,
|
|
180
|
+
init_dist
|
|
181
|
+
} from "./chunk-XPRGWHBL.js";
|
|
172
182
|
import {
|
|
173
183
|
createCreateSkillCommand
|
|
174
184
|
} from "./chunk-ZP5E7YET.js";
|
|
@@ -189,10 +199,10 @@ import {
|
|
|
189
199
|
} from "./chunk-KFQGP6VL.js";
|
|
190
200
|
|
|
191
201
|
// src/index.ts
|
|
192
|
-
import { Command as
|
|
202
|
+
import { Command as Command93 } from "commander";
|
|
193
203
|
|
|
194
204
|
// src/commands/add.ts
|
|
195
|
-
|
|
205
|
+
init_dist2();
|
|
196
206
|
import { Command } from "commander";
|
|
197
207
|
import * as fs from "fs";
|
|
198
208
|
import * as path from "path";
|
|
@@ -378,7 +388,7 @@ function registerSkillsCommand(adoption) {
|
|
|
378
388
|
const globalOpts = cmd.optsWithGlobals();
|
|
379
389
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
380
390
|
const cwd = process.cwd();
|
|
381
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
391
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-ZS3Y3YWP.js");
|
|
382
392
|
const records = readAdoptionRecords(cwd);
|
|
383
393
|
if (records.length === 0) {
|
|
384
394
|
if (globalOpts.json) {
|
|
@@ -414,7 +424,7 @@ function registerRecentCommand(adoption) {
|
|
|
414
424
|
const globalOpts = cmd.optsWithGlobals();
|
|
415
425
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
416
426
|
const cwd = process.cwd();
|
|
417
|
-
const { readAdoptionRecords } = await import("./dist-
|
|
427
|
+
const { readAdoptionRecords } = await import("./dist-ZS3Y3YWP.js");
|
|
418
428
|
const records = readAdoptionRecords(cwd);
|
|
419
429
|
if (records.length === 0) {
|
|
420
430
|
if (globalOpts.json) {
|
|
@@ -492,7 +502,7 @@ function registerSkillCommand(adoption) {
|
|
|
492
502
|
adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
|
|
493
503
|
const globalOpts = cmd.optsWithGlobals();
|
|
494
504
|
const cwd = process.cwd();
|
|
495
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
505
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-ZS3Y3YWP.js");
|
|
496
506
|
const records = readAdoptionRecords(cwd);
|
|
497
507
|
const skillRecords = records.filter((r) => r.skill === name);
|
|
498
508
|
if (skillRecords.length === 0) {
|
|
@@ -523,8 +533,8 @@ function createAdoptionCommand() {
|
|
|
523
533
|
import { Command as Command5 } from "commander";
|
|
524
534
|
|
|
525
535
|
// src/commands/agent/run.ts
|
|
526
|
-
|
|
527
|
-
|
|
536
|
+
init_dist2();
|
|
537
|
+
init_dist2();
|
|
528
538
|
import { Command as Command3 } from "commander";
|
|
529
539
|
import * as path2 from "path";
|
|
530
540
|
import * as childProcess from "child_process";
|
|
@@ -646,7 +656,7 @@ function createRunCommand() {
|
|
|
646
656
|
}
|
|
647
657
|
|
|
648
658
|
// src/commands/agent/review.ts
|
|
649
|
-
|
|
659
|
+
init_dist2();
|
|
650
660
|
import { Command as Command4 } from "commander";
|
|
651
661
|
import { execSync } from "child_process";
|
|
652
662
|
async function runAgentReview(options) {
|
|
@@ -783,7 +793,7 @@ function createAgentCommand() {
|
|
|
783
793
|
}
|
|
784
794
|
|
|
785
795
|
// src/commands/audit-protected.ts
|
|
786
|
-
|
|
796
|
+
init_dist2();
|
|
787
797
|
import { Command as Command6 } from "commander";
|
|
788
798
|
import * as path3 from "path";
|
|
789
799
|
import * as fs2 from "fs";
|
|
@@ -891,7 +901,7 @@ function createAuditProtectedCommand() {
|
|
|
891
901
|
}
|
|
892
902
|
|
|
893
903
|
// src/commands/blueprint.ts
|
|
894
|
-
|
|
904
|
+
init_dist2();
|
|
895
905
|
import { Command as Command7 } from "commander";
|
|
896
906
|
import * as path4 from "path";
|
|
897
907
|
function createBlueprintCommand() {
|
|
@@ -916,7 +926,7 @@ function createBlueprintCommand() {
|
|
|
916
926
|
}
|
|
917
927
|
|
|
918
928
|
// src/commands/check-arch.ts
|
|
919
|
-
|
|
929
|
+
init_dist2();
|
|
920
930
|
import { Command as Command8 } from "commander";
|
|
921
931
|
import { execSync as execSync2 } from "child_process";
|
|
922
932
|
function getCommitHash(cwd) {
|
|
@@ -1076,8 +1086,8 @@ function createCheckArchCommand() {
|
|
|
1076
1086
|
}
|
|
1077
1087
|
|
|
1078
1088
|
// src/commands/check-deps.ts
|
|
1079
|
-
|
|
1080
|
-
|
|
1089
|
+
init_dist2();
|
|
1090
|
+
init_dist2();
|
|
1081
1091
|
import { Command as Command9 } from "commander";
|
|
1082
1092
|
import * as path5 from "path";
|
|
1083
1093
|
async function runCheckDeps(options) {
|
|
@@ -1177,8 +1187,8 @@ function createCheckDepsCommand() {
|
|
|
1177
1187
|
}
|
|
1178
1188
|
|
|
1179
1189
|
// src/commands/check-docs.ts
|
|
1180
|
-
|
|
1181
|
-
|
|
1190
|
+
init_dist2();
|
|
1191
|
+
init_dist2();
|
|
1182
1192
|
import { Command as Command10 } from "commander";
|
|
1183
1193
|
import * as path6 from "path";
|
|
1184
1194
|
async function runCheckDocs(options) {
|
|
@@ -1287,7 +1297,7 @@ function createCheckDocsCommand() {
|
|
|
1287
1297
|
}
|
|
1288
1298
|
|
|
1289
1299
|
// src/commands/check-perf.ts
|
|
1290
|
-
|
|
1300
|
+
init_dist2();
|
|
1291
1301
|
import { Command as Command11 } from "commander";
|
|
1292
1302
|
import * as path7 from "path";
|
|
1293
1303
|
async function runCheckPerf(cwd, options) {
|
|
@@ -1412,7 +1422,7 @@ function createCheckPerfCommand() {
|
|
|
1412
1422
|
}
|
|
1413
1423
|
|
|
1414
1424
|
// src/commands/check-security.ts
|
|
1415
|
-
|
|
1425
|
+
init_dist2();
|
|
1416
1426
|
import { Command as Command12 } from "commander";
|
|
1417
1427
|
import * as path8 from "path";
|
|
1418
1428
|
import { execSync as execSync3 } from "child_process";
|
|
@@ -1436,10 +1446,10 @@ async function runCheckSecurity(cwd, options) {
|
|
|
1436
1446
|
const projectRoot = path8.resolve(cwd);
|
|
1437
1447
|
let configData = {};
|
|
1438
1448
|
try {
|
|
1439
|
-
const
|
|
1449
|
+
const fs44 = await import("fs");
|
|
1440
1450
|
const configPath = path8.join(projectRoot, "harness.config.json");
|
|
1441
|
-
if (
|
|
1442
|
-
const raw =
|
|
1451
|
+
if (fs44.existsSync(configPath)) {
|
|
1452
|
+
const raw = fs44.readFileSync(configPath, "utf-8");
|
|
1443
1453
|
const parsed = JSON.parse(raw);
|
|
1444
1454
|
configData = parsed.security ?? {};
|
|
1445
1455
|
}
|
|
@@ -1534,7 +1544,7 @@ function createCheckSecurityCommand() {
|
|
|
1534
1544
|
import { Command as Command16 } from "commander";
|
|
1535
1545
|
|
|
1536
1546
|
// src/commands/ci/check.ts
|
|
1537
|
-
|
|
1547
|
+
init_dist2();
|
|
1538
1548
|
import { Command as Command13 } from "commander";
|
|
1539
1549
|
var VALID_CHECKS = [
|
|
1540
1550
|
"validate",
|
|
@@ -1631,7 +1641,7 @@ function createCheckCommand() {
|
|
|
1631
1641
|
}
|
|
1632
1642
|
|
|
1633
1643
|
// src/commands/ci/init.ts
|
|
1634
|
-
|
|
1644
|
+
init_dist2();
|
|
1635
1645
|
import { Command as Command14 } from "commander";
|
|
1636
1646
|
import * as fs3 from "fs";
|
|
1637
1647
|
import * as path9 from "path";
|
|
@@ -1775,7 +1785,7 @@ function createInitCommand() {
|
|
|
1775
1785
|
}
|
|
1776
1786
|
|
|
1777
1787
|
// src/commands/ci/notify.ts
|
|
1778
|
-
|
|
1788
|
+
init_dist2();
|
|
1779
1789
|
import { Command as Command15 } from "commander";
|
|
1780
1790
|
import * as fs4 from "fs";
|
|
1781
1791
|
function loadReport(reportPath) {
|
|
@@ -1888,7 +1898,7 @@ function createCICommand() {
|
|
|
1888
1898
|
}
|
|
1889
1899
|
|
|
1890
1900
|
// src/commands/cleanup.ts
|
|
1891
|
-
|
|
1901
|
+
init_dist2();
|
|
1892
1902
|
import { Command as Command17 } from "commander";
|
|
1893
1903
|
import * as path10 from "path";
|
|
1894
1904
|
async function runCleanup(options) {
|
|
@@ -1910,7 +1920,7 @@ async function runCleanup(options) {
|
|
|
1910
1920
|
const entropyConfig = {
|
|
1911
1921
|
rootDir,
|
|
1912
1922
|
...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
|
|
1913
|
-
docPaths: [docsDir],
|
|
1923
|
+
docPaths: [path10.join(docsDir, "**/*.md")],
|
|
1914
1924
|
analyze: {
|
|
1915
1925
|
drift: type === "all" || type === "drift",
|
|
1916
1926
|
deadCode: type === "all" || type === "dead-code",
|
|
@@ -2008,7 +2018,7 @@ function createCleanupCommand() {
|
|
|
2008
2018
|
}
|
|
2009
2019
|
|
|
2010
2020
|
// src/commands/cleanup-sessions.ts
|
|
2011
|
-
|
|
2021
|
+
init_dist2();
|
|
2012
2022
|
import { Command as Command18 } from "commander";
|
|
2013
2023
|
import * as fs5 from "fs";
|
|
2014
2024
|
import * as path11 from "path";
|
|
@@ -2117,9 +2127,9 @@ function createCleanupSessionsCommand() {
|
|
|
2117
2127
|
import { Command as Command20 } from "commander";
|
|
2118
2128
|
|
|
2119
2129
|
// src/commands/compound/scan-candidates.ts
|
|
2120
|
-
|
|
2130
|
+
init_dist2();
|
|
2121
2131
|
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
2122
|
-
import { dirname as dirname2, join as
|
|
2132
|
+
import { dirname as dirname2, join as join7, resolve as resolve11 } from "path";
|
|
2123
2133
|
import { Command as Command19 } from "commander";
|
|
2124
2134
|
var HOTSPOT_THRESHOLD = 7;
|
|
2125
2135
|
var DEFAULT_LOOKBACK = "7d";
|
|
@@ -2146,7 +2156,7 @@ async function runCompoundScanCandidatesCommand(opts) {
|
|
|
2146
2156
|
isoWeek: week,
|
|
2147
2157
|
lookback
|
|
2148
2158
|
});
|
|
2149
|
-
const outputPath = opts.outputPath ??
|
|
2159
|
+
const outputPath = opts.outputPath ?? join7(cwd, "docs/solutions/.candidates", `${formatIsoWeek(week)}.md`);
|
|
2150
2160
|
mkdirSync3(dirname2(outputPath), { recursive: true });
|
|
2151
2161
|
writeFileSync3(outputPath, report);
|
|
2152
2162
|
const candidatesFound = undocumented.length + hotspots.length;
|
|
@@ -2210,7 +2220,7 @@ import { Command as Command21 } from "commander";
|
|
|
2210
2220
|
import { spawn } from "child_process";
|
|
2211
2221
|
import { existsSync as existsSync4 } from "fs";
|
|
2212
2222
|
import { createRequire } from "module";
|
|
2213
|
-
import { dirname as dirname3, join as
|
|
2223
|
+
import { dirname as dirname3, join as join8, resolve as resolve12 } from "path";
|
|
2214
2224
|
import { setTimeout } from "timers";
|
|
2215
2225
|
import { fileURLToPath } from "url";
|
|
2216
2226
|
var __filename = fileURLToPath(import.meta.url);
|
|
@@ -2226,20 +2236,20 @@ function resolveServerScript() {
|
|
|
2226
2236
|
try {
|
|
2227
2237
|
const pkgPath = require2.resolve("@harness-engineering/dashboard/package.json");
|
|
2228
2238
|
const pkgDir = dirname3(pkgPath);
|
|
2229
|
-
const built2 =
|
|
2239
|
+
const built2 = join8(pkgDir, "dist", "server", "serve.js");
|
|
2230
2240
|
if (existsSync4(built2)) return { script: built2, dev: false };
|
|
2231
|
-
const dev2 =
|
|
2241
|
+
const dev2 = join8(pkgDir, "src", "server", "serve.ts");
|
|
2232
2242
|
if (existsSync4(dev2)) return { script: dev2, dev: true };
|
|
2233
2243
|
} catch {
|
|
2234
2244
|
}
|
|
2235
2245
|
const built = [
|
|
2236
|
-
|
|
2237
|
-
|
|
2246
|
+
join8(__dirname, "..", "..", "..", "dashboard", "dist", "server", "serve.js"),
|
|
2247
|
+
join8(__dirname, "..", "..", "..", "..", "packages", "dashboard", "dist", "server", "serve.js")
|
|
2238
2248
|
].find((p) => existsSync4(p));
|
|
2239
2249
|
if (built) return { script: built, dev: false };
|
|
2240
2250
|
const dev = [
|
|
2241
|
-
|
|
2242
|
-
|
|
2251
|
+
join8(__dirname, "..", "..", "..", "dashboard", "src", "server", "serve.ts"),
|
|
2252
|
+
join8(__dirname, "..", "..", "..", "..", "packages", "dashboard", "src", "server", "serve.ts")
|
|
2243
2253
|
].find((p) => existsSync4(p));
|
|
2244
2254
|
return dev ? { script: dev, dev: true } : null;
|
|
2245
2255
|
}
|
|
@@ -2653,7 +2663,7 @@ function createDoctorCommand() {
|
|
|
2653
2663
|
}
|
|
2654
2664
|
|
|
2655
2665
|
// src/commands/fix-drift.ts
|
|
2656
|
-
|
|
2666
|
+
init_dist2();
|
|
2657
2667
|
import { Command as Command23 } from "commander";
|
|
2658
2668
|
import * as path14 from "path";
|
|
2659
2669
|
async function runFixDrift(options) {
|
|
@@ -2669,7 +2679,7 @@ async function runFixDrift(options) {
|
|
|
2669
2679
|
const entropyConfig = {
|
|
2670
2680
|
rootDir,
|
|
2671
2681
|
...config.entropy?.entryPoints && { entryPoints: config.entropy.entryPoints },
|
|
2672
|
-
docPaths: [docsDir],
|
|
2682
|
+
docPaths: [path14.join(docsDir, "**/*.md")],
|
|
2673
2683
|
analyze: {
|
|
2674
2684
|
drift: true,
|
|
2675
2685
|
deadCode: true,
|
|
@@ -2888,11 +2898,11 @@ import { Command as Command27 } from "commander";
|
|
|
2888
2898
|
// src/commands/graph/status.ts
|
|
2889
2899
|
import * as path15 from "path";
|
|
2890
2900
|
async function readConnectorSyncStatus(graphDir) {
|
|
2891
|
-
const
|
|
2901
|
+
const fs44 = await import("fs/promises");
|
|
2892
2902
|
const result = {};
|
|
2893
2903
|
try {
|
|
2894
2904
|
const syncMetaPath = path15.join(graphDir, "sync-metadata.json");
|
|
2895
|
-
const syncMeta = JSON.parse(await
|
|
2905
|
+
const syncMeta = JSON.parse(await fs44.readFile(syncMetaPath, "utf-8"));
|
|
2896
2906
|
for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
|
|
2897
2907
|
result[name] = data.lastSyncTimestamp;
|
|
2898
2908
|
}
|
|
@@ -2901,7 +2911,7 @@ async function readConnectorSyncStatus(graphDir) {
|
|
|
2901
2911
|
return result;
|
|
2902
2912
|
}
|
|
2903
2913
|
async function runGraphStatus(projectPath) {
|
|
2904
|
-
const { GraphStore, loadGraphMetadata } = await import("./dist-
|
|
2914
|
+
const { GraphStore, loadGraphMetadata } = await import("./dist-SQAOKWXM.js");
|
|
2905
2915
|
const graphDir = path15.join(projectPath, ".harness", "graph");
|
|
2906
2916
|
const metaResult = await loadGraphMetadata(graphDir);
|
|
2907
2917
|
if (metaResult.status === "not_found") {
|
|
@@ -2953,7 +2963,7 @@ async function runGraphStatus(projectPath) {
|
|
|
2953
2963
|
// src/commands/graph/export.ts
|
|
2954
2964
|
import * as path16 from "path";
|
|
2955
2965
|
async function runGraphExport(projectPath, format) {
|
|
2956
|
-
const { GraphStore } = await import("./dist-
|
|
2966
|
+
const { GraphStore } = await import("./dist-SQAOKWXM.js");
|
|
2957
2967
|
const graphDir = path16.join(projectPath, ".harness", "graph");
|
|
2958
2968
|
const store = new GraphStore();
|
|
2959
2969
|
const loaded = await store.load(graphDir);
|
|
@@ -2989,7 +2999,7 @@ import * as path19 from "path";
|
|
|
2989
2999
|
import { Command as Command25 } from "commander";
|
|
2990
3000
|
import * as path17 from "path";
|
|
2991
3001
|
async function runQuery(projectPath, rootNodeId, opts) {
|
|
2992
|
-
const { GraphStore, ContextQL } = await import("./dist-
|
|
3002
|
+
const { GraphStore, ContextQL } = await import("./dist-SQAOKWXM.js");
|
|
2993
3003
|
const store = new GraphStore();
|
|
2994
3004
|
const graphDir = path17.join(projectPath, ".harness", "graph");
|
|
2995
3005
|
const loaded = await store.load(graphDir);
|
|
@@ -3042,9 +3052,9 @@ import { Command as Command26 } from "commander";
|
|
|
3042
3052
|
import * as path18 from "path";
|
|
3043
3053
|
async function loadConnectorConfig(projectPath, source) {
|
|
3044
3054
|
try {
|
|
3045
|
-
const
|
|
3055
|
+
const fs44 = await import("fs/promises");
|
|
3046
3056
|
const configPath = path18.join(projectPath, "harness.config.json");
|
|
3047
|
-
const config = JSON.parse(await
|
|
3057
|
+
const config = JSON.parse(await fs44.readFile(configPath, "utf-8"));
|
|
3048
3058
|
const connector = config.graph?.connectors?.[source];
|
|
3049
3059
|
return connector ?? {};
|
|
3050
3060
|
} catch {
|
|
@@ -3086,7 +3096,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
3086
3096
|
ConfluenceConnector,
|
|
3087
3097
|
FigmaConnector,
|
|
3088
3098
|
MiroConnector
|
|
3089
|
-
} = await import("./dist-
|
|
3099
|
+
} = await import("./dist-SQAOKWXM.js");
|
|
3090
3100
|
const graphDir = path18.join(projectPath, ".harness", "graph");
|
|
3091
3101
|
const store = new GraphStore();
|
|
3092
3102
|
await store.load(graphDir);
|
|
@@ -3100,7 +3110,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
3100
3110
|
path18.join(projectPath, "docs", "changes")
|
|
3101
3111
|
);
|
|
3102
3112
|
const gitResult = await new GitIngestor(store).ingest(projectPath);
|
|
3103
|
-
const { createExtractionRunner } = await import("./dist-
|
|
3113
|
+
const { createExtractionRunner } = await import("./dist-SQAOKWXM.js");
|
|
3104
3114
|
const extractedDir = path18.join(projectPath, ".harness", "knowledge", "extracted");
|
|
3105
3115
|
const signalsResult = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
3106
3116
|
const syncManager = new SyncManager(store, graphDir);
|
|
@@ -3146,7 +3156,7 @@ async function runIngest(projectPath, source, opts) {
|
|
|
3146
3156
|
);
|
|
3147
3157
|
break;
|
|
3148
3158
|
case "business-signals": {
|
|
3149
|
-
const { createExtractionRunner } = await import("./dist-
|
|
3159
|
+
const { createExtractionRunner } = await import("./dist-SQAOKWXM.js");
|
|
3150
3160
|
const extractedDir = path18.join(projectPath, ".harness", "knowledge", "extracted");
|
|
3151
3161
|
result = await createExtractionRunner().run(projectPath, store, extractedDir);
|
|
3152
3162
|
break;
|
|
@@ -3840,7 +3850,7 @@ function createImpactPreviewCommand() {
|
|
|
3840
3850
|
}
|
|
3841
3851
|
|
|
3842
3852
|
// src/commands/init.ts
|
|
3843
|
-
|
|
3853
|
+
init_dist2();
|
|
3844
3854
|
import { Command as Command35 } from "commander";
|
|
3845
3855
|
import chalk3 from "chalk";
|
|
3846
3856
|
import * as fs15 from "fs";
|
|
@@ -4372,8 +4382,8 @@ function extractSkillName(packageName) {
|
|
|
4372
4382
|
return packageName;
|
|
4373
4383
|
}
|
|
4374
4384
|
function readNpmrcToken(registryUrl) {
|
|
4375
|
-
const { hostname, pathname } = new URL(registryUrl);
|
|
4376
|
-
const registryPath = `//${
|
|
4385
|
+
const { hostname: hostname2, pathname } = new URL(registryUrl);
|
|
4386
|
+
const registryPath = `//${hostname2}${pathname.replace(/\/$/, "")}/:_authToken=`;
|
|
4377
4387
|
const candidates = [path28.join(process.cwd(), ".npmrc"), path28.join(os3.homedir(), ".npmrc")];
|
|
4378
4388
|
for (const npmrcPath of candidates) {
|
|
4379
4389
|
try {
|
|
@@ -4654,6 +4664,7 @@ function getBundledSkillNames(bundledSkillsDir) {
|
|
|
4654
4664
|
}
|
|
4655
4665
|
|
|
4656
4666
|
// src/commands/install.ts
|
|
4667
|
+
init_dist();
|
|
4657
4668
|
function validateSkillYaml(parsed) {
|
|
4658
4669
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
4659
4670
|
if (!result.success) {
|
|
@@ -4720,7 +4731,7 @@ function discoverSkillDirs(rootDir) {
|
|
|
4720
4731
|
const entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
4721
4732
|
for (const entry of entries) {
|
|
4722
4733
|
if (!entry.isDirectory()) continue;
|
|
4723
|
-
if (entry.name.startsWith(".") || entry.name
|
|
4734
|
+
if (entry.name.startsWith(".") || DEFAULT_SKIP_DIRS.has(entry.name)) continue;
|
|
4724
4735
|
scan(path32.join(dir, entry.name), depth + 1);
|
|
4725
4736
|
}
|
|
4726
4737
|
}
|
|
@@ -4939,7 +4950,7 @@ function createInstallCommand() {
|
|
|
4939
4950
|
}
|
|
4940
4951
|
|
|
4941
4952
|
// src/commands/install-constraints.ts
|
|
4942
|
-
|
|
4953
|
+
init_dist2();
|
|
4943
4954
|
import * as fs21 from "fs/promises";
|
|
4944
4955
|
import * as path33 from "path";
|
|
4945
4956
|
import { Command as Command37 } from "commander";
|
|
@@ -5205,7 +5216,7 @@ function createInstallConstraintsCommand() {
|
|
|
5205
5216
|
import { Command as Command42 } from "commander";
|
|
5206
5217
|
|
|
5207
5218
|
// src/commands/integrations/add.ts
|
|
5208
|
-
|
|
5219
|
+
init_dist2();
|
|
5209
5220
|
import { Command as Command38 } from "commander";
|
|
5210
5221
|
import * as fs22 from "fs";
|
|
5211
5222
|
import * as path34 from "path";
|
|
@@ -5345,7 +5356,7 @@ function createListIntegrationsCommand() {
|
|
|
5345
5356
|
}
|
|
5346
5357
|
|
|
5347
5358
|
// src/commands/integrations/remove.ts
|
|
5348
|
-
|
|
5359
|
+
init_dist2();
|
|
5349
5360
|
import { Command as Command40 } from "commander";
|
|
5350
5361
|
import * as fs23 from "fs";
|
|
5351
5362
|
import * as path36 from "path";
|
|
@@ -5394,7 +5405,7 @@ function createRemoveIntegrationCommand() {
|
|
|
5394
5405
|
}
|
|
5395
5406
|
|
|
5396
5407
|
// src/commands/integrations/dismiss.ts
|
|
5397
|
-
|
|
5408
|
+
init_dist2();
|
|
5398
5409
|
import { Command as Command41 } from "commander";
|
|
5399
5410
|
import * as path37 from "path";
|
|
5400
5411
|
function dismissIntegration(cwd, name) {
|
|
@@ -5461,9 +5472,9 @@ function createKnowledgePipelineCommand() {
|
|
|
5461
5472
|
const projectDir = process.cwd();
|
|
5462
5473
|
try {
|
|
5463
5474
|
const graphDir = path38.join(projectDir, ".harness", "graph");
|
|
5464
|
-
const
|
|
5465
|
-
await
|
|
5466
|
-
const { GraphStore, KnowledgePipelineRunner } = await import("./dist-
|
|
5475
|
+
const fs44 = await import("fs/promises");
|
|
5476
|
+
await fs44.mkdir(graphDir, { recursive: true });
|
|
5477
|
+
const { GraphStore, KnowledgePipelineRunner } = await import("./dist-SQAOKWXM.js");
|
|
5467
5478
|
const store = new GraphStore();
|
|
5468
5479
|
try {
|
|
5469
5480
|
await store.load(graphDir);
|
|
@@ -5623,7 +5634,7 @@ function createKnowledgePipelineCommand() {
|
|
|
5623
5634
|
import { Command as Command45 } from "commander";
|
|
5624
5635
|
|
|
5625
5636
|
// src/commands/learnings/prune.ts
|
|
5626
|
-
|
|
5637
|
+
init_dist2();
|
|
5627
5638
|
import { Command as Command44 } from "commander";
|
|
5628
5639
|
import * as path39 from "path";
|
|
5629
5640
|
async function handlePrune(opts) {
|
|
@@ -5805,7 +5816,7 @@ function createMcpCommand() {
|
|
|
5805
5816
|
parseBudget
|
|
5806
5817
|
).action(async (opts) => {
|
|
5807
5818
|
const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
|
|
5808
|
-
import("./mcp-
|
|
5819
|
+
import("./mcp-ST26QMDK.js"),
|
|
5809
5820
|
import("./tool-tiers-7QGZ3FKY.js")
|
|
5810
5821
|
]);
|
|
5811
5822
|
if (opts.tools && opts.tools.length > 0) {
|
|
@@ -5826,6 +5837,7 @@ function createMcpCommand() {
|
|
|
5826
5837
|
}
|
|
5827
5838
|
|
|
5828
5839
|
// src/commands/migrate.ts
|
|
5840
|
+
init_dist2();
|
|
5829
5841
|
init_dist();
|
|
5830
5842
|
import { Command as Command50 } from "commander";
|
|
5831
5843
|
import * as fs24 from "fs";
|
|
@@ -5860,25 +5872,27 @@ function walk(dir, predicate) {
|
|
|
5860
5872
|
if (!fs24.existsSync(dir)) return [];
|
|
5861
5873
|
const out = [];
|
|
5862
5874
|
for (const entry of fs24.readdirSync(dir, { withFileTypes: true })) {
|
|
5863
|
-
if (
|
|
5875
|
+
if (DEFAULT_SKIP_DIRS.has(entry.name)) continue;
|
|
5864
5876
|
const full = path40.join(dir, entry.name);
|
|
5865
5877
|
if (entry.isDirectory()) out.push(...walk(full, predicate));
|
|
5866
5878
|
else if (predicate(full)) out.push(full);
|
|
5867
5879
|
}
|
|
5868
5880
|
return out;
|
|
5869
5881
|
}
|
|
5882
|
+
function extractAutopilotTopic(state) {
|
|
5883
|
+
const match = (state.specPath ?? "").match(/^docs\/changes\/([^/]+)\/proposal\.md$/);
|
|
5884
|
+
return match?.[1] ?? null;
|
|
5885
|
+
}
|
|
5870
5886
|
function buildAutopilotMap(cwd) {
|
|
5871
5887
|
const map = /* @__PURE__ */ new Map();
|
|
5872
5888
|
const sessionsDir = path40.join(cwd, ".harness", "sessions");
|
|
5873
5889
|
if (!fs24.existsSync(sessionsDir)) return map;
|
|
5874
5890
|
for (const session of listDirs(sessionsDir)) {
|
|
5875
5891
|
if (!session.startsWith("changes--")) continue;
|
|
5876
|
-
const
|
|
5877
|
-
const state = readJson2(statePath);
|
|
5892
|
+
const state = readJson2(path40.join(sessionsDir, session, "autopilot-state.json"));
|
|
5878
5893
|
if (!state) continue;
|
|
5879
|
-
const
|
|
5880
|
-
if (!
|
|
5881
|
-
const topic = specMatch[1];
|
|
5894
|
+
const topic = extractAutopilotTopic(state);
|
|
5895
|
+
if (!topic) continue;
|
|
5882
5896
|
for (const phase of state.phases ?? []) {
|
|
5883
5897
|
if (phase.planPath) map.set(phase.planPath, topic);
|
|
5884
5898
|
}
|
|
@@ -5912,8 +5926,7 @@ function extractTopicFromFilename(filename, proposalTopics) {
|
|
|
5912
5926
|
}
|
|
5913
5927
|
return best;
|
|
5914
5928
|
}
|
|
5915
|
-
|
|
5916
|
-
const cwd = opts.cwd ?? process.cwd();
|
|
5929
|
+
function resolveDocsDir(cwd) {
|
|
5917
5930
|
const configPath = path40.join(cwd, "harness.config.json");
|
|
5918
5931
|
const configResult = fs24.existsSync(configPath) ? resolveConfig(configPath) : null;
|
|
5919
5932
|
let docsDir;
|
|
@@ -5929,71 +5942,72 @@ async function buildPlan(opts) {
|
|
|
5929
5942
|
}
|
|
5930
5943
|
const docsAbs = path40.resolve(cwd, docsDir);
|
|
5931
5944
|
const relRaw = path40.relative(cwd, docsAbs).replaceAll("\\", "/");
|
|
5932
|
-
|
|
5945
|
+
return relRaw === "" ? "docs" : relRaw;
|
|
5946
|
+
}
|
|
5947
|
+
function collectAdrMoves(cwd, docsRel) {
|
|
5948
|
+
const moves = [];
|
|
5949
|
+
const legacyAdrDir = path40.join(cwd, ".harness", "architecture");
|
|
5950
|
+
if (!fs24.existsSync(legacyAdrDir)) return moves;
|
|
5951
|
+
for (const topic of listDirs(legacyAdrDir)) {
|
|
5952
|
+
const topicSrc = path40.join(legacyAdrDir, topic);
|
|
5953
|
+
for (const file of listFiles(topicSrc, (n) => n.endsWith(".md"))) {
|
|
5954
|
+
moves.push({
|
|
5955
|
+
src: `.harness/architecture/${topic}/${file}`,
|
|
5956
|
+
dest: `${docsRel}/architecture/${topic}/${file}`
|
|
5957
|
+
});
|
|
5958
|
+
}
|
|
5959
|
+
}
|
|
5960
|
+
return moves;
|
|
5961
|
+
}
|
|
5962
|
+
function collectProposalTopics(cwd) {
|
|
5963
|
+
const topics = /* @__PURE__ */ new Set();
|
|
5964
|
+
const changesDir = path40.join(cwd, "docs", "changes");
|
|
5965
|
+
for (const topic of listDirs(changesDir)) {
|
|
5966
|
+
if (fs24.existsSync(path40.join(changesDir, topic, "proposal.md"))) {
|
|
5967
|
+
topics.add(topic);
|
|
5968
|
+
}
|
|
5969
|
+
}
|
|
5970
|
+
return topics;
|
|
5971
|
+
}
|
|
5972
|
+
function classifyPlan(planRel, planAbs, filename, autopilotMap, proposalTopics) {
|
|
5973
|
+
const fromAutopilot = autopilotMap.get(planRel);
|
|
5974
|
+
if (fromAutopilot) return { topic: fromAutopilot, via: "autopilot" };
|
|
5975
|
+
const fromHeader = extractTopicFromHeader(planAbs, proposalTopics);
|
|
5976
|
+
if (fromHeader) return { topic: fromHeader, via: "header" };
|
|
5977
|
+
const fromFilename = extractTopicFromFilename(filename, proposalTopics);
|
|
5978
|
+
if (fromFilename) return { topic: fromFilename, via: "filename" };
|
|
5979
|
+
return null;
|
|
5980
|
+
}
|
|
5981
|
+
async function buildPlan(opts) {
|
|
5982
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
5983
|
+
const docsRel = resolveDocsDir(cwd);
|
|
5933
5984
|
const plan = {
|
|
5934
5985
|
cwd,
|
|
5935
5986
|
isGitRepo: isGitRepo(cwd),
|
|
5936
5987
|
docsDir: docsRel,
|
|
5937
|
-
adrMoves:
|
|
5988
|
+
adrMoves: collectAdrMoves(cwd, docsRel),
|
|
5938
5989
|
planMoves: [],
|
|
5939
5990
|
orphanPlans: [],
|
|
5940
|
-
proposalTopics:
|
|
5991
|
+
proposalTopics: collectProposalTopics(cwd)
|
|
5941
5992
|
};
|
|
5942
|
-
const legacyAdrDir = path40.join(cwd, ".harness", "architecture");
|
|
5943
|
-
if (fs24.existsSync(legacyAdrDir)) {
|
|
5944
|
-
for (const topic of listDirs(legacyAdrDir)) {
|
|
5945
|
-
const topicSrc = path40.join(legacyAdrDir, topic);
|
|
5946
|
-
for (const file of listFiles(topicSrc, (n) => n.endsWith(".md"))) {
|
|
5947
|
-
const src = `.harness/architecture/${topic}/${file}`;
|
|
5948
|
-
const dest = `${docsRel}/architecture/${topic}/${file}`;
|
|
5949
|
-
plan.adrMoves.push({ src, dest });
|
|
5950
|
-
}
|
|
5951
|
-
}
|
|
5952
|
-
}
|
|
5953
5993
|
const legacyPlanDir = path40.join(cwd, "docs", "plans");
|
|
5954
|
-
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
topic = fromAutopilot;
|
|
5972
|
-
via = "autopilot";
|
|
5973
|
-
} else {
|
|
5974
|
-
const fromHeader = extractTopicFromHeader(planAbs, plan.proposalTopics);
|
|
5975
|
-
if (fromHeader) {
|
|
5976
|
-
topic = fromHeader;
|
|
5977
|
-
via = "header";
|
|
5978
|
-
} else {
|
|
5979
|
-
const fromFilename = extractTopicFromFilename(file, plan.proposalTopics);
|
|
5980
|
-
if (fromFilename) {
|
|
5981
|
-
topic = fromFilename;
|
|
5982
|
-
via = "filename";
|
|
5983
|
-
}
|
|
5984
|
-
}
|
|
5985
|
-
}
|
|
5986
|
-
if (topic && via) {
|
|
5987
|
-
plan.planMoves.push({
|
|
5988
|
-
src: planRel,
|
|
5989
|
-
dest: `docs/changes/${topic}/${subdir}/${file}`,
|
|
5990
|
-
topic,
|
|
5991
|
-
via,
|
|
5992
|
-
subdir
|
|
5993
|
-
});
|
|
5994
|
-
} else {
|
|
5995
|
-
plan.orphanPlans.push(planRel);
|
|
5996
|
-
}
|
|
5994
|
+
if (!fs24.existsSync(legacyPlanDir)) return plan;
|
|
5995
|
+
const autopilotMap = buildAutopilotMap(cwd);
|
|
5996
|
+
for (const file of listFiles(legacyPlanDir, (n) => n.endsWith(".md") && n !== "index.md")) {
|
|
5997
|
+
const planRel = `docs/plans/${file}`;
|
|
5998
|
+
const planAbs = path40.join(legacyPlanDir, file);
|
|
5999
|
+
const subdir = file.includes("VERIFICATION") ? "verifications" : "plans";
|
|
6000
|
+
const classified = classifyPlan(planRel, planAbs, file, autopilotMap, plan.proposalTopics);
|
|
6001
|
+
if (classified) {
|
|
6002
|
+
plan.planMoves.push({
|
|
6003
|
+
src: planRel,
|
|
6004
|
+
dest: `docs/changes/${classified.topic}/${subdir}/${file}`,
|
|
6005
|
+
topic: classified.topic,
|
|
6006
|
+
via: classified.via,
|
|
6007
|
+
subdir
|
|
6008
|
+
});
|
|
6009
|
+
} else {
|
|
6010
|
+
plan.orphanPlans.push(planRel);
|
|
5997
6011
|
}
|
|
5998
6012
|
}
|
|
5999
6013
|
return plan;
|
|
@@ -6068,7 +6082,7 @@ function updateReferences(plan) {
|
|
|
6068
6082
|
}
|
|
6069
6083
|
async function prompt(question, def = "n") {
|
|
6070
6084
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
6071
|
-
const answer = await new Promise((
|
|
6085
|
+
const answer = await new Promise((resolve40) => rl.question(question, resolve40));
|
|
6072
6086
|
rl.close();
|
|
6073
6087
|
return (answer.trim() || def).toLowerCase();
|
|
6074
6088
|
}
|
|
@@ -6166,6 +6180,29 @@ async function detectLegacyArtifacts(cwd) {
|
|
|
6166
6180
|
const planLegacy = fs24.existsSync(planLegacyDir) && listFiles(planLegacyDir, (n) => n.endsWith(".md") && n !== "index.md").length > 0;
|
|
6167
6181
|
return { adrLegacy, planLegacy };
|
|
6168
6182
|
}
|
|
6183
|
+
async function confirmApply(opts) {
|
|
6184
|
+
if (opts.yes || !process.stdin.isTTY) return true;
|
|
6185
|
+
const answer = await prompt("Apply this migration? [y/N] ", "n");
|
|
6186
|
+
return answer === "y" || answer === "yes";
|
|
6187
|
+
}
|
|
6188
|
+
function applyMigration(plan, opts) {
|
|
6189
|
+
const { moved, failed } = executeMigration(plan);
|
|
6190
|
+
console.log("");
|
|
6191
|
+
if (failed > 0) logger.error(` Moved ${moved} file(s); ${failed} failed.`);
|
|
6192
|
+
else logger.success(` Moved ${moved} file(s).`);
|
|
6193
|
+
let references = { files: 0, replacements: 0 };
|
|
6194
|
+
if (!opts.skipReferences) {
|
|
6195
|
+
references = updateReferences(plan);
|
|
6196
|
+
if (references.replacements > 0) {
|
|
6197
|
+
logger.success(
|
|
6198
|
+
` Updated ${references.replacements} reference(s) across ${references.files} file(s).`
|
|
6199
|
+
);
|
|
6200
|
+
} else {
|
|
6201
|
+
logger.info(" No reference updates needed.");
|
|
6202
|
+
}
|
|
6203
|
+
}
|
|
6204
|
+
return { moved, failed, references };
|
|
6205
|
+
}
|
|
6169
6206
|
async function runMigrate(opts) {
|
|
6170
6207
|
const cwd = opts.cwd ?? process.cwd();
|
|
6171
6208
|
const plan = await buildPlan({ ...opts, cwd });
|
|
@@ -6188,34 +6225,13 @@ async function runMigrate(opts) {
|
|
|
6188
6225
|
const movesPlanned = plan.adrMoves.length + plan.planMoves.length;
|
|
6189
6226
|
if (opts.dryRun) {
|
|
6190
6227
|
logger.info(" Dry run \u2014 no files moved.");
|
|
6191
|
-
return Ok({
|
|
6192
|
-
...empty,
|
|
6193
|
-
movesPlanned,
|
|
6194
|
-
orphansRemaining: plan.orphanPlans.length
|
|
6195
|
-
});
|
|
6228
|
+
return Ok({ ...empty, movesPlanned, orphansRemaining: plan.orphanPlans.length });
|
|
6196
6229
|
}
|
|
6197
|
-
if (!opts
|
|
6198
|
-
|
|
6199
|
-
|
|
6200
|
-
logger.info(" Cancelled.");
|
|
6201
|
-
return Ok({ ...empty, movesPlanned, cancelled: true });
|
|
6202
|
-
}
|
|
6203
|
-
}
|
|
6204
|
-
const { moved, failed } = executeMigration(plan);
|
|
6205
|
-
console.log("");
|
|
6206
|
-
if (failed > 0) logger.error(` Moved ${moved} file(s); ${failed} failed.`);
|
|
6207
|
-
else logger.success(` Moved ${moved} file(s).`);
|
|
6208
|
-
let references = { files: 0, replacements: 0 };
|
|
6209
|
-
if (!opts.skipReferences) {
|
|
6210
|
-
references = updateReferences(plan);
|
|
6211
|
-
if (references.replacements > 0) {
|
|
6212
|
-
logger.success(
|
|
6213
|
-
` Updated ${references.replacements} reference(s) across ${references.files} file(s).`
|
|
6214
|
-
);
|
|
6215
|
-
} else {
|
|
6216
|
-
logger.info(" No reference updates needed.");
|
|
6217
|
-
}
|
|
6230
|
+
if (!await confirmApply(opts)) {
|
|
6231
|
+
logger.info(" Cancelled.");
|
|
6232
|
+
return Ok({ ...empty, movesPlanned, cancelled: true });
|
|
6218
6233
|
}
|
|
6234
|
+
const { moved, failed, references } = applyMigration(plan, opts);
|
|
6219
6235
|
console.log("");
|
|
6220
6236
|
logger.info(" Next: review changes with `git status` / `git diff`, then commit.");
|
|
6221
6237
|
return Ok({
|
|
@@ -6289,14 +6305,14 @@ function createOrchestratorCommand() {
|
|
|
6289
6305
|
}
|
|
6290
6306
|
|
|
6291
6307
|
// src/commands/perf.ts
|
|
6292
|
-
|
|
6308
|
+
init_dist2();
|
|
6293
6309
|
import { Command as Command52 } from "commander";
|
|
6294
6310
|
import * as path42 from "path";
|
|
6295
6311
|
function registerBenchCommand(perf) {
|
|
6296
6312
|
perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
|
|
6297
6313
|
const globalOpts = cmd.optsWithGlobals();
|
|
6298
6314
|
const cwd = process.cwd();
|
|
6299
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
6315
|
+
const { BenchmarkRunner } = await import("./dist-ZS3Y3YWP.js");
|
|
6300
6316
|
const runner = new BenchmarkRunner();
|
|
6301
6317
|
const benchFiles = runner.discover(cwd, glob2);
|
|
6302
6318
|
if (benchFiles.length === 0) {
|
|
@@ -6360,7 +6376,7 @@ async function runBaselinesShow(globalOpts) {
|
|
|
6360
6376
|
}
|
|
6361
6377
|
async function runBaselinesUpdate(globalOpts) {
|
|
6362
6378
|
const cwd = process.cwd();
|
|
6363
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
6379
|
+
const { BenchmarkRunner } = await import("./dist-ZS3Y3YWP.js");
|
|
6364
6380
|
const runner = new BenchmarkRunner();
|
|
6365
6381
|
const manager = new BaselineManager(cwd);
|
|
6366
6382
|
logger.info("Running benchmarks to update baselines...");
|
|
@@ -6401,7 +6417,7 @@ function registerReportCommand(perf) {
|
|
|
6401
6417
|
perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
|
|
6402
6418
|
const globalOpts = cmd.optsWithGlobals();
|
|
6403
6419
|
const cwd = process.cwd();
|
|
6404
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-
|
|
6420
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-ZS3Y3YWP.js");
|
|
6405
6421
|
const analyzer = new EntropyAnalyzer2({
|
|
6406
6422
|
rootDir: path42.resolve(cwd),
|
|
6407
6423
|
analyze: { complexity: true, coupling: true }
|
|
@@ -6572,7 +6588,7 @@ function createPersonaCommand() {
|
|
|
6572
6588
|
}
|
|
6573
6589
|
|
|
6574
6590
|
// src/commands/predict.ts
|
|
6575
|
-
|
|
6591
|
+
init_dist2();
|
|
6576
6592
|
import { Command as Command56 } from "commander";
|
|
6577
6593
|
import chalk8 from "chalk";
|
|
6578
6594
|
var CATEGORY_ORDER = [
|
|
@@ -6705,7 +6721,7 @@ function createPredictCommand() {
|
|
|
6705
6721
|
import * as fs26 from "fs";
|
|
6706
6722
|
import * as path44 from "path";
|
|
6707
6723
|
import { Command as Command57 } from "commander";
|
|
6708
|
-
|
|
6724
|
+
init_dist2();
|
|
6709
6725
|
import {
|
|
6710
6726
|
renderAnalysisComment,
|
|
6711
6727
|
loadPublishedIndex,
|
|
@@ -6735,14 +6751,39 @@ function bootstrapTrackerCommand(opts, verb) {
|
|
|
6735
6751
|
}
|
|
6736
6752
|
return { token, projectPath, trackerConfig };
|
|
6737
6753
|
}
|
|
6738
|
-
function buildNameToExternalIdMap(projectPath) {
|
|
6754
|
+
async function buildNameToExternalIdMap(projectPath) {
|
|
6755
|
+
const mode = loadProjectRoadmapMode(projectPath);
|
|
6756
|
+
if (mode === "file-less") {
|
|
6757
|
+
const trackerCfg = loadTrackerClientConfigFromProject(projectPath);
|
|
6758
|
+
if (!trackerCfg.ok) {
|
|
6759
|
+
logger.error(`File-less mode: ${trackerCfg.error.message}`);
|
|
6760
|
+
return null;
|
|
6761
|
+
}
|
|
6762
|
+
const clientR = createTrackerClient(trackerCfg.value);
|
|
6763
|
+
if (!clientR.ok) {
|
|
6764
|
+
logger.error(`File-less mode: ${clientR.error.message}`);
|
|
6765
|
+
return null;
|
|
6766
|
+
}
|
|
6767
|
+
const r = await clientR.value.fetchAll();
|
|
6768
|
+
if (!r.ok) {
|
|
6769
|
+
logger.error(`File-less mode: failed to fetch features from tracker: ${r.error.message}`);
|
|
6770
|
+
return null;
|
|
6771
|
+
}
|
|
6772
|
+
const fileLessMap = /* @__PURE__ */ new Map();
|
|
6773
|
+
for (const f of r.value.features) {
|
|
6774
|
+
if (f.externalId) {
|
|
6775
|
+
fileLessMap.set(f.name.toLowerCase(), f.externalId);
|
|
6776
|
+
}
|
|
6777
|
+
}
|
|
6778
|
+
return fileLessMap;
|
|
6779
|
+
}
|
|
6739
6780
|
const roadmapFile = path44.join(projectPath, "docs", "roadmap.md");
|
|
6740
6781
|
if (!fs26.existsSync(roadmapFile)) {
|
|
6741
6782
|
logger.error("No docs/roadmap.md found. Cannot map issue hashes to external IDs.");
|
|
6742
6783
|
return null;
|
|
6743
6784
|
}
|
|
6744
|
-
const { parseRoadmap } = (
|
|
6745
|
-
const roadmapParsed =
|
|
6785
|
+
const { parseRoadmap: parseRoadmap2 } = (init_dist2(), __toCommonJS(dist_exports));
|
|
6786
|
+
const roadmapParsed = parseRoadmap2(fs26.readFileSync(roadmapFile, "utf-8"));
|
|
6746
6787
|
if (!roadmapParsed.ok) {
|
|
6747
6788
|
logger.error("Failed to parse docs/roadmap.md");
|
|
6748
6789
|
return null;
|
|
@@ -6789,10 +6830,10 @@ async function runPublishAnalyses(opts) {
|
|
|
6789
6830
|
const bootstrap = bootstrapTrackerCommand(opts, "publish");
|
|
6790
6831
|
if (!bootstrap) return process.exit(1);
|
|
6791
6832
|
const { token, projectPath, trackerConfig } = bootstrap;
|
|
6792
|
-
const nameToExternalId = buildNameToExternalIdMap(projectPath);
|
|
6833
|
+
const nameToExternalId = await buildNameToExternalIdMap(projectPath);
|
|
6793
6834
|
if (!nameToExternalId) return process.exit(1);
|
|
6794
6835
|
const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
|
|
6795
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
6836
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-ZS3Y3YWP.js");
|
|
6796
6837
|
const archive = new AnalysisArchive(path44.join(projectPath, ".harness", "analyses"));
|
|
6797
6838
|
const analyses = await archive.list();
|
|
6798
6839
|
if (analyses.length === 0) {
|
|
@@ -6834,9 +6875,9 @@ function createPublishAnalysesCommand() {
|
|
|
6834
6875
|
import { Command as Command59 } from "commander";
|
|
6835
6876
|
|
|
6836
6877
|
// src/commands/pulse/run.ts
|
|
6837
|
-
|
|
6878
|
+
init_dist2();
|
|
6838
6879
|
import { readFileSync as readFileSync15, mkdirSync as mkdirSync13, writeFileSync as writeFileSync14 } from "fs";
|
|
6839
|
-
import { dirname as dirname17, join as
|
|
6880
|
+
import { dirname as dirname17, join as join35, resolve as resolve28 } from "path";
|
|
6840
6881
|
import { Command as Command58 } from "commander";
|
|
6841
6882
|
async function runPulseRunCommand(opts) {
|
|
6842
6883
|
const startedAt = Date.now();
|
|
@@ -6868,7 +6909,7 @@ async function runPulseRunCommand(opts) {
|
|
|
6868
6909
|
const report = assembleReport(result, productName, lookback);
|
|
6869
6910
|
const now = /* @__PURE__ */ new Date();
|
|
6870
6911
|
const ts = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}_${String(now.getHours()).padStart(2, "0")}-${String(now.getMinutes()).padStart(2, "0")}`;
|
|
6871
|
-
const filePath =
|
|
6912
|
+
const filePath = join35(opts.outputDir, `${ts}.md`);
|
|
6872
6913
|
mkdirSync13(dirname17(filePath), { recursive: true });
|
|
6873
6914
|
writeFileSync14(filePath, report);
|
|
6874
6915
|
const headlines = extractHeadlines(report);
|
|
@@ -7031,11 +7072,326 @@ function createRecommendCommand() {
|
|
|
7031
7072
|
return command;
|
|
7032
7073
|
}
|
|
7033
7074
|
|
|
7034
|
-
// src/commands/
|
|
7035
|
-
|
|
7075
|
+
// src/commands/roadmap/index.ts
|
|
7076
|
+
import { Command as Command62 } from "commander";
|
|
7077
|
+
|
|
7078
|
+
// src/commands/roadmap/migrate.ts
|
|
7079
|
+
init_dist2();
|
|
7036
7080
|
import { Command as Command61 } from "commander";
|
|
7037
|
-
import
|
|
7038
|
-
import
|
|
7081
|
+
import * as fs28 from "fs";
|
|
7082
|
+
import * as path46 from "path";
|
|
7083
|
+
import chalk10 from "chalk";
|
|
7084
|
+
|
|
7085
|
+
// src/commands/roadmap/migrate-lock.ts
|
|
7086
|
+
import * as fs27 from "fs";
|
|
7087
|
+
import * as path45 from "path";
|
|
7088
|
+
import * as os6 from "os";
|
|
7089
|
+
var LOCK_FILE_NAME = "migrate.lock";
|
|
7090
|
+
var STALE_LOCK_MS = 30 * 60 * 1e3;
|
|
7091
|
+
function isPidAlive(pid) {
|
|
7092
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
7093
|
+
try {
|
|
7094
|
+
process.kill(pid, 0);
|
|
7095
|
+
return true;
|
|
7096
|
+
} catch {
|
|
7097
|
+
return false;
|
|
7098
|
+
}
|
|
7099
|
+
}
|
|
7100
|
+
function acquireMigrateLock(projectRoot) {
|
|
7101
|
+
const harnessDir = path45.join(projectRoot, ".harness");
|
|
7102
|
+
const lockPath = path45.join(harnessDir, LOCK_FILE_NAME);
|
|
7103
|
+
if (!fs27.existsSync(harnessDir)) fs27.mkdirSync(harnessDir, { recursive: true });
|
|
7104
|
+
if (fs27.existsSync(lockPath)) {
|
|
7105
|
+
const existingRaw = (() => {
|
|
7106
|
+
try {
|
|
7107
|
+
return fs27.readFileSync(lockPath, "utf-8");
|
|
7108
|
+
} catch {
|
|
7109
|
+
return null;
|
|
7110
|
+
}
|
|
7111
|
+
})();
|
|
7112
|
+
const existing = (() => {
|
|
7113
|
+
if (!existingRaw) return null;
|
|
7114
|
+
try {
|
|
7115
|
+
return JSON.parse(existingRaw);
|
|
7116
|
+
} catch {
|
|
7117
|
+
return null;
|
|
7118
|
+
}
|
|
7119
|
+
})();
|
|
7120
|
+
if (existing) {
|
|
7121
|
+
const ageMs = Date.now() - new Date(existing.startedAt).getTime();
|
|
7122
|
+
const stale = !isPidAlive(existing.pid) || ageMs > STALE_LOCK_MS;
|
|
7123
|
+
if (!stale) {
|
|
7124
|
+
return {
|
|
7125
|
+
reason: "in-progress",
|
|
7126
|
+
existing,
|
|
7127
|
+
message: `another migration is in progress (pid ${existing.pid} on ${existing.hostname}, started ${existing.startedAt})`
|
|
7128
|
+
};
|
|
7129
|
+
}
|
|
7130
|
+
}
|
|
7131
|
+
}
|
|
7132
|
+
const payload = {
|
|
7133
|
+
pid: process.pid,
|
|
7134
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7135
|
+
hostname: os6.hostname()
|
|
7136
|
+
};
|
|
7137
|
+
fs27.writeFileSync(lockPath, JSON.stringify(payload, null, 2) + "\n");
|
|
7138
|
+
return {
|
|
7139
|
+
release: () => {
|
|
7140
|
+
try {
|
|
7141
|
+
fs27.unlinkSync(lockPath);
|
|
7142
|
+
} catch {
|
|
7143
|
+
}
|
|
7144
|
+
}
|
|
7145
|
+
};
|
|
7146
|
+
}
|
|
7147
|
+
function isRefusal(r) {
|
|
7148
|
+
return r.reason === "in-progress";
|
|
7149
|
+
}
|
|
7150
|
+
|
|
7151
|
+
// src/commands/roadmap/migrate.ts
|
|
7152
|
+
var MigrateExitCode = {
|
|
7153
|
+
SUCCESS: 0,
|
|
7154
|
+
GENERIC_FAILURE: 1,
|
|
7155
|
+
AMBIGUOUS: 2,
|
|
7156
|
+
ARCHIVE_COLLISION: 3,
|
|
7157
|
+
CONFIG_ERROR: 4,
|
|
7158
|
+
PARTIAL_CREATE: 5
|
|
7159
|
+
};
|
|
7160
|
+
function reportToExitCode(report) {
|
|
7161
|
+
if (report.mode !== "aborted") return MigrateExitCode.SUCCESS;
|
|
7162
|
+
const reason = report.abortReason ?? "";
|
|
7163
|
+
if (reason.startsWith("create failed") && report.createdSoFar && report.createdSoFar.length > 0) {
|
|
7164
|
+
return MigrateExitCode.PARTIAL_CREATE;
|
|
7165
|
+
}
|
|
7166
|
+
if (reason.startsWith("ambiguous features")) return MigrateExitCode.AMBIGUOUS;
|
|
7167
|
+
if (reason.startsWith("archive-collision")) return MigrateExitCode.ARCHIVE_COLLISION;
|
|
7168
|
+
if (reason.startsWith("config rewrite failed")) return MigrateExitCode.CONFIG_ERROR;
|
|
7169
|
+
return MigrateExitCode.GENERIC_FAILURE;
|
|
7170
|
+
}
|
|
7171
|
+
function printPlanSummary(plan, dryRun) {
|
|
7172
|
+
const banner = dryRun ? chalk10.cyan("DRY RUN ") : "";
|
|
7173
|
+
console.log(`${banner}Migration plan:`);
|
|
7174
|
+
console.log(` Would create: ${plan.toCreate.length}`);
|
|
7175
|
+
for (const e of plan.toCreate) console.log(` - ${e.name}`);
|
|
7176
|
+
console.log(` Would update: ${plan.toUpdate.length}`);
|
|
7177
|
+
for (const e of plan.toUpdate) console.log(` - ${e.name} (${e.externalId}): ${e.diff}`);
|
|
7178
|
+
console.log(` Unchanged: ${plan.unchanged.length}`);
|
|
7179
|
+
console.log(` Would append history: ${plan.historyToAppend.length}`);
|
|
7180
|
+
console.log(` Ambiguous: ${plan.ambiguous.length}`);
|
|
7181
|
+
for (const a of plan.ambiguous) {
|
|
7182
|
+
console.log(` - ${a.name} (existing: ${a.existingIssueRef})`);
|
|
7183
|
+
}
|
|
7184
|
+
}
|
|
7185
|
+
function printReport(report) {
|
|
7186
|
+
if (report.mode === "aborted") {
|
|
7187
|
+
logger.error(`Migration aborted: ${report.abortReason ?? "unknown reason"}`);
|
|
7188
|
+
if (report.createdSoFar && report.createdSoFar.length > 0) {
|
|
7189
|
+
logger.warn(
|
|
7190
|
+
`Features created before abort (record manually): ${report.createdSoFar.map((c) => `${c.name} \u2192 ${c.externalId}`).join(", ")}`
|
|
7191
|
+
);
|
|
7192
|
+
}
|
|
7193
|
+
return;
|
|
7194
|
+
}
|
|
7195
|
+
if (report.mode === "dry-run") {
|
|
7196
|
+
logger.info(`DRY RUN complete: ${report.created + report.updated} writes would be performed.`);
|
|
7197
|
+
return;
|
|
7198
|
+
}
|
|
7199
|
+
if (report.mode === "already-migrated") {
|
|
7200
|
+
logger.success("Already migrated; nothing to do.");
|
|
7201
|
+
return;
|
|
7202
|
+
}
|
|
7203
|
+
logger.success(
|
|
7204
|
+
`Migration applied: ${report.created} created, ${report.updated} updated, ${report.unchanged} unchanged, ${report.historyAppended} history events appended.`
|
|
7205
|
+
);
|
|
7206
|
+
if (report.archivedTo) logger.info(`Archived: ${report.archivedFrom} -> ${report.archivedTo}`);
|
|
7207
|
+
if (report.configBackup) logger.info(`Config backup: ${report.configBackup}`);
|
|
7208
|
+
}
|
|
7209
|
+
async function collectHistoryHashes(client, externalId) {
|
|
7210
|
+
const result = await client.fetchHistory(externalId);
|
|
7211
|
+
const set = /* @__PURE__ */ new Set();
|
|
7212
|
+
if (!result.ok) return set;
|
|
7213
|
+
for (const e of result.value) set.add(migrate_exports.hashHistoryEvent(e));
|
|
7214
|
+
return set;
|
|
7215
|
+
}
|
|
7216
|
+
function makeRawBodyResolver(_client, _features) {
|
|
7217
|
+
return async () => null;
|
|
7218
|
+
}
|
|
7219
|
+
function buildJsonOutput(plan, report, error, exitCode) {
|
|
7220
|
+
const out = {
|
|
7221
|
+
ok: exitCode === MigrateExitCode.SUCCESS,
|
|
7222
|
+
mode: report ? report.mode : "error",
|
|
7223
|
+
exitCode
|
|
7224
|
+
};
|
|
7225
|
+
if (plan) {
|
|
7226
|
+
out.plan = {
|
|
7227
|
+
toCreate: plan.toCreate.map((e) => ({ name: e.name })),
|
|
7228
|
+
toUpdate: plan.toUpdate.map((e) => ({
|
|
7229
|
+
name: e.name,
|
|
7230
|
+
externalId: e.externalId,
|
|
7231
|
+
diff: e.diff
|
|
7232
|
+
})),
|
|
7233
|
+
unchanged: plan.unchanged,
|
|
7234
|
+
historyToAppend: plan.historyToAppend.map((e) => ({
|
|
7235
|
+
externalId: e.externalId,
|
|
7236
|
+
type: e.event.type
|
|
7237
|
+
})),
|
|
7238
|
+
ambiguous: plan.ambiguous
|
|
7239
|
+
};
|
|
7240
|
+
}
|
|
7241
|
+
if (report) out.report = report;
|
|
7242
|
+
if (error) out.error = error;
|
|
7243
|
+
return out;
|
|
7244
|
+
}
|
|
7245
|
+
async function runRoadmapMigrate(opts) {
|
|
7246
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
7247
|
+
const format = opts.format ?? "human";
|
|
7248
|
+
const isJson = format === "json";
|
|
7249
|
+
if (!opts.to) {
|
|
7250
|
+
return Err(new CLIError("missing required argument: --to <target>", ExitCode.ERROR));
|
|
7251
|
+
}
|
|
7252
|
+
if (opts.to === "file-backed") {
|
|
7253
|
+
return Err(
|
|
7254
|
+
new CLIError(
|
|
7255
|
+
"--to=file-backed reverse migration is not yet implemented; track in a future spec"
|
|
7256
|
+
)
|
|
7257
|
+
);
|
|
7258
|
+
}
|
|
7259
|
+
if (opts.to !== "file-less") {
|
|
7260
|
+
return Err(
|
|
7261
|
+
new CLIError(`unsupported migration target: ${opts.to} (only "file-less" supported today)`)
|
|
7262
|
+
);
|
|
7263
|
+
}
|
|
7264
|
+
if (loadProjectRoadmapMode(cwd) === "file-less") {
|
|
7265
|
+
const alreadyMigratedReport = {
|
|
7266
|
+
created: 0,
|
|
7267
|
+
updated: 0,
|
|
7268
|
+
unchanged: 0,
|
|
7269
|
+
historyAppended: 0,
|
|
7270
|
+
archivedFrom: null,
|
|
7271
|
+
archivedTo: null,
|
|
7272
|
+
configBackup: null,
|
|
7273
|
+
mode: "already-migrated"
|
|
7274
|
+
};
|
|
7275
|
+
if (isJson) {
|
|
7276
|
+
console.log(
|
|
7277
|
+
JSON.stringify(
|
|
7278
|
+
buildJsonOutput(void 0, alreadyMigratedReport, void 0, MigrateExitCode.SUCCESS)
|
|
7279
|
+
)
|
|
7280
|
+
);
|
|
7281
|
+
} else {
|
|
7282
|
+
logger.success("Already migrated; nothing to do.");
|
|
7283
|
+
}
|
|
7284
|
+
return Ok(alreadyMigratedReport);
|
|
7285
|
+
}
|
|
7286
|
+
const lockResult = acquireMigrateLock(cwd);
|
|
7287
|
+
if (isRefusal(lockResult)) {
|
|
7288
|
+
return Err(new CLIError(lockResult.message));
|
|
7289
|
+
}
|
|
7290
|
+
try {
|
|
7291
|
+
let client;
|
|
7292
|
+
if (opts.client) {
|
|
7293
|
+
client = opts.client;
|
|
7294
|
+
} else {
|
|
7295
|
+
const cfgR = loadTrackerClientConfigFromProject(cwd);
|
|
7296
|
+
if (!cfgR.ok) return Err(new CLIError(cfgR.error.message));
|
|
7297
|
+
const clientR = createTrackerClient(cfgR.value);
|
|
7298
|
+
if (!clientR.ok) return Err(new CLIError(clientR.error.message));
|
|
7299
|
+
client = clientR.value;
|
|
7300
|
+
}
|
|
7301
|
+
const roadmapPath = path46.join(cwd, "docs", "roadmap.md");
|
|
7302
|
+
if (!fs28.existsSync(roadmapPath)) {
|
|
7303
|
+
return Err(new CLIError(`docs/roadmap.md not found in ${cwd}`));
|
|
7304
|
+
}
|
|
7305
|
+
const roadmapR = parseRoadmap(fs28.readFileSync(roadmapPath, "utf-8"));
|
|
7306
|
+
if (!roadmapR.ok) {
|
|
7307
|
+
return Err(new CLIError(`failed to parse docs/roadmap.md: ${roadmapR.error.message}`));
|
|
7308
|
+
}
|
|
7309
|
+
const roadmap = roadmapR.value;
|
|
7310
|
+
const fetchR = await client.fetchAll();
|
|
7311
|
+
if (!fetchR.ok) {
|
|
7312
|
+
return Err(new CLIError(`failed to fetch tracker features: ${fetchR.error.message}`));
|
|
7313
|
+
}
|
|
7314
|
+
const existingFeatures = fetchR.value.features;
|
|
7315
|
+
const getRawBody = makeRawBodyResolver(client, existingFeatures);
|
|
7316
|
+
const fetchHashes = (id) => collectHistoryHashes(client, id);
|
|
7317
|
+
const plan = await migrate_exports.buildMigrationPlan(
|
|
7318
|
+
roadmap,
|
|
7319
|
+
existingFeatures,
|
|
7320
|
+
fetchHashes,
|
|
7321
|
+
getRawBody
|
|
7322
|
+
);
|
|
7323
|
+
if (!isJson) printPlanSummary(plan, opts.dryRun);
|
|
7324
|
+
const deps = {
|
|
7325
|
+
client,
|
|
7326
|
+
readFile: (p) => fs28.readFileSync(p, "utf-8"),
|
|
7327
|
+
writeFile: (p, b) => fs28.writeFileSync(p, b),
|
|
7328
|
+
renameFile: (from, to) => fs28.renameSync(from, to),
|
|
7329
|
+
existsFile: (p) => fs28.existsSync(p)
|
|
7330
|
+
};
|
|
7331
|
+
const reportR = await migrate_exports.runMigrationPlan(plan, deps, {
|
|
7332
|
+
projectRoot: cwd,
|
|
7333
|
+
dryRun: opts.dryRun
|
|
7334
|
+
});
|
|
7335
|
+
if (!reportR.ok) {
|
|
7336
|
+
return Err(new CLIError(`migration failed: ${reportR.error.message}`));
|
|
7337
|
+
}
|
|
7338
|
+
if (isJson) {
|
|
7339
|
+
const exitCode = reportToExitCode(reportR.value);
|
|
7340
|
+
console.log(JSON.stringify(buildJsonOutput(plan, reportR.value, void 0, exitCode)));
|
|
7341
|
+
} else {
|
|
7342
|
+
printReport(reportR.value);
|
|
7343
|
+
}
|
|
7344
|
+
return Ok(reportR.value);
|
|
7345
|
+
} finally {
|
|
7346
|
+
lockResult.release();
|
|
7347
|
+
}
|
|
7348
|
+
}
|
|
7349
|
+
function createRoadmapMigrateCommand() {
|
|
7350
|
+
return new Command61("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(
|
|
7351
|
+
"--format <fmt>",
|
|
7352
|
+
'Output format: "human" (default) or "json" (single JSON object for CI consumers)',
|
|
7353
|
+
"human"
|
|
7354
|
+
).action(async (options) => {
|
|
7355
|
+
const format = options.format === "json" ? "json" : "human";
|
|
7356
|
+
const result = await runRoadmapMigrate({
|
|
7357
|
+
to: options.to,
|
|
7358
|
+
dryRun: Boolean(options.dryRun),
|
|
7359
|
+
format
|
|
7360
|
+
});
|
|
7361
|
+
if (!result.ok) {
|
|
7362
|
+
if (format === "json") {
|
|
7363
|
+
console.log(
|
|
7364
|
+
JSON.stringify(
|
|
7365
|
+
buildJsonOutput(
|
|
7366
|
+
void 0,
|
|
7367
|
+
void 0,
|
|
7368
|
+
result.error.message,
|
|
7369
|
+
MigrateExitCode.CONFIG_ERROR
|
|
7370
|
+
)
|
|
7371
|
+
)
|
|
7372
|
+
);
|
|
7373
|
+
} else {
|
|
7374
|
+
logger.error(result.error.message);
|
|
7375
|
+
}
|
|
7376
|
+
process.exit(format === "json" ? MigrateExitCode.CONFIG_ERROR : result.error.exitCode);
|
|
7377
|
+
}
|
|
7378
|
+
const exitCode = reportToExitCode(result.value);
|
|
7379
|
+
process.exit(exitCode);
|
|
7380
|
+
});
|
|
7381
|
+
}
|
|
7382
|
+
|
|
7383
|
+
// src/commands/roadmap/index.ts
|
|
7384
|
+
function createRoadmapCommand() {
|
|
7385
|
+
const roadmap = new Command62("roadmap").description("Roadmap management");
|
|
7386
|
+
roadmap.addCommand(createRoadmapMigrateCommand());
|
|
7387
|
+
return roadmap;
|
|
7388
|
+
}
|
|
7389
|
+
|
|
7390
|
+
// src/commands/scan-config.ts
|
|
7391
|
+
init_dist2();
|
|
7392
|
+
import { Command as Command63 } from "commander";
|
|
7393
|
+
import { existsSync as existsSync25, readFileSync as readFileSync18, writeFileSync as writeFileSync17 } from "fs";
|
|
7394
|
+
import { join as join38, relative as relative3 } from "path";
|
|
7039
7395
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
7040
7396
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
7041
7397
|
const highLines = /* @__PURE__ */ new Set();
|
|
@@ -7061,17 +7417,17 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
|
|
|
7061
7417
|
if (!hasHighSeverity) return;
|
|
7062
7418
|
const { cleaned, linesStripped } = stripHighSeverityPatterns(content, injectionFindings);
|
|
7063
7419
|
if (linesStripped > 0) {
|
|
7064
|
-
|
|
7420
|
+
writeFileSync17(filePath, cleaned);
|
|
7065
7421
|
logger.info(
|
|
7066
7422
|
`scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${relative3(targetDir, filePath).replaceAll("\\", "/")}`
|
|
7067
7423
|
);
|
|
7068
7424
|
}
|
|
7069
7425
|
}
|
|
7070
7426
|
function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
7071
|
-
if (!
|
|
7427
|
+
if (!existsSync25(filePath)) return null;
|
|
7072
7428
|
let content;
|
|
7073
7429
|
try {
|
|
7074
|
-
content =
|
|
7430
|
+
content = readFileSync18(filePath, "utf8");
|
|
7075
7431
|
} catch {
|
|
7076
7432
|
return null;
|
|
7077
7433
|
}
|
|
@@ -7092,7 +7448,7 @@ async function runScanConfig(targetDir, options) {
|
|
|
7092
7448
|
const scanner = new SecurityScanner(parseSecurityConfig({}));
|
|
7093
7449
|
const results = [];
|
|
7094
7450
|
for (const configFile of CONFIG_FILES) {
|
|
7095
|
-
const result = scanSingleFile(
|
|
7451
|
+
const result = scanSingleFile(join38(targetDir, configFile), targetDir, scanner, options);
|
|
7096
7452
|
if (result) results.push(result);
|
|
7097
7453
|
}
|
|
7098
7454
|
return { exitCode: computeScanExitCode(results), results };
|
|
@@ -7122,7 +7478,7 @@ function formatTextOutput(result) {
|
|
|
7122
7478
|
}
|
|
7123
7479
|
}
|
|
7124
7480
|
function createScanConfigCommand() {
|
|
7125
|
-
const command = new
|
|
7481
|
+
const command = new Command63("scan-config").description(
|
|
7126
7482
|
"Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
|
|
7127
7483
|
).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
|
|
7128
7484
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -7139,24 +7495,24 @@ function createScanConfigCommand() {
|
|
|
7139
7495
|
}
|
|
7140
7496
|
|
|
7141
7497
|
// src/commands/setup.ts
|
|
7142
|
-
import { Command as
|
|
7498
|
+
import { Command as Command64 } from "commander";
|
|
7499
|
+
import * as fs31 from "fs";
|
|
7500
|
+
import * as os8 from "os";
|
|
7501
|
+
import * as path49 from "path";
|
|
7502
|
+
import chalk12 from "chalk";
|
|
7503
|
+
|
|
7504
|
+
// src/utils/first-run.ts
|
|
7143
7505
|
import * as fs29 from "fs";
|
|
7144
7506
|
import * as os7 from "os";
|
|
7145
7507
|
import * as path47 from "path";
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
// src/utils/first-run.ts
|
|
7149
|
-
import * as fs27 from "fs";
|
|
7150
|
-
import * as os6 from "os";
|
|
7151
|
-
import * as path45 from "path";
|
|
7152
|
-
var HARNESS_DIR = path45.join(os6.homedir(), ".harness");
|
|
7153
|
-
var MARKER_FILE = path45.join(HARNESS_DIR, ".setup-complete");
|
|
7508
|
+
var HARNESS_DIR = path47.join(os7.homedir(), ".harness");
|
|
7509
|
+
var MARKER_FILE = path47.join(HARNESS_DIR, ".setup-complete");
|
|
7154
7510
|
function isFirstRun() {
|
|
7155
|
-
return !
|
|
7511
|
+
return !fs29.existsSync(MARKER_FILE);
|
|
7156
7512
|
}
|
|
7157
7513
|
function markSetupComplete() {
|
|
7158
|
-
|
|
7159
|
-
|
|
7514
|
+
fs29.mkdirSync(HARNESS_DIR, { recursive: true });
|
|
7515
|
+
fs29.writeFileSync(MARKER_FILE, "", "utf-8");
|
|
7160
7516
|
}
|
|
7161
7517
|
function printFirstRunWelcome() {
|
|
7162
7518
|
try {
|
|
@@ -7169,19 +7525,19 @@ function printFirstRunWelcome() {
|
|
|
7169
7525
|
}
|
|
7170
7526
|
|
|
7171
7527
|
// src/commands/telemetry-wizard.ts
|
|
7172
|
-
import * as
|
|
7173
|
-
import * as
|
|
7528
|
+
import * as fs30 from "fs";
|
|
7529
|
+
import * as path48 from "path";
|
|
7174
7530
|
import readline2 from "readline";
|
|
7175
|
-
import
|
|
7531
|
+
import chalk11 from "chalk";
|
|
7176
7532
|
function prompt2(question) {
|
|
7177
7533
|
const rl = readline2.createInterface({
|
|
7178
7534
|
input: process.stdin,
|
|
7179
7535
|
output: process.stdout
|
|
7180
7536
|
});
|
|
7181
|
-
return new Promise((
|
|
7537
|
+
return new Promise((resolve40) => {
|
|
7182
7538
|
rl.question(question, (answer) => {
|
|
7183
7539
|
rl.close();
|
|
7184
|
-
|
|
7540
|
+
resolve40(answer.trim().toLowerCase());
|
|
7185
7541
|
});
|
|
7186
7542
|
});
|
|
7187
7543
|
}
|
|
@@ -7190,10 +7546,10 @@ function promptRaw(question) {
|
|
|
7190
7546
|
input: process.stdin,
|
|
7191
7547
|
output: process.stdout
|
|
7192
7548
|
});
|
|
7193
|
-
return new Promise((
|
|
7549
|
+
return new Promise((resolve40) => {
|
|
7194
7550
|
rl.question(question, (answer) => {
|
|
7195
7551
|
rl.close();
|
|
7196
|
-
|
|
7552
|
+
resolve40(answer.trim());
|
|
7197
7553
|
});
|
|
7198
7554
|
});
|
|
7199
7555
|
}
|
|
@@ -7201,21 +7557,21 @@ function isNo(answer) {
|
|
|
7201
7557
|
return answer === "n" || answer === "no";
|
|
7202
7558
|
}
|
|
7203
7559
|
function isTelemetryConfigured(cwd) {
|
|
7204
|
-
const configPath =
|
|
7560
|
+
const configPath = path48.join(cwd, "harness.config.json");
|
|
7205
7561
|
try {
|
|
7206
|
-
const raw = JSON.parse(
|
|
7562
|
+
const raw = JSON.parse(fs30.readFileSync(configPath, "utf-8"));
|
|
7207
7563
|
return raw.telemetry !== void 0 && raw.adoption !== void 0;
|
|
7208
7564
|
} catch {
|
|
7209
7565
|
return false;
|
|
7210
7566
|
}
|
|
7211
7567
|
}
|
|
7212
7568
|
function writeTelemetryConfig(cwd, result) {
|
|
7213
|
-
const configPath =
|
|
7569
|
+
const configPath = path48.join(cwd, "harness.config.json");
|
|
7214
7570
|
try {
|
|
7215
|
-
const raw = JSON.parse(
|
|
7571
|
+
const raw = JSON.parse(fs30.readFileSync(configPath, "utf-8"));
|
|
7216
7572
|
raw.telemetry = { enabled: result.telemetryEnabled };
|
|
7217
7573
|
raw.adoption = { enabled: result.adoptionEnabled };
|
|
7218
|
-
|
|
7574
|
+
fs30.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
7219
7575
|
} catch (err) {
|
|
7220
7576
|
console.warn(
|
|
7221
7577
|
`\u26A0 Could not update ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -7223,15 +7579,15 @@ function writeTelemetryConfig(cwd, result) {
|
|
|
7223
7579
|
}
|
|
7224
7580
|
const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
|
|
7225
7581
|
if (hasIdentity) {
|
|
7226
|
-
const harnessDir =
|
|
7227
|
-
|
|
7228
|
-
const telemetryFile =
|
|
7229
|
-
|
|
7582
|
+
const harnessDir = path48.join(cwd, ".harness");
|
|
7583
|
+
fs30.mkdirSync(harnessDir, { recursive: true });
|
|
7584
|
+
const telemetryFile = path48.join(harnessDir, "telemetry.json");
|
|
7585
|
+
fs30.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
|
|
7230
7586
|
}
|
|
7231
7587
|
}
|
|
7232
7588
|
async function promptIdentity() {
|
|
7233
7589
|
console.log(
|
|
7234
|
-
` ${
|
|
7590
|
+
` ${chalk11.cyan("Identity")} (optional) \u2014 tag telemetry with project/team/alias for filtering.`
|
|
7235
7591
|
);
|
|
7236
7592
|
console.log(" Leave blank to keep anonymous.");
|
|
7237
7593
|
const answer = await prompt2(" Set identity fields? (y/N) ");
|
|
@@ -7249,20 +7605,20 @@ async function promptIdentity() {
|
|
|
7249
7605
|
async function runTelemetryWizard() {
|
|
7250
7606
|
if (!process.stdin.isTTY) return null;
|
|
7251
7607
|
console.log("");
|
|
7252
|
-
console.log(` ${
|
|
7608
|
+
console.log(` ${chalk11.bold("Telemetry Configuration")}`);
|
|
7253
7609
|
console.log("");
|
|
7254
7610
|
console.log(" Harness can collect anonymous usage data to help improve the tool.");
|
|
7255
7611
|
console.log(" You can change these settings anytime in harness.config.json.");
|
|
7256
7612
|
console.log("");
|
|
7257
7613
|
console.log(
|
|
7258
|
-
` ${
|
|
7614
|
+
` ${chalk11.cyan("Anonymous telemetry")} \u2014 error rates, command usage, performance metrics.`
|
|
7259
7615
|
);
|
|
7260
7616
|
console.log(" No code, file contents, or personally identifiable info is sent.");
|
|
7261
7617
|
const telemetryAnswer = await prompt2(" Enable anonymous telemetry? (Y/n) ");
|
|
7262
7618
|
const telemetryEnabled = !isNo(telemetryAnswer);
|
|
7263
7619
|
console.log("");
|
|
7264
7620
|
console.log(
|
|
7265
|
-
` ${
|
|
7621
|
+
` ${chalk11.cyan("Adoption tracking")} \u2014 which skills are used, how often, and success rates.`
|
|
7266
7622
|
);
|
|
7267
7623
|
console.log(" Stored locally in .harness/adoption/. Never sent externally.");
|
|
7268
7624
|
const adoptionAnswer = await prompt2(" Enable local adoption tracking? (Y/n) ");
|
|
@@ -7272,8 +7628,8 @@ async function runTelemetryWizard() {
|
|
|
7272
7628
|
return { telemetryEnabled, adoptionEnabled, identity };
|
|
7273
7629
|
}
|
|
7274
7630
|
async function ensureTelemetryConfigured(cwd) {
|
|
7275
|
-
const configPath =
|
|
7276
|
-
if (!
|
|
7631
|
+
const configPath = path48.join(cwd, "harness.config.json");
|
|
7632
|
+
if (!fs30.existsSync(configPath)) {
|
|
7277
7633
|
return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
|
|
7278
7634
|
}
|
|
7279
7635
|
if (isTelemetryConfigured(cwd)) {
|
|
@@ -7319,7 +7675,7 @@ function runSlashCommandGeneration() {
|
|
|
7319
7675
|
}
|
|
7320
7676
|
}
|
|
7321
7677
|
function detectClient(dirName) {
|
|
7322
|
-
return
|
|
7678
|
+
return fs31.existsSync(path49.join(os8.homedir(), dirName));
|
|
7323
7679
|
}
|
|
7324
7680
|
async function runMcpSetup(cwd) {
|
|
7325
7681
|
const results = [];
|
|
@@ -7335,7 +7691,7 @@ async function runMcpSetup(cwd) {
|
|
|
7335
7691
|
{ name: "Cursor", dir: ".cursor", client: "cursor", configTarget: ".cursor/mcp.json" },
|
|
7336
7692
|
{
|
|
7337
7693
|
name: "OpenCode",
|
|
7338
|
-
dir:
|
|
7694
|
+
dir: path49.join(".config", "opencode"),
|
|
7339
7695
|
client: "opencode",
|
|
7340
7696
|
configTarget: "opencode.json"
|
|
7341
7697
|
}
|
|
@@ -7362,12 +7718,12 @@ async function runMcpSetup(cwd) {
|
|
|
7362
7718
|
return results;
|
|
7363
7719
|
}
|
|
7364
7720
|
function formatStep(result) {
|
|
7365
|
-
const icon = result.status === "pass" ?
|
|
7721
|
+
const icon = result.status === "pass" ? chalk12.green("\u2713") : result.status === "warn" ? chalk12.yellow("\u26A0") : chalk12.red("\u2717");
|
|
7366
7722
|
return ` ${icon} ${result.message}`;
|
|
7367
7723
|
}
|
|
7368
7724
|
function configureTier0Integrations(cwd) {
|
|
7369
7725
|
try {
|
|
7370
|
-
const mcpPath =
|
|
7726
|
+
const mcpPath = path49.join(cwd, ".mcp.json");
|
|
7371
7727
|
const config = readMcpConfig(mcpPath);
|
|
7372
7728
|
const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
|
|
7373
7729
|
const added = [];
|
|
@@ -7376,9 +7732,9 @@ function configureTier0Integrations(cwd) {
|
|
|
7376
7732
|
writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
|
|
7377
7733
|
added.push(integration.displayName);
|
|
7378
7734
|
}
|
|
7379
|
-
const geminiDir =
|
|
7380
|
-
if (
|
|
7381
|
-
const geminiPath =
|
|
7735
|
+
const geminiDir = path49.join(cwd, ".gemini");
|
|
7736
|
+
if (fs31.existsSync(geminiDir)) {
|
|
7737
|
+
const geminiPath = path49.join(geminiDir, "settings.json");
|
|
7382
7738
|
const geminiConfig = readMcpConfig(geminiPath);
|
|
7383
7739
|
for (const integration of tier0) {
|
|
7384
7740
|
if (!geminiConfig.mcpServers[integration.name]) {
|
|
@@ -7386,9 +7742,9 @@ function configureTier0Integrations(cwd) {
|
|
|
7386
7742
|
}
|
|
7387
7743
|
}
|
|
7388
7744
|
}
|
|
7389
|
-
const opencodePath =
|
|
7390
|
-
const opencodeGlobalDir =
|
|
7391
|
-
if (
|
|
7745
|
+
const opencodePath = path49.join(cwd, "opencode.json");
|
|
7746
|
+
const opencodeGlobalDir = path49.join(os8.homedir(), ".config", "opencode");
|
|
7747
|
+
if (fs31.existsSync(opencodePath) || fs31.existsSync(opencodeGlobalDir)) {
|
|
7392
7748
|
for (const integration of tier0) {
|
|
7393
7749
|
writeOpencodeMcpEntry(opencodePath, integration.name, integration.mcpConfig);
|
|
7394
7750
|
}
|
|
@@ -7406,14 +7762,14 @@ function configureTier0Integrations(cwd) {
|
|
|
7406
7762
|
}
|
|
7407
7763
|
}
|
|
7408
7764
|
function ensureHooks(cwd) {
|
|
7409
|
-
const configPath =
|
|
7410
|
-
if (!
|
|
7765
|
+
const configPath = path49.join(cwd, "harness.config.json");
|
|
7766
|
+
if (!fs31.existsSync(configPath)) {
|
|
7411
7767
|
return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
|
|
7412
7768
|
}
|
|
7413
7769
|
let profile = "standard";
|
|
7414
|
-
const profilePath =
|
|
7770
|
+
const profilePath = path49.join(cwd, ".harness", "hooks", "profile.json");
|
|
7415
7771
|
try {
|
|
7416
|
-
const data = JSON.parse(
|
|
7772
|
+
const data = JSON.parse(fs31.readFileSync(profilePath, "utf-8"));
|
|
7417
7773
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
7418
7774
|
profile = data.profile;
|
|
7419
7775
|
}
|
|
@@ -7445,7 +7801,7 @@ async function detectLegacyLayout(cwd) {
|
|
|
7445
7801
|
}
|
|
7446
7802
|
async function runInitialGraphScan(cwd) {
|
|
7447
7803
|
try {
|
|
7448
|
-
const { runScan: runScan2 } = await import("./scan-
|
|
7804
|
+
const { runScan: runScan2 } = await import("./scan-SFX5U3TE.js");
|
|
7449
7805
|
const result = await runScan2(cwd);
|
|
7450
7806
|
return {
|
|
7451
7807
|
status: "pass",
|
|
@@ -7484,10 +7840,10 @@ async function runSetup(cwd) {
|
|
|
7484
7840
|
return { steps, success };
|
|
7485
7841
|
}
|
|
7486
7842
|
function createSetupCommand() {
|
|
7487
|
-
return new
|
|
7843
|
+
return new Command64("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
|
|
7488
7844
|
const cwd = process.cwd();
|
|
7489
7845
|
console.log("");
|
|
7490
|
-
console.log(` ${
|
|
7846
|
+
console.log(` ${chalk12.bold("harness setup")}`);
|
|
7491
7847
|
console.log("");
|
|
7492
7848
|
const { steps, success } = await runSetup(cwd);
|
|
7493
7849
|
for (const step of steps) {
|
|
@@ -7506,16 +7862,16 @@ function createSetupCommand() {
|
|
|
7506
7862
|
}
|
|
7507
7863
|
|
|
7508
7864
|
// src/commands/share.ts
|
|
7509
|
-
|
|
7510
|
-
import { Command as
|
|
7511
|
-
import * as
|
|
7512
|
-
import * as
|
|
7865
|
+
init_dist2();
|
|
7866
|
+
import { Command as Command65 } from "commander";
|
|
7867
|
+
import * as fs32 from "fs";
|
|
7868
|
+
import * as path50 from "path";
|
|
7513
7869
|
import { parse as parseYaml } from "yaml";
|
|
7514
7870
|
var MANIFEST_FILENAME = "constraints.yaml";
|
|
7515
7871
|
async function runShareAction(projectPath, options) {
|
|
7516
|
-
const rootDir =
|
|
7517
|
-
const manifestPath =
|
|
7518
|
-
if (!
|
|
7872
|
+
const rootDir = path50.resolve(projectPath);
|
|
7873
|
+
const manifestPath = path50.join(rootDir, MANIFEST_FILENAME);
|
|
7874
|
+
if (!fs32.existsSync(manifestPath)) {
|
|
7519
7875
|
logger.error(
|
|
7520
7876
|
`No ${MANIFEST_FILENAME} found at ${manifestPath}.
|
|
7521
7877
|
Create a constraints.yaml in your project root to define what to share.`
|
|
@@ -7524,7 +7880,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
7524
7880
|
}
|
|
7525
7881
|
let parsed;
|
|
7526
7882
|
try {
|
|
7527
|
-
const raw =
|
|
7883
|
+
const raw = fs32.readFileSync(manifestPath, "utf-8");
|
|
7528
7884
|
parsed = parseYaml(raw);
|
|
7529
7885
|
} catch (err) {
|
|
7530
7886
|
logger.error(
|
|
@@ -7538,7 +7894,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
7538
7894
|
process.exit(1);
|
|
7539
7895
|
}
|
|
7540
7896
|
const manifest = manifestResult.value;
|
|
7541
|
-
const configResult = resolveConfig(
|
|
7897
|
+
const configResult = resolveConfig(path50.join(rootDir, "harness.config.json"));
|
|
7542
7898
|
if (!configResult.ok) {
|
|
7543
7899
|
logger.error(configResult.error.message);
|
|
7544
7900
|
process.exit(1);
|
|
@@ -7556,8 +7912,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
7556
7912
|
);
|
|
7557
7913
|
process.exit(1);
|
|
7558
7914
|
}
|
|
7559
|
-
const outputDir =
|
|
7560
|
-
const outputPath =
|
|
7915
|
+
const outputDir = path50.resolve(options.output);
|
|
7916
|
+
const outputPath = path50.join(outputDir, `${manifest.name}.harness-constraints.json`);
|
|
7561
7917
|
const writeResult = await writeConfig(outputPath, bundle);
|
|
7562
7918
|
if (!writeResult.ok) {
|
|
7563
7919
|
logger.error(`Failed to write bundle: ${writeResult.error.message}`);
|
|
@@ -7566,28 +7922,28 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
7566
7922
|
logger.success(`Bundle written to ${outputPath}`);
|
|
7567
7923
|
}
|
|
7568
7924
|
function createShareCommand() {
|
|
7569
|
-
return new
|
|
7925
|
+
return new Command65("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) => {
|
|
7570
7926
|
await runShareAction(projectPath, options);
|
|
7571
7927
|
});
|
|
7572
7928
|
}
|
|
7573
7929
|
|
|
7574
7930
|
// src/commands/skill/index.ts
|
|
7575
|
-
import { Command as
|
|
7931
|
+
import { Command as Command73 } from "commander";
|
|
7576
7932
|
|
|
7577
7933
|
// src/commands/skill/list.ts
|
|
7578
|
-
import { Command as
|
|
7579
|
-
import * as
|
|
7580
|
-
import * as
|
|
7934
|
+
import { Command as Command66 } from "commander";
|
|
7935
|
+
import * as fs33 from "fs";
|
|
7936
|
+
import * as path51 from "path";
|
|
7581
7937
|
import { parse } from "yaml";
|
|
7582
7938
|
function scanDirectory(dirPath, source) {
|
|
7583
|
-
if (!
|
|
7584
|
-
const entries =
|
|
7939
|
+
if (!fs33.existsSync(dirPath)) return [];
|
|
7940
|
+
const entries = fs33.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
7585
7941
|
const skills = [];
|
|
7586
7942
|
for (const name of entries) {
|
|
7587
|
-
const yamlPath =
|
|
7588
|
-
if (!
|
|
7943
|
+
const yamlPath = path51.join(dirPath, name, "skill.yaml");
|
|
7944
|
+
if (!fs33.existsSync(yamlPath)) continue;
|
|
7589
7945
|
try {
|
|
7590
|
-
const raw =
|
|
7946
|
+
const raw = fs33.readFileSync(yamlPath, "utf-8");
|
|
7591
7947
|
const parsed = parse(raw);
|
|
7592
7948
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
7593
7949
|
if (result.success) {
|
|
@@ -7605,10 +7961,10 @@ function scanDirectory(dirPath, source) {
|
|
|
7605
7961
|
}
|
|
7606
7962
|
function collectCommunitySkills(seen, allSkills) {
|
|
7607
7963
|
const globalDir = resolveGlobalSkillsDir();
|
|
7608
|
-
const skillsDir =
|
|
7609
|
-
const communityBase =
|
|
7610
|
-
const communityPlatformDir =
|
|
7611
|
-
const lockfilePath =
|
|
7964
|
+
const skillsDir = path51.dirname(globalDir);
|
|
7965
|
+
const communityBase = path51.join(skillsDir, "community");
|
|
7966
|
+
const communityPlatformDir = path51.join(communityBase, "claude-code");
|
|
7967
|
+
const lockfilePath = path51.join(communityBase, "skills-lock.json");
|
|
7612
7968
|
const lockfile = readLockfile2(lockfilePath);
|
|
7613
7969
|
const communitySkills = scanDirectory(communityPlatformDir, "community");
|
|
7614
7970
|
for (const skill of communitySkills) {
|
|
@@ -7678,7 +8034,7 @@ function printSkillsVerbose(skills) {
|
|
|
7678
8034
|
for (const s of skills) printSkillEntry(s);
|
|
7679
8035
|
}
|
|
7680
8036
|
function createListCommand3() {
|
|
7681
|
-
return new
|
|
8037
|
+
return new Command66("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) => {
|
|
7682
8038
|
const globalOpts = cmd.optsWithGlobals();
|
|
7683
8039
|
const skills = collectSkills({ filter: resolveFilter(opts) });
|
|
7684
8040
|
if (globalOpts.json) {
|
|
@@ -7693,9 +8049,9 @@ function createListCommand3() {
|
|
|
7693
8049
|
}
|
|
7694
8050
|
|
|
7695
8051
|
// src/commands/skill/run.ts
|
|
7696
|
-
import { Command as
|
|
7697
|
-
import * as
|
|
7698
|
-
import * as
|
|
8052
|
+
import { Command as Command67 } from "commander";
|
|
8053
|
+
import * as fs34 from "fs";
|
|
8054
|
+
import * as path52 from "path";
|
|
7699
8055
|
import { parse as parse2 } from "yaml";
|
|
7700
8056
|
|
|
7701
8057
|
// src/skill/complexity.ts
|
|
@@ -7791,10 +8147,10 @@ ${options.principles}`);
|
|
|
7791
8147
|
|
|
7792
8148
|
// src/commands/skill/run.ts
|
|
7793
8149
|
function loadSkillMetadata(skillDir) {
|
|
7794
|
-
const yamlPath =
|
|
7795
|
-
if (!
|
|
8150
|
+
const yamlPath = path52.join(skillDir, "skill.yaml");
|
|
8151
|
+
if (!fs34.existsSync(yamlPath)) return null;
|
|
7796
8152
|
try {
|
|
7797
|
-
const result = SkillMetadataSchema.safeParse(parse2(
|
|
8153
|
+
const result = SkillMetadataSchema.safeParse(parse2(fs34.readFileSync(yamlPath, "utf-8")));
|
|
7798
8154
|
return result.success ? result.data : null;
|
|
7799
8155
|
} catch {
|
|
7800
8156
|
return null;
|
|
@@ -7806,22 +8162,22 @@ function resolveComplexity(metadata, requested, projectPath) {
|
|
|
7806
8162
|
return requested;
|
|
7807
8163
|
}
|
|
7808
8164
|
function loadPrinciples(projectPath) {
|
|
7809
|
-
const principlesPath =
|
|
7810
|
-
return
|
|
8165
|
+
const principlesPath = path52.join(projectPath, "docs", "principles.md");
|
|
8166
|
+
return fs34.existsSync(principlesPath) ? fs34.readFileSync(principlesPath, "utf-8") : void 0;
|
|
7811
8167
|
}
|
|
7812
8168
|
function readMostRecentFileInDir(dirPath) {
|
|
7813
|
-
const files =
|
|
7814
|
-
if (files.length > 0) return
|
|
8169
|
+
const files = fs34.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs34.statSync(path52.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
8170
|
+
if (files.length > 0) return fs34.readFileSync(path52.join(dirPath, files[0].name), "utf-8");
|
|
7815
8171
|
return void 0;
|
|
7816
8172
|
}
|
|
7817
8173
|
function loadPriorState(metadata, projectPath) {
|
|
7818
8174
|
if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
|
|
7819
8175
|
for (const stateFilePath of metadata.state.files) {
|
|
7820
|
-
const fullPath =
|
|
7821
|
-
if (!
|
|
7822
|
-
const stat =
|
|
8176
|
+
const fullPath = path52.join(projectPath, stateFilePath);
|
|
8177
|
+
if (!fs34.existsSync(fullPath)) continue;
|
|
8178
|
+
const stat = fs34.statSync(fullPath);
|
|
7823
8179
|
if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
|
|
7824
|
-
return
|
|
8180
|
+
return fs34.readFileSync(fullPath, "utf-8");
|
|
7825
8181
|
}
|
|
7826
8182
|
return void 0;
|
|
7827
8183
|
}
|
|
@@ -7841,9 +8197,9 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
7841
8197
|
}
|
|
7842
8198
|
function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
7843
8199
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
7844
|
-
const stateFile =
|
|
7845
|
-
if (!
|
|
7846
|
-
const stateContent =
|
|
8200
|
+
const stateFile = path52.join(projectPath, ".harness", "state.json");
|
|
8201
|
+
if (!fs34.existsSync(stateFile)) return content;
|
|
8202
|
+
const stateContent = fs34.readFileSync(stateFile, "utf-8");
|
|
7847
8203
|
return content + `
|
|
7848
8204
|
|
|
7849
8205
|
---
|
|
@@ -7855,14 +8211,14 @@ ${stateContent}
|
|
|
7855
8211
|
}
|
|
7856
8212
|
async function runSkill(name, opts) {
|
|
7857
8213
|
const skillsDir = resolveSkillsDir();
|
|
7858
|
-
const skillDir =
|
|
7859
|
-
if (!
|
|
8214
|
+
const skillDir = path52.join(skillsDir, name);
|
|
8215
|
+
if (!fs34.existsSync(skillDir)) {
|
|
7860
8216
|
logger.error(`Skill not found: ${name}`);
|
|
7861
8217
|
process.exit(ExitCode.ERROR);
|
|
7862
8218
|
return;
|
|
7863
8219
|
}
|
|
7864
8220
|
const metadata = loadSkillMetadata(skillDir);
|
|
7865
|
-
const projectPath = opts.path ?
|
|
8221
|
+
const projectPath = opts.path ? path52.resolve(opts.path) : process.cwd();
|
|
7866
8222
|
const complexity = resolveComplexity(
|
|
7867
8223
|
metadata,
|
|
7868
8224
|
opts.complexity ?? "standard",
|
|
@@ -7889,14 +8245,14 @@ async function runSkill(name, opts) {
|
|
|
7889
8245
|
...stateWarning !== void 0 && { stateWarning },
|
|
7890
8246
|
...opts.party !== void 0 && { party: opts.party }
|
|
7891
8247
|
});
|
|
7892
|
-
const skillMdPath =
|
|
7893
|
-
if (!
|
|
8248
|
+
const skillMdPath = path52.join(skillDir, "SKILL.md");
|
|
8249
|
+
if (!fs34.existsSync(skillMdPath)) {
|
|
7894
8250
|
logger.error(`SKILL.md not found for skill: ${name}`);
|
|
7895
8251
|
process.exit(ExitCode.ERROR);
|
|
7896
8252
|
return;
|
|
7897
8253
|
}
|
|
7898
8254
|
const content = appendProjectState(
|
|
7899
|
-
|
|
8255
|
+
fs34.readFileSync(skillMdPath, "utf-8"),
|
|
7900
8256
|
metadata,
|
|
7901
8257
|
projectPath,
|
|
7902
8258
|
!!opts.path
|
|
@@ -7905,13 +8261,13 @@ async function runSkill(name, opts) {
|
|
|
7905
8261
|
process.exit(ExitCode.SUCCESS);
|
|
7906
8262
|
}
|
|
7907
8263
|
function createRunCommand3() {
|
|
7908
|
-
return new
|
|
8264
|
+
return new Command67("run").description("Run a skill (outputs SKILL.md content with context preamble)").argument("<name>", "Skill name (e.g., harness-tdd)").option("--path <path>", "Project root path for context injection").option("--complexity <level>", "Rigor level: fast, standard, thorough", "standard").option("--phase <name>", "Start at a specific phase (for re-entry)").option("--party", "Enable multi-perspective evaluation").action(async (name, opts) => runSkill(name, opts));
|
|
7909
8265
|
}
|
|
7910
8266
|
|
|
7911
8267
|
// src/commands/skill/validate.ts
|
|
7912
|
-
import { Command as
|
|
7913
|
-
import * as
|
|
7914
|
-
import * as
|
|
8268
|
+
import { Command as Command68 } from "commander";
|
|
8269
|
+
import * as fs35 from "fs";
|
|
8270
|
+
import * as path53 from "path";
|
|
7915
8271
|
import { parse as parse3 } from "yaml";
|
|
7916
8272
|
var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
7917
8273
|
"## When to Use",
|
|
@@ -7923,11 +8279,11 @@ var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
|
7923
8279
|
];
|
|
7924
8280
|
var KNOWLEDGE_REQUIRED_SECTIONS = ["## Instructions"];
|
|
7925
8281
|
function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
7926
|
-
if (!
|
|
8282
|
+
if (!fs35.existsSync(skillMdPath)) {
|
|
7927
8283
|
errors.push(`${name}: missing SKILL.md`);
|
|
7928
8284
|
return;
|
|
7929
8285
|
}
|
|
7930
|
-
const mdContent =
|
|
8286
|
+
const mdContent = fs35.readFileSync(skillMdPath, "utf-8");
|
|
7931
8287
|
if (!mdContent.trim().startsWith("# ")) {
|
|
7932
8288
|
errors.push(`${name}/SKILL.md: must start with an h1 heading`);
|
|
7933
8289
|
}
|
|
@@ -7952,20 +8308,20 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
|
7952
8308
|
}
|
|
7953
8309
|
}
|
|
7954
8310
|
function validateSkillEntry(name, skillsDir, errors) {
|
|
7955
|
-
const skillDir =
|
|
7956
|
-
const yamlPath =
|
|
7957
|
-
if (!
|
|
8311
|
+
const skillDir = path53.join(skillsDir, name);
|
|
8312
|
+
const yamlPath = path53.join(skillDir, "skill.yaml");
|
|
8313
|
+
if (!fs35.existsSync(yamlPath)) {
|
|
7958
8314
|
errors.push(`${name}: missing skill.yaml`);
|
|
7959
8315
|
return false;
|
|
7960
8316
|
}
|
|
7961
8317
|
try {
|
|
7962
|
-
const raw =
|
|
8318
|
+
const raw = fs35.readFileSync(yamlPath, "utf-8");
|
|
7963
8319
|
const result = SkillMetadataSchema.safeParse(parse3(raw));
|
|
7964
8320
|
if (!result.success) {
|
|
7965
8321
|
errors.push(`${name}/skill.yaml: ${result.error.message}`);
|
|
7966
8322
|
return false;
|
|
7967
8323
|
}
|
|
7968
|
-
validateSkillMd(name,
|
|
8324
|
+
validateSkillMd(name, path53.join(skillDir, "SKILL.md"), result.data.type, errors);
|
|
7969
8325
|
return true;
|
|
7970
8326
|
} catch (e) {
|
|
7971
8327
|
errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -7973,15 +8329,15 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
7973
8329
|
}
|
|
7974
8330
|
}
|
|
7975
8331
|
function createValidateCommand2() {
|
|
7976
|
-
return new
|
|
8332
|
+
return new Command68("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
7977
8333
|
const globalOpts = cmd.optsWithGlobals();
|
|
7978
8334
|
const skillsDir = resolveSkillsDir();
|
|
7979
|
-
if (!
|
|
8335
|
+
if (!fs35.existsSync(skillsDir)) {
|
|
7980
8336
|
logger.info("No skills directory found.");
|
|
7981
8337
|
process.exit(ExitCode.SUCCESS);
|
|
7982
8338
|
return;
|
|
7983
8339
|
}
|
|
7984
|
-
const entries =
|
|
8340
|
+
const entries = fs35.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
7985
8341
|
const errors = [];
|
|
7986
8342
|
let validated = 0;
|
|
7987
8343
|
for (const name of entries) {
|
|
@@ -8003,24 +8359,24 @@ function createValidateCommand2() {
|
|
|
8003
8359
|
}
|
|
8004
8360
|
|
|
8005
8361
|
// src/commands/skill/info.ts
|
|
8006
|
-
import { Command as
|
|
8007
|
-
import * as
|
|
8008
|
-
import * as
|
|
8362
|
+
import { Command as Command69 } from "commander";
|
|
8363
|
+
import * as fs36 from "fs";
|
|
8364
|
+
import * as path54 from "path";
|
|
8009
8365
|
import { parse as parse4 } from "yaml";
|
|
8010
8366
|
function loadSkillMetadata2(name) {
|
|
8011
8367
|
const skillsDir = resolveSkillsDir();
|
|
8012
|
-
const skillDir =
|
|
8013
|
-
if (!
|
|
8368
|
+
const skillDir = path54.join(skillsDir, name);
|
|
8369
|
+
if (!fs36.existsSync(skillDir)) {
|
|
8014
8370
|
logger.error(`Skill not found: ${name}`);
|
|
8015
8371
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
8016
8372
|
}
|
|
8017
|
-
const yamlPath =
|
|
8018
|
-
if (!
|
|
8373
|
+
const yamlPath = path54.join(skillDir, "skill.yaml");
|
|
8374
|
+
if (!fs36.existsSync(yamlPath)) {
|
|
8019
8375
|
logger.error(`skill.yaml not found for skill: ${name}`);
|
|
8020
8376
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
8021
8377
|
}
|
|
8022
8378
|
try {
|
|
8023
|
-
const raw =
|
|
8379
|
+
const raw = fs36.readFileSync(yamlPath, "utf-8");
|
|
8024
8380
|
const parsed = parse4(raw);
|
|
8025
8381
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
8026
8382
|
if (!result.success) {
|
|
@@ -8049,7 +8405,7 @@ function printSkillInfo(skill) {
|
|
|
8049
8405
|
console.log(`Persistent: ${skill.state.persistent}`);
|
|
8050
8406
|
}
|
|
8051
8407
|
function createInfoCommand() {
|
|
8052
|
-
return new
|
|
8408
|
+
return new Command69("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
|
|
8053
8409
|
const globalOpts = cmd.optsWithGlobals();
|
|
8054
8410
|
const loaded = loadSkillMetadata2(name);
|
|
8055
8411
|
if (!loaded.ok) {
|
|
@@ -8066,7 +8422,7 @@ function createInfoCommand() {
|
|
|
8066
8422
|
}
|
|
8067
8423
|
|
|
8068
8424
|
// src/commands/skill/search.ts
|
|
8069
|
-
import { Command as
|
|
8425
|
+
import { Command as Command70 } from "commander";
|
|
8070
8426
|
async function runSearch(query, opts) {
|
|
8071
8427
|
const results = await searchNpmRegistry(query, opts.registry);
|
|
8072
8428
|
return results.filter((r) => {
|
|
@@ -8111,15 +8467,15 @@ async function runSearchAction(query, opts, globalOpts) {
|
|
|
8111
8467
|
}
|
|
8112
8468
|
}
|
|
8113
8469
|
function createSearchCommand() {
|
|
8114
|
-
return new
|
|
8470
|
+
return new Command70("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) => {
|
|
8115
8471
|
await runSearchAction(query, opts, cmd.optsWithGlobals());
|
|
8116
8472
|
});
|
|
8117
8473
|
}
|
|
8118
8474
|
|
|
8119
8475
|
// src/commands/skill/create.ts
|
|
8120
|
-
import { Command as
|
|
8121
|
-
import * as
|
|
8122
|
-
import * as
|
|
8476
|
+
import { Command as Command71 } from "commander";
|
|
8477
|
+
import * as path55 from "path";
|
|
8478
|
+
import * as fs37 from "fs";
|
|
8123
8479
|
import YAML from "yaml";
|
|
8124
8480
|
var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
8125
8481
|
function buildReadme(name, description) {
|
|
@@ -8203,22 +8559,22 @@ function runCreate(name, opts) {
|
|
|
8203
8559
|
if (!KEBAB_CASE_RE.test(name)) {
|
|
8204
8560
|
throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
|
|
8205
8561
|
}
|
|
8206
|
-
const baseDir = opts.outputDir ??
|
|
8207
|
-
const skillDir =
|
|
8208
|
-
if (
|
|
8562
|
+
const baseDir = opts.outputDir ?? path55.join(process.cwd(), "agents", "skills", "claude-code");
|
|
8563
|
+
const skillDir = path55.join(baseDir, name);
|
|
8564
|
+
if (fs37.existsSync(skillDir)) {
|
|
8209
8565
|
throw new Error(`Skill directory already exists: ${skillDir}`);
|
|
8210
8566
|
}
|
|
8211
|
-
|
|
8567
|
+
fs37.mkdirSync(skillDir, { recursive: true });
|
|
8212
8568
|
const description = opts.description || `A community skill: ${name}`;
|
|
8213
8569
|
const skillYaml = buildSkillYaml(name, opts);
|
|
8214
|
-
const skillYamlPath =
|
|
8215
|
-
|
|
8570
|
+
const skillYamlPath = path55.join(skillDir, "skill.yaml");
|
|
8571
|
+
fs37.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
|
|
8216
8572
|
const skillMd = buildSkillMd(name, description);
|
|
8217
|
-
const skillMdPath =
|
|
8218
|
-
|
|
8573
|
+
const skillMdPath = path55.join(skillDir, "SKILL.md");
|
|
8574
|
+
fs37.writeFileSync(skillMdPath, skillMd);
|
|
8219
8575
|
const readme = buildReadme(name, description);
|
|
8220
|
-
const readmePath =
|
|
8221
|
-
|
|
8576
|
+
const readmePath = path55.join(skillDir, "README.md");
|
|
8577
|
+
fs37.writeFileSync(readmePath, readme);
|
|
8222
8578
|
return {
|
|
8223
8579
|
name,
|
|
8224
8580
|
directory: skillDir,
|
|
@@ -8232,12 +8588,12 @@ function printCreateResult(name, result) {
|
|
|
8232
8588
|
}
|
|
8233
8589
|
logger.info(`
|
|
8234
8590
|
Next steps:`);
|
|
8235
|
-
logger.info(` 1. Edit ${
|
|
8591
|
+
logger.info(` 1. Edit ${path55.join(result.directory, "SKILL.md")} with your skill content`);
|
|
8236
8592
|
logger.info(` 2. Run: harness skill validate ${name}`);
|
|
8237
8593
|
logger.info(` 3. Run: harness skills publish`);
|
|
8238
8594
|
}
|
|
8239
8595
|
function createCreateCommand() {
|
|
8240
|
-
return new
|
|
8596
|
+
return new Command71("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) => {
|
|
8241
8597
|
const globalOpts = cmd.optsWithGlobals();
|
|
8242
8598
|
try {
|
|
8243
8599
|
const result = runCreate(name, {
|
|
@@ -8260,26 +8616,26 @@ function createCreateCommand() {
|
|
|
8260
8616
|
}
|
|
8261
8617
|
|
|
8262
8618
|
// src/commands/skill/publish.ts
|
|
8263
|
-
import { Command as
|
|
8264
|
-
import * as
|
|
8265
|
-
import * as
|
|
8619
|
+
import { Command as Command72 } from "commander";
|
|
8620
|
+
import * as fs39 from "fs";
|
|
8621
|
+
import * as path57 from "path";
|
|
8266
8622
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
8267
8623
|
|
|
8268
8624
|
// src/registry/validator.ts
|
|
8269
|
-
import * as
|
|
8270
|
-
import * as
|
|
8625
|
+
import * as fs38 from "fs";
|
|
8626
|
+
import * as path56 from "path";
|
|
8271
8627
|
import { parse as parse5 } from "yaml";
|
|
8272
8628
|
import semver4 from "semver";
|
|
8273
8629
|
async function validateForPublish(skillDir, registryUrl) {
|
|
8274
8630
|
const errors = [];
|
|
8275
|
-
const skillYamlPath =
|
|
8276
|
-
if (!
|
|
8631
|
+
const skillYamlPath = path56.join(skillDir, "skill.yaml");
|
|
8632
|
+
if (!fs38.existsSync(skillYamlPath)) {
|
|
8277
8633
|
errors.push("skill.yaml not found. Create one with: harness skill create <name>");
|
|
8278
8634
|
return { valid: false, errors };
|
|
8279
8635
|
}
|
|
8280
8636
|
let skillMeta;
|
|
8281
8637
|
try {
|
|
8282
|
-
const raw =
|
|
8638
|
+
const raw = fs38.readFileSync(skillYamlPath, "utf-8");
|
|
8283
8639
|
const parsed = parse5(raw);
|
|
8284
8640
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
8285
8641
|
if (!result.success) {
|
|
@@ -8301,11 +8657,11 @@ async function validateForPublish(skillDir, registryUrl) {
|
|
|
8301
8657
|
if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
|
|
8302
8658
|
errors.push("At least one trigger is required. Add triggers to skill.yaml.");
|
|
8303
8659
|
}
|
|
8304
|
-
const skillMdPath =
|
|
8305
|
-
if (!
|
|
8660
|
+
const skillMdPath = path56.join(skillDir, "SKILL.md");
|
|
8661
|
+
if (!fs38.existsSync(skillMdPath)) {
|
|
8306
8662
|
errors.push("SKILL.md not found. Create it with content describing your skill.");
|
|
8307
8663
|
} else {
|
|
8308
|
-
const content =
|
|
8664
|
+
const content = fs38.readFileSync(skillMdPath, "utf-8");
|
|
8309
8665
|
if (!content.includes("## When to Use")) {
|
|
8310
8666
|
errors.push('SKILL.md must contain a "## When to Use" section.');
|
|
8311
8667
|
}
|
|
@@ -8385,11 +8741,11 @@ ${errorList}`);
|
|
|
8385
8741
|
}
|
|
8386
8742
|
const meta = validation.skillMeta;
|
|
8387
8743
|
const pkg = derivePackageJson(meta);
|
|
8388
|
-
const pkgPath =
|
|
8389
|
-
|
|
8390
|
-
const readmePath =
|
|
8391
|
-
if (!
|
|
8392
|
-
const skillMdContent =
|
|
8744
|
+
const pkgPath = path57.join(skillDir, "package.json");
|
|
8745
|
+
fs39.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
8746
|
+
const readmePath = path57.join(skillDir, "README.md");
|
|
8747
|
+
if (!fs39.existsSync(readmePath)) {
|
|
8748
|
+
const skillMdContent = fs39.readFileSync(path57.join(skillDir, "SKILL.md"), "utf-8");
|
|
8393
8749
|
const readme = `# ${pkg.name}
|
|
8394
8750
|
|
|
8395
8751
|
${meta.description}
|
|
@@ -8403,7 +8759,7 @@ harness install ${meta.name}
|
|
|
8403
8759
|
---
|
|
8404
8760
|
|
|
8405
8761
|
${skillMdContent}`;
|
|
8406
|
-
|
|
8762
|
+
fs39.writeFileSync(readmePath, readme);
|
|
8407
8763
|
}
|
|
8408
8764
|
if (opts.dryRun) {
|
|
8409
8765
|
return {
|
|
@@ -8429,7 +8785,7 @@ ${skillMdContent}`;
|
|
|
8429
8785
|
};
|
|
8430
8786
|
}
|
|
8431
8787
|
function createPublishCommand() {
|
|
8432
|
-
return new
|
|
8788
|
+
return new Command72("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) => {
|
|
8433
8789
|
const globalOpts = cmd.optsWithGlobals();
|
|
8434
8790
|
const skillDir = opts.dir || process.cwd();
|
|
8435
8791
|
try {
|
|
@@ -8454,7 +8810,7 @@ function createPublishCommand() {
|
|
|
8454
8810
|
|
|
8455
8811
|
// src/commands/skill/index.ts
|
|
8456
8812
|
function createSkillCommand() {
|
|
8457
|
-
const command = new
|
|
8813
|
+
const command = new Command73("skill").description("Skill management commands");
|
|
8458
8814
|
command.addCommand(createListCommand3());
|
|
8459
8815
|
command.addCommand(createRunCommand3());
|
|
8460
8816
|
command.addCommand(createValidateCommand2());
|
|
@@ -8466,10 +8822,10 @@ function createSkillCommand() {
|
|
|
8466
8822
|
}
|
|
8467
8823
|
|
|
8468
8824
|
// src/commands/snapshot.ts
|
|
8469
|
-
|
|
8470
|
-
import { Command as
|
|
8825
|
+
init_dist2();
|
|
8826
|
+
import { Command as Command74 } from "commander";
|
|
8471
8827
|
import { execSync as execSync5 } from "child_process";
|
|
8472
|
-
import
|
|
8828
|
+
import chalk13 from "chalk";
|
|
8473
8829
|
function getCommitHash3(cwd) {
|
|
8474
8830
|
try {
|
|
8475
8831
|
return execSync5("git rev-parse --short HEAD", { cwd, encoding: "utf-8" }).toString().trim();
|
|
@@ -8494,9 +8850,9 @@ function formatDelta(delta) {
|
|
|
8494
8850
|
function directionSymbol(direction) {
|
|
8495
8851
|
switch (direction) {
|
|
8496
8852
|
case "improving":
|
|
8497
|
-
return
|
|
8853
|
+
return chalk13.green("improving");
|
|
8498
8854
|
case "declining":
|
|
8499
|
-
return
|
|
8855
|
+
return chalk13.red("declining");
|
|
8500
8856
|
case "stable":
|
|
8501
8857
|
return "=";
|
|
8502
8858
|
}
|
|
@@ -8649,7 +9005,7 @@ function registerListCommand(parent) {
|
|
|
8649
9005
|
});
|
|
8650
9006
|
}
|
|
8651
9007
|
function createSnapshotCommand() {
|
|
8652
|
-
const command = new
|
|
9008
|
+
const command = new Command74("snapshot").description("Architecture timeline snapshot commands");
|
|
8653
9009
|
registerCaptureCommand(command);
|
|
8654
9010
|
registerTrendsCommand(command);
|
|
8655
9011
|
registerListCommand(command);
|
|
@@ -8657,12 +9013,12 @@ function createSnapshotCommand() {
|
|
|
8657
9013
|
}
|
|
8658
9014
|
|
|
8659
9015
|
// src/commands/state/index.ts
|
|
8660
|
-
import { Command as
|
|
9016
|
+
import { Command as Command79 } from "commander";
|
|
8661
9017
|
|
|
8662
9018
|
// src/commands/state/show.ts
|
|
8663
|
-
|
|
8664
|
-
import { Command as
|
|
8665
|
-
import * as
|
|
9019
|
+
init_dist2();
|
|
9020
|
+
import { Command as Command75 } from "commander";
|
|
9021
|
+
import * as path58 from "path";
|
|
8666
9022
|
function printStateText(state, stream) {
|
|
8667
9023
|
if (stream) console.log(`Stream: ${stream}`);
|
|
8668
9024
|
console.log(`Schema Version: ${state.schemaVersion}`);
|
|
@@ -8686,9 +9042,9 @@ function printStateProgress(progress) {
|
|
|
8686
9042
|
}
|
|
8687
9043
|
}
|
|
8688
9044
|
function createShowCommand() {
|
|
8689
|
-
return new
|
|
9045
|
+
return new Command75("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
|
|
8690
9046
|
const globalOpts = cmd.optsWithGlobals();
|
|
8691
|
-
const result = await loadState(
|
|
9047
|
+
const result = await loadState(path58.resolve(opts.path), opts.stream);
|
|
8692
9048
|
if (!result.ok) {
|
|
8693
9049
|
logger.error(result.error.message);
|
|
8694
9050
|
process.exit(ExitCode.ERROR);
|
|
@@ -8703,10 +9059,10 @@ function createShowCommand() {
|
|
|
8703
9059
|
}
|
|
8704
9060
|
|
|
8705
9061
|
// src/commands/state/reset.ts
|
|
8706
|
-
|
|
8707
|
-
import { Command as
|
|
8708
|
-
import * as
|
|
8709
|
-
import * as
|
|
9062
|
+
init_dist2();
|
|
9063
|
+
import { Command as Command76 } from "commander";
|
|
9064
|
+
import * as fs40 from "fs";
|
|
9065
|
+
import * as path59 from "path";
|
|
8710
9066
|
import * as readline3 from "readline";
|
|
8711
9067
|
async function resolveStatePath(projectPath, stream) {
|
|
8712
9068
|
if (stream) {
|
|
@@ -8715,22 +9071,22 @@ async function resolveStatePath(projectPath, stream) {
|
|
|
8715
9071
|
logger.error(streamResult.error.message);
|
|
8716
9072
|
process.exit(ExitCode.ERROR);
|
|
8717
9073
|
}
|
|
8718
|
-
return
|
|
9074
|
+
return path59.join(streamResult.value, "state.json");
|
|
8719
9075
|
}
|
|
8720
|
-
return
|
|
9076
|
+
return path59.join(projectPath, ".harness", "state.json");
|
|
8721
9077
|
}
|
|
8722
9078
|
async function confirmReset() {
|
|
8723
9079
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
8724
|
-
const answer = await new Promise((
|
|
8725
|
-
rl.question("Reset project state? This cannot be undone. [y/N] ",
|
|
9080
|
+
const answer = await new Promise((resolve40) => {
|
|
9081
|
+
rl.question("Reset project state? This cannot be undone. [y/N] ", resolve40);
|
|
8726
9082
|
});
|
|
8727
9083
|
rl.close();
|
|
8728
9084
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
8729
9085
|
}
|
|
8730
9086
|
async function runReset(opts) {
|
|
8731
|
-
const projectPath =
|
|
9087
|
+
const projectPath = path59.resolve(opts.path);
|
|
8732
9088
|
const statePath = await resolveStatePath(projectPath, opts.stream);
|
|
8733
|
-
if (!
|
|
9089
|
+
if (!fs40.existsSync(statePath)) {
|
|
8734
9090
|
logger.info("No state file found. Nothing to reset.");
|
|
8735
9091
|
process.exit(ExitCode.SUCCESS);
|
|
8736
9092
|
}
|
|
@@ -8739,7 +9095,7 @@ async function runReset(opts) {
|
|
|
8739
9095
|
process.exit(ExitCode.SUCCESS);
|
|
8740
9096
|
}
|
|
8741
9097
|
try {
|
|
8742
|
-
|
|
9098
|
+
fs40.unlinkSync(statePath);
|
|
8743
9099
|
logger.success("Project state reset.");
|
|
8744
9100
|
} catch (e) {
|
|
8745
9101
|
logger.error(`Failed to reset state: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -8748,16 +9104,16 @@ async function runReset(opts) {
|
|
|
8748
9104
|
process.exit(ExitCode.SUCCESS);
|
|
8749
9105
|
}
|
|
8750
9106
|
function createResetCommand() {
|
|
8751
|
-
return new
|
|
9107
|
+
return new Command76("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));
|
|
8752
9108
|
}
|
|
8753
9109
|
|
|
8754
9110
|
// src/commands/state/learn.ts
|
|
8755
|
-
|
|
8756
|
-
import { Command as
|
|
8757
|
-
import * as
|
|
9111
|
+
init_dist2();
|
|
9112
|
+
import { Command as Command77 } from "commander";
|
|
9113
|
+
import * as path60 from "path";
|
|
8758
9114
|
function createLearnCommand() {
|
|
8759
|
-
return new
|
|
8760
|
-
const projectPath =
|
|
9115
|
+
return new Command77("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) => {
|
|
9116
|
+
const projectPath = path60.resolve(opts.path);
|
|
8761
9117
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
8762
9118
|
if (!result.ok) {
|
|
8763
9119
|
logger.error(result.error.message);
|
|
@@ -8770,11 +9126,11 @@ function createLearnCommand() {
|
|
|
8770
9126
|
}
|
|
8771
9127
|
|
|
8772
9128
|
// src/commands/state/streams.ts
|
|
8773
|
-
|
|
8774
|
-
import { Command as
|
|
8775
|
-
import * as
|
|
9129
|
+
init_dist2();
|
|
9130
|
+
import { Command as Command78 } from "commander";
|
|
9131
|
+
import * as path61 from "path";
|
|
8776
9132
|
async function runListStreams(opts, globalOpts) {
|
|
8777
|
-
const projectPath =
|
|
9133
|
+
const projectPath = path61.resolve(opts.path);
|
|
8778
9134
|
const indexResult = await loadStreamIndex(projectPath);
|
|
8779
9135
|
const result = await listStreams(projectPath);
|
|
8780
9136
|
if (!result.ok) {
|
|
@@ -8795,7 +9151,7 @@ async function runListStreams(opts, globalOpts) {
|
|
|
8795
9151
|
process.exit(ExitCode.SUCCESS);
|
|
8796
9152
|
}
|
|
8797
9153
|
async function runCreateStream(name, opts) {
|
|
8798
|
-
const result = await createStream(
|
|
9154
|
+
const result = await createStream(path61.resolve(opts.path), name, opts.branch);
|
|
8799
9155
|
if (!result.ok) {
|
|
8800
9156
|
logger.error(result.error.message);
|
|
8801
9157
|
process.exit(ExitCode.ERROR);
|
|
@@ -8804,7 +9160,7 @@ async function runCreateStream(name, opts) {
|
|
|
8804
9160
|
process.exit(ExitCode.SUCCESS);
|
|
8805
9161
|
}
|
|
8806
9162
|
async function runArchiveStream(name, opts) {
|
|
8807
|
-
const result = await archiveStream(
|
|
9163
|
+
const result = await archiveStream(path61.resolve(opts.path), name);
|
|
8808
9164
|
if (!result.ok) {
|
|
8809
9165
|
logger.error(result.error.message);
|
|
8810
9166
|
process.exit(ExitCode.ERROR);
|
|
@@ -8813,7 +9169,7 @@ async function runArchiveStream(name, opts) {
|
|
|
8813
9169
|
process.exit(ExitCode.SUCCESS);
|
|
8814
9170
|
}
|
|
8815
9171
|
async function runActivateStream(name, opts) {
|
|
8816
|
-
const result = await setActiveStream(
|
|
9172
|
+
const result = await setActiveStream(path61.resolve(opts.path), name);
|
|
8817
9173
|
if (!result.ok) {
|
|
8818
9174
|
logger.error(result.error.message);
|
|
8819
9175
|
process.exit(ExitCode.ERROR);
|
|
@@ -8822,7 +9178,7 @@ async function runActivateStream(name, opts) {
|
|
|
8822
9178
|
process.exit(ExitCode.SUCCESS);
|
|
8823
9179
|
}
|
|
8824
9180
|
function createStreamsCommand() {
|
|
8825
|
-
const command = new
|
|
9181
|
+
const command = new Command78("streams").description("Manage state streams");
|
|
8826
9182
|
command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
|
|
8827
9183
|
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));
|
|
8828
9184
|
command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
|
|
@@ -8832,7 +9188,7 @@ function createStreamsCommand() {
|
|
|
8832
9188
|
|
|
8833
9189
|
// src/commands/state/index.ts
|
|
8834
9190
|
function createStateCommand() {
|
|
8835
|
-
const command = new
|
|
9191
|
+
const command = new Command79("state").description("Project state management commands");
|
|
8836
9192
|
command.addCommand(createShowCommand());
|
|
8837
9193
|
command.addCommand(createResetCommand());
|
|
8838
9194
|
command.addCommand(createLearnCommand());
|
|
@@ -8841,10 +9197,10 @@ function createStateCommand() {
|
|
|
8841
9197
|
}
|
|
8842
9198
|
|
|
8843
9199
|
// src/commands/sync-analyses.ts
|
|
8844
|
-
import * as
|
|
8845
|
-
import * as
|
|
8846
|
-
import { Command as
|
|
8847
|
-
|
|
9200
|
+
import * as fs41 from "fs";
|
|
9201
|
+
import * as path62 from "path";
|
|
9202
|
+
import { Command as Command80 } from "commander";
|
|
9203
|
+
init_dist2();
|
|
8848
9204
|
function isValidRecord(record) {
|
|
8849
9205
|
return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
|
|
8850
9206
|
}
|
|
@@ -8869,7 +9225,7 @@ function extractAnalysisFromComments(comments) {
|
|
|
8869
9225
|
return null;
|
|
8870
9226
|
}
|
|
8871
9227
|
function bootstrapTrackerCommand2(opts, verb) {
|
|
8872
|
-
const projectPath =
|
|
9228
|
+
const projectPath = path62.resolve(opts.dir);
|
|
8873
9229
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
8874
9230
|
if (!trackerConfig) {
|
|
8875
9231
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -8881,8 +9237,8 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
8881
9237
|
);
|
|
8882
9238
|
return null;
|
|
8883
9239
|
}
|
|
8884
|
-
const projectEnvPath =
|
|
8885
|
-
if (
|
|
9240
|
+
const projectEnvPath = path62.join(projectPath, ".env");
|
|
9241
|
+
if (fs41.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
8886
9242
|
__require("dotenv").config({ path: projectEnvPath });
|
|
8887
9243
|
}
|
|
8888
9244
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -8892,14 +9248,33 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
8892
9248
|
}
|
|
8893
9249
|
return { token, projectPath, trackerConfig };
|
|
8894
9250
|
}
|
|
8895
|
-
function loadRoadmapFeatures(projectPath) {
|
|
8896
|
-
const
|
|
8897
|
-
if (
|
|
9251
|
+
async function loadRoadmapFeatures(projectPath) {
|
|
9252
|
+
const mode = loadProjectRoadmapMode(projectPath);
|
|
9253
|
+
if (mode === "file-less") {
|
|
9254
|
+
const trackerCfg = loadTrackerClientConfigFromProject(projectPath);
|
|
9255
|
+
if (!trackerCfg.ok) {
|
|
9256
|
+
logger.error(`File-less mode: ${trackerCfg.error.message}`);
|
|
9257
|
+
return null;
|
|
9258
|
+
}
|
|
9259
|
+
const clientR = createTrackerClient(trackerCfg.value);
|
|
9260
|
+
if (!clientR.ok) {
|
|
9261
|
+
logger.error(`File-less mode: ${clientR.error.message}`);
|
|
9262
|
+
return null;
|
|
9263
|
+
}
|
|
9264
|
+
const r = await clientR.value.fetchAll();
|
|
9265
|
+
if (!r.ok) {
|
|
9266
|
+
logger.error(`File-less mode: failed to fetch features from tracker: ${r.error.message}`);
|
|
9267
|
+
return null;
|
|
9268
|
+
}
|
|
9269
|
+
return r.value.features.filter((f) => !!f.externalId).map((f) => ({ name: f.name, externalId: f.externalId }));
|
|
9270
|
+
}
|
|
9271
|
+
const roadmapFile = path62.join(projectPath, "docs", "roadmap.md");
|
|
9272
|
+
if (!fs41.existsSync(roadmapFile)) {
|
|
8898
9273
|
logger.error("No docs/roadmap.md found. Cannot discover features with externalIds.");
|
|
8899
9274
|
return null;
|
|
8900
9275
|
}
|
|
8901
|
-
const { parseRoadmap } = (
|
|
8902
|
-
const roadmapParsed =
|
|
9276
|
+
const { parseRoadmap: parseRoadmap2 } = (init_dist2(), __toCommonJS(dist_exports));
|
|
9277
|
+
const roadmapParsed = parseRoadmap2(fs41.readFileSync(roadmapFile, "utf-8"));
|
|
8903
9278
|
if (!roadmapParsed.ok) {
|
|
8904
9279
|
logger.error("Failed to parse docs/roadmap.md");
|
|
8905
9280
|
return null;
|
|
@@ -8943,16 +9318,16 @@ async function runSyncAnalyses(opts) {
|
|
|
8943
9318
|
const bootstrap = bootstrapTrackerCommand2(opts, "sync");
|
|
8944
9319
|
if (!bootstrap) return process.exit(1);
|
|
8945
9320
|
const { token, projectPath, trackerConfig } = bootstrap;
|
|
8946
|
-
const features = loadRoadmapFeatures(projectPath);
|
|
9321
|
+
const features = await loadRoadmapFeatures(projectPath);
|
|
8947
9322
|
if (!features) return process.exit(1);
|
|
8948
9323
|
if (features.length === 0) {
|
|
8949
9324
|
logger.info("No roadmap features have externalIds. Nothing to sync.");
|
|
8950
9325
|
return;
|
|
8951
9326
|
}
|
|
8952
9327
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
8953
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
9328
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-ZS3Y3YWP.js");
|
|
8954
9329
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
8955
|
-
const archive = new AnalysisArchive(
|
|
9330
|
+
const archive = new AnalysisArchive(path62.join(projectPath, ".harness", "analyses"));
|
|
8956
9331
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
8957
9332
|
const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
|
|
8958
9333
|
features,
|
|
@@ -8972,7 +9347,7 @@ async function runSyncAnalyses(opts) {
|
|
|
8972
9347
|
}
|
|
8973
9348
|
}
|
|
8974
9349
|
function createSyncAnalysesCommand() {
|
|
8975
|
-
const command = new
|
|
9350
|
+
const command = new Command80("sync-analyses").description(
|
|
8976
9351
|
"Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
|
|
8977
9352
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
8978
9353
|
try {
|
|
@@ -8985,9 +9360,48 @@ function createSyncAnalysesCommand() {
|
|
|
8985
9360
|
return command;
|
|
8986
9361
|
}
|
|
8987
9362
|
|
|
9363
|
+
// src/commands/sync-main.ts
|
|
9364
|
+
import { Command as Command81 } from "commander";
|
|
9365
|
+
import * as path63 from "path";
|
|
9366
|
+
import {
|
|
9367
|
+
syncMain as defaultSyncMain
|
|
9368
|
+
} from "@harness-engineering/orchestrator";
|
|
9369
|
+
async function runSyncMain(opts) {
|
|
9370
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
9371
|
+
const fn = opts.syncMainFn ?? ((root) => defaultSyncMain(root));
|
|
9372
|
+
const result = await fn(cwd);
|
|
9373
|
+
if (opts.json) {
|
|
9374
|
+
process.stdout.write(`${JSON.stringify(result)}
|
|
9375
|
+
`);
|
|
9376
|
+
} else {
|
|
9377
|
+
process.stdout.write(`${formatHuman(result)}
|
|
9378
|
+
`);
|
|
9379
|
+
}
|
|
9380
|
+
return result.status === "error" ? ExitCode.ERROR : ExitCode.SUCCESS;
|
|
9381
|
+
}
|
|
9382
|
+
function formatHuman(r) {
|
|
9383
|
+
switch (r.status) {
|
|
9384
|
+
case "updated":
|
|
9385
|
+
return `updated ${r.defaultBranch}: ${r.from.slice(0, 7)} -> ${r.to.slice(0, 7)}`;
|
|
9386
|
+
case "no-op":
|
|
9387
|
+
return `up-to-date: ${r.defaultBranch}`;
|
|
9388
|
+
case "skipped":
|
|
9389
|
+
return `skipped (${r.reason}): ${r.detail}`;
|
|
9390
|
+
case "error":
|
|
9391
|
+
return `error: ${r.message}`;
|
|
9392
|
+
}
|
|
9393
|
+
}
|
|
9394
|
+
function createSyncMainCommand() {
|
|
9395
|
+
return new Command81("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) => {
|
|
9396
|
+
const cwd = path63.resolve(opts.path ?? ".");
|
|
9397
|
+
const exitCode = await runSyncMain({ cwd, json: Boolean(opts.json) });
|
|
9398
|
+
process.exit(exitCode);
|
|
9399
|
+
});
|
|
9400
|
+
}
|
|
9401
|
+
|
|
8988
9402
|
// src/commands/taint.ts
|
|
8989
|
-
|
|
8990
|
-
import { Command as
|
|
9403
|
+
init_dist2();
|
|
9404
|
+
import { Command as Command82 } from "commander";
|
|
8991
9405
|
function getProjectRoot() {
|
|
8992
9406
|
return process.cwd();
|
|
8993
9407
|
}
|
|
@@ -9059,28 +9473,28 @@ function registerStatusCommand(taint) {
|
|
|
9059
9473
|
});
|
|
9060
9474
|
}
|
|
9061
9475
|
function createTaintCommand() {
|
|
9062
|
-
const taint = new
|
|
9476
|
+
const taint = new Command82("taint").description("Manage sentinel session taint state");
|
|
9063
9477
|
registerClearCommand(taint);
|
|
9064
9478
|
registerStatusCommand(taint);
|
|
9065
9479
|
return taint;
|
|
9066
9480
|
}
|
|
9067
9481
|
|
|
9068
9482
|
// src/commands/telemetry/index.ts
|
|
9069
|
-
import { Command as
|
|
9483
|
+
import { Command as Command86 } from "commander";
|
|
9070
9484
|
|
|
9071
9485
|
// src/commands/telemetry/identify.ts
|
|
9072
|
-
|
|
9073
|
-
import { Command as
|
|
9074
|
-
import * as
|
|
9075
|
-
import * as
|
|
9486
|
+
init_dist2();
|
|
9487
|
+
import { Command as Command83 } from "commander";
|
|
9488
|
+
import * as fs42 from "fs";
|
|
9489
|
+
import * as path64 from "path";
|
|
9076
9490
|
function telemetryFilePath(cwd) {
|
|
9077
|
-
return
|
|
9491
|
+
return path64.join(cwd, ".harness", "telemetry.json");
|
|
9078
9492
|
}
|
|
9079
9493
|
function writeTelemetryFile(cwd, data) {
|
|
9080
9494
|
const filePath = telemetryFilePath(cwd);
|
|
9081
|
-
const dir =
|
|
9082
|
-
|
|
9083
|
-
|
|
9495
|
+
const dir = path64.dirname(filePath);
|
|
9496
|
+
fs42.mkdirSync(dir, { recursive: true });
|
|
9497
|
+
fs42.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
9084
9498
|
}
|
|
9085
9499
|
var IDENTITY_FIELDS = ["project", "team", "alias"];
|
|
9086
9500
|
function applyIdentityFields(opts, identity) {
|
|
@@ -9094,7 +9508,7 @@ function printIdentity(identity) {
|
|
|
9094
9508
|
}
|
|
9095
9509
|
}
|
|
9096
9510
|
function createIdentifyCommand() {
|
|
9097
|
-
const cmd = new
|
|
9511
|
+
const cmd = new Command83("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) => {
|
|
9098
9512
|
const cwd = process.cwd();
|
|
9099
9513
|
if (opts.clear) {
|
|
9100
9514
|
writeTelemetryFile(cwd, { identity: {} });
|
|
@@ -9118,8 +9532,8 @@ function createIdentifyCommand() {
|
|
|
9118
9532
|
}
|
|
9119
9533
|
|
|
9120
9534
|
// src/commands/telemetry/status.ts
|
|
9121
|
-
|
|
9122
|
-
import { Command as
|
|
9535
|
+
init_dist2();
|
|
9536
|
+
import { Command as Command84 } from "commander";
|
|
9123
9537
|
function gatherEnvOverrides() {
|
|
9124
9538
|
const overrides = {};
|
|
9125
9539
|
if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
|
|
@@ -9161,7 +9575,7 @@ function printHumanStatus(result) {
|
|
|
9161
9575
|
printEnvOverrides(result.envOverrides);
|
|
9162
9576
|
}
|
|
9163
9577
|
function createStatusCommand() {
|
|
9164
|
-
const cmd = new
|
|
9578
|
+
const cmd = new Command84("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
|
|
9165
9579
|
const cwd = process.cwd();
|
|
9166
9580
|
const envOverrides = gatherEnvOverrides();
|
|
9167
9581
|
const consent = resolveConsent(cwd, void 0);
|
|
@@ -9193,21 +9607,21 @@ function createStatusCommand() {
|
|
|
9193
9607
|
}
|
|
9194
9608
|
|
|
9195
9609
|
// src/commands/telemetry/test.ts
|
|
9196
|
-
|
|
9197
|
-
import { Command as
|
|
9610
|
+
init_dist2();
|
|
9611
|
+
import { Command as Command85 } from "commander";
|
|
9198
9612
|
|
|
9199
9613
|
// src/bin/command-telemetry.ts
|
|
9200
|
-
import { existsSync as
|
|
9201
|
-
import { join as
|
|
9614
|
+
import { existsSync as existsSync39, readFileSync as readFileSync29, mkdirSync as mkdirSync19, appendFileSync, writeFileSync as writeFileSync23 } from "fs";
|
|
9615
|
+
import { join as join53, parse as parsePath, resolve as resolve37 } from "path";
|
|
9202
9616
|
import { spawn as spawn2 } from "child_process";
|
|
9203
9617
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
9204
9618
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
9205
9619
|
function findProjectRoot(cwd) {
|
|
9206
|
-
let dir =
|
|
9620
|
+
let dir = resolve37(cwd);
|
|
9207
9621
|
const { root } = parsePath(dir);
|
|
9208
9622
|
while (dir !== root) {
|
|
9209
|
-
if (
|
|
9210
|
-
dir =
|
|
9623
|
+
if (existsSync39(join53(dir, "harness.config.json"))) return dir;
|
|
9624
|
+
dir = resolve37(dir, "..");
|
|
9211
9625
|
}
|
|
9212
9626
|
return cwd;
|
|
9213
9627
|
}
|
|
@@ -9244,8 +9658,8 @@ function resolveCommandName(cmd) {
|
|
|
9244
9658
|
}
|
|
9245
9659
|
function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
9246
9660
|
try {
|
|
9247
|
-
const metricsDir =
|
|
9248
|
-
|
|
9661
|
+
const metricsDir = join53(cwd, ".harness", "metrics");
|
|
9662
|
+
mkdirSync19(metricsDir, { recursive: true });
|
|
9249
9663
|
const record = {
|
|
9250
9664
|
skill: command,
|
|
9251
9665
|
session: `cli-${Date.now()}`,
|
|
@@ -9254,16 +9668,16 @@ function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
|
9254
9668
|
outcome,
|
|
9255
9669
|
phasesReached: []
|
|
9256
9670
|
};
|
|
9257
|
-
const adoptionFile =
|
|
9671
|
+
const adoptionFile = join53(metricsDir, "adoption.jsonl");
|
|
9258
9672
|
appendFileSync(adoptionFile, JSON.stringify(record) + "\n");
|
|
9259
9673
|
} catch {
|
|
9260
9674
|
}
|
|
9261
9675
|
}
|
|
9262
9676
|
function isTelemetryDisabledByConfig(cwd) {
|
|
9263
|
-
const configPath =
|
|
9264
|
-
if (!
|
|
9677
|
+
const configPath = join53(cwd, "harness.config.json");
|
|
9678
|
+
if (!existsSync39(configPath)) return false;
|
|
9265
9679
|
try {
|
|
9266
|
-
const config = JSON.parse(
|
|
9680
|
+
const config = JSON.parse(readFileSync29(configPath, "utf-8"));
|
|
9267
9681
|
return config?.telemetry?.enabled === false;
|
|
9268
9682
|
} catch {
|
|
9269
9683
|
return false;
|
|
@@ -9275,11 +9689,11 @@ function isTelemetryOptedOut(cwd) {
|
|
|
9275
9689
|
}
|
|
9276
9690
|
function flushTelemetryBackground(cwd) {
|
|
9277
9691
|
try {
|
|
9278
|
-
const adoptionFile =
|
|
9279
|
-
if (!
|
|
9692
|
+
const adoptionFile = join53(cwd, ".harness", "metrics", "adoption.jsonl");
|
|
9693
|
+
if (!existsSync39(adoptionFile)) return;
|
|
9280
9694
|
if (isTelemetryOptedOut(cwd)) return;
|
|
9281
|
-
const reporterPath =
|
|
9282
|
-
if (!
|
|
9695
|
+
const reporterPath = join53(cwd, ".harness", "hooks", "telemetry-reporter.js");
|
|
9696
|
+
if (!existsSync39(reporterPath)) return;
|
|
9283
9697
|
const child = spawn2(process.execPath, [reporterPath], {
|
|
9284
9698
|
cwd,
|
|
9285
9699
|
stdio: ["pipe", "ignore", "ignore"],
|
|
@@ -9316,7 +9730,7 @@ function reportTestSuccess(status, distinctId, identity) {
|
|
|
9316
9730
|
if (identity.team) logger.info(` team: ${identity.team}`);
|
|
9317
9731
|
}
|
|
9318
9732
|
function createTestCommand() {
|
|
9319
|
-
return new
|
|
9733
|
+
return new Command85("test").description("Send a test event to PostHog and verify connectivity").action(async () => {
|
|
9320
9734
|
const cwd = process.cwd();
|
|
9321
9735
|
const consent = resolveConsent(cwd, void 0);
|
|
9322
9736
|
if (!consent.allowed) {
|
|
@@ -9353,7 +9767,7 @@ function createTestCommand() {
|
|
|
9353
9767
|
|
|
9354
9768
|
// src/commands/telemetry/index.ts
|
|
9355
9769
|
function createTelemetryCommand() {
|
|
9356
|
-
const command = new
|
|
9770
|
+
const command = new Command86("telemetry").description("Telemetry identity and status management");
|
|
9357
9771
|
command.addCommand(createIdentifyCommand());
|
|
9358
9772
|
command.addCommand(createStatusCommand());
|
|
9359
9773
|
command.addCommand(createTestCommand());
|
|
@@ -9361,8 +9775,8 @@ function createTelemetryCommand() {
|
|
|
9361
9775
|
}
|
|
9362
9776
|
|
|
9363
9777
|
// src/commands/traceability.ts
|
|
9364
|
-
import { Command as
|
|
9365
|
-
import
|
|
9778
|
+
import { Command as Command87 } from "commander";
|
|
9779
|
+
import chalk14 from "chalk";
|
|
9366
9780
|
function confidenceLabel(maxConfidence) {
|
|
9367
9781
|
if (maxConfidence >= 0.8) return "explicit";
|
|
9368
9782
|
if (maxConfidence > 0) return "inferred";
|
|
@@ -9371,13 +9785,13 @@ function confidenceLabel(maxConfidence) {
|
|
|
9371
9785
|
function statusIcon(status) {
|
|
9372
9786
|
switch (status) {
|
|
9373
9787
|
case "full":
|
|
9374
|
-
return
|
|
9788
|
+
return chalk14.green("\u2713 full");
|
|
9375
9789
|
case "code-only":
|
|
9376
|
-
return
|
|
9790
|
+
return chalk14.yellow("\u25D0 code-only");
|
|
9377
9791
|
case "test-only":
|
|
9378
|
-
return
|
|
9792
|
+
return chalk14.yellow("\u25D0 test-only");
|
|
9379
9793
|
case "none":
|
|
9380
|
-
return
|
|
9794
|
+
return chalk14.red("\u2717 none");
|
|
9381
9795
|
default:
|
|
9382
9796
|
return status;
|
|
9383
9797
|
}
|
|
@@ -9424,14 +9838,14 @@ function handleEmptyResults(mode) {
|
|
|
9424
9838
|
}
|
|
9425
9839
|
function printResultTable(result, mode) {
|
|
9426
9840
|
const specLabel = result.specPath || result.featureName || "Unknown";
|
|
9427
|
-
console.log(` ${
|
|
9841
|
+
console.log(` ${chalk14.cyan(specLabel)} ${chalk14.dim(`(${result.summary.total} requirements)`)}`);
|
|
9428
9842
|
console.log("");
|
|
9429
9843
|
const numWidth = 4;
|
|
9430
9844
|
const nameWidth = mode === OutputMode.VERBOSE ? 44 : 36;
|
|
9431
9845
|
const codeWidth = 6;
|
|
9432
9846
|
const testWidth = 7;
|
|
9433
9847
|
const confWidth = 12;
|
|
9434
|
-
const header =
|
|
9848
|
+
const header = chalk14.dim(
|
|
9435
9849
|
` ${pad("#", numWidth)}${pad("Requirement", nameWidth)}${pad("Code", codeWidth)}${pad("Tests", testWidth)}${pad("Confidence", confWidth)}Status`
|
|
9436
9850
|
);
|
|
9437
9851
|
console.log(header);
|
|
@@ -9460,10 +9874,10 @@ function printRequirementRows(requirements, mode, cols) {
|
|
|
9460
9874
|
);
|
|
9461
9875
|
if (mode === OutputMode.VERBOSE) {
|
|
9462
9876
|
for (const f of req.codeFiles) {
|
|
9463
|
-
console.log(
|
|
9877
|
+
console.log(chalk14.dim(` code: ${f.path} (${f.method})`));
|
|
9464
9878
|
}
|
|
9465
9879
|
for (const f of req.testFiles) {
|
|
9466
|
-
console.log(
|
|
9880
|
+
console.log(chalk14.dim(` test: ${f.path} (${f.method})`));
|
|
9467
9881
|
}
|
|
9468
9882
|
}
|
|
9469
9883
|
}
|
|
@@ -9474,7 +9888,7 @@ function printCoverageSummary(summary) {
|
|
|
9474
9888
|
const codePct = s.total > 0 ? Math.round(s.withCode / s.total * 100) : 0;
|
|
9475
9889
|
const testPct = s.total > 0 ? Math.round(s.withTests / s.total * 100) : 0;
|
|
9476
9890
|
console.log(
|
|
9477
|
-
` ${
|
|
9891
|
+
` ${chalk14.bold("Coverage:")} ${fullyPct}% fully traced (${s.fullyTraced}/${s.total}), ${codePct}% with code (${s.withCode}/${s.total}), ${testPct}% with tests (${s.withTests}/${s.total})`
|
|
9478
9892
|
);
|
|
9479
9893
|
}
|
|
9480
9894
|
async function runTraceability(options) {
|
|
@@ -9482,7 +9896,7 @@ async function runTraceability(options) {
|
|
|
9482
9896
|
const projectPath = process.cwd();
|
|
9483
9897
|
const store = await loadGraphStore(projectPath);
|
|
9484
9898
|
if (!store) handleNoStore(mode);
|
|
9485
|
-
const graphModule = await import("./dist-
|
|
9899
|
+
const graphModule = await import("./dist-SQAOKWXM.js");
|
|
9486
9900
|
const filterOptions = buildFilterOptions(options);
|
|
9487
9901
|
const results = graphModule.queryTraceability(store, filterOptions);
|
|
9488
9902
|
if (results.length === 0) handleEmptyResults(mode);
|
|
@@ -9490,14 +9904,14 @@ async function runTraceability(options) {
|
|
|
9490
9904
|
console.log(JSON.stringify(results, null, 2));
|
|
9491
9905
|
} else {
|
|
9492
9906
|
console.log("");
|
|
9493
|
-
console.log(
|
|
9907
|
+
console.log(chalk14.bold("Spec-to-Implementation Traceability"));
|
|
9494
9908
|
console.log("");
|
|
9495
9909
|
for (const result of results) printResultTable(result, mode);
|
|
9496
9910
|
}
|
|
9497
9911
|
process.exit(ExitCode.SUCCESS);
|
|
9498
9912
|
}
|
|
9499
9913
|
function createTraceabilityCommand() {
|
|
9500
|
-
return new
|
|
9914
|
+
return new Command87("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) => {
|
|
9501
9915
|
const globalOpts = cmd.optsWithGlobals();
|
|
9502
9916
|
await runTraceability({
|
|
9503
9917
|
spec: opts.spec,
|
|
@@ -9510,15 +9924,15 @@ function createTraceabilityCommand() {
|
|
|
9510
9924
|
}
|
|
9511
9925
|
|
|
9512
9926
|
// src/commands/uninstall.ts
|
|
9513
|
-
import * as
|
|
9514
|
-
import { Command as
|
|
9927
|
+
import * as path65 from "path";
|
|
9928
|
+
import { Command as Command88 } from "commander";
|
|
9515
9929
|
async function runUninstall(skillName, options) {
|
|
9516
9930
|
const packageName = resolvePackageName(skillName);
|
|
9517
9931
|
const shortName = extractSkillName(packageName);
|
|
9518
9932
|
const globalDir = resolveGlobalSkillsDir();
|
|
9519
|
-
const skillsDir =
|
|
9520
|
-
const communityBase =
|
|
9521
|
-
const lockfilePath =
|
|
9933
|
+
const skillsDir = path65.dirname(globalDir);
|
|
9934
|
+
const communityBase = path65.join(skillsDir, "community");
|
|
9935
|
+
const lockfilePath = path65.join(communityBase, "skills-lock.json");
|
|
9522
9936
|
const lockfile = readLockfile2(lockfilePath);
|
|
9523
9937
|
const entry = lockfile.skills[packageName];
|
|
9524
9938
|
if (!entry) {
|
|
@@ -9548,7 +9962,7 @@ async function runUninstall(skillName, options) {
|
|
|
9548
9962
|
return result;
|
|
9549
9963
|
}
|
|
9550
9964
|
function createUninstallCommand() {
|
|
9551
|
-
const cmd = new
|
|
9965
|
+
const cmd = new Command88("uninstall");
|
|
9552
9966
|
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) => {
|
|
9553
9967
|
try {
|
|
9554
9968
|
const result = await runUninstall(skill, opts);
|
|
@@ -9567,10 +9981,10 @@ function createUninstallCommand() {
|
|
|
9567
9981
|
}
|
|
9568
9982
|
|
|
9569
9983
|
// src/commands/uninstall-constraints.ts
|
|
9570
|
-
|
|
9571
|
-
import * as
|
|
9572
|
-
import * as
|
|
9573
|
-
import { Command as
|
|
9984
|
+
init_dist2();
|
|
9985
|
+
import * as fs43 from "fs/promises";
|
|
9986
|
+
import * as path66 from "path";
|
|
9987
|
+
import { Command as Command89 } from "commander";
|
|
9574
9988
|
async function runUninstallConstraints(options) {
|
|
9575
9989
|
const { packageName, configPath, lockfilePath } = options;
|
|
9576
9990
|
const lockfileResult = await readLockfile(lockfilePath);
|
|
@@ -9590,7 +10004,7 @@ async function runUninstallConstraints(options) {
|
|
|
9590
10004
|
}
|
|
9591
10005
|
let localConfig;
|
|
9592
10006
|
try {
|
|
9593
|
-
const raw = await
|
|
10007
|
+
const raw = await fs43.readFile(configPath, "utf-8");
|
|
9594
10008
|
localConfig = JSON.parse(raw);
|
|
9595
10009
|
} catch (err) {
|
|
9596
10010
|
return {
|
|
@@ -9627,11 +10041,11 @@ async function runUninstallConstraints(options) {
|
|
|
9627
10041
|
};
|
|
9628
10042
|
}
|
|
9629
10043
|
function createUninstallConstraintsCommand() {
|
|
9630
|
-
const cmd = new
|
|
10044
|
+
const cmd = new Command89("uninstall-constraints");
|
|
9631
10045
|
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) => {
|
|
9632
10046
|
let configPath;
|
|
9633
10047
|
if (opts.config) {
|
|
9634
|
-
configPath =
|
|
10048
|
+
configPath = path66.resolve(opts.config);
|
|
9635
10049
|
} else {
|
|
9636
10050
|
const found = findConfigFile();
|
|
9637
10051
|
if (!found.ok) {
|
|
@@ -9640,8 +10054,8 @@ function createUninstallConstraintsCommand() {
|
|
|
9640
10054
|
}
|
|
9641
10055
|
configPath = found.value;
|
|
9642
10056
|
}
|
|
9643
|
-
const projectRoot =
|
|
9644
|
-
const lockfilePath =
|
|
10057
|
+
const projectRoot = path66.dirname(configPath);
|
|
10058
|
+
const lockfilePath = path66.join(projectRoot, ".harness", "constraints.lock.json");
|
|
9645
10059
|
const result = await runUninstallConstraints({
|
|
9646
10060
|
packageName: name,
|
|
9647
10061
|
configPath,
|
|
@@ -9666,13 +10080,13 @@ function createUninstallConstraintsCommand() {
|
|
|
9666
10080
|
}
|
|
9667
10081
|
|
|
9668
10082
|
// src/commands/update.ts
|
|
9669
|
-
import { Command as
|
|
10083
|
+
import { Command as Command90 } from "commander";
|
|
9670
10084
|
import { execFile, execFileSync as execFileSync7 } from "child_process";
|
|
9671
|
-
import { realpathSync, existsSync as
|
|
9672
|
-
import { join as
|
|
10085
|
+
import { realpathSync, existsSync as existsSync40, readFileSync as readFileSync30 } from "fs";
|
|
10086
|
+
import { join as join56 } from "path";
|
|
9673
10087
|
import { promisify } from "util";
|
|
9674
10088
|
import readline4 from "readline";
|
|
9675
|
-
import
|
|
10089
|
+
import chalk15 from "chalk";
|
|
9676
10090
|
function detectPackageManager() {
|
|
9677
10091
|
try {
|
|
9678
10092
|
const argv1 = process.argv[1];
|
|
@@ -9735,10 +10149,10 @@ function prompt3(question) {
|
|
|
9735
10149
|
input: process.stdin,
|
|
9736
10150
|
output: process.stdout
|
|
9737
10151
|
});
|
|
9738
|
-
return new Promise((
|
|
10152
|
+
return new Promise((resolve40) => {
|
|
9739
10153
|
rl.question(question, (answer) => {
|
|
9740
10154
|
rl.close();
|
|
9741
|
-
|
|
10155
|
+
resolve40(answer.trim().toLowerCase());
|
|
9742
10156
|
});
|
|
9743
10157
|
});
|
|
9744
10158
|
}
|
|
@@ -9753,12 +10167,12 @@ async function ensureTelemetryIfNeeded() {
|
|
|
9753
10167
|
}
|
|
9754
10168
|
function refreshHooks() {
|
|
9755
10169
|
const cwd = process.cwd();
|
|
9756
|
-
const configPath =
|
|
9757
|
-
if (!
|
|
10170
|
+
const configPath = join56(cwd, "harness.config.json");
|
|
10171
|
+
if (!existsSync40(configPath)) return;
|
|
9758
10172
|
let profile = "standard";
|
|
9759
|
-
const profilePath =
|
|
10173
|
+
const profilePath = join56(cwd, ".harness", "hooks", "profile.json");
|
|
9760
10174
|
try {
|
|
9761
|
-
const data = JSON.parse(
|
|
10175
|
+
const data = JSON.parse(readFileSync30(profilePath, "utf-8"));
|
|
9762
10176
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
9763
10177
|
profile = data.profile;
|
|
9764
10178
|
}
|
|
@@ -9778,7 +10192,7 @@ function runLocalGraphScan() {
|
|
|
9778
10192
|
execFileSync7("harness", ["graph", "scan", "."], { stdio: "inherit" });
|
|
9779
10193
|
} catch {
|
|
9780
10194
|
logger.warn("Graph scan failed. Run manually:");
|
|
9781
|
-
console.log(` ${
|
|
10195
|
+
console.log(` ${chalk15.cyan("harness graph scan .")}`);
|
|
9782
10196
|
}
|
|
9783
10197
|
}
|
|
9784
10198
|
async function offerRegeneration() {
|
|
@@ -9795,7 +10209,7 @@ async function offerRegeneration() {
|
|
|
9795
10209
|
});
|
|
9796
10210
|
} catch {
|
|
9797
10211
|
logger.warn("Generation failed. Run manually:");
|
|
9798
|
-
console.log(` ${
|
|
10212
|
+
console.log(` ${chalk15.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
|
|
9799
10213
|
}
|
|
9800
10214
|
if (!isGlobal) {
|
|
9801
10215
|
runLocalGraphScan();
|
|
@@ -9858,8 +10272,8 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
9858
10272
|
console.log("");
|
|
9859
10273
|
for (const { pkg, current, latest } of outdated) {
|
|
9860
10274
|
const shortName = pkg.replace("@harness-engineering/", "");
|
|
9861
|
-
const currentStr = current ?
|
|
9862
|
-
logger.info(`${shortName}: ${currentStr} \u2192 ${
|
|
10275
|
+
const currentStr = current ? chalk15.dim(`v${current}`) : chalk15.dim("not installed");
|
|
10276
|
+
logger.info(`${shortName}: ${currentStr} \u2192 ${chalk15.green(`v${latest}`)}`);
|
|
9863
10277
|
}
|
|
9864
10278
|
console.log("");
|
|
9865
10279
|
}
|
|
@@ -9875,7 +10289,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
9875
10289
|
} catch {
|
|
9876
10290
|
console.log("");
|
|
9877
10291
|
logger.error("Update failed. You can try manually:");
|
|
9878
|
-
console.log(` ${
|
|
10292
|
+
console.log(` ${chalk15.cyan(installCmd)}`);
|
|
9879
10293
|
process.exit(ExitCode.ERROR);
|
|
9880
10294
|
}
|
|
9881
10295
|
refreshHooks();
|
|
@@ -9884,7 +10298,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
9884
10298
|
process.exit(ExitCode.SUCCESS);
|
|
9885
10299
|
}
|
|
9886
10300
|
function createUpdateCommand() {
|
|
9887
|
-
return new
|
|
10301
|
+
return new Command90("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(
|
|
9888
10302
|
"--regenerate",
|
|
9889
10303
|
"Only regenerate slash commands and agent definitions (skip package updates)"
|
|
9890
10304
|
).action(async (opts, cmd) => {
|
|
@@ -9894,9 +10308,9 @@ function createUpdateCommand() {
|
|
|
9894
10308
|
}
|
|
9895
10309
|
|
|
9896
10310
|
// src/commands/usage.ts
|
|
9897
|
-
import { Command as
|
|
10311
|
+
import { Command as Command91 } from "commander";
|
|
9898
10312
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
9899
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
10313
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-ZS3Y3YWP.js");
|
|
9900
10314
|
const records = readCostRecords(cwd);
|
|
9901
10315
|
if (includeClaudeSessions) {
|
|
9902
10316
|
const ccRecords = parseCCRecords();
|
|
@@ -9989,7 +10403,7 @@ function registerDailyCommand(usage) {
|
|
|
9989
10403
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
9990
10404
|
return;
|
|
9991
10405
|
}
|
|
9992
|
-
const { aggregateByDay } = await import("./dist-
|
|
10406
|
+
const { aggregateByDay } = await import("./dist-ZS3Y3YWP.js");
|
|
9993
10407
|
const limited = aggregateByDay(records).slice(0, days);
|
|
9994
10408
|
if (globalOpts.json) {
|
|
9995
10409
|
console.log(
|
|
@@ -10018,7 +10432,7 @@ function registerSessionsCommand(usage) {
|
|
|
10018
10432
|
}
|
|
10019
10433
|
return;
|
|
10020
10434
|
}
|
|
10021
|
-
const { aggregateBySession } = await import("./dist-
|
|
10435
|
+
const { aggregateBySession } = await import("./dist-ZS3Y3YWP.js");
|
|
10022
10436
|
const sessionData = aggregateBySession(records);
|
|
10023
10437
|
const limited = sessionData.slice(0, limit);
|
|
10024
10438
|
if (globalOpts.json) {
|
|
@@ -10098,7 +10512,7 @@ function registerSessionCommand(usage) {
|
|
|
10098
10512
|
cwd,
|
|
10099
10513
|
globalOpts.includeClaudeSessions
|
|
10100
10514
|
);
|
|
10101
|
-
const { aggregateBySession } = await import("./dist-
|
|
10515
|
+
const { aggregateBySession } = await import("./dist-ZS3Y3YWP.js");
|
|
10102
10516
|
const sessionData = aggregateBySession(records);
|
|
10103
10517
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
10104
10518
|
if (!match) {
|
|
@@ -10133,7 +10547,7 @@ function registerLatestCommand(usage) {
|
|
|
10133
10547
|
}
|
|
10134
10548
|
return;
|
|
10135
10549
|
}
|
|
10136
|
-
const { aggregateBySession } = await import("./dist-
|
|
10550
|
+
const { aggregateBySession } = await import("./dist-ZS3Y3YWP.js");
|
|
10137
10551
|
const sessionData = aggregateBySession(records);
|
|
10138
10552
|
const latest = sessionData[0];
|
|
10139
10553
|
if (!latest) {
|
|
@@ -10159,7 +10573,7 @@ function registerLatestCommand(usage) {
|
|
|
10159
10573
|
});
|
|
10160
10574
|
}
|
|
10161
10575
|
function createUsageCommand() {
|
|
10162
|
-
const usage = new
|
|
10576
|
+
const usage = new Command91("usage").description("Token usage and cost tracking");
|
|
10163
10577
|
usage.option(
|
|
10164
10578
|
"--include-claude-sessions",
|
|
10165
10579
|
"Include Claude Code session data from ~/.claude/projects/"
|
|
@@ -10172,17 +10586,17 @@ function createUsageCommand() {
|
|
|
10172
10586
|
}
|
|
10173
10587
|
|
|
10174
10588
|
// src/commands/validate.ts
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
import { Command as
|
|
10178
|
-
import * as
|
|
10589
|
+
init_dist2();
|
|
10590
|
+
init_dist2();
|
|
10591
|
+
import { Command as Command92 } from "commander";
|
|
10592
|
+
import * as path67 from "path";
|
|
10179
10593
|
async function runValidate(options) {
|
|
10180
10594
|
const configResult = resolveConfig(options.configPath);
|
|
10181
10595
|
if (!configResult.ok) {
|
|
10182
10596
|
return configResult;
|
|
10183
10597
|
}
|
|
10184
10598
|
const config = configResult.value;
|
|
10185
|
-
const cwd = options.cwd ?? (options.configPath ?
|
|
10599
|
+
const cwd = options.cwd ?? (options.configPath ? path67.dirname(path67.resolve(options.configPath)) : process.cwd());
|
|
10186
10600
|
const result = {
|
|
10187
10601
|
valid: true,
|
|
10188
10602
|
checks: {
|
|
@@ -10192,7 +10606,7 @@ async function runValidate(options) {
|
|
|
10192
10606
|
},
|
|
10193
10607
|
issues: []
|
|
10194
10608
|
};
|
|
10195
|
-
const agentsMapPath =
|
|
10609
|
+
const agentsMapPath = path67.resolve(cwd, config.agentsMapPath);
|
|
10196
10610
|
const agentsResult = await validateAgentsMap(agentsMapPath);
|
|
10197
10611
|
if (agentsResult.ok) {
|
|
10198
10612
|
result.checks.agentsMap = true;
|
|
@@ -10256,6 +10670,23 @@ async function runValidate(options) {
|
|
|
10256
10670
|
result.issues.push({ check: "solutionsDir", file: issue.file, message: issue.message });
|
|
10257
10671
|
}
|
|
10258
10672
|
}
|
|
10673
|
+
const roadmapModeResult = validateRoadmapMode(config, cwd);
|
|
10674
|
+
if (roadmapModeResult.ok) {
|
|
10675
|
+
result.checks.roadmapMode = true;
|
|
10676
|
+
} else {
|
|
10677
|
+
result.valid = false;
|
|
10678
|
+
result.checks.roadmapMode = false;
|
|
10679
|
+
result.issues.push({
|
|
10680
|
+
check: "roadmapMode",
|
|
10681
|
+
file: "harness.config.json",
|
|
10682
|
+
ruleId: roadmapModeResult.error.code,
|
|
10683
|
+
severity: "error",
|
|
10684
|
+
message: roadmapModeResult.error.message,
|
|
10685
|
+
...roadmapModeResult.error.suggestions?.[0] !== void 0 && {
|
|
10686
|
+
suggestion: roadmapModeResult.error.suggestions[0]
|
|
10687
|
+
}
|
|
10688
|
+
});
|
|
10689
|
+
}
|
|
10259
10690
|
if (options.agentConfigs) {
|
|
10260
10691
|
const agentCfg = await validateAgentConfigs(cwd, {
|
|
10261
10692
|
strict: options.strict === true,
|
|
@@ -10285,11 +10716,11 @@ function resolveValidateMode(globalOpts) {
|
|
|
10285
10716
|
return OutputMode.TEXT;
|
|
10286
10717
|
}
|
|
10287
10718
|
async function printCrossCheckWarnings(mode) {
|
|
10288
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
10719
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-F5YQVXLT.js");
|
|
10289
10720
|
const cwd = process.cwd();
|
|
10290
10721
|
const crossResult = await runCrossCheck2({
|
|
10291
|
-
specsDir:
|
|
10292
|
-
plansDir:
|
|
10722
|
+
specsDir: path67.join(cwd, "docs", "specs"),
|
|
10723
|
+
plansDir: path67.join(cwd, "docs", "plans"),
|
|
10293
10724
|
projectPath: cwd
|
|
10294
10725
|
});
|
|
10295
10726
|
if (!crossResult.ok || crossResult.value.warnings === 0) return;
|
|
@@ -10301,7 +10732,7 @@ async function printCrossCheckWarnings(mode) {
|
|
|
10301
10732
|
${crossResult.value.warnings} warnings`);
|
|
10302
10733
|
}
|
|
10303
10734
|
function createValidateCommand3() {
|
|
10304
|
-
const command = new
|
|
10735
|
+
const command = new Command92("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
|
|
10305
10736
|
"--agent-configs",
|
|
10306
10737
|
"Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
|
|
10307
10738
|
).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()));
|
|
@@ -10394,6 +10825,7 @@ var commandCreators = [
|
|
|
10394
10825
|
createPulseCommand,
|
|
10395
10826
|
createQueryCommand,
|
|
10396
10827
|
createRecommendCommand,
|
|
10828
|
+
createRoadmapCommand,
|
|
10397
10829
|
createScanCommand,
|
|
10398
10830
|
createScanConfigCommand,
|
|
10399
10831
|
createSetupCommand,
|
|
@@ -10403,6 +10835,7 @@ var commandCreators = [
|
|
|
10403
10835
|
createSnapshotCommand,
|
|
10404
10836
|
createStateCommand,
|
|
10405
10837
|
createSyncAnalysesCommand,
|
|
10838
|
+
createSyncMainCommand,
|
|
10406
10839
|
createTaintCommand,
|
|
10407
10840
|
createTelemetryCommand,
|
|
10408
10841
|
createTraceabilityCommand,
|
|
@@ -10415,7 +10848,7 @@ var commandCreators = [
|
|
|
10415
10848
|
|
|
10416
10849
|
// src/index.ts
|
|
10417
10850
|
function createProgram() {
|
|
10418
|
-
const program = new
|
|
10851
|
+
const program = new Command93();
|
|
10419
10852
|
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");
|
|
10420
10853
|
for (const creator of commandCreators) {
|
|
10421
10854
|
program.addCommand(creator());
|