@harness-engineering/cli 6.0.1 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agents-md-OTKZE5HH.js → agents-md-XBJ6YOSW.js} +2 -2
- package/dist/{architecture-3VYDN7MQ.js → architecture-D7PWJDXA.js} +3 -3
- package/dist/{assess-project-27X2FSZZ.js → assess-project-ENSWO4AX.js} +1 -1
- package/dist/bin/harness-mcp.js +13 -13
- package/dist/bin/harness.js +23 -23
- package/dist/{check-phase-gate-IXLFZ6YH.js → check-phase-gate-GTYAJ2YL.js} +3 -3
- package/dist/{chunk-QHFXVVUY.js → chunk-54RE35Q7.js} +2 -2
- package/dist/{chunk-XNHKXELN.js → chunk-5VP4ZQ3F.js} +1 -1
- package/dist/{chunk-CTMHHMHQ.js → chunk-6SP665CR.js} +89 -2
- package/dist/{chunk-VRA64HYI.js → chunk-7IVIRKHD.js} +1 -1
- package/dist/{chunk-3H7JA24B.js → chunk-CAGNARCD.js} +6 -6
- package/dist/{chunk-BMNHLKBA.js → chunk-GUL75WAM.js} +2 -2
- package/dist/{chunk-IXGYIQSR.js → chunk-HQUQA5D7.js} +1 -1
- package/dist/{chunk-354MCPT3.js → chunk-I6YMJ7ZD.js} +3 -3
- package/dist/{chunk-IOJGLIEQ.js → chunk-ILKL5I4J.js} +17 -2
- package/dist/{chunk-D7QXGI6E.js → chunk-INT5MUQX.js} +1 -1
- package/dist/{chunk-VGDZAWCJ.js → chunk-MSJJU6KH.js} +1022 -377
- package/dist/{chunk-6Y6M3AZA.js → chunk-N3S6JTRS.js} +2 -2
- package/dist/{chunk-L5UKNGC5.js → chunk-NXTNLNIP.js} +2 -2
- package/dist/{chunk-EFWRZHZ7.js → chunk-P7DXQAKU.js} +82 -82
- package/dist/{chunk-MEIUXTFY.js → chunk-PQBSP5AO.js} +1 -1
- package/dist/{chunk-TM4KFUWP.js → chunk-PYDUECJ3.js} +1 -1
- package/dist/{chunk-LISEPDVT.js → chunk-QB67FMZZ.js} +4 -4
- package/dist/{chunk-WAFTJFT3.js → chunk-TNR4BUWB.js} +3 -3
- package/dist/{chunk-KPBGAB35.js → chunk-UFZ6FMFC.js} +1 -1
- package/dist/{chunk-S2ETJDQJ.js → chunk-XRPHOCKU.js} +6 -6
- package/dist/{chunk-XF6RE3JQ.js → chunk-XYP5CIEN.js} +1 -1
- package/dist/{chunk-JJLOWMGC.js → chunk-YQK3GRW7.js} +1 -1
- package/dist/{chunk-3MKQKNY3.js → chunk-ZGK3JT4S.js} +5 -5
- package/dist/{ci-workflow-Y5GBKNZP.js → ci-workflow-ZODLVH62.js} +2 -2
- package/dist/{dist-4CZLCE33.js → dist-DTXVNQAY.js} +1 -1
- package/dist/{docs-3MWDPK4S.js → docs-ZU2HJCKW.js} +3 -3
- package/dist/{engine-RJXQ5DHK.js → engine-2DAJGPFZ.js} +2 -2
- package/dist/{entropy-OX5LSM4S.js → entropy-6JK3TCWY.js} +3 -3
- package/dist/{feedback-OODAP2G2.js → feedback-GEVH6CY3.js} +1 -1
- package/dist/{generate-agent-definitions-CCREQZEA.js → generate-agent-definitions-4DULEX3H.js} +3 -3
- package/dist/index.d.ts +90 -0
- package/dist/index.js +23 -23
- package/dist/{loader-ZICOJBVX.js → loader-K3A4YKWA.js} +2 -2
- package/dist/{mcp-XX6EIBLL.js → mcp-5MEIUQLV.js} +13 -13
- package/dist/{performance-OLZVGMAY.js → performance-HUXOQW7Q.js} +3 -3
- package/dist/{review-pipeline-3QPHMWPC.js → review-pipeline-QEYZXEMW.js} +2 -2
- package/dist/{runtime-ZFFIH76Z.js → runtime-45A4EGZD.js} +2 -2
- package/dist/{security-7YW3UK2C.js → security-CLZZRKND.js} +1 -1
- package/dist/{state-events-PTWAEGJ2.js → state-events-YLQEYOLI.js} +2 -2
- package/dist/{validate-H5SB7X3Z.js → validate-35GZKAOF.js} +3 -3
- package/dist/{validate-cross-check-YYVYMSPA.js → validate-cross-check-QJGCDWRO.js} +2 -2
- package/package.json +8 -8
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-PYDUECJ3.js";
|
|
4
4
|
import {
|
|
5
5
|
generateAgentsMd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-7IVIRKHD.js";
|
|
7
7
|
import {
|
|
8
8
|
generateCIWorkflow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-XYP5CIEN.js";
|
|
10
10
|
import {
|
|
11
11
|
readHarnessState
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-PQBSP5AO.js";
|
|
13
13
|
import {
|
|
14
14
|
runCrossCheck
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-INT5MUQX.js";
|
|
16
16
|
import {
|
|
17
17
|
createGenerateAgentDefinitionsCommand,
|
|
18
18
|
generateAgentDefinitions
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-HQUQA5D7.js";
|
|
20
20
|
import {
|
|
21
21
|
toKebabCase
|
|
22
22
|
} from "./chunk-KET4QQZB.js";
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from "./chunk-YYFSFSB3.js";
|
|
27
27
|
import {
|
|
28
28
|
TemplateEngine
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-YQK3GRW7.js";
|
|
30
30
|
import {
|
|
31
31
|
generate,
|
|
32
32
|
validate
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
import {
|
|
38
38
|
listPersonas,
|
|
39
39
|
loadPersona
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-UFZ6FMFC.js";
|
|
41
41
|
import {
|
|
42
42
|
runPersona
|
|
43
43
|
} from "./chunk-GISMXMVL.js";
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
OutputFormatter,
|
|
52
52
|
OutputMode,
|
|
53
53
|
createCheckPhaseGateCommand
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-N3S6JTRS.js";
|
|
55
55
|
import {
|
|
56
56
|
appendFrameworkAgents,
|
|
57
57
|
captureHealthSnapshot,
|
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
runSecurityCraft,
|
|
80
80
|
runSpecCraft,
|
|
81
81
|
runTestCraft
|
|
82
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-P7DXQAKU.js";
|
|
83
83
|
import {
|
|
84
84
|
VALID_PLATFORMS
|
|
85
85
|
} from "./chunk-3ISINLYT.js";
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
BranchingConfigSchema,
|
|
109
109
|
findConfigFile,
|
|
110
110
|
resolveConfig
|
|
111
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-ILKL5I4J.js";
|
|
112
112
|
import {
|
|
113
113
|
logger
|
|
114
114
|
} from "./chunk-EBJQ6N4M.js";
|
|
@@ -202,6 +202,7 @@ import {
|
|
|
202
202
|
requestPeerReview,
|
|
203
203
|
resolveConsent,
|
|
204
204
|
resolveRoadmapStore,
|
|
205
|
+
resolveRoadmapStoreForFile,
|
|
205
206
|
roadmapSourceExists,
|
|
206
207
|
roadmapToShards,
|
|
207
208
|
runAll,
|
|
@@ -226,7 +227,7 @@ import {
|
|
|
226
227
|
writeConfig,
|
|
227
228
|
writeLockfile,
|
|
228
229
|
writeRegeneratedRoadmap
|
|
229
|
-
} from "./chunk-
|
|
230
|
+
} from "./chunk-6SP665CR.js";
|
|
230
231
|
import {
|
|
231
232
|
CLI_VERSION
|
|
232
233
|
} from "./chunk-BM3PWGXQ.js";
|
|
@@ -263,7 +264,7 @@ import {
|
|
|
263
264
|
} from "./chunk-KFQGP6VL.js";
|
|
264
265
|
|
|
265
266
|
// src/index.ts
|
|
266
|
-
import { Command as
|
|
267
|
+
import { Command as Command134 } from "commander";
|
|
267
268
|
|
|
268
269
|
// src/commands/add.ts
|
|
269
270
|
init_dist3();
|
|
@@ -454,7 +455,7 @@ function registerSkillsCommand(adoption) {
|
|
|
454
455
|
const globalOpts = cmd.optsWithGlobals();
|
|
455
456
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
456
457
|
const cwd = process.cwd();
|
|
457
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
458
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-DTXVNQAY.js");
|
|
458
459
|
const records = readAdoptionRecords(cwd);
|
|
459
460
|
if (records.length === 0) {
|
|
460
461
|
if (globalOpts.json) {
|
|
@@ -490,7 +491,7 @@ function registerRecentCommand(adoption) {
|
|
|
490
491
|
const globalOpts = cmd.optsWithGlobals();
|
|
491
492
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
492
493
|
const cwd = process.cwd();
|
|
493
|
-
const { readAdoptionRecords } = await import("./dist-
|
|
494
|
+
const { readAdoptionRecords } = await import("./dist-DTXVNQAY.js");
|
|
494
495
|
const records = readAdoptionRecords(cwd);
|
|
495
496
|
if (records.length === 0) {
|
|
496
497
|
if (globalOpts.json) {
|
|
@@ -568,7 +569,7 @@ function registerSkillCommand(adoption) {
|
|
|
568
569
|
adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
|
|
569
570
|
const globalOpts = cmd.optsWithGlobals();
|
|
570
571
|
const cwd = process.cwd();
|
|
571
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
572
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-DTXVNQAY.js");
|
|
572
573
|
const records = readAdoptionRecords(cwd);
|
|
573
574
|
const skillRecords = records.filter((r) => r.skill === name);
|
|
574
575
|
if (skillRecords.length === 0) {
|
|
@@ -602,7 +603,7 @@ function registerRetrospectiveCommand(adoption) {
|
|
|
602
603
|
adoption.command("retrospective").description("Generate a catalog retrospective from adoption telemetry").option("--inactive-days <n>", "Inactivity threshold in days (default: 90)", "90").option("--top <n>", "Rows per ranked section (default: 10)", "10").option("--out <path>", "Output file path (default: docs/retrospectives/YYYY-MM-DD.md)").option("--no-write", "Print the report to stdout instead of writing a file").action(async (opts, cmd) => {
|
|
603
604
|
const globalOpts = cmd.optsWithGlobals();
|
|
604
605
|
const cwd = process.cwd();
|
|
605
|
-
const { readAdoptionRecords, getCatalogRetrospectiveReport, renderRetrospectiveMarkdown } = await import("./dist-
|
|
606
|
+
const { readAdoptionRecords, getCatalogRetrospectiveReport, renderRetrospectiveMarkdown } = await import("./dist-DTXVNQAY.js");
|
|
606
607
|
const records = readAdoptionRecords(cwd);
|
|
607
608
|
const catalogSkills = discoverCatalogSkills(cwd);
|
|
608
609
|
const report = getCatalogRetrospectiveReport(records, {
|
|
@@ -1750,9 +1751,9 @@ ${name} (${count} finding${count === 1 ? "" : "s"})`);
|
|
|
1750
1751
|
}
|
|
1751
1752
|
function printAnatomyFindings(findings, verbose) {
|
|
1752
1753
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1753
|
-
for (const [file,
|
|
1754
|
+
for (const [file, fs52] of byFile) {
|
|
1754
1755
|
console.log(` ${file}`);
|
|
1755
|
-
for (const f of
|
|
1756
|
+
for (const f of fs52) {
|
|
1756
1757
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1757
1758
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1758
1759
|
if (verbose) {
|
|
@@ -1763,9 +1764,9 @@ function printAnatomyFindings(findings, verbose) {
|
|
|
1763
1764
|
}
|
|
1764
1765
|
function printCraftFindings(findings, verbose) {
|
|
1765
1766
|
const byFile = groupByFile(findings, (f) => f.target.file);
|
|
1766
|
-
for (const [file,
|
|
1767
|
+
for (const [file, fs52] of byFile) {
|
|
1767
1768
|
console.log(` ${file}`);
|
|
1768
|
-
for (const f of
|
|
1769
|
+
for (const f of fs52) {
|
|
1769
1770
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
1770
1771
|
const sev = craftTierToSeverity(f.tier);
|
|
1771
1772
|
console.log(
|
|
@@ -1779,9 +1780,9 @@ function printCraftFindings(findings, verbose) {
|
|
|
1779
1780
|
}
|
|
1780
1781
|
function printDriftFindings(findings, verbose) {
|
|
1781
1782
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1782
|
-
for (const [file,
|
|
1783
|
+
for (const [file, fs52] of byFile) {
|
|
1783
1784
|
console.log(` ${file}`);
|
|
1784
|
-
for (const f of
|
|
1785
|
+
for (const f of fs52) {
|
|
1785
1786
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1786
1787
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1787
1788
|
if (verbose) {
|
|
@@ -1792,9 +1793,9 @@ function printDriftFindings(findings, verbose) {
|
|
|
1792
1793
|
}
|
|
1793
1794
|
function printBrandFindings(findings, verbose) {
|
|
1794
1795
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1795
|
-
for (const [file,
|
|
1796
|
+
for (const [file, fs52] of byFile) {
|
|
1796
1797
|
console.log(` ${file}`);
|
|
1797
|
-
for (const f of
|
|
1798
|
+
for (const f of fs52) {
|
|
1798
1799
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1799
1800
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1800
1801
|
if (verbose) {
|
|
@@ -2203,10 +2204,10 @@ async function runCheckSecurity(cwd, options) {
|
|
|
2203
2204
|
const projectRoot3 = path11.resolve(cwd);
|
|
2204
2205
|
let configData = {};
|
|
2205
2206
|
try {
|
|
2206
|
-
const
|
|
2207
|
+
const fs52 = await import("fs");
|
|
2207
2208
|
const configPath = path11.join(projectRoot3, "harness.config.json");
|
|
2208
|
-
if (
|
|
2209
|
-
const raw =
|
|
2209
|
+
if (fs52.existsSync(configPath)) {
|
|
2210
|
+
const raw = fs52.readFileSync(configPath, "utf-8");
|
|
2210
2211
|
const parsed = JSON.parse(raw);
|
|
2211
2212
|
configData = parsed.security ?? {};
|
|
2212
2213
|
}
|
|
@@ -3239,10 +3240,10 @@ function printResult2(result, mode, _formatter) {
|
|
|
3239
3240
|
list.push(f);
|
|
3240
3241
|
bySurface.set(f.target.surface, list);
|
|
3241
3242
|
}
|
|
3242
|
-
for (const [surface,
|
|
3243
|
+
for (const [surface, fs52] of bySurface) {
|
|
3243
3244
|
console.log(`
|
|
3244
3245
|
[${surface}]`);
|
|
3245
|
-
for (const f of
|
|
3246
|
+
for (const f of fs52) {
|
|
3246
3247
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
3247
3248
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.file}${line}`);
|
|
3248
3249
|
console.log(` "${truncate(f.target.snippet, 80)}"`);
|
|
@@ -4548,11 +4549,11 @@ import { Command as Command37 } from "commander";
|
|
|
4548
4549
|
// src/commands/graph/status.ts
|
|
4549
4550
|
import * as path19 from "path";
|
|
4550
4551
|
async function readConnectorSyncStatus(graphDir) {
|
|
4551
|
-
const
|
|
4552
|
+
const fs52 = await import("fs/promises");
|
|
4552
4553
|
const result = {};
|
|
4553
4554
|
try {
|
|
4554
4555
|
const syncMetaPath = path19.join(graphDir, "sync-metadata.json");
|
|
4555
|
-
const syncMeta = JSON.parse(await
|
|
4556
|
+
const syncMeta = JSON.parse(await fs52.readFile(syncMetaPath, "utf-8"));
|
|
4556
4557
|
for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
|
|
4557
4558
|
result[name] = data.lastSyncTimestamp;
|
|
4558
4559
|
}
|
|
@@ -4699,9 +4700,9 @@ import { Command as Command36 } from "commander";
|
|
|
4699
4700
|
import * as path22 from "path";
|
|
4700
4701
|
async function loadConnectorConfig(projectPath, source) {
|
|
4701
4702
|
try {
|
|
4702
|
-
const
|
|
4703
|
+
const fs52 = await import("fs/promises");
|
|
4703
4704
|
const configPath = path22.join(projectPath, "harness.config.json");
|
|
4704
|
-
const config = JSON.parse(await
|
|
4705
|
+
const config = JSON.parse(await fs52.readFile(configPath, "utf-8"));
|
|
4705
4706
|
const connector = config.graph?.connectors?.[source];
|
|
4706
4707
|
return connector ?? {};
|
|
4707
4708
|
} catch {
|
|
@@ -6203,7 +6204,7 @@ function createInsightsCommand() {
|
|
|
6203
6204
|
return new Command46("insights").description("Composite project report \u2014 health, entropy, decay, attention, impact.").option("--json", "Emit JSON to stdout instead of pretty text").option("--skip <list>", `Comma-separated keys to skip (${INSIGHTS_KEYS.join(",")})`).action(async (opts) => {
|
|
6204
6205
|
const cwd = process.cwd();
|
|
6205
6206
|
const skip = parseSkip2(opts.skip);
|
|
6206
|
-
const { composeInsights } = await import("./dist-
|
|
6207
|
+
const { composeInsights } = await import("./dist-DTXVNQAY.js");
|
|
6207
6208
|
const report = await composeInsights(cwd, { skip });
|
|
6208
6209
|
if (opts.json) {
|
|
6209
6210
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -7375,10 +7376,10 @@ function printResult3(result, mode, _formatter) {
|
|
|
7375
7376
|
list.push(f);
|
|
7376
7377
|
byFile.set(f.target.file, list);
|
|
7377
7378
|
}
|
|
7378
|
-
for (const [file,
|
|
7379
|
+
for (const [file, fs52] of byFile) {
|
|
7379
7380
|
console.log(`
|
|
7380
7381
|
${file}`);
|
|
7381
|
-
for (const f of
|
|
7382
|
+
for (const f of fs52) {
|
|
7382
7383
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.relative}`);
|
|
7383
7384
|
console.log(` ${f.message}`);
|
|
7384
7385
|
if (verbose) console.log(` source: ${f.cite.source}`);
|
|
@@ -7561,8 +7562,8 @@ function createKnowledgePipelineCommand() {
|
|
|
7561
7562
|
const projectDir = process.cwd();
|
|
7562
7563
|
try {
|
|
7563
7564
|
const graphDir = path42.join(projectDir, ".harness", "graph");
|
|
7564
|
-
const
|
|
7565
|
-
await
|
|
7565
|
+
const fs52 = await import("fs/promises");
|
|
7566
|
+
await fs52.mkdir(graphDir, { recursive: true });
|
|
7566
7567
|
const { GraphStore: GraphStore2, KnowledgePipelineRunner } = await import("./dist-DWBTZXR5.js");
|
|
7567
7568
|
const store = new GraphStore2();
|
|
7568
7569
|
try {
|
|
@@ -8314,7 +8315,7 @@ function createMcpCommand() {
|
|
|
8314
8315
|
parseBudget
|
|
8315
8316
|
).action(async (opts) => {
|
|
8316
8317
|
const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
|
|
8317
|
-
import("./mcp-
|
|
8318
|
+
import("./mcp-5MEIUQLV.js"),
|
|
8318
8319
|
import("./tool-tiers-OKK5FE57.js")
|
|
8319
8320
|
]);
|
|
8320
8321
|
if (opts.tools && opts.tools.length > 0) {
|
|
@@ -9395,10 +9396,10 @@ function printResult4(result, mode, _formatter) {
|
|
|
9395
9396
|
list.push(f);
|
|
9396
9397
|
byFile.set(f.target.file, list);
|
|
9397
9398
|
}
|
|
9398
|
-
for (const [file,
|
|
9399
|
+
for (const [file, fs52] of byFile) {
|
|
9399
9400
|
console.log(`
|
|
9400
9401
|
${file}`);
|
|
9401
|
-
for (const f of
|
|
9402
|
+
for (const f of fs52) {
|
|
9402
9403
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
9403
9404
|
console.log(
|
|
9404
9405
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.kind} ${f.target.identifier}${line}`
|
|
@@ -9550,7 +9551,7 @@ function registerBenchCommand(perf) {
|
|
|
9550
9551
|
perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
|
|
9551
9552
|
const globalOpts = cmd.optsWithGlobals();
|
|
9552
9553
|
const cwd = process.cwd();
|
|
9553
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
9554
|
+
const { BenchmarkRunner } = await import("./dist-DTXVNQAY.js");
|
|
9554
9555
|
const runner = new BenchmarkRunner();
|
|
9555
9556
|
const benchFiles = runner.discover(cwd, glob2);
|
|
9556
9557
|
if (benchFiles.length === 0) {
|
|
@@ -9614,7 +9615,7 @@ async function runBaselinesShow(globalOpts) {
|
|
|
9614
9615
|
}
|
|
9615
9616
|
async function runBaselinesUpdate(globalOpts) {
|
|
9616
9617
|
const cwd = process.cwd();
|
|
9617
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
9618
|
+
const { BenchmarkRunner } = await import("./dist-DTXVNQAY.js");
|
|
9618
9619
|
const runner = new BenchmarkRunner();
|
|
9619
9620
|
const manager = new BaselineManager(cwd);
|
|
9620
9621
|
logger.info("Running benchmarks to update baselines...");
|
|
@@ -9655,7 +9656,7 @@ function registerReportCommand(perf) {
|
|
|
9655
9656
|
perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
|
|
9656
9657
|
const globalOpts = cmd.optsWithGlobals();
|
|
9657
9658
|
const cwd = process.cwd();
|
|
9658
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-
|
|
9659
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-DTXVNQAY.js");
|
|
9659
9660
|
const analyzer = new EntropyAnalyzer2({
|
|
9660
9661
|
rootDir: path51.resolve(cwd),
|
|
9661
9662
|
analyze: { complexity: true, coupling: true }
|
|
@@ -10323,11 +10324,11 @@ var defaultPostBrief = (body) => {
|
|
|
10323
10324
|
}
|
|
10324
10325
|
);
|
|
10325
10326
|
};
|
|
10326
|
-
var defaultReadFile = (
|
|
10327
|
-
function readReview(
|
|
10328
|
-
if (!
|
|
10327
|
+
var defaultReadFile = (path82) => readFileSync18(path82, "utf-8");
|
|
10328
|
+
function readReview(path82, readFile4 = defaultReadFile) {
|
|
10329
|
+
if (!path82) return void 0;
|
|
10329
10330
|
try {
|
|
10330
|
-
const parsed = JSON.parse(readFile4(
|
|
10331
|
+
const parsed = JSON.parse(readFile4(path82));
|
|
10331
10332
|
return parsed?.verdict && typeof parsed.verdict === "object" ? parsed.verdict : void 0;
|
|
10332
10333
|
} catch {
|
|
10333
10334
|
return void 0;
|
|
@@ -10678,7 +10679,7 @@ async function runPublishAnalyses(opts) {
|
|
|
10678
10679
|
const nameToExternalId = await buildNameToExternalIdMap(projectPath);
|
|
10679
10680
|
if (!nameToExternalId) return process.exit(1);
|
|
10680
10681
|
const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
|
|
10681
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
10682
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-DTXVNQAY.js");
|
|
10682
10683
|
const archive = new AnalysisArchive(path53.join(projectPath, ".harness", "analyses"));
|
|
10683
10684
|
const analyses = await archive.list();
|
|
10684
10685
|
if (analyses.length === 0) {
|
|
@@ -10918,7 +10919,7 @@ function createRecommendCommand() {
|
|
|
10918
10919
|
}
|
|
10919
10920
|
|
|
10920
10921
|
// src/commands/roadmap/index.ts
|
|
10921
|
-
import { Command as
|
|
10922
|
+
import { Command as Command90 } from "commander";
|
|
10922
10923
|
|
|
10923
10924
|
// src/commands/roadmap/migrate.ts
|
|
10924
10925
|
init_dist3();
|
|
@@ -11374,17 +11375,17 @@ import * as path56 from "path";
|
|
|
11374
11375
|
import * as fsp from "fs/promises";
|
|
11375
11376
|
function createNodeShardIO() {
|
|
11376
11377
|
return {
|
|
11377
|
-
readFile: (
|
|
11378
|
-
writeFile: (
|
|
11378
|
+
readFile: (path82) => fsp.readFile(path82, "utf-8"),
|
|
11379
|
+
writeFile: (path82, data) => fsp.writeFile(path82, data, "utf-8"),
|
|
11379
11380
|
listDir: (dir) => fsp.readdir(dir),
|
|
11380
|
-
deleteFile: (
|
|
11381
|
+
deleteFile: (path82) => fsp.rm(path82),
|
|
11381
11382
|
mkdirp: async (dir) => {
|
|
11382
11383
|
await fsp.mkdir(dir, { recursive: true });
|
|
11383
11384
|
},
|
|
11384
|
-
rmrf: (
|
|
11385
|
-
exists: async (
|
|
11385
|
+
rmrf: (path82) => fsp.rm(path82, { recursive: true, force: true }),
|
|
11386
|
+
exists: async (path82) => {
|
|
11386
11387
|
try {
|
|
11387
|
-
await fsp.access(
|
|
11388
|
+
await fsp.access(path82);
|
|
11388
11389
|
return true;
|
|
11389
11390
|
} catch {
|
|
11390
11391
|
return false;
|
|
@@ -11769,22 +11770,658 @@ function createRoadmapReferencedIssuesCommand() {
|
|
|
11769
11770
|
});
|
|
11770
11771
|
}
|
|
11771
11772
|
|
|
11773
|
+
// src/commands/roadmap/triage.ts
|
|
11774
|
+
init_dist3();
|
|
11775
|
+
import { Command as Command89 } from "commander";
|
|
11776
|
+
import * as fs36 from "fs";
|
|
11777
|
+
import * as path60 from "path";
|
|
11778
|
+
import {
|
|
11779
|
+
triageIssue,
|
|
11780
|
+
rankTriageCandidates,
|
|
11781
|
+
runBrainstormForIssue,
|
|
11782
|
+
markApprovedForDispatch,
|
|
11783
|
+
precedentLookupFromStored
|
|
11784
|
+
} from "@harness-engineering/orchestrator";
|
|
11785
|
+
|
|
11786
|
+
// src/commands/roadmap/triage-provider.ts
|
|
11787
|
+
import {
|
|
11788
|
+
AnthropicAnalysisProvider,
|
|
11789
|
+
OpenAICompatibleAnalysisProvider as OpenAICompatibleAnalysisProvider2
|
|
11790
|
+
} from "@harness-engineering/intelligence";
|
|
11791
|
+
function firstModel(model) {
|
|
11792
|
+
if (Array.isArray(model)) return model[0];
|
|
11793
|
+
return model;
|
|
11794
|
+
}
|
|
11795
|
+
function resolveTriageProvider(config, modelOverride) {
|
|
11796
|
+
const explicit = config?.intelligence?.provider;
|
|
11797
|
+
if (explicit) {
|
|
11798
|
+
if (explicit.kind === "anthropic") {
|
|
11799
|
+
const apiKey = explicit.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
11800
|
+
if (!apiKey) return null;
|
|
11801
|
+
const model2 = modelOverride ?? explicit.model;
|
|
11802
|
+
return new AnthropicAnalysisProvider(
|
|
11803
|
+
model2 !== void 0 ? { apiKey, defaultModel: model2 } : { apiKey }
|
|
11804
|
+
);
|
|
11805
|
+
}
|
|
11806
|
+
const baseUrl = explicit.baseUrl ?? "http://localhost:11434/v1";
|
|
11807
|
+
const model = modelOverride ?? explicit.model;
|
|
11808
|
+
return new OpenAICompatibleAnalysisProvider2({
|
|
11809
|
+
apiKey: explicit.apiKey ?? "ollama",
|
|
11810
|
+
baseUrl,
|
|
11811
|
+
...model !== void 0 && { defaultModel: model }
|
|
11812
|
+
});
|
|
11813
|
+
}
|
|
11814
|
+
const backends = config?.agent?.backends;
|
|
11815
|
+
if (!backends) return null;
|
|
11816
|
+
const local = pickBackend(backends, ["local", "pi"]);
|
|
11817
|
+
if (local && local.endpoint) {
|
|
11818
|
+
const model = modelOverride ?? firstModel(local.model);
|
|
11819
|
+
return new OpenAICompatibleAnalysisProvider2({
|
|
11820
|
+
apiKey: local.apiKey ?? "ollama",
|
|
11821
|
+
baseUrl: local.endpoint,
|
|
11822
|
+
...model !== void 0 && { defaultModel: model },
|
|
11823
|
+
...local.timeoutMs !== void 0 && { timeoutMs: local.timeoutMs }
|
|
11824
|
+
});
|
|
11825
|
+
}
|
|
11826
|
+
const anthropic = pickBackend(backends, ["anthropic"]);
|
|
11827
|
+
if (anthropic) {
|
|
11828
|
+
const apiKey = anthropic.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
11829
|
+
if (!apiKey) return null;
|
|
11830
|
+
const model = modelOverride ?? firstModel(anthropic.model);
|
|
11831
|
+
return new AnthropicAnalysisProvider(
|
|
11832
|
+
model !== void 0 ? { apiKey, defaultModel: model } : { apiKey }
|
|
11833
|
+
);
|
|
11834
|
+
}
|
|
11835
|
+
return null;
|
|
11836
|
+
}
|
|
11837
|
+
function pickBackend(backends, types) {
|
|
11838
|
+
for (const def of Object.values(backends)) {
|
|
11839
|
+
if (def.type !== void 0 && types.includes(def.type)) return def;
|
|
11840
|
+
}
|
|
11841
|
+
return null;
|
|
11842
|
+
}
|
|
11843
|
+
|
|
11844
|
+
// src/commands/roadmap/triage-approve.ts
|
|
11845
|
+
import {
|
|
11846
|
+
detectScopeTier,
|
|
11847
|
+
artifactPresenceFromIssue
|
|
11848
|
+
} from "@harness-engineering/orchestrator";
|
|
11849
|
+
import {
|
|
11850
|
+
resolveGoNoGoStaged,
|
|
11851
|
+
resolveStage,
|
|
11852
|
+
dispatchableShapeKey,
|
|
11853
|
+
V1_MAX_STAGE
|
|
11854
|
+
} from "@harness-engineering/intelligence";
|
|
11855
|
+
|
|
11856
|
+
// src/commands/roadmap/triage-feature.ts
|
|
11857
|
+
function isActionable(feature) {
|
|
11858
|
+
return feature.status === "planned" || feature.status === "backlog";
|
|
11859
|
+
}
|
|
11860
|
+
function featureToIssue(feature) {
|
|
11861
|
+
return {
|
|
11862
|
+
id: feature.name,
|
|
11863
|
+
identifier: feature.name,
|
|
11864
|
+
title: feature.name,
|
|
11865
|
+
description: feature.summary,
|
|
11866
|
+
priority: null,
|
|
11867
|
+
state: feature.status,
|
|
11868
|
+
branchName: null,
|
|
11869
|
+
url: null,
|
|
11870
|
+
labels: [],
|
|
11871
|
+
blockedBy: feature.blockedBy.map((b) => ({ id: b, identifier: b, state: null })),
|
|
11872
|
+
spec: feature.spec,
|
|
11873
|
+
plans: feature.plans,
|
|
11874
|
+
createdAt: null,
|
|
11875
|
+
updatedAt: feature.updatedAt ?? null,
|
|
11876
|
+
externalId: feature.externalId ?? null,
|
|
11877
|
+
assignee: feature.assignee ?? null
|
|
11878
|
+
};
|
|
11879
|
+
}
|
|
11880
|
+
|
|
11881
|
+
// src/commands/roadmap/triage-approve.ts
|
|
11882
|
+
function deriveReadyCandidates(rows, roadmap) {
|
|
11883
|
+
const featureByExternalId = new Map(
|
|
11884
|
+
roadmap.milestones.flatMap((m) => m.features).filter((f) => f.externalId != null).map((f) => [f.externalId, f])
|
|
11885
|
+
);
|
|
11886
|
+
const ready = [];
|
|
11887
|
+
for (const row of rows) {
|
|
11888
|
+
if (row.result.outcome.kind !== "completed") continue;
|
|
11889
|
+
if (!row.result.specPath) continue;
|
|
11890
|
+
const rescore = row.result.rescore;
|
|
11891
|
+
if (!rescore || !rescore.dispatchable) continue;
|
|
11892
|
+
const feature = featureByExternalId.get(row.externalId);
|
|
11893
|
+
if (!feature || !isActionable(feature)) continue;
|
|
11894
|
+
const issue = featureToIssue({ ...feature, spec: row.result.specPath });
|
|
11895
|
+
const category = detectScopeTier(issue, artifactPresenceFromIssue(issue));
|
|
11896
|
+
const v = rescore.verdict;
|
|
11897
|
+
ready.push({
|
|
11898
|
+
externalId: row.externalId,
|
|
11899
|
+
featureName: feature.name,
|
|
11900
|
+
specPath: row.result.specPath,
|
|
11901
|
+
category,
|
|
11902
|
+
level: v.level,
|
|
11903
|
+
confidence: v.confidence,
|
|
11904
|
+
signals: v.signals,
|
|
11905
|
+
source: v.source,
|
|
11906
|
+
// scopeEstimate: the resolved-entity blast radius from the scope lever, when present.
|
|
11907
|
+
scopeEstimate: scopeEstimateOf(rescore),
|
|
11908
|
+
// Roadmap features carry no labels today, so this is always `[]`. The shapeKey
|
|
11909
|
+
// buckets on empty labels for now (Phase-4 calibration note): if/when labels land,
|
|
11910
|
+
// this is the single place to feed them into the precedent/ratchet bucket.
|
|
11911
|
+
labels: []
|
|
11912
|
+
});
|
|
11913
|
+
}
|
|
11914
|
+
return ready;
|
|
11915
|
+
}
|
|
11916
|
+
function scopeEstimateOf(rescore) {
|
|
11917
|
+
const scope = rescore?.levers.scope.value;
|
|
11918
|
+
if (scope && scope !== "unknown") return scope.blastRadius;
|
|
11919
|
+
return 0;
|
|
11920
|
+
}
|
|
11921
|
+
function shapeKeyForCandidate(r) {
|
|
11922
|
+
return dispatchableShapeKey(r.labels, r.level);
|
|
11923
|
+
}
|
|
11924
|
+
function resolveEffectiveStage(history, ceiling) {
|
|
11925
|
+
const earned = resolveStage(history);
|
|
11926
|
+
return Math.min(earned, ceiling, V1_MAX_STAGE);
|
|
11927
|
+
}
|
|
11928
|
+
function buildApprovalPlan(ready, opts) {
|
|
11929
|
+
const historyForShape = opts.historyForShape ?? (() => []);
|
|
11930
|
+
const candidates = ready.map((r) => ({
|
|
11931
|
+
externalId: r.externalId,
|
|
11932
|
+
category: r.category,
|
|
11933
|
+
humanApproved: opts.approveAll === true || opts.approvedIds.has(r.externalId),
|
|
11934
|
+
// Per-shape evidence-derived stage, ceilinged by config + hard-capped at 2 (SC6).
|
|
11935
|
+
effectiveStage: resolveEffectiveStage(historyForShape(shapeKeyForCandidate(r)), opts.stage)
|
|
11936
|
+
}));
|
|
11937
|
+
const decision = resolveGoNoGoStaged(candidates);
|
|
11938
|
+
const byId = new Map(ready.map((r) => [r.externalId, r]));
|
|
11939
|
+
const toMark = decision.approved.map((c) => {
|
|
11940
|
+
const r = byId.get(c.externalId);
|
|
11941
|
+
return {
|
|
11942
|
+
candidate: c,
|
|
11943
|
+
featureName: r.featureName,
|
|
11944
|
+
specPath: r.specPath,
|
|
11945
|
+
labels: r.labels,
|
|
11946
|
+
verdict: { level: r.level, confidence: r.confidence, signals: r.signals, source: r.source },
|
|
11947
|
+
scopeEstimate: r.scopeEstimate,
|
|
11948
|
+
// The per-shape earned stage the marker stamps onto the prediction (SC6).
|
|
11949
|
+
effectiveStage: c.effectiveStage
|
|
11950
|
+
};
|
|
11951
|
+
});
|
|
11952
|
+
const held = decision.held.map((h) => ({ externalId: h.externalId, reason: h.reason }));
|
|
11953
|
+
return { toMark, held };
|
|
11954
|
+
}
|
|
11955
|
+
|
|
11956
|
+
// src/commands/roadmap/triage.ts
|
|
11957
|
+
var CONFIDENCE_SCORE = { low: 1, medium: 2, high: 3 };
|
|
11958
|
+
var EFFORT_BY_LEVEL = { trivial: 1, simple: 2, moderate: 3, complex: 4 };
|
|
11959
|
+
var IMPACT_BY_PRIORITY = { P0: 4, P1: 3, P2: 2, P3: 1 };
|
|
11960
|
+
function rankingInputs(feature, verdict) {
|
|
11961
|
+
return {
|
|
11962
|
+
impact: feature.priority ? IMPACT_BY_PRIORITY[feature.priority] ?? 2 : 2,
|
|
11963
|
+
confidence: CONFIDENCE_SCORE[verdict.verdict.confidence],
|
|
11964
|
+
effort: EFFORT_BY_LEVEL[verdict.verdict.level] ?? 3
|
|
11965
|
+
};
|
|
11966
|
+
}
|
|
11967
|
+
async function buildPrecedentLookup(cwd) {
|
|
11968
|
+
const loaded = await event_sourcing_exports.loadTriageRecords(cwd);
|
|
11969
|
+
if (!loaded.ok) return void 0;
|
|
11970
|
+
return precedentLookupFromStored(
|
|
11971
|
+
loaded.value.map((r) => ({
|
|
11972
|
+
shapeKey: r.shapeKey,
|
|
11973
|
+
...r.outcome ? { outcome: { matched: r.outcome.matched } } : {}
|
|
11974
|
+
}))
|
|
11975
|
+
);
|
|
11976
|
+
}
|
|
11977
|
+
async function buildShapeHistory(cwd) {
|
|
11978
|
+
const loaded = await event_sourcing_exports.loadTriageRecords(cwd);
|
|
11979
|
+
const byShape = /* @__PURE__ */ new Map();
|
|
11980
|
+
if (loaded.ok) {
|
|
11981
|
+
for (const rec of loaded.value) {
|
|
11982
|
+
if (!rec.outcome) continue;
|
|
11983
|
+
const list = byShape.get(rec.shapeKey) ?? [];
|
|
11984
|
+
list.push({ matched: rec.outcome.matched, ts: rec.ts });
|
|
11985
|
+
byShape.set(rec.shapeKey, list);
|
|
11986
|
+
}
|
|
11987
|
+
}
|
|
11988
|
+
return (shapeKey) => byShape.get(shapeKey) ?? [];
|
|
11989
|
+
}
|
|
11990
|
+
function selectActionableFeatures(roadmap, opts = {}) {
|
|
11991
|
+
let features = roadmap.milestones.flatMap((m) => m.features).filter(isActionable);
|
|
11992
|
+
if (opts.only !== void 0 && opts.only.trim().length > 0) {
|
|
11993
|
+
const needle = opts.only.trim().toLowerCase();
|
|
11994
|
+
features = features.filter((f) => f.name.toLowerCase().includes(needle));
|
|
11995
|
+
}
|
|
11996
|
+
if (opts.limit !== void 0 && opts.limit >= 0) {
|
|
11997
|
+
features = features.slice(0, opts.limit);
|
|
11998
|
+
}
|
|
11999
|
+
return features;
|
|
12000
|
+
}
|
|
12001
|
+
function isPlausibleForModel(verdict) {
|
|
12002
|
+
const scope = verdict.levers.scope.value;
|
|
12003
|
+
const scopeResolvedBounded = scope !== "unknown" && scope.resolved.length > 0;
|
|
12004
|
+
const level = verdict.verdict.level;
|
|
12005
|
+
const inBand = level === "trivial" || level === "simple";
|
|
12006
|
+
return scopeResolvedBounded && inBand;
|
|
12007
|
+
}
|
|
12008
|
+
async function runTriageReport(roadmap, deps = {}) {
|
|
12009
|
+
const features = selectActionableFeatures(roadmap, {
|
|
12010
|
+
...deps.only !== void 0 ? { only: deps.only } : {},
|
|
12011
|
+
...deps.limit !== void 0 ? { limit: deps.limit } : {}
|
|
12012
|
+
});
|
|
12013
|
+
const cheapDeps = {
|
|
12014
|
+
...deps.graphStore ? { graphStore: deps.graphStore } : {},
|
|
12015
|
+
...deps.precedent ? { precedent: deps.precedent } : {},
|
|
12016
|
+
...deps.config ? { config: deps.config } : {}
|
|
12017
|
+
};
|
|
12018
|
+
const useModel = deps.offline !== true && deps.provider != null;
|
|
12019
|
+
const rows = [];
|
|
12020
|
+
for (const feature of features) {
|
|
12021
|
+
let verdict = await triageIssue(featureToIssue(feature), cheapDeps);
|
|
12022
|
+
if (useModel && isPlausibleForModel(verdict)) {
|
|
12023
|
+
deps.onProgress?.(`Refining "${feature.name}" on the local model\u2026`);
|
|
12024
|
+
verdict = await triageIssue(featureToIssue(feature), {
|
|
12025
|
+
...cheapDeps,
|
|
12026
|
+
...deps.provider ? { provider: deps.provider } : {}
|
|
12027
|
+
});
|
|
12028
|
+
}
|
|
12029
|
+
rows.push({
|
|
12030
|
+
externalId: verdict.externalId,
|
|
12031
|
+
name: feature.name,
|
|
12032
|
+
status: feature.status,
|
|
12033
|
+
...rankingInputs(feature, verdict),
|
|
12034
|
+
verdict
|
|
12035
|
+
});
|
|
12036
|
+
}
|
|
12037
|
+
return rankTriageCandidates(rows);
|
|
12038
|
+
}
|
|
12039
|
+
function renderHuman(rows) {
|
|
12040
|
+
if (rows.length === 0) return "No actionable roadmap items to triage.";
|
|
12041
|
+
const lines = [];
|
|
12042
|
+
lines.push(`Triaged ${rows.length} actionable item(s):
|
|
12043
|
+
`);
|
|
12044
|
+
for (const row of rows) {
|
|
12045
|
+
const v = row.verdict;
|
|
12046
|
+
const badge = v.dispatchable ? "\u2713 DISPATCHABLE" : `\u2717 HELD (${v.holdReason})`;
|
|
12047
|
+
lines.push(`${badge} ${row.name}`);
|
|
12048
|
+
lines.push(
|
|
12049
|
+
` level=${v.verdict.level} confidence=${v.verdict.confidence} impact=${row.impact} effort=${row.effort}`
|
|
12050
|
+
);
|
|
12051
|
+
lines.push(` ${v.rationale}`);
|
|
12052
|
+
lines.push("");
|
|
12053
|
+
}
|
|
12054
|
+
const dispatchable = rows.filter((r) => r.verdict.dispatchable).length;
|
|
12055
|
+
lines.push(
|
|
12056
|
+
`${dispatchable}/${rows.length} dispatchable; ${rows.length - dispatchable} to human.`
|
|
12057
|
+
);
|
|
12058
|
+
return lines.join("\n");
|
|
12059
|
+
}
|
|
12060
|
+
function renderJson(rows) {
|
|
12061
|
+
return JSON.stringify(
|
|
12062
|
+
{
|
|
12063
|
+
count: rows.length,
|
|
12064
|
+
dispatchable: rows.filter((r) => r.verdict.dispatchable).length,
|
|
12065
|
+
items: rows.map((r) => ({
|
|
12066
|
+
externalId: r.externalId,
|
|
12067
|
+
name: r.name,
|
|
12068
|
+
status: r.status,
|
|
12069
|
+
dispatchable: r.verdict.dispatchable,
|
|
12070
|
+
holdReason: r.verdict.holdReason ?? null,
|
|
12071
|
+
level: r.verdict.verdict.level,
|
|
12072
|
+
confidence: r.verdict.verdict.confidence,
|
|
12073
|
+
impact: r.impact,
|
|
12074
|
+
effort: r.effort,
|
|
12075
|
+
pilotInputs: { impact: r.impact, confidence: r.confidence, effort: r.effort },
|
|
12076
|
+
levers: r.verdict.levers,
|
|
12077
|
+
rationale: r.verdict.rationale
|
|
12078
|
+
}))
|
|
12079
|
+
},
|
|
12080
|
+
null,
|
|
12081
|
+
2
|
|
12082
|
+
);
|
|
12083
|
+
}
|
|
12084
|
+
function skippedBrainstormRow(feature, verdict, level) {
|
|
12085
|
+
return {
|
|
12086
|
+
externalId: verdict.externalId,
|
|
12087
|
+
name: feature.name,
|
|
12088
|
+
status: feature.status,
|
|
12089
|
+
level,
|
|
12090
|
+
result: {
|
|
12091
|
+
outcome: {
|
|
12092
|
+
kind: "halted",
|
|
12093
|
+
reason: "error",
|
|
12094
|
+
fork: { id: "scope-gate", question: "", options: [] },
|
|
12095
|
+
detail: `Skipped brainstorm \u2014 not a plausible candidate (${verdict.holdReason ?? "held"}). Held to human by the cheap scope/band gate.`
|
|
12096
|
+
}
|
|
12097
|
+
}
|
|
12098
|
+
};
|
|
12099
|
+
}
|
|
12100
|
+
async function runBrainstormReport(roadmap, deps = {}) {
|
|
12101
|
+
const features = selectActionableFeatures(roadmap, {
|
|
12102
|
+
...deps.only !== void 0 ? { only: deps.only } : {},
|
|
12103
|
+
...deps.limit !== void 0 ? { limit: deps.limit } : {}
|
|
12104
|
+
});
|
|
12105
|
+
const rows = [];
|
|
12106
|
+
for (const feature of features) {
|
|
12107
|
+
const issue = featureToIssue(feature);
|
|
12108
|
+
const verdict = await triageIssue(issue, {
|
|
12109
|
+
...deps.graphStore ? { graphStore: deps.graphStore } : {},
|
|
12110
|
+
...deps.provider ? { provider: deps.provider } : {},
|
|
12111
|
+
...deps.precedent ? { precedent: deps.precedent } : {},
|
|
12112
|
+
...deps.config ? { config: deps.config } : {}
|
|
12113
|
+
});
|
|
12114
|
+
const level = verdict.verdict.level;
|
|
12115
|
+
if (deps.gatePlausible === true && !isPlausibleForModel(verdict)) {
|
|
12116
|
+
rows.push(skippedBrainstormRow(feature, verdict, level));
|
|
12117
|
+
continue;
|
|
12118
|
+
}
|
|
12119
|
+
deps.onProgress?.(`Brainstorming "${feature.name}" [${level}]\u2026`);
|
|
12120
|
+
const wiringDeps = {
|
|
12121
|
+
...deps.generator ? { generator: deps.generator } : {},
|
|
12122
|
+
...deps.provider ? { provider: deps.provider } : {},
|
|
12123
|
+
...deps.generatorOptions ? { generatorOptions: deps.generatorOptions } : {},
|
|
12124
|
+
...deps.docsRoot ? { docsRoot: deps.docsRoot } : {},
|
|
12125
|
+
// Re-score the enriched item on completion (SC4), reusing the same seams — the
|
|
12126
|
+
// precedent lever included so the RE-SCORE (the prediction the marker records) is
|
|
12127
|
+
// graded against real base-rates too (SC5).
|
|
12128
|
+
rescore: {
|
|
12129
|
+
...deps.graphStore ? { graphStore: deps.graphStore } : {},
|
|
12130
|
+
...deps.provider ? { provider: deps.provider } : {},
|
|
12131
|
+
...deps.precedent ? { precedent: deps.precedent } : {},
|
|
12132
|
+
...deps.config ? { config: deps.config } : {}
|
|
12133
|
+
}
|
|
12134
|
+
};
|
|
12135
|
+
const result = await runBrainstormForIssue(issue, level, wiringDeps);
|
|
12136
|
+
rows.push({
|
|
12137
|
+
externalId: verdict.externalId,
|
|
12138
|
+
name: feature.name,
|
|
12139
|
+
status: feature.status,
|
|
12140
|
+
level,
|
|
12141
|
+
result
|
|
12142
|
+
});
|
|
12143
|
+
}
|
|
12144
|
+
return rows;
|
|
12145
|
+
}
|
|
12146
|
+
function renderBrainstormHuman(rows) {
|
|
12147
|
+
if (rows.length === 0) return "No actionable roadmap items to brainstorm.";
|
|
12148
|
+
const lines = [];
|
|
12149
|
+
lines.push(`Brainstormed ${rows.length} actionable item(s):
|
|
12150
|
+
`);
|
|
12151
|
+
for (const row of rows) {
|
|
12152
|
+
const o = row.result.outcome;
|
|
12153
|
+
if (o.kind === "completed") {
|
|
12154
|
+
const where = row.result.specPath ? ` \u2192 ${row.result.specPath}` : " (dry run \u2014 no docs root)";
|
|
12155
|
+
lines.push(`\u2713 SPEC DRAFTED ${row.name} [${row.level}]${where}`);
|
|
12156
|
+
lines.push(` ${o.spec.decisions.length} fork(s) resolved at high confidence`);
|
|
12157
|
+
if (row.result.rescore) {
|
|
12158
|
+
const rv = row.result.rescore;
|
|
12159
|
+
lines.push(
|
|
12160
|
+
` re-score: ${rv.dispatchable ? "DISPATCHABLE" : `held (${rv.holdReason})`} \u2014 ${rv.verdict.level}/${rv.verdict.confidence}`
|
|
12161
|
+
);
|
|
12162
|
+
}
|
|
12163
|
+
} else {
|
|
12164
|
+
lines.push(`\u2717 HALTED \u2192 HUMAN ${row.name} [${row.level}] (${o.reason})`);
|
|
12165
|
+
lines.push(` fork: ${o.fork.question || o.fork.id}`);
|
|
12166
|
+
lines.push(` ${o.detail}`);
|
|
12167
|
+
}
|
|
12168
|
+
lines.push("");
|
|
12169
|
+
}
|
|
12170
|
+
const completed = rows.filter((r) => r.result.outcome.kind === "completed").length;
|
|
12171
|
+
lines.push(
|
|
12172
|
+
`${completed}/${rows.length} spec(s) drafted; ${rows.length - completed} halted to human.`
|
|
12173
|
+
);
|
|
12174
|
+
return lines.join("\n");
|
|
12175
|
+
}
|
|
12176
|
+
function renderBrainstormJson(rows) {
|
|
12177
|
+
return JSON.stringify(
|
|
12178
|
+
{
|
|
12179
|
+
count: rows.length,
|
|
12180
|
+
drafted: rows.filter((r) => r.result.outcome.kind === "completed").length,
|
|
12181
|
+
items: rows.map((r) => {
|
|
12182
|
+
const o = r.result.outcome;
|
|
12183
|
+
return {
|
|
12184
|
+
externalId: r.externalId,
|
|
12185
|
+
name: r.name,
|
|
12186
|
+
status: r.status,
|
|
12187
|
+
level: r.level,
|
|
12188
|
+
outcome: o.kind,
|
|
12189
|
+
...o.kind === "completed" ? {
|
|
12190
|
+
specPath: r.result.specPath ?? null,
|
|
12191
|
+
forks: o.spec.decisions.map((d) => ({
|
|
12192
|
+
id: d.fork.id,
|
|
12193
|
+
question: d.fork.question,
|
|
12194
|
+
recommendation: d.recommendation,
|
|
12195
|
+
confidence: d.confidence
|
|
12196
|
+
})),
|
|
12197
|
+
rescore: r.result.rescore ? {
|
|
12198
|
+
dispatchable: r.result.rescore.dispatchable,
|
|
12199
|
+
holdReason: r.result.rescore.holdReason ?? null,
|
|
12200
|
+
level: r.result.rescore.verdict.level,
|
|
12201
|
+
confidence: r.result.rescore.verdict.confidence
|
|
12202
|
+
} : null
|
|
12203
|
+
} : {
|
|
12204
|
+
halt: {
|
|
12205
|
+
reason: o.reason,
|
|
12206
|
+
fork: { id: o.fork.id, question: o.fork.question },
|
|
12207
|
+
detail: o.detail
|
|
12208
|
+
}
|
|
12209
|
+
}
|
|
12210
|
+
};
|
|
12211
|
+
})
|
|
12212
|
+
},
|
|
12213
|
+
null,
|
|
12214
|
+
2
|
|
12215
|
+
);
|
|
12216
|
+
}
|
|
12217
|
+
function resolveBrainstormProvider(configPath, model) {
|
|
12218
|
+
const configResult = resolveConfig(configPath);
|
|
12219
|
+
if (!configResult.ok) return null;
|
|
12220
|
+
return resolveTriageProvider(configResult.value, model);
|
|
12221
|
+
}
|
|
12222
|
+
async function runApproveCommand(cwd, opts) {
|
|
12223
|
+
const configResult = resolveConfig(opts.config);
|
|
12224
|
+
const cfg = configResult.ok ? configResult.value : void 0;
|
|
12225
|
+
const autoTriage = cfg?.roadmap?.autoTriage;
|
|
12226
|
+
if (!autoTriage || autoTriage.enabled !== true) {
|
|
12227
|
+
return { marked: [], held: [], note: "disabled" };
|
|
12228
|
+
}
|
|
12229
|
+
const stage = autoTriage.ratchetStage ?? 1;
|
|
12230
|
+
const roadmapPath = path60.join(cwd, "docs", "roadmap.md");
|
|
12231
|
+
if (!fs36.existsSync(roadmapPath)) {
|
|
12232
|
+
return { marked: [], held: [], note: "no-roadmap" };
|
|
12233
|
+
}
|
|
12234
|
+
const parsed = parseRoadmap(fs36.readFileSync(roadmapPath, "utf-8"));
|
|
12235
|
+
if (!parsed.ok) return { marked: [], held: [], note: "roadmap-parse-error" };
|
|
12236
|
+
const graphStore = await loadGraphStore(cwd);
|
|
12237
|
+
const provider = resolveBrainstormProvider(opts.config);
|
|
12238
|
+
const precedent = await buildPrecedentLookup(cwd);
|
|
12239
|
+
const rows = await runBrainstormReport(parsed.value, {
|
|
12240
|
+
...graphStore ? { graphStore } : {},
|
|
12241
|
+
...provider ? { provider } : {},
|
|
12242
|
+
...precedent ? { precedent } : {},
|
|
12243
|
+
docsRoot: path60.join(cwd, "docs")
|
|
12244
|
+
});
|
|
12245
|
+
const ready = deriveReadyCandidates(rows, parsed.value);
|
|
12246
|
+
const approvedIds = new Set(
|
|
12247
|
+
(opts.approve ?? "").split(",").map((s) => s.trim()).filter((s) => s.length > 0)
|
|
12248
|
+
);
|
|
12249
|
+
const historyForShape = await buildShapeHistory(cwd);
|
|
12250
|
+
const plan = buildApprovalPlan(ready, {
|
|
12251
|
+
approvedIds,
|
|
12252
|
+
...opts.approveAll ? { approveAll: true } : {},
|
|
12253
|
+
stage,
|
|
12254
|
+
historyForShape
|
|
12255
|
+
});
|
|
12256
|
+
const store = resolveRoadmapStoreForFile({ roadmapPath });
|
|
12257
|
+
const recordPrediction = (payload) => event_sourcing_exports.recordTriagePrediction(cwd, payload);
|
|
12258
|
+
const result = await markApprovedForDispatch(plan.toMark, {
|
|
12259
|
+
store,
|
|
12260
|
+
config: { enabled: true, ratchetStage: stage },
|
|
12261
|
+
recordPrediction
|
|
12262
|
+
});
|
|
12263
|
+
if (!result.ok) {
|
|
12264
|
+
return { marked: [], held: plan.held, note: `mark-failed: ${result.error.message}` };
|
|
12265
|
+
}
|
|
12266
|
+
return { marked: result.value.marked, held: plan.held };
|
|
12267
|
+
}
|
|
12268
|
+
function renderApproveHuman(res) {
|
|
12269
|
+
if (res.note === "disabled") {
|
|
12270
|
+
return "Roadmap auto-triage is disabled (roadmap.autoTriage.enabled is not true). Enable it to use `triage approve`. No changes made.";
|
|
12271
|
+
}
|
|
12272
|
+
if (res.note === "no-roadmap") return "No roadmap aggregate at docs/roadmap.md. No changes made.";
|
|
12273
|
+
if (res.note === "roadmap-parse-error") return "Failed to parse roadmap. No changes made.";
|
|
12274
|
+
const lines = [];
|
|
12275
|
+
if (res.marked.length > 0) {
|
|
12276
|
+
lines.push(`Marked ${res.marked.length} item(s) eligible for orchestrator pickup:`);
|
|
12277
|
+
for (const id of res.marked) lines.push(` \u2713 ${id}`);
|
|
12278
|
+
} else {
|
|
12279
|
+
lines.push("No items marked.");
|
|
12280
|
+
}
|
|
12281
|
+
if (res.held.length > 0) {
|
|
12282
|
+
lines.push(`
|
|
12283
|
+
Held to human (${res.held.length}):`);
|
|
12284
|
+
for (const h of res.held) lines.push(` \u2717 ${h.externalId} \u2014 ${h.reason}`);
|
|
12285
|
+
}
|
|
12286
|
+
if (res.note && res.note.startsWith("mark-failed")) lines.push(`
|
|
12287
|
+
ERROR: ${res.note}`);
|
|
12288
|
+
lines.push(
|
|
12289
|
+
"\nApproved items are NOT dispatched here \u2014 the existing orchestrator pickup loop dispatches them through its normal gating on its next tick."
|
|
12290
|
+
);
|
|
12291
|
+
return lines.join("\n");
|
|
12292
|
+
}
|
|
12293
|
+
function createTriageApproveCommand() {
|
|
12294
|
+
return new Command89("approve").description(
|
|
12295
|
+
"Batched human go/no-go (autonomy ratchet stage 1): review READY candidates (Phase-2 spec-bearing, re-score dispatchable) and approve a subset. Approved + auto-executable items are MARKED eligible for the EXISTING orchestrator pickup \u2014 nothing is dispatched here, and nothing is marked without an explicit approval. Gated behind roadmap.autoTriage.enabled (default off)."
|
|
12296
|
+
).option(
|
|
12297
|
+
"--approve <ids>",
|
|
12298
|
+
"Comma-separated externalIds to approve (the explicit human go). Omit to mark nothing."
|
|
12299
|
+
).option("--approve-all", "Approve every READY candidate (still subject to the category gate).").action(async (opts, cmd) => {
|
|
12300
|
+
const cwd = process.cwd();
|
|
12301
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
12302
|
+
const res = await runApproveCommand(cwd, {
|
|
12303
|
+
...opts,
|
|
12304
|
+
...globalOpts.config !== void 0 ? { config: globalOpts.config } : {}
|
|
12305
|
+
});
|
|
12306
|
+
if (globalOpts.json) {
|
|
12307
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
12308
|
+
} else {
|
|
12309
|
+
logger.info(renderApproveHuman(res));
|
|
12310
|
+
}
|
|
12311
|
+
});
|
|
12312
|
+
}
|
|
12313
|
+
function createRoadmapTriageCommand() {
|
|
12314
|
+
return new Command89("triage").description(
|
|
12315
|
+
"Read-only triage report: score every actionable roadmap item with the four-lever scoping probe and rank dispatchability. Gated behind roadmap.autoTriage.enabled (default off); never writes. Offline by default (holds to human without a live model). With --brainstorm, additionally run the autonomous brainstorm per candidate, emitting a drafted spec (docs only) or a halt handoff (fork + reason) \u2014 still no dispatch."
|
|
12316
|
+
).option(
|
|
12317
|
+
"--brainstorm",
|
|
12318
|
+
"Run the autonomous brainstorm per candidate: draft a spec (docs only) or halt to a human at the first fork it can not confidently recommend. No dispatch, no execution."
|
|
12319
|
+
).option(
|
|
12320
|
+
"--only <substring>",
|
|
12321
|
+
'Process ONLY roadmap items whose title contains this substring (case-insensitive). Lets you triage/brainstorm a single item, e.g. --only "prefer-execfile".'
|
|
12322
|
+
).option(
|
|
12323
|
+
"--limit <n>",
|
|
12324
|
+
"Process at most N items (applied after --only). Useful for a quick partial scan.",
|
|
12325
|
+
(v) => Number.parseInt(v, 10)
|
|
12326
|
+
).option(
|
|
12327
|
+
"--offline",
|
|
12328
|
+
"Force the pure static path \u2014 never consult the local model. A fast scan that holds every item to a human (byte-identical to running without a resolvable model)."
|
|
12329
|
+
).action(
|
|
12330
|
+
async (opts, cmd) => {
|
|
12331
|
+
await runTriageCommandAction(opts, cmd);
|
|
12332
|
+
}
|
|
12333
|
+
).addCommand(createTriageApproveCommand());
|
|
12334
|
+
}
|
|
12335
|
+
async function runTriageCommandAction(opts, cmd) {
|
|
12336
|
+
const cwd = process.cwd();
|
|
12337
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
12338
|
+
const parsed = gateAndParseRoadmap(cwd, globalOpts.config);
|
|
12339
|
+
if (!parsed) return;
|
|
12340
|
+
const graphStore = await loadGraphStore(cwd);
|
|
12341
|
+
const precedent = await buildPrecedentLookup(cwd);
|
|
12342
|
+
const provider = opts.offline === true ? null : resolveBrainstormProvider(globalOpts.config);
|
|
12343
|
+
const onProgress = globalOpts.json === true ? void 0 : (message) => logger.info(message);
|
|
12344
|
+
const shared = {
|
|
12345
|
+
...graphStore ? { graphStore } : {},
|
|
12346
|
+
...provider ? { provider } : {},
|
|
12347
|
+
...precedent ? { precedent } : {},
|
|
12348
|
+
...opts.only !== void 0 ? { only: opts.only } : {},
|
|
12349
|
+
...typeof opts.limit === "number" && !Number.isNaN(opts.limit) ? { limit: opts.limit } : {},
|
|
12350
|
+
...onProgress ? { onProgress } : {}
|
|
12351
|
+
};
|
|
12352
|
+
if (opts.brainstorm) {
|
|
12353
|
+
const rows2 = await runBrainstormReport(parsed, {
|
|
12354
|
+
...shared,
|
|
12355
|
+
gatePlausible: true,
|
|
12356
|
+
docsRoot: path60.join(cwd, "docs")
|
|
12357
|
+
});
|
|
12358
|
+
emitReport(
|
|
12359
|
+
globalOpts.json === true,
|
|
12360
|
+
() => renderBrainstormJson(rows2),
|
|
12361
|
+
() => renderBrainstormHuman(rows2)
|
|
12362
|
+
);
|
|
12363
|
+
return;
|
|
12364
|
+
}
|
|
12365
|
+
const rows = await runTriageReport(parsed, {
|
|
12366
|
+
...shared,
|
|
12367
|
+
...opts.offline === true ? { offline: true } : {}
|
|
12368
|
+
});
|
|
12369
|
+
emitReport(
|
|
12370
|
+
globalOpts.json === true,
|
|
12371
|
+
() => renderJson(rows),
|
|
12372
|
+
() => renderHuman(rows)
|
|
12373
|
+
);
|
|
12374
|
+
}
|
|
12375
|
+
function gateAndParseRoadmap(cwd, configPath) {
|
|
12376
|
+
const configResult = resolveConfig(configPath);
|
|
12377
|
+
const enabled = configResult.ok && configResult.value.roadmap?.autoTriage?.enabled === true;
|
|
12378
|
+
if (!enabled) {
|
|
12379
|
+
logger.info(
|
|
12380
|
+
"Roadmap auto-triage is disabled (roadmap.autoTriage.enabled is not true). Enable it in harness.config.json to run the read-only triage report. No changes made."
|
|
12381
|
+
);
|
|
12382
|
+
return null;
|
|
12383
|
+
}
|
|
12384
|
+
const roadmapPath = path60.join(cwd, "docs", "roadmap.md");
|
|
12385
|
+
if (!fs36.existsSync(roadmapPath)) {
|
|
12386
|
+
logger.error(
|
|
12387
|
+
`No roadmap aggregate at ${roadmapPath}. If your roadmap is sharded, run \`harness roadmap regen\` first, then re-run triage.`
|
|
12388
|
+
);
|
|
12389
|
+
process.exitCode = 1;
|
|
12390
|
+
return null;
|
|
12391
|
+
}
|
|
12392
|
+
const parsed = parseRoadmap(fs36.readFileSync(roadmapPath, "utf-8"));
|
|
12393
|
+
if (!parsed.ok) {
|
|
12394
|
+
logger.error(`Failed to parse roadmap: ${parsed.error.message}`);
|
|
12395
|
+
process.exitCode = 1;
|
|
12396
|
+
return null;
|
|
12397
|
+
}
|
|
12398
|
+
return parsed.value;
|
|
12399
|
+
}
|
|
12400
|
+
function emitReport(json, toJson, toHuman) {
|
|
12401
|
+
if (json) {
|
|
12402
|
+
process.stdout.write(toJson() + "\n");
|
|
12403
|
+
} else {
|
|
12404
|
+
logger.info(toHuman());
|
|
12405
|
+
}
|
|
12406
|
+
}
|
|
12407
|
+
|
|
11772
12408
|
// src/commands/roadmap/index.ts
|
|
11773
12409
|
function createRoadmapCommand() {
|
|
11774
|
-
const roadmap = new
|
|
12410
|
+
const roadmap = new Command90("roadmap").description("Roadmap management");
|
|
11775
12411
|
roadmap.addCommand(createRoadmapMigrateCommand());
|
|
11776
12412
|
roadmap.addCommand(createRoadmapShardCommand());
|
|
11777
12413
|
roadmap.addCommand(createRoadmapUnshardCommand());
|
|
11778
12414
|
roadmap.addCommand(createRoadmapRegenCommand());
|
|
11779
12415
|
roadmap.addCommand(createRoadmapReconcileCommand());
|
|
11780
12416
|
roadmap.addCommand(createRoadmapReferencedIssuesCommand());
|
|
12417
|
+
roadmap.addCommand(createRoadmapTriageCommand());
|
|
11781
12418
|
return roadmap;
|
|
11782
12419
|
}
|
|
11783
12420
|
|
|
11784
12421
|
// src/commands/rollback.ts
|
|
11785
12422
|
init_dist3();
|
|
11786
12423
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
11787
|
-
import { Command as
|
|
12424
|
+
import { Command as Command91, Option as Option3, InvalidArgumentError } from "commander";
|
|
11788
12425
|
|
|
11789
12426
|
// src/rollback/compose.ts
|
|
11790
12427
|
var ROLLBACK_LABEL = "harness:rollback";
|
|
@@ -11842,12 +12479,12 @@ async function composeRevertPr(decision, originalTitle, opts) {
|
|
|
11842
12479
|
|
|
11843
12480
|
// src/rollback/breadcrumb.ts
|
|
11844
12481
|
import { appendFileSync, mkdirSync as mkdirSync19 } from "fs";
|
|
11845
|
-
import { dirname as dirname20, join as
|
|
11846
|
-
var ROLLBACK_EVENTS_FILE =
|
|
12482
|
+
import { dirname as dirname20, join as join51 } from "path";
|
|
12483
|
+
var ROLLBACK_EVENTS_FILE = join51(".harness", "signals", "rollback-events.jsonl");
|
|
11847
12484
|
async function appendRollbackEvent(event, opts = {}) {
|
|
11848
12485
|
const root = opts.root ?? process.cwd();
|
|
11849
12486
|
const ts = (opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))();
|
|
11850
|
-
const file =
|
|
12487
|
+
const file = join51(root, ROLLBACK_EVENTS_FILE);
|
|
11851
12488
|
mkdirSync19(dirname20(file), { recursive: true });
|
|
11852
12489
|
appendFileSync(file, `${JSON.stringify({ ...event, ts })}
|
|
11853
12490
|
`, "utf-8");
|
|
@@ -11874,7 +12511,7 @@ async function linkRollbackEventToGraph(event, opts = {}) {
|
|
|
11874
12511
|
if (target) {
|
|
11875
12512
|
store.addEdge({ from: nodeId, to: target.id, type: "references" });
|
|
11876
12513
|
}
|
|
11877
|
-
await store.save(
|
|
12514
|
+
await store.save(join51(root, ".harness", "graph"));
|
|
11878
12515
|
} catch {
|
|
11879
12516
|
}
|
|
11880
12517
|
}
|
|
@@ -11906,8 +12543,8 @@ function parseConflictPaths(stdout) {
|
|
|
11906
12543
|
if (line === void 0 || line.trim() === "") break;
|
|
11907
12544
|
const tab = line.indexOf(" ");
|
|
11908
12545
|
if (tab === -1) continue;
|
|
11909
|
-
const
|
|
11910
|
-
if (
|
|
12546
|
+
const path82 = line.slice(tab + 1).trim();
|
|
12547
|
+
if (path82) paths.add(path82);
|
|
11911
12548
|
}
|
|
11912
12549
|
return [...paths];
|
|
11913
12550
|
}
|
|
@@ -12153,7 +12790,7 @@ async function runRollbackSweepCommand(deps) {
|
|
|
12153
12790
|
});
|
|
12154
12791
|
}
|
|
12155
12792
|
function createRollbackCommand() {
|
|
12156
|
-
const rollback = new
|
|
12793
|
+
const rollback = new Command91("rollback").description(
|
|
12157
12794
|
"Post-ship revert circuit breaker (propose-only in v1)"
|
|
12158
12795
|
);
|
|
12159
12796
|
rollback.command("evaluate").description("Classify a merged PR for revert-readiness and, if ready, propose a revert PR").requiredOption("--pr <n>", "target merged PR number", (v) => {
|
|
@@ -12206,10 +12843,10 @@ function createRollbackCommand() {
|
|
|
12206
12843
|
}
|
|
12207
12844
|
|
|
12208
12845
|
// src/commands/routing/index.ts
|
|
12209
|
-
import { Command as
|
|
12846
|
+
import { Command as Command97 } from "commander";
|
|
12210
12847
|
|
|
12211
12848
|
// src/commands/routing/config.ts
|
|
12212
|
-
import { Command as
|
|
12849
|
+
import { Command as Command92 } from "commander";
|
|
12213
12850
|
|
|
12214
12851
|
// src/commands/routing/http-client.ts
|
|
12215
12852
|
function orchestratorBase() {
|
|
@@ -12219,9 +12856,9 @@ function authHeader() {
|
|
|
12219
12856
|
const tok = process.env["HARNESS_API_TOKEN"];
|
|
12220
12857
|
return tok ? { Authorization: `Bearer ${tok}` } : {};
|
|
12221
12858
|
}
|
|
12222
|
-
async function getJson(
|
|
12859
|
+
async function getJson(path82) {
|
|
12223
12860
|
try {
|
|
12224
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
12861
|
+
const res = await fetch(`${orchestratorBase()}${path82}`, {
|
|
12225
12862
|
headers: { ...authHeader() }
|
|
12226
12863
|
});
|
|
12227
12864
|
const text = await res.text();
|
|
@@ -12236,9 +12873,9 @@ async function getJson(path81) {
|
|
|
12236
12873
|
};
|
|
12237
12874
|
}
|
|
12238
12875
|
}
|
|
12239
|
-
async function postJson(
|
|
12876
|
+
async function postJson(path82, body) {
|
|
12240
12877
|
try {
|
|
12241
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
12878
|
+
const res = await fetch(`${orchestratorBase()}${path82}`, {
|
|
12242
12879
|
method: "POST",
|
|
12243
12880
|
headers: { "Content-Type": "application/json", ...authHeader() },
|
|
12244
12881
|
body: JSON.stringify(body)
|
|
@@ -12257,7 +12894,7 @@ async function postJson(path81, body) {
|
|
|
12257
12894
|
}
|
|
12258
12895
|
|
|
12259
12896
|
// src/commands/routing/config.ts
|
|
12260
|
-
function
|
|
12897
|
+
function renderHuman2(data) {
|
|
12261
12898
|
console.log("Backends:");
|
|
12262
12899
|
for (const b of data.backends) console.log(` - ${b}`);
|
|
12263
12900
|
console.log("");
|
|
@@ -12274,7 +12911,7 @@ function renderHuman(data) {
|
|
|
12274
12911
|
}
|
|
12275
12912
|
}
|
|
12276
12913
|
function createConfigCommand() {
|
|
12277
|
-
return new
|
|
12914
|
+
return new Command92("config").description("Print active routing config and resolved fallback chains").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
12278
12915
|
const r = await getJson("/api/v1/routing/config");
|
|
12279
12916
|
if (!r.ok) {
|
|
12280
12917
|
if (r.status === 0) {
|
|
@@ -12295,12 +12932,12 @@ function createConfigCommand() {
|
|
|
12295
12932
|
console.log(JSON.stringify(r.body, null, 2));
|
|
12296
12933
|
return;
|
|
12297
12934
|
}
|
|
12298
|
-
if (r.body)
|
|
12935
|
+
if (r.body) renderHuman2(r.body);
|
|
12299
12936
|
});
|
|
12300
12937
|
}
|
|
12301
12938
|
|
|
12302
12939
|
// src/commands/routing/trace.ts
|
|
12303
|
-
import { Command as
|
|
12940
|
+
import { Command as Command93 } from "commander";
|
|
12304
12941
|
var COMPLEXITY_VALUES = ["trivial", "simple", "moderate", "complex"];
|
|
12305
12942
|
var RISK_VALUES = ["low", "high"];
|
|
12306
12943
|
function validateEnum(flag, value, allowed) {
|
|
@@ -12314,20 +12951,28 @@ function buildUseCase(opts) {
|
|
|
12314
12951
|
if (opts.mode) return { kind: "mode", cognitiveMode: opts.mode };
|
|
12315
12952
|
return null;
|
|
12316
12953
|
}
|
|
12317
|
-
function
|
|
12318
|
-
|
|
12954
|
+
function renderHuman3(r) {
|
|
12955
|
+
const amr = r.tierRequired !== void 0 && r.costedBackendName !== void 0;
|
|
12956
|
+
const backend = amr ? r.costedBackendName : r.decision.backendName;
|
|
12957
|
+
const type = amr && r.costedBackendType !== void 0 ? r.costedBackendType : r.def.type;
|
|
12958
|
+
console.log(`Backend: ${backend} (type: ${type})`);
|
|
12319
12959
|
console.log(`Duration: ${r.decision.durationMs.toFixed(2)} ms`);
|
|
12320
12960
|
console.log("Resolution path:");
|
|
12321
12961
|
if (r.decision.resolutionPath.length === 0) {
|
|
12322
12962
|
console.log(" (empty)");
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12963
|
+
} else {
|
|
12964
|
+
for (const step of r.decision.resolutionPath) {
|
|
12965
|
+
console.log(` ${step.source}:${step.candidate} -> ${step.outcome}`);
|
|
12966
|
+
}
|
|
12327
12967
|
}
|
|
12328
12968
|
if (r.tierRequired !== void 0) {
|
|
12329
12969
|
console.log(`Tier required: ${r.tierRequired}`);
|
|
12330
12970
|
}
|
|
12971
|
+
if (amr && r.costedBackendName !== r.decision.backendName) {
|
|
12972
|
+
console.log(
|
|
12973
|
+
` (AMR overrides the identity pick '${r.decision.backendName}' \u2192 cheapest backend at/above the tier)`
|
|
12974
|
+
);
|
|
12975
|
+
}
|
|
12331
12976
|
if (r.estCostUsd !== void 0) {
|
|
12332
12977
|
console.log(`Est cost: $${r.estCostUsd.toFixed(4)}`);
|
|
12333
12978
|
}
|
|
@@ -12368,17 +13013,17 @@ async function runTrace(opts) {
|
|
|
12368
13013
|
console.log(JSON.stringify(r.body, null, 2));
|
|
12369
13014
|
return;
|
|
12370
13015
|
}
|
|
12371
|
-
if (r.body)
|
|
13016
|
+
if (r.body) renderHuman3(r.body);
|
|
12372
13017
|
}
|
|
12373
13018
|
function createTraceCommand() {
|
|
12374
|
-
return new
|
|
13019
|
+
return new Command93("trace").description("Dry-run a routing decision without dispatching (Spec B F7)").option("--skill <name>", "Skill name to trace").option("--mode <m>", "Cognitive mode to trace (or attach to --skill per spec D12)").option(
|
|
12375
13020
|
"--complexity <level>",
|
|
12376
13021
|
"Synthetic complexity (trivial|simple|moderate|complex) \u2014 dry-run only"
|
|
12377
13022
|
).option("--risk <band>", "Synthetic risk band (low|high) \u2014 dry-run only").option("--json", "Emit JSON to stdout instead of human-readable text").action(runTrace);
|
|
12378
13023
|
}
|
|
12379
13024
|
|
|
12380
13025
|
// src/commands/routing/decisions.ts
|
|
12381
|
-
import { Command as
|
|
13026
|
+
import { Command as Command94 } from "commander";
|
|
12382
13027
|
function summarizeUseCase(uc) {
|
|
12383
13028
|
switch (uc.kind) {
|
|
12384
13029
|
case "skill":
|
|
@@ -12402,7 +13047,7 @@ function shortIso(iso) {
|
|
|
12402
13047
|
const tail = parts[1] ?? iso;
|
|
12403
13048
|
return tail.replace("Z", "");
|
|
12404
13049
|
}
|
|
12405
|
-
function
|
|
13050
|
+
function renderHuman4(data) {
|
|
12406
13051
|
if (data.decisions.length === 0) {
|
|
12407
13052
|
console.log("(no decisions in buffer)");
|
|
12408
13053
|
return;
|
|
@@ -12426,7 +13071,7 @@ function buildQuery(opts) {
|
|
|
12426
13071
|
return q ? `?${q}` : "";
|
|
12427
13072
|
}
|
|
12428
13073
|
function createDecisionsCommand() {
|
|
12429
|
-
return new
|
|
13074
|
+
return new Command94("decisions").description("List recent routing decisions from the orchestrator ring buffer (Spec B F8)").option("--skill <name>", "Filter by useCase.skillName").option("--mode <m>", "Filter by useCase.cognitiveMode").option("--backend <name>", "Filter by chosen backendName").option("--last <N>", "Limit to the N most recent decisions").option("--json", "Emit JSON to stdout instead of human-readable text").action(
|
|
12430
13075
|
async (opts) => {
|
|
12431
13076
|
const query = buildQuery(opts);
|
|
12432
13077
|
const r = await getJson(`/api/v1/routing/decisions${query}`);
|
|
@@ -12449,19 +13094,19 @@ function createDecisionsCommand() {
|
|
|
12449
13094
|
console.log(JSON.stringify(r.body, null, 2));
|
|
12450
13095
|
return;
|
|
12451
13096
|
}
|
|
12452
|
-
if (r.body)
|
|
13097
|
+
if (r.body) renderHuman4(r.body);
|
|
12453
13098
|
}
|
|
12454
13099
|
);
|
|
12455
13100
|
}
|
|
12456
13101
|
|
|
12457
13102
|
// src/commands/routing/telemetry.ts
|
|
12458
|
-
import { Command as
|
|
13103
|
+
import { Command as Command95 } from "commander";
|
|
12459
13104
|
var TIERS = ["fast", "standard", "strong"];
|
|
12460
13105
|
function shortIso2(iso) {
|
|
12461
13106
|
const tail = iso.split("T")[1] ?? iso;
|
|
12462
13107
|
return tail.replace("Z", "");
|
|
12463
13108
|
}
|
|
12464
|
-
function
|
|
13109
|
+
function renderHuman5(data, tableLimit) {
|
|
12465
13110
|
if (data.decisions.length === 0) {
|
|
12466
13111
|
console.log("(no routing telemetry \u2014 AMR is off or no decisions recorded yet)");
|
|
12467
13112
|
return;
|
|
@@ -12499,7 +13144,7 @@ function renderHuman4(data, tableLimit) {
|
|
|
12499
13144
|
}
|
|
12500
13145
|
}
|
|
12501
13146
|
function createTelemetryCommand() {
|
|
12502
|
-
return new
|
|
13147
|
+
return new Command95("telemetry").description("Routing telemetry: per-decision tier/backend/cost + tier distribution (AMR)").option("--last <N>", "Show only the N most recent decisions in the per-decision table").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
12503
13148
|
const r = await getJson("/api/v1/routing/telemetry");
|
|
12504
13149
|
if (!r.ok) {
|
|
12505
13150
|
if (r.status === 0) {
|
|
@@ -12518,18 +13163,18 @@ function createTelemetryCommand() {
|
|
|
12518
13163
|
return;
|
|
12519
13164
|
}
|
|
12520
13165
|
const tableLimit = opts.last && Number.isFinite(Number(opts.last)) && Number(opts.last) > 0 ? Math.floor(Number(opts.last)) : void 0;
|
|
12521
|
-
|
|
13166
|
+
renderHuman5(body, tableLimit);
|
|
12522
13167
|
});
|
|
12523
13168
|
}
|
|
12524
13169
|
|
|
12525
13170
|
// src/commands/routing/status.ts
|
|
12526
|
-
import { Command as
|
|
13171
|
+
import { Command as Command96 } from "commander";
|
|
12527
13172
|
function bar(pct, width = 24) {
|
|
12528
13173
|
const clamped = Math.max(0, Math.min(100, pct));
|
|
12529
13174
|
const filled = Math.round(clamped / 100 * width);
|
|
12530
13175
|
return `[${"#".repeat(filled)}${"-".repeat(width - filled)}]`;
|
|
12531
13176
|
}
|
|
12532
|
-
function
|
|
13177
|
+
function renderHuman6(s) {
|
|
12533
13178
|
if (!s.active) {
|
|
12534
13179
|
console.log("Adaptive routing: OFF (no routing.policy configured \u2014 dispatch is unchanged)");
|
|
12535
13180
|
return;
|
|
@@ -12563,7 +13208,7 @@ function renderHuman5(s) {
|
|
|
12563
13208
|
);
|
|
12564
13209
|
}
|
|
12565
13210
|
function createStatusCommand() {
|
|
12566
|
-
return new
|
|
13211
|
+
return new Command96("status").description(
|
|
12567
13212
|
"Live routing status: budget spend-vs-cap, escalated units, provider allowlist (AMR)"
|
|
12568
13213
|
).option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
12569
13214
|
const r = await getJson("/api/v1/routing/status");
|
|
@@ -12588,13 +13233,13 @@ function createStatusCommand() {
|
|
|
12588
13233
|
console.log(JSON.stringify(body, null, 2));
|
|
12589
13234
|
return;
|
|
12590
13235
|
}
|
|
12591
|
-
|
|
13236
|
+
renderHuman6(body);
|
|
12592
13237
|
});
|
|
12593
13238
|
}
|
|
12594
13239
|
|
|
12595
13240
|
// src/commands/routing/index.ts
|
|
12596
13241
|
function createRoutingCommand() {
|
|
12597
|
-
const cmd = new
|
|
13242
|
+
const cmd = new Command97("routing").description(
|
|
12598
13243
|
"Inspect routing config, trace decisions, and read AMR telemetry + status"
|
|
12599
13244
|
);
|
|
12600
13245
|
cmd.addCommand(createConfigCommand());
|
|
@@ -12607,9 +13252,9 @@ function createRoutingCommand() {
|
|
|
12607
13252
|
|
|
12608
13253
|
// src/commands/scan-config.ts
|
|
12609
13254
|
init_dist3();
|
|
12610
|
-
import { Command as
|
|
12611
|
-
import { existsSync as
|
|
12612
|
-
import { join as
|
|
13255
|
+
import { Command as Command98 } from "commander";
|
|
13256
|
+
import { existsSync as existsSync32, readFileSync as readFileSync23, writeFileSync as writeFileSync23 } from "fs";
|
|
13257
|
+
import { join as join52, relative as relative4 } from "path";
|
|
12613
13258
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
12614
13259
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
12615
13260
|
const highLines = /* @__PURE__ */ new Set();
|
|
@@ -12642,10 +13287,10 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
|
|
|
12642
13287
|
}
|
|
12643
13288
|
}
|
|
12644
13289
|
function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
12645
|
-
if (!
|
|
13290
|
+
if (!existsSync32(filePath)) return null;
|
|
12646
13291
|
let content;
|
|
12647
13292
|
try {
|
|
12648
|
-
content =
|
|
13293
|
+
content = readFileSync23(filePath, "utf8");
|
|
12649
13294
|
} catch {
|
|
12650
13295
|
return null;
|
|
12651
13296
|
}
|
|
@@ -12666,7 +13311,7 @@ async function runScanConfig(targetDir, options) {
|
|
|
12666
13311
|
const scanner = new SecurityScanner(parseSecurityConfig({}));
|
|
12667
13312
|
const results = [];
|
|
12668
13313
|
for (const configFile of CONFIG_FILES) {
|
|
12669
|
-
const result = scanSingleFile(
|
|
13314
|
+
const result = scanSingleFile(join52(targetDir, configFile), targetDir, scanner, options);
|
|
12670
13315
|
if (result) results.push(result);
|
|
12671
13316
|
}
|
|
12672
13317
|
return { exitCode: computeScanExitCode(results), results };
|
|
@@ -12696,7 +13341,7 @@ function formatTextOutput(result) {
|
|
|
12696
13341
|
}
|
|
12697
13342
|
}
|
|
12698
13343
|
function createScanConfigCommand() {
|
|
12699
|
-
const command = new
|
|
13344
|
+
const command = new Command98("scan-config").description(
|
|
12700
13345
|
"Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
|
|
12701
13346
|
).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
|
|
12702
13347
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -12714,7 +13359,7 @@ function createScanConfigCommand() {
|
|
|
12714
13359
|
|
|
12715
13360
|
// src/commands/search.ts
|
|
12716
13361
|
init_dist();
|
|
12717
|
-
import { Command as
|
|
13362
|
+
import { Command as Command99 } from "commander";
|
|
12718
13363
|
function parseFileKinds(raw) {
|
|
12719
13364
|
if (!raw) return void 0;
|
|
12720
13365
|
const set = /* @__PURE__ */ new Set();
|
|
@@ -12735,7 +13380,7 @@ function fmtBytes(n) {
|
|
|
12735
13380
|
return n.toFixed(0);
|
|
12736
13381
|
}
|
|
12737
13382
|
function createSearchCommand() {
|
|
12738
|
-
return new
|
|
13383
|
+
return new Command99("search").description("Full-text search over archived + live session content.").argument(
|
|
12739
13384
|
"<query>",
|
|
12740
13385
|
"FTS5 query (bare words AND-joined; quotes/AND/OR/NOT/column: for advanced syntax)"
|
|
12741
13386
|
).option("-n, --limit <n>", "Max results", "20").option("--archived-only", "Skip live sessions, only search archived ones").option("--json", "Emit JSON to stdout instead of pretty text").option(
|
|
@@ -12795,9 +13440,9 @@ function createSearchCommand() {
|
|
|
12795
13440
|
}
|
|
12796
13441
|
|
|
12797
13442
|
// src/commands/security-craft.ts
|
|
12798
|
-
import { Command as
|
|
13443
|
+
import { Command as Command100 } from "commander";
|
|
12799
13444
|
function createSecurityCraftCommand() {
|
|
12800
|
-
return new
|
|
13445
|
+
return new Command100("security-craft").description(
|
|
12801
13446
|
"LLM-judgment critique of security posture (TS/JS source). Sixth non-design craft-pipeline ceiling skill (the final sub-project). AST-driven signal detection fires only on files with security-relevant constructs; conservative confidence defaults manage the FP risk inherent in judgment-based security."
|
|
12802
13447
|
).option("-f, --files <files...>", "Optional file scope (overrides discovery)").option("-p, --packages <names...>", "Restrict to specific packages under packages/").option("--max-files <n>", "Cap source-file count (default: 100)").option("--max-signals-per-file <n>", "Cap per-file signal critique (default: 10)").action(async (opts, cmd) => {
|
|
12803
13448
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -12844,10 +13489,10 @@ function printResult5(result, mode, _formatter) {
|
|
|
12844
13489
|
list.push(f);
|
|
12845
13490
|
byFile.set(f.target.file, list);
|
|
12846
13491
|
}
|
|
12847
|
-
for (const [file,
|
|
13492
|
+
for (const [file, fs52] of byFile) {
|
|
12848
13493
|
console.log(`
|
|
12849
13494
|
${file}`);
|
|
12850
|
-
for (const f of
|
|
13495
|
+
for (const f of fs52) {
|
|
12851
13496
|
console.log(
|
|
12852
13497
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.signal}:${f.target.line}`
|
|
12853
13498
|
);
|
|
@@ -12863,24 +13508,24 @@ ${file}`);
|
|
|
12863
13508
|
}
|
|
12864
13509
|
|
|
12865
13510
|
// src/commands/setup.ts
|
|
12866
|
-
import { Command as
|
|
12867
|
-
import * as
|
|
13511
|
+
import { Command as Command101 } from "commander";
|
|
13512
|
+
import * as fs39 from "fs";
|
|
12868
13513
|
import * as os9 from "os";
|
|
12869
|
-
import * as
|
|
13514
|
+
import * as path63 from "path";
|
|
12870
13515
|
import chalk16 from "chalk";
|
|
12871
13516
|
|
|
12872
13517
|
// src/utils/first-run.ts
|
|
12873
|
-
import * as
|
|
13518
|
+
import * as fs37 from "fs";
|
|
12874
13519
|
import * as os8 from "os";
|
|
12875
|
-
import * as
|
|
12876
|
-
var HARNESS_DIR =
|
|
12877
|
-
var MARKER_FILE =
|
|
13520
|
+
import * as path61 from "path";
|
|
13521
|
+
var HARNESS_DIR = path61.join(os8.homedir(), ".harness");
|
|
13522
|
+
var MARKER_FILE = path61.join(HARNESS_DIR, ".setup-complete");
|
|
12878
13523
|
function isFirstRun() {
|
|
12879
|
-
return !
|
|
13524
|
+
return !fs37.existsSync(MARKER_FILE);
|
|
12880
13525
|
}
|
|
12881
13526
|
function markSetupComplete() {
|
|
12882
|
-
|
|
12883
|
-
|
|
13527
|
+
fs37.mkdirSync(HARNESS_DIR, { recursive: true });
|
|
13528
|
+
fs37.writeFileSync(MARKER_FILE, "", "utf-8");
|
|
12884
13529
|
}
|
|
12885
13530
|
function printFirstRunWelcome() {
|
|
12886
13531
|
try {
|
|
@@ -12893,8 +13538,8 @@ function printFirstRunWelcome() {
|
|
|
12893
13538
|
}
|
|
12894
13539
|
|
|
12895
13540
|
// src/commands/telemetry-wizard.ts
|
|
12896
|
-
import * as
|
|
12897
|
-
import * as
|
|
13541
|
+
import * as fs38 from "fs";
|
|
13542
|
+
import * as path62 from "path";
|
|
12898
13543
|
import readline2 from "readline";
|
|
12899
13544
|
import chalk15 from "chalk";
|
|
12900
13545
|
function prompt2(question) {
|
|
@@ -12925,21 +13570,21 @@ function isNo(answer) {
|
|
|
12925
13570
|
return answer === "n" || answer === "no";
|
|
12926
13571
|
}
|
|
12927
13572
|
function isTelemetryConfigured(cwd) {
|
|
12928
|
-
const configPath =
|
|
13573
|
+
const configPath = path62.join(cwd, "harness.config.json");
|
|
12929
13574
|
try {
|
|
12930
|
-
const raw = JSON.parse(
|
|
13575
|
+
const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
|
|
12931
13576
|
return raw.telemetry !== void 0 && raw.adoption !== void 0;
|
|
12932
13577
|
} catch {
|
|
12933
13578
|
return false;
|
|
12934
13579
|
}
|
|
12935
13580
|
}
|
|
12936
13581
|
function writeTelemetryConfig(cwd, result) {
|
|
12937
|
-
const configPath =
|
|
13582
|
+
const configPath = path62.join(cwd, "harness.config.json");
|
|
12938
13583
|
try {
|
|
12939
|
-
const raw = JSON.parse(
|
|
13584
|
+
const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
|
|
12940
13585
|
raw.telemetry = { enabled: result.telemetryEnabled };
|
|
12941
13586
|
raw.adoption = { enabled: result.adoptionEnabled };
|
|
12942
|
-
|
|
13587
|
+
fs38.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
12943
13588
|
} catch (err) {
|
|
12944
13589
|
console.warn(
|
|
12945
13590
|
`\u26A0 Could not update ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -12947,10 +13592,10 @@ function writeTelemetryConfig(cwd, result) {
|
|
|
12947
13592
|
}
|
|
12948
13593
|
const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
|
|
12949
13594
|
if (hasIdentity) {
|
|
12950
|
-
const harnessDir =
|
|
12951
|
-
|
|
12952
|
-
const telemetryFile =
|
|
12953
|
-
|
|
13595
|
+
const harnessDir = path62.join(cwd, ".harness");
|
|
13596
|
+
fs38.mkdirSync(harnessDir, { recursive: true });
|
|
13597
|
+
const telemetryFile = path62.join(harnessDir, "telemetry.json");
|
|
13598
|
+
fs38.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
|
|
12954
13599
|
}
|
|
12955
13600
|
}
|
|
12956
13601
|
async function promptIdentity() {
|
|
@@ -12996,8 +13641,8 @@ async function runTelemetryWizard() {
|
|
|
12996
13641
|
return { telemetryEnabled, adoptionEnabled, identity };
|
|
12997
13642
|
}
|
|
12998
13643
|
async function ensureTelemetryConfigured(cwd) {
|
|
12999
|
-
const configPath =
|
|
13000
|
-
if (!
|
|
13644
|
+
const configPath = path62.join(cwd, "harness.config.json");
|
|
13645
|
+
if (!fs38.existsSync(configPath)) {
|
|
13001
13646
|
return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
|
|
13002
13647
|
}
|
|
13003
13648
|
if (isTelemetryConfigured(cwd)) {
|
|
@@ -13043,7 +13688,7 @@ function runSlashCommandGeneration() {
|
|
|
13043
13688
|
}
|
|
13044
13689
|
}
|
|
13045
13690
|
function detectClient(dirName) {
|
|
13046
|
-
return
|
|
13691
|
+
return fs39.existsSync(path63.join(os9.homedir(), dirName));
|
|
13047
13692
|
}
|
|
13048
13693
|
async function runMcpSetup(cwd) {
|
|
13049
13694
|
const results = [];
|
|
@@ -13059,7 +13704,7 @@ async function runMcpSetup(cwd) {
|
|
|
13059
13704
|
{ name: "Cursor", dir: ".cursor", client: "cursor", configTarget: ".cursor/mcp.json" },
|
|
13060
13705
|
{
|
|
13061
13706
|
name: "OpenCode",
|
|
13062
|
-
dir:
|
|
13707
|
+
dir: path63.join(".config", "opencode"),
|
|
13063
13708
|
client: "opencode",
|
|
13064
13709
|
configTarget: "opencode.json"
|
|
13065
13710
|
}
|
|
@@ -13091,7 +13736,7 @@ function formatStep(result) {
|
|
|
13091
13736
|
}
|
|
13092
13737
|
function configureTier0Integrations(cwd) {
|
|
13093
13738
|
try {
|
|
13094
|
-
const mcpPath =
|
|
13739
|
+
const mcpPath = path63.join(cwd, ".mcp.json");
|
|
13095
13740
|
const config = readMcpConfig(mcpPath);
|
|
13096
13741
|
const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
|
|
13097
13742
|
const added = [];
|
|
@@ -13100,9 +13745,9 @@ function configureTier0Integrations(cwd) {
|
|
|
13100
13745
|
writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
|
|
13101
13746
|
added.push(integration.displayName);
|
|
13102
13747
|
}
|
|
13103
|
-
const geminiDir =
|
|
13104
|
-
if (
|
|
13105
|
-
const geminiPath =
|
|
13748
|
+
const geminiDir = path63.join(cwd, ".gemini");
|
|
13749
|
+
if (fs39.existsSync(geminiDir)) {
|
|
13750
|
+
const geminiPath = path63.join(geminiDir, "settings.json");
|
|
13106
13751
|
const geminiConfig = readMcpConfig(geminiPath);
|
|
13107
13752
|
for (const integration of tier0) {
|
|
13108
13753
|
if (!geminiConfig.mcpServers[integration.name]) {
|
|
@@ -13110,9 +13755,9 @@ function configureTier0Integrations(cwd) {
|
|
|
13110
13755
|
}
|
|
13111
13756
|
}
|
|
13112
13757
|
}
|
|
13113
|
-
const opencodePath =
|
|
13114
|
-
const opencodeGlobalDir =
|
|
13115
|
-
if (
|
|
13758
|
+
const opencodePath = path63.join(cwd, "opencode.json");
|
|
13759
|
+
const opencodeGlobalDir = path63.join(os9.homedir(), ".config", "opencode");
|
|
13760
|
+
if (fs39.existsSync(opencodePath) || fs39.existsSync(opencodeGlobalDir)) {
|
|
13116
13761
|
for (const integration of tier0) {
|
|
13117
13762
|
writeOpencodeMcpEntry(opencodePath, integration.name, integration.mcpConfig);
|
|
13118
13763
|
}
|
|
@@ -13130,14 +13775,14 @@ function configureTier0Integrations(cwd) {
|
|
|
13130
13775
|
}
|
|
13131
13776
|
}
|
|
13132
13777
|
function ensureHooks(cwd) {
|
|
13133
|
-
const configPath =
|
|
13134
|
-
if (!
|
|
13778
|
+
const configPath = path63.join(cwd, "harness.config.json");
|
|
13779
|
+
if (!fs39.existsSync(configPath)) {
|
|
13135
13780
|
return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
|
|
13136
13781
|
}
|
|
13137
13782
|
let profile = "standard";
|
|
13138
|
-
const profilePath =
|
|
13783
|
+
const profilePath = path63.join(cwd, ".harness", "hooks", "profile.json");
|
|
13139
13784
|
try {
|
|
13140
|
-
const data = JSON.parse(
|
|
13785
|
+
const data = JSON.parse(fs39.readFileSync(profilePath, "utf-8"));
|
|
13141
13786
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
13142
13787
|
profile = data.profile;
|
|
13143
13788
|
}
|
|
@@ -13208,7 +13853,7 @@ async function runSetup(cwd) {
|
|
|
13208
13853
|
return { steps, success };
|
|
13209
13854
|
}
|
|
13210
13855
|
function createSetupCommand() {
|
|
13211
|
-
return new
|
|
13856
|
+
return new Command101("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
|
|
13212
13857
|
const cwd = process.cwd();
|
|
13213
13858
|
console.log("");
|
|
13214
13859
|
console.log(` ${chalk16.bold("harness setup")}`);
|
|
@@ -13231,15 +13876,15 @@ function createSetupCommand() {
|
|
|
13231
13876
|
|
|
13232
13877
|
// src/commands/share.ts
|
|
13233
13878
|
init_dist3();
|
|
13234
|
-
import { Command as
|
|
13235
|
-
import * as
|
|
13236
|
-
import * as
|
|
13879
|
+
import { Command as Command102 } from "commander";
|
|
13880
|
+
import * as fs40 from "fs";
|
|
13881
|
+
import * as path64 from "path";
|
|
13237
13882
|
import { parse as parseYaml3 } from "yaml";
|
|
13238
13883
|
var MANIFEST_FILENAME = "constraints.yaml";
|
|
13239
13884
|
async function runShareAction(projectPath, options) {
|
|
13240
|
-
const rootDir =
|
|
13241
|
-
const manifestPath =
|
|
13242
|
-
if (!
|
|
13885
|
+
const rootDir = path64.resolve(projectPath);
|
|
13886
|
+
const manifestPath = path64.join(rootDir, MANIFEST_FILENAME);
|
|
13887
|
+
if (!fs40.existsSync(manifestPath)) {
|
|
13243
13888
|
logger.error(
|
|
13244
13889
|
`No ${MANIFEST_FILENAME} found at ${manifestPath}.
|
|
13245
13890
|
Create a constraints.yaml in your project root to define what to share.`
|
|
@@ -13248,7 +13893,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
13248
13893
|
}
|
|
13249
13894
|
let parsed;
|
|
13250
13895
|
try {
|
|
13251
|
-
const raw =
|
|
13896
|
+
const raw = fs40.readFileSync(manifestPath, "utf-8");
|
|
13252
13897
|
parsed = parseYaml3(raw);
|
|
13253
13898
|
} catch (err) {
|
|
13254
13899
|
logger.error(
|
|
@@ -13262,7 +13907,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
13262
13907
|
process.exit(1);
|
|
13263
13908
|
}
|
|
13264
13909
|
const manifest = manifestResult.value;
|
|
13265
|
-
const configResult = resolveConfig(
|
|
13910
|
+
const configResult = resolveConfig(path64.join(rootDir, "harness.config.json"));
|
|
13266
13911
|
if (!configResult.ok) {
|
|
13267
13912
|
logger.error(configResult.error.message);
|
|
13268
13913
|
process.exit(1);
|
|
@@ -13280,8 +13925,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
13280
13925
|
);
|
|
13281
13926
|
process.exit(1);
|
|
13282
13927
|
}
|
|
13283
|
-
const outputDir =
|
|
13284
|
-
const outputPath =
|
|
13928
|
+
const outputDir = path64.resolve(options.output);
|
|
13929
|
+
const outputPath = path64.join(outputDir, `${manifest.name}.harness-constraints.json`);
|
|
13285
13930
|
const writeResult = await writeConfig(outputPath, bundle);
|
|
13286
13931
|
if (!writeResult.ok) {
|
|
13287
13932
|
logger.error(`Failed to write bundle: ${writeResult.error.message}`);
|
|
@@ -13290,28 +13935,28 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
13290
13935
|
logger.success(`Bundle written to ${outputPath}`);
|
|
13291
13936
|
}
|
|
13292
13937
|
function createShareCommand() {
|
|
13293
|
-
return new
|
|
13938
|
+
return new Command102("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) => {
|
|
13294
13939
|
await runShareAction(projectPath, options);
|
|
13295
13940
|
});
|
|
13296
13941
|
}
|
|
13297
13942
|
|
|
13298
13943
|
// src/commands/skill/index.ts
|
|
13299
|
-
import { Command as
|
|
13944
|
+
import { Command as Command110 } from "commander";
|
|
13300
13945
|
|
|
13301
13946
|
// src/commands/skill/list.ts
|
|
13302
|
-
import { Command as
|
|
13303
|
-
import * as
|
|
13304
|
-
import * as
|
|
13947
|
+
import { Command as Command103 } from "commander";
|
|
13948
|
+
import * as fs41 from "fs";
|
|
13949
|
+
import * as path65 from "path";
|
|
13305
13950
|
import { parse } from "yaml";
|
|
13306
13951
|
function scanDirectory(dirPath, source) {
|
|
13307
|
-
if (!
|
|
13308
|
-
const entries =
|
|
13952
|
+
if (!fs41.existsSync(dirPath)) return [];
|
|
13953
|
+
const entries = fs41.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
13309
13954
|
const skills = [];
|
|
13310
13955
|
for (const name of entries) {
|
|
13311
|
-
const yamlPath =
|
|
13312
|
-
if (!
|
|
13956
|
+
const yamlPath = path65.join(dirPath, name, "skill.yaml");
|
|
13957
|
+
if (!fs41.existsSync(yamlPath)) continue;
|
|
13313
13958
|
try {
|
|
13314
|
-
const raw =
|
|
13959
|
+
const raw = fs41.readFileSync(yamlPath, "utf-8");
|
|
13315
13960
|
const parsed = parse(raw);
|
|
13316
13961
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
13317
13962
|
if (result.success) {
|
|
@@ -13329,10 +13974,10 @@ function scanDirectory(dirPath, source) {
|
|
|
13329
13974
|
}
|
|
13330
13975
|
function collectCommunitySkills(seen, allSkills) {
|
|
13331
13976
|
const globalDir = resolveGlobalSkillsDir();
|
|
13332
|
-
const skillsDir =
|
|
13333
|
-
const communityBase =
|
|
13334
|
-
const communityPlatformDir =
|
|
13335
|
-
const lockfilePath =
|
|
13977
|
+
const skillsDir = path65.dirname(globalDir);
|
|
13978
|
+
const communityBase = path65.join(skillsDir, "community");
|
|
13979
|
+
const communityPlatformDir = path65.join(communityBase, "claude-code");
|
|
13980
|
+
const lockfilePath = path65.join(communityBase, "skills-lock.json");
|
|
13336
13981
|
const lockfile = readLockfile2(lockfilePath);
|
|
13337
13982
|
const communitySkills = scanDirectory(communityPlatformDir, "community");
|
|
13338
13983
|
for (const skill of communitySkills) {
|
|
@@ -13402,7 +14047,7 @@ function printSkillsVerbose(skills) {
|
|
|
13402
14047
|
for (const s of skills) printSkillEntry(s);
|
|
13403
14048
|
}
|
|
13404
14049
|
function createListCommand3() {
|
|
13405
|
-
return new
|
|
14050
|
+
return new Command103("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) => {
|
|
13406
14051
|
const globalOpts = cmd.optsWithGlobals();
|
|
13407
14052
|
const skills = collectSkills({ filter: resolveFilter(opts) });
|
|
13408
14053
|
if (globalOpts.json) {
|
|
@@ -13417,9 +14062,9 @@ function createListCommand3() {
|
|
|
13417
14062
|
}
|
|
13418
14063
|
|
|
13419
14064
|
// src/commands/skill/run.ts
|
|
13420
|
-
import { Command as
|
|
13421
|
-
import * as
|
|
13422
|
-
import * as
|
|
14065
|
+
import { Command as Command104 } from "commander";
|
|
14066
|
+
import * as fs42 from "fs";
|
|
14067
|
+
import * as path66 from "path";
|
|
13423
14068
|
import { parse as parse2 } from "yaml";
|
|
13424
14069
|
|
|
13425
14070
|
// src/skill/complexity.ts
|
|
@@ -13515,10 +14160,10 @@ ${options.principles}`);
|
|
|
13515
14160
|
|
|
13516
14161
|
// src/commands/skill/run.ts
|
|
13517
14162
|
function loadSkillMetadata(skillDir) {
|
|
13518
|
-
const yamlPath =
|
|
13519
|
-
if (!
|
|
14163
|
+
const yamlPath = path66.join(skillDir, "skill.yaml");
|
|
14164
|
+
if (!fs42.existsSync(yamlPath)) return null;
|
|
13520
14165
|
try {
|
|
13521
|
-
const result = SkillMetadataSchema.safeParse(parse2(
|
|
14166
|
+
const result = SkillMetadataSchema.safeParse(parse2(fs42.readFileSync(yamlPath, "utf-8")));
|
|
13522
14167
|
return result.success ? result.data : null;
|
|
13523
14168
|
} catch {
|
|
13524
14169
|
return null;
|
|
@@ -13530,22 +14175,22 @@ function resolveComplexity(metadata, requested, projectPath) {
|
|
|
13530
14175
|
return requested;
|
|
13531
14176
|
}
|
|
13532
14177
|
function loadPrinciples(projectPath) {
|
|
13533
|
-
const principlesPath =
|
|
13534
|
-
return
|
|
14178
|
+
const principlesPath = path66.join(projectPath, "docs", "principles.md");
|
|
14179
|
+
return fs42.existsSync(principlesPath) ? fs42.readFileSync(principlesPath, "utf-8") : void 0;
|
|
13535
14180
|
}
|
|
13536
14181
|
function readMostRecentFileInDir(dirPath) {
|
|
13537
|
-
const files =
|
|
13538
|
-
if (files.length > 0) return
|
|
14182
|
+
const files = fs42.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs42.statSync(path66.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
14183
|
+
if (files.length > 0) return fs42.readFileSync(path66.join(dirPath, files[0].name), "utf-8");
|
|
13539
14184
|
return void 0;
|
|
13540
14185
|
}
|
|
13541
14186
|
function loadPriorState(metadata, projectPath) {
|
|
13542
14187
|
if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
|
|
13543
14188
|
for (const stateFilePath of metadata.state.files) {
|
|
13544
|
-
const fullPath =
|
|
13545
|
-
if (!
|
|
13546
|
-
const stat =
|
|
14189
|
+
const fullPath = path66.join(projectPath, stateFilePath);
|
|
14190
|
+
if (!fs42.existsSync(fullPath)) continue;
|
|
14191
|
+
const stat = fs42.statSync(fullPath);
|
|
13547
14192
|
if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
|
|
13548
|
-
return
|
|
14193
|
+
return fs42.readFileSync(fullPath, "utf-8");
|
|
13549
14194
|
}
|
|
13550
14195
|
return void 0;
|
|
13551
14196
|
}
|
|
@@ -13565,7 +14210,7 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
13565
14210
|
}
|
|
13566
14211
|
async function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
13567
14212
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
13568
|
-
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-
|
|
14213
|
+
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-YLQEYOLI.js");
|
|
13569
14214
|
const stateResult = await readHarnessState2(projectPath);
|
|
13570
14215
|
if (!stateResult.ok || isEmptyHarnessState(stateResult.value)) return content;
|
|
13571
14216
|
const stateContent = JSON.stringify(stateResult.value, null, 2);
|
|
@@ -13586,7 +14231,7 @@ async function runSkill(name, opts) {
|
|
|
13586
14231
|
return;
|
|
13587
14232
|
}
|
|
13588
14233
|
const metadata = loadSkillMetadata(skillDir);
|
|
13589
|
-
const projectPath = opts.path ?
|
|
14234
|
+
const projectPath = opts.path ? path66.resolve(opts.path) : process.cwd();
|
|
13590
14235
|
const complexity = resolveComplexity(
|
|
13591
14236
|
metadata,
|
|
13592
14237
|
opts.complexity ?? "standard",
|
|
@@ -13613,14 +14258,14 @@ async function runSkill(name, opts) {
|
|
|
13613
14258
|
...stateWarning !== void 0 && { stateWarning },
|
|
13614
14259
|
...opts.party !== void 0 && { party: opts.party }
|
|
13615
14260
|
});
|
|
13616
|
-
const skillMdPath =
|
|
13617
|
-
if (!
|
|
14261
|
+
const skillMdPath = path66.join(skillDir, "SKILL.md");
|
|
14262
|
+
if (!fs42.existsSync(skillMdPath)) {
|
|
13618
14263
|
logger.error(`SKILL.md not found for skill: ${name}`);
|
|
13619
14264
|
process.exit(ExitCode.ERROR);
|
|
13620
14265
|
return;
|
|
13621
14266
|
}
|
|
13622
14267
|
const content = await appendProjectState(
|
|
13623
|
-
|
|
14268
|
+
fs42.readFileSync(skillMdPath, "utf-8"),
|
|
13624
14269
|
metadata,
|
|
13625
14270
|
projectPath,
|
|
13626
14271
|
!!opts.path
|
|
@@ -13631,16 +14276,16 @@ async function runSkill(name, opts) {
|
|
|
13631
14276
|
process.exit(ExitCode.SUCCESS);
|
|
13632
14277
|
}
|
|
13633
14278
|
function createRunCommand3() {
|
|
13634
|
-
return new
|
|
14279
|
+
return new Command104("run").description("Run a skill (outputs SKILL.md content with context preamble)").argument("<name>", "Skill name (e.g., harness-tdd)").option("--path <path>", "Project root path for context injection").option("--complexity <level>", "Rigor level: fast, standard, thorough", "standard").option("--phase <name>", "Start at a specific phase (for re-entry)").option("--party", "Enable multi-perspective evaluation").option(
|
|
13635
14280
|
"--backend <name>",
|
|
13636
14281
|
"Spec B: one-shot routing override forwarded to the orchestrator as HARNESS_BACKEND_OVERRIDE"
|
|
13637
14282
|
).action(async (name, opts) => runSkill(name, opts));
|
|
13638
14283
|
}
|
|
13639
14284
|
|
|
13640
14285
|
// src/commands/skill/validate.ts
|
|
13641
|
-
import { Command as
|
|
13642
|
-
import * as
|
|
13643
|
-
import * as
|
|
14286
|
+
import { Command as Command105 } from "commander";
|
|
14287
|
+
import * as fs43 from "fs";
|
|
14288
|
+
import * as path67 from "path";
|
|
13644
14289
|
import { parse as parse3 } from "yaml";
|
|
13645
14290
|
var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
13646
14291
|
"## When to Use",
|
|
@@ -13652,11 +14297,11 @@ var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
|
13652
14297
|
];
|
|
13653
14298
|
var KNOWLEDGE_REQUIRED_SECTIONS = ["## Instructions"];
|
|
13654
14299
|
function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
13655
|
-
if (!
|
|
14300
|
+
if (!fs43.existsSync(skillMdPath)) {
|
|
13656
14301
|
errors.push(`${name}: missing SKILL.md`);
|
|
13657
14302
|
return;
|
|
13658
14303
|
}
|
|
13659
|
-
const mdContent =
|
|
14304
|
+
const mdContent = fs43.readFileSync(skillMdPath, "utf-8");
|
|
13660
14305
|
if (!mdContent.trim().startsWith("# ")) {
|
|
13661
14306
|
errors.push(`${name}/SKILL.md: must start with an h1 heading`);
|
|
13662
14307
|
}
|
|
@@ -13681,20 +14326,20 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
|
13681
14326
|
}
|
|
13682
14327
|
}
|
|
13683
14328
|
function validateSkillEntry(name, skillsDir, errors) {
|
|
13684
|
-
const skillDir =
|
|
13685
|
-
const yamlPath =
|
|
13686
|
-
if (!
|
|
14329
|
+
const skillDir = path67.join(skillsDir, name);
|
|
14330
|
+
const yamlPath = path67.join(skillDir, "skill.yaml");
|
|
14331
|
+
if (!fs43.existsSync(yamlPath)) {
|
|
13687
14332
|
errors.push(`${name}: missing skill.yaml`);
|
|
13688
14333
|
return false;
|
|
13689
14334
|
}
|
|
13690
14335
|
try {
|
|
13691
|
-
const raw =
|
|
14336
|
+
const raw = fs43.readFileSync(yamlPath, "utf-8");
|
|
13692
14337
|
const result = SkillMetadataSchema.safeParse(parse3(raw));
|
|
13693
14338
|
if (!result.success) {
|
|
13694
14339
|
errors.push(`${name}/skill.yaml: ${result.error.message}`);
|
|
13695
14340
|
return false;
|
|
13696
14341
|
}
|
|
13697
|
-
validateSkillMd(name,
|
|
14342
|
+
validateSkillMd(name, path67.join(skillDir, "SKILL.md"), result.data.type, errors);
|
|
13698
14343
|
return true;
|
|
13699
14344
|
} catch (e) {
|
|
13700
14345
|
errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -13702,15 +14347,15 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
13702
14347
|
}
|
|
13703
14348
|
}
|
|
13704
14349
|
function createValidateCommand2() {
|
|
13705
|
-
return new
|
|
14350
|
+
return new Command105("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
13706
14351
|
const globalOpts = cmd.optsWithGlobals();
|
|
13707
14352
|
const skillsDir = resolveSkillsDir();
|
|
13708
|
-
if (!
|
|
14353
|
+
if (!fs43.existsSync(skillsDir)) {
|
|
13709
14354
|
logger.info("No skills directory found.");
|
|
13710
14355
|
process.exit(ExitCode.SUCCESS);
|
|
13711
14356
|
return;
|
|
13712
14357
|
}
|
|
13713
|
-
const entries =
|
|
14358
|
+
const entries = fs43.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
13714
14359
|
const errors = [];
|
|
13715
14360
|
let validated = 0;
|
|
13716
14361
|
for (const name of entries) {
|
|
@@ -13732,9 +14377,9 @@ function createValidateCommand2() {
|
|
|
13732
14377
|
}
|
|
13733
14378
|
|
|
13734
14379
|
// src/commands/skill/info.ts
|
|
13735
|
-
import { Command as
|
|
13736
|
-
import * as
|
|
13737
|
-
import * as
|
|
14380
|
+
import { Command as Command106 } from "commander";
|
|
14381
|
+
import * as fs44 from "fs";
|
|
14382
|
+
import * as path68 from "path";
|
|
13738
14383
|
import { parse as parse4 } from "yaml";
|
|
13739
14384
|
function loadSkillMetadata2(name) {
|
|
13740
14385
|
const skillDir = resolveSkillDir(name);
|
|
@@ -13742,13 +14387,13 @@ function loadSkillMetadata2(name) {
|
|
|
13742
14387
|
logger.error(`Skill not found: ${name}`);
|
|
13743
14388
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
13744
14389
|
}
|
|
13745
|
-
const yamlPath =
|
|
13746
|
-
if (!
|
|
14390
|
+
const yamlPath = path68.join(skillDir, "skill.yaml");
|
|
14391
|
+
if (!fs44.existsSync(yamlPath)) {
|
|
13747
14392
|
logger.error(`skill.yaml not found for skill: ${name}`);
|
|
13748
14393
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
13749
14394
|
}
|
|
13750
14395
|
try {
|
|
13751
|
-
const raw =
|
|
14396
|
+
const raw = fs44.readFileSync(yamlPath, "utf-8");
|
|
13752
14397
|
const parsed = parse4(raw);
|
|
13753
14398
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
13754
14399
|
if (!result.success) {
|
|
@@ -13777,7 +14422,7 @@ function printSkillInfo(skill) {
|
|
|
13777
14422
|
console.log(`Persistent: ${skill.state.persistent}`);
|
|
13778
14423
|
}
|
|
13779
14424
|
function createInfoCommand() {
|
|
13780
|
-
return new
|
|
14425
|
+
return new Command106("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
|
|
13781
14426
|
const globalOpts = cmd.optsWithGlobals();
|
|
13782
14427
|
const loaded = loadSkillMetadata2(name);
|
|
13783
14428
|
if (!loaded.ok) {
|
|
@@ -13794,7 +14439,7 @@ function createInfoCommand() {
|
|
|
13794
14439
|
}
|
|
13795
14440
|
|
|
13796
14441
|
// src/commands/skill/search.ts
|
|
13797
|
-
import { Command as
|
|
14442
|
+
import { Command as Command107 } from "commander";
|
|
13798
14443
|
async function runSearch(query, opts) {
|
|
13799
14444
|
const results = await searchNpmRegistry(query, opts.registry);
|
|
13800
14445
|
return results.filter((r) => {
|
|
@@ -13839,15 +14484,15 @@ async function runSearchAction(query, opts, globalOpts) {
|
|
|
13839
14484
|
}
|
|
13840
14485
|
}
|
|
13841
14486
|
function createSearchCommand2() {
|
|
13842
|
-
return new
|
|
14487
|
+
return new Command107("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) => {
|
|
13843
14488
|
await runSearchAction(query, opts, cmd.optsWithGlobals());
|
|
13844
14489
|
});
|
|
13845
14490
|
}
|
|
13846
14491
|
|
|
13847
14492
|
// src/commands/skill/create.ts
|
|
13848
|
-
import { Command as
|
|
13849
|
-
import * as
|
|
13850
|
-
import * as
|
|
14493
|
+
import { Command as Command108 } from "commander";
|
|
14494
|
+
import * as path69 from "path";
|
|
14495
|
+
import * as fs45 from "fs";
|
|
13851
14496
|
import YAML from "yaml";
|
|
13852
14497
|
var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
13853
14498
|
function buildReadme(name, description) {
|
|
@@ -13931,22 +14576,22 @@ function runCreate(name, opts) {
|
|
|
13931
14576
|
if (!KEBAB_CASE_RE.test(name)) {
|
|
13932
14577
|
throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
|
|
13933
14578
|
}
|
|
13934
|
-
const baseDir = opts.outputDir ??
|
|
13935
|
-
const skillDir =
|
|
13936
|
-
if (
|
|
14579
|
+
const baseDir = opts.outputDir ?? path69.join(process.cwd(), "agents", "skills", "claude-code");
|
|
14580
|
+
const skillDir = path69.join(baseDir, name);
|
|
14581
|
+
if (fs45.existsSync(skillDir)) {
|
|
13937
14582
|
throw new Error(`Skill directory already exists: ${skillDir}`);
|
|
13938
14583
|
}
|
|
13939
|
-
|
|
14584
|
+
fs45.mkdirSync(skillDir, { recursive: true });
|
|
13940
14585
|
const description = opts.description || `A community skill: ${name}`;
|
|
13941
14586
|
const skillYaml = buildSkillYaml(name, opts);
|
|
13942
|
-
const skillYamlPath =
|
|
13943
|
-
|
|
14587
|
+
const skillYamlPath = path69.join(skillDir, "skill.yaml");
|
|
14588
|
+
fs45.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
|
|
13944
14589
|
const skillMd = buildSkillMd(name, description);
|
|
13945
|
-
const skillMdPath =
|
|
13946
|
-
|
|
14590
|
+
const skillMdPath = path69.join(skillDir, "SKILL.md");
|
|
14591
|
+
fs45.writeFileSync(skillMdPath, skillMd);
|
|
13947
14592
|
const readme = buildReadme(name, description);
|
|
13948
|
-
const readmePath =
|
|
13949
|
-
|
|
14593
|
+
const readmePath = path69.join(skillDir, "README.md");
|
|
14594
|
+
fs45.writeFileSync(readmePath, readme);
|
|
13950
14595
|
return {
|
|
13951
14596
|
name,
|
|
13952
14597
|
directory: skillDir,
|
|
@@ -13960,12 +14605,12 @@ function printCreateResult(name, result) {
|
|
|
13960
14605
|
}
|
|
13961
14606
|
logger.info(`
|
|
13962
14607
|
Next steps:`);
|
|
13963
|
-
logger.info(` 1. Edit ${
|
|
14608
|
+
logger.info(` 1. Edit ${path69.join(result.directory, "SKILL.md")} with your skill content`);
|
|
13964
14609
|
logger.info(` 2. Run: harness skill validate ${name}`);
|
|
13965
14610
|
logger.info(` 3. Run: harness skills publish`);
|
|
13966
14611
|
}
|
|
13967
14612
|
function createCreateCommand() {
|
|
13968
|
-
return new
|
|
14613
|
+
return new Command108("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) => {
|
|
13969
14614
|
const globalOpts = cmd.optsWithGlobals();
|
|
13970
14615
|
try {
|
|
13971
14616
|
const result = runCreate(name, {
|
|
@@ -13988,26 +14633,26 @@ function createCreateCommand() {
|
|
|
13988
14633
|
}
|
|
13989
14634
|
|
|
13990
14635
|
// src/commands/skill/publish.ts
|
|
13991
|
-
import { Command as
|
|
13992
|
-
import * as
|
|
13993
|
-
import * as
|
|
14636
|
+
import { Command as Command109 } from "commander";
|
|
14637
|
+
import * as fs47 from "fs";
|
|
14638
|
+
import * as path71 from "path";
|
|
13994
14639
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
13995
14640
|
|
|
13996
14641
|
// src/registry/validator.ts
|
|
13997
|
-
import * as
|
|
13998
|
-
import * as
|
|
14642
|
+
import * as fs46 from "fs";
|
|
14643
|
+
import * as path70 from "path";
|
|
13999
14644
|
import { parse as parse5 } from "yaml";
|
|
14000
14645
|
import semver4 from "semver";
|
|
14001
14646
|
async function validateForPublish(skillDir, registryUrl) {
|
|
14002
14647
|
const errors = [];
|
|
14003
|
-
const skillYamlPath =
|
|
14004
|
-
if (!
|
|
14648
|
+
const skillYamlPath = path70.join(skillDir, "skill.yaml");
|
|
14649
|
+
if (!fs46.existsSync(skillYamlPath)) {
|
|
14005
14650
|
errors.push("skill.yaml not found. Create one with: harness skill create <name>");
|
|
14006
14651
|
return { valid: false, errors };
|
|
14007
14652
|
}
|
|
14008
14653
|
let skillMeta;
|
|
14009
14654
|
try {
|
|
14010
|
-
const raw =
|
|
14655
|
+
const raw = fs46.readFileSync(skillYamlPath, "utf-8");
|
|
14011
14656
|
const parsed = parse5(raw);
|
|
14012
14657
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
14013
14658
|
if (!result.success) {
|
|
@@ -14029,11 +14674,11 @@ async function validateForPublish(skillDir, registryUrl) {
|
|
|
14029
14674
|
if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
|
|
14030
14675
|
errors.push("At least one trigger is required. Add triggers to skill.yaml.");
|
|
14031
14676
|
}
|
|
14032
|
-
const skillMdPath =
|
|
14033
|
-
if (!
|
|
14677
|
+
const skillMdPath = path70.join(skillDir, "SKILL.md");
|
|
14678
|
+
if (!fs46.existsSync(skillMdPath)) {
|
|
14034
14679
|
errors.push("SKILL.md not found. Create it with content describing your skill.");
|
|
14035
14680
|
} else {
|
|
14036
|
-
const content =
|
|
14681
|
+
const content = fs46.readFileSync(skillMdPath, "utf-8");
|
|
14037
14682
|
if (!content.includes("## When to Use")) {
|
|
14038
14683
|
errors.push('SKILL.md must contain a "## When to Use" section.');
|
|
14039
14684
|
}
|
|
@@ -14113,11 +14758,11 @@ ${errorList}`);
|
|
|
14113
14758
|
}
|
|
14114
14759
|
const meta = validation.skillMeta;
|
|
14115
14760
|
const pkg = derivePackageJson(meta);
|
|
14116
|
-
const pkgPath =
|
|
14117
|
-
|
|
14118
|
-
const readmePath =
|
|
14119
|
-
if (!
|
|
14120
|
-
const skillMdContent =
|
|
14761
|
+
const pkgPath = path71.join(skillDir, "package.json");
|
|
14762
|
+
fs47.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
14763
|
+
const readmePath = path71.join(skillDir, "README.md");
|
|
14764
|
+
if (!fs47.existsSync(readmePath)) {
|
|
14765
|
+
const skillMdContent = fs47.readFileSync(path71.join(skillDir, "SKILL.md"), "utf-8");
|
|
14121
14766
|
const readme = `# ${pkg.name}
|
|
14122
14767
|
|
|
14123
14768
|
${meta.description}
|
|
@@ -14131,7 +14776,7 @@ harness install ${meta.name}
|
|
|
14131
14776
|
---
|
|
14132
14777
|
|
|
14133
14778
|
${skillMdContent}`;
|
|
14134
|
-
|
|
14779
|
+
fs47.writeFileSync(readmePath, readme);
|
|
14135
14780
|
}
|
|
14136
14781
|
if (opts.dryRun) {
|
|
14137
14782
|
return {
|
|
@@ -14157,7 +14802,7 @@ ${skillMdContent}`;
|
|
|
14157
14802
|
};
|
|
14158
14803
|
}
|
|
14159
14804
|
function createPublishCommand() {
|
|
14160
|
-
return new
|
|
14805
|
+
return new Command109("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) => {
|
|
14161
14806
|
const globalOpts = cmd.optsWithGlobals();
|
|
14162
14807
|
const skillDir = opts.dir || process.cwd();
|
|
14163
14808
|
try {
|
|
@@ -14182,7 +14827,7 @@ function createPublishCommand() {
|
|
|
14182
14827
|
|
|
14183
14828
|
// src/commands/skill/index.ts
|
|
14184
14829
|
function createSkillCommand() {
|
|
14185
|
-
const command = new
|
|
14830
|
+
const command = new Command110("skill").description("Skill management commands");
|
|
14186
14831
|
command.addCommand(createListCommand3());
|
|
14187
14832
|
command.addCommand(createRunCommand3());
|
|
14188
14833
|
command.addCommand(createValidateCommand2());
|
|
@@ -14195,7 +14840,7 @@ function createSkillCommand() {
|
|
|
14195
14840
|
|
|
14196
14841
|
// src/commands/snapshot.ts
|
|
14197
14842
|
init_dist3();
|
|
14198
|
-
import { Command as
|
|
14843
|
+
import { Command as Command111 } from "commander";
|
|
14199
14844
|
import { execSync as execSync5 } from "child_process";
|
|
14200
14845
|
import chalk17 from "chalk";
|
|
14201
14846
|
function getCommitHash3(cwd) {
|
|
@@ -14377,7 +15022,7 @@ function registerListCommand2(parent) {
|
|
|
14377
15022
|
});
|
|
14378
15023
|
}
|
|
14379
15024
|
function createSnapshotCommand() {
|
|
14380
|
-
const command = new
|
|
15025
|
+
const command = new Command111("snapshot").description("Architecture timeline snapshot commands");
|
|
14381
15026
|
registerCaptureCommand(command);
|
|
14382
15027
|
registerTrendsCommand(command);
|
|
14383
15028
|
registerListCommand2(command);
|
|
@@ -14385,9 +15030,9 @@ function createSnapshotCommand() {
|
|
|
14385
15030
|
}
|
|
14386
15031
|
|
|
14387
15032
|
// src/commands/spec-craft.ts
|
|
14388
|
-
import { Command as
|
|
15033
|
+
import { Command as Command112 } from "commander";
|
|
14389
15034
|
function createSpecCraftCommand() {
|
|
14390
|
-
return new
|
|
15035
|
+
return new Command112("spec-craft").description(
|
|
14391
15036
|
"LLM-judgment critique of spec quality (proposals + ADRs). Second craft-pipeline ceiling skill; 7 seed rubrics from the spec-quality canon. Per-section critique."
|
|
14392
15037
|
).option("-f, --files <files...>", "Optional spec file/glob scope").option("-k, --kinds <kinds...>", "Restrict to proposal / adr (default: both)").option("-s, --sections <names...>", "Restrict to specific canonical section names").option("--max-files <n>", "Cap doc count (default: 50)").option("--max-sections-per-file <n>", "Cap per-doc section critique (default: 10)").action(async (opts, cmd) => {
|
|
14393
15038
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -14435,10 +15080,10 @@ function printResult6(result, mode, _formatter) {
|
|
|
14435
15080
|
list.push(f);
|
|
14436
15081
|
byFile.set(f.target.file, list);
|
|
14437
15082
|
}
|
|
14438
|
-
for (const [file,
|
|
15083
|
+
for (const [file, fs52] of byFile) {
|
|
14439
15084
|
console.log(`
|
|
14440
15085
|
${file}`);
|
|
14441
|
-
for (const f of
|
|
15086
|
+
for (const f of fs52) {
|
|
14442
15087
|
console.log(
|
|
14443
15088
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ## ${f.target.section}:${f.target.line}`
|
|
14444
15089
|
);
|
|
@@ -14455,7 +15100,7 @@ ${file}`);
|
|
|
14455
15100
|
|
|
14456
15101
|
// src/commands/stale-constraints.ts
|
|
14457
15102
|
init_dist3();
|
|
14458
|
-
import { Command as
|
|
15103
|
+
import { Command as Command113 } from "commander";
|
|
14459
15104
|
import chalk18 from "chalk";
|
|
14460
15105
|
var CATEGORIES = [
|
|
14461
15106
|
"circular-deps",
|
|
@@ -14542,7 +15187,7 @@ async function runStaleConstraintsCommand(options) {
|
|
|
14542
15187
|
);
|
|
14543
15188
|
}
|
|
14544
15189
|
function createStaleConstraintsCommand() {
|
|
14545
|
-
return new
|
|
15190
|
+
return new Command113("stale-constraints").description(
|
|
14546
15191
|
"Detect architectural constraints not violated within a window (candidates for removal)"
|
|
14547
15192
|
).option(
|
|
14548
15193
|
"--window <days>",
|
|
@@ -14560,11 +15205,11 @@ function createStaleConstraintsCommand() {
|
|
|
14560
15205
|
}
|
|
14561
15206
|
|
|
14562
15207
|
// src/commands/state/index.ts
|
|
14563
|
-
import { Command as
|
|
15208
|
+
import { Command as Command118 } from "commander";
|
|
14564
15209
|
|
|
14565
15210
|
// src/commands/state/show.ts
|
|
14566
|
-
import { Command as
|
|
14567
|
-
import * as
|
|
15211
|
+
import { Command as Command114 } from "commander";
|
|
15212
|
+
import * as path72 from "path";
|
|
14568
15213
|
function printStateText(state, stream) {
|
|
14569
15214
|
if (stream) console.log(`Stream: ${stream}`);
|
|
14570
15215
|
console.log(`Schema Version: ${state.schemaVersion}`);
|
|
@@ -14588,9 +15233,9 @@ function printStateProgress(progress) {
|
|
|
14588
15233
|
}
|
|
14589
15234
|
}
|
|
14590
15235
|
function createShowCommand() {
|
|
14591
|
-
return new
|
|
15236
|
+
return new Command114("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
|
|
14592
15237
|
const globalOpts = cmd.optsWithGlobals();
|
|
14593
|
-
const result = await readHarnessState(
|
|
15238
|
+
const result = await readHarnessState(path72.resolve(opts.path), { stream: opts.stream });
|
|
14594
15239
|
if (!result.ok) {
|
|
14595
15240
|
logger.error(result.error.message);
|
|
14596
15241
|
process.exit(ExitCode.ERROR);
|
|
@@ -14606,8 +15251,8 @@ function createShowCommand() {
|
|
|
14606
15251
|
|
|
14607
15252
|
// src/commands/state/reset.ts
|
|
14608
15253
|
init_dist3();
|
|
14609
|
-
import { Command as
|
|
14610
|
-
import * as
|
|
15254
|
+
import { Command as Command115 } from "commander";
|
|
15255
|
+
import * as path73 from "path";
|
|
14611
15256
|
import * as readline3 from "readline";
|
|
14612
15257
|
async function confirmReset() {
|
|
14613
15258
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -14618,7 +15263,7 @@ async function confirmReset() {
|
|
|
14618
15263
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
14619
15264
|
}
|
|
14620
15265
|
async function runReset(opts) {
|
|
14621
|
-
const projectPath =
|
|
15266
|
+
const projectPath = path73.resolve(opts.path);
|
|
14622
15267
|
if (!opts.yes && !await confirmReset()) {
|
|
14623
15268
|
logger.info("Reset cancelled.");
|
|
14624
15269
|
process.exit(ExitCode.SUCCESS);
|
|
@@ -14632,16 +15277,16 @@ async function runReset(opts) {
|
|
|
14632
15277
|
process.exit(ExitCode.SUCCESS);
|
|
14633
15278
|
}
|
|
14634
15279
|
function createResetCommand() {
|
|
14635
|
-
return new
|
|
15280
|
+
return new Command115("reset").description("Reset project state (truncates the event log to a fresh default)").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").option("--yes", "Skip confirmation prompt").action(async (opts) => runReset(opts));
|
|
14636
15281
|
}
|
|
14637
15282
|
|
|
14638
15283
|
// src/commands/state/learn.ts
|
|
14639
15284
|
init_dist3();
|
|
14640
|
-
import { Command as
|
|
14641
|
-
import * as
|
|
15285
|
+
import { Command as Command116 } from "commander";
|
|
15286
|
+
import * as path74 from "path";
|
|
14642
15287
|
function createLearnCommand() {
|
|
14643
|
-
return new
|
|
14644
|
-
const projectPath =
|
|
15288
|
+
return new Command116("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) => {
|
|
15289
|
+
const projectPath = path74.resolve(opts.path);
|
|
14645
15290
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
14646
15291
|
if (!result.ok) {
|
|
14647
15292
|
logger.error(result.error.message);
|
|
@@ -14655,10 +15300,10 @@ function createLearnCommand() {
|
|
|
14655
15300
|
|
|
14656
15301
|
// src/commands/state/streams.ts
|
|
14657
15302
|
init_dist3();
|
|
14658
|
-
import { Command as
|
|
14659
|
-
import * as
|
|
15303
|
+
import { Command as Command117 } from "commander";
|
|
15304
|
+
import * as path75 from "path";
|
|
14660
15305
|
async function runListStreams(opts, globalOpts) {
|
|
14661
|
-
const projectPath =
|
|
15306
|
+
const projectPath = path75.resolve(opts.path);
|
|
14662
15307
|
const indexResult = await loadStreamIndex(projectPath);
|
|
14663
15308
|
const result = await listStreams(projectPath);
|
|
14664
15309
|
if (!result.ok) {
|
|
@@ -14679,7 +15324,7 @@ async function runListStreams(opts, globalOpts) {
|
|
|
14679
15324
|
process.exit(ExitCode.SUCCESS);
|
|
14680
15325
|
}
|
|
14681
15326
|
async function runCreateStream(name, opts) {
|
|
14682
|
-
const result = await createStream(
|
|
15327
|
+
const result = await createStream(path75.resolve(opts.path), name, opts.branch);
|
|
14683
15328
|
if (!result.ok) {
|
|
14684
15329
|
logger.error(result.error.message);
|
|
14685
15330
|
process.exit(ExitCode.ERROR);
|
|
@@ -14688,7 +15333,7 @@ async function runCreateStream(name, opts) {
|
|
|
14688
15333
|
process.exit(ExitCode.SUCCESS);
|
|
14689
15334
|
}
|
|
14690
15335
|
async function runArchiveStream(name, opts) {
|
|
14691
|
-
const result = await archiveStream(
|
|
15336
|
+
const result = await archiveStream(path75.resolve(opts.path), name);
|
|
14692
15337
|
if (!result.ok) {
|
|
14693
15338
|
logger.error(result.error.message);
|
|
14694
15339
|
process.exit(ExitCode.ERROR);
|
|
@@ -14697,7 +15342,7 @@ async function runArchiveStream(name, opts) {
|
|
|
14697
15342
|
process.exit(ExitCode.SUCCESS);
|
|
14698
15343
|
}
|
|
14699
15344
|
async function runActivateStream(name, opts) {
|
|
14700
|
-
const result = await setActiveStream(
|
|
15345
|
+
const result = await setActiveStream(path75.resolve(opts.path), name);
|
|
14701
15346
|
if (!result.ok) {
|
|
14702
15347
|
logger.error(result.error.message);
|
|
14703
15348
|
process.exit(ExitCode.ERROR);
|
|
@@ -14706,7 +15351,7 @@ async function runActivateStream(name, opts) {
|
|
|
14706
15351
|
process.exit(ExitCode.SUCCESS);
|
|
14707
15352
|
}
|
|
14708
15353
|
function createStreamsCommand() {
|
|
14709
|
-
const command = new
|
|
15354
|
+
const command = new Command117("streams").description("Manage state streams");
|
|
14710
15355
|
command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
|
|
14711
15356
|
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));
|
|
14712
15357
|
command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
|
|
@@ -14716,7 +15361,7 @@ function createStreamsCommand() {
|
|
|
14716
15361
|
|
|
14717
15362
|
// src/commands/state/index.ts
|
|
14718
15363
|
function createStateCommand() {
|
|
14719
|
-
const command = new
|
|
15364
|
+
const command = new Command118("state").description("Project state management commands");
|
|
14720
15365
|
command.addCommand(createShowCommand());
|
|
14721
15366
|
command.addCommand(createResetCommand());
|
|
14722
15367
|
command.addCommand(createLearnCommand());
|
|
@@ -14725,9 +15370,9 @@ function createStateCommand() {
|
|
|
14725
15370
|
}
|
|
14726
15371
|
|
|
14727
15372
|
// src/commands/sync-analyses.ts
|
|
14728
|
-
import * as
|
|
14729
|
-
import * as
|
|
14730
|
-
import { Command as
|
|
15373
|
+
import * as fs48 from "fs";
|
|
15374
|
+
import * as path76 from "path";
|
|
15375
|
+
import { Command as Command119 } from "commander";
|
|
14731
15376
|
init_dist3();
|
|
14732
15377
|
function isValidRecord(record) {
|
|
14733
15378
|
return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
|
|
@@ -14753,7 +15398,7 @@ function extractAnalysisFromComments(comments) {
|
|
|
14753
15398
|
return null;
|
|
14754
15399
|
}
|
|
14755
15400
|
function bootstrapTrackerCommand2(opts, verb) {
|
|
14756
|
-
const projectPath =
|
|
15401
|
+
const projectPath = path76.resolve(opts.dir);
|
|
14757
15402
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
14758
15403
|
if (!trackerConfig) {
|
|
14759
15404
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -14765,8 +15410,8 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
14765
15410
|
);
|
|
14766
15411
|
return null;
|
|
14767
15412
|
}
|
|
14768
|
-
const projectEnvPath =
|
|
14769
|
-
if (
|
|
15413
|
+
const projectEnvPath = path76.join(projectPath, ".env");
|
|
15414
|
+
if (fs48.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
14770
15415
|
__require("dotenv").config({ path: projectEnvPath });
|
|
14771
15416
|
}
|
|
14772
15417
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -14852,9 +15497,9 @@ async function runSyncAnalyses(opts) {
|
|
|
14852
15497
|
return;
|
|
14853
15498
|
}
|
|
14854
15499
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
14855
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
15500
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-DTXVNQAY.js");
|
|
14856
15501
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
14857
|
-
const archive = new AnalysisArchive(
|
|
15502
|
+
const archive = new AnalysisArchive(path76.join(projectPath, ".harness", "analyses"));
|
|
14858
15503
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
14859
15504
|
const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
|
|
14860
15505
|
features,
|
|
@@ -14874,7 +15519,7 @@ async function runSyncAnalyses(opts) {
|
|
|
14874
15519
|
}
|
|
14875
15520
|
}
|
|
14876
15521
|
function createSyncAnalysesCommand() {
|
|
14877
|
-
const command = new
|
|
15522
|
+
const command = new Command119("sync-analyses").description(
|
|
14878
15523
|
"Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
|
|
14879
15524
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
14880
15525
|
try {
|
|
@@ -14888,8 +15533,8 @@ function createSyncAnalysesCommand() {
|
|
|
14888
15533
|
}
|
|
14889
15534
|
|
|
14890
15535
|
// src/commands/sync-main.ts
|
|
14891
|
-
import { Command as
|
|
14892
|
-
import * as
|
|
15536
|
+
import { Command as Command120 } from "commander";
|
|
15537
|
+
import * as path77 from "path";
|
|
14893
15538
|
import {
|
|
14894
15539
|
syncMain as defaultSyncMain
|
|
14895
15540
|
} from "@harness-engineering/orchestrator";
|
|
@@ -14919,8 +15564,8 @@ function formatHuman2(r) {
|
|
|
14919
15564
|
}
|
|
14920
15565
|
}
|
|
14921
15566
|
function createSyncMainCommand() {
|
|
14922
|
-
return new
|
|
14923
|
-
const cwd =
|
|
15567
|
+
return new Command120("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) => {
|
|
15568
|
+
const cwd = path77.resolve(opts.path ?? ".");
|
|
14924
15569
|
const exitCode = await runSyncMain({ cwd, json: Boolean(opts.json) });
|
|
14925
15570
|
process.exit(exitCode);
|
|
14926
15571
|
});
|
|
@@ -14928,7 +15573,7 @@ function createSyncMainCommand() {
|
|
|
14928
15573
|
|
|
14929
15574
|
// src/commands/taint.ts
|
|
14930
15575
|
init_dist3();
|
|
14931
|
-
import { Command as
|
|
15576
|
+
import { Command as Command121 } from "commander";
|
|
14932
15577
|
function getProjectRoot() {
|
|
14933
15578
|
return process.cwd();
|
|
14934
15579
|
}
|
|
@@ -15000,28 +15645,28 @@ function registerStatusCommand(taint) {
|
|
|
15000
15645
|
});
|
|
15001
15646
|
}
|
|
15002
15647
|
function createTaintCommand() {
|
|
15003
|
-
const taint = new
|
|
15648
|
+
const taint = new Command121("taint").description("Manage sentinel session taint state");
|
|
15004
15649
|
registerClearCommand(taint);
|
|
15005
15650
|
registerStatusCommand(taint);
|
|
15006
15651
|
return taint;
|
|
15007
15652
|
}
|
|
15008
15653
|
|
|
15009
15654
|
// src/commands/telemetry/index.ts
|
|
15010
|
-
import { Command as
|
|
15655
|
+
import { Command as Command125 } from "commander";
|
|
15011
15656
|
|
|
15012
15657
|
// src/commands/telemetry/identify.ts
|
|
15013
15658
|
init_dist3();
|
|
15014
|
-
import { Command as
|
|
15015
|
-
import * as
|
|
15016
|
-
import * as
|
|
15659
|
+
import { Command as Command122 } from "commander";
|
|
15660
|
+
import * as fs49 from "fs";
|
|
15661
|
+
import * as path78 from "path";
|
|
15017
15662
|
function telemetryFilePath(cwd) {
|
|
15018
|
-
return
|
|
15663
|
+
return path78.join(cwd, ".harness", "telemetry.json");
|
|
15019
15664
|
}
|
|
15020
15665
|
function writeTelemetryFile(cwd, data) {
|
|
15021
15666
|
const filePath = telemetryFilePath(cwd);
|
|
15022
|
-
const dir =
|
|
15023
|
-
|
|
15024
|
-
|
|
15667
|
+
const dir = path78.dirname(filePath);
|
|
15668
|
+
fs49.mkdirSync(dir, { recursive: true });
|
|
15669
|
+
fs49.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
15025
15670
|
}
|
|
15026
15671
|
var IDENTITY_FIELDS = ["project", "team", "alias"];
|
|
15027
15672
|
function applyIdentityFields(opts, identity) {
|
|
@@ -15035,7 +15680,7 @@ function printIdentity(identity) {
|
|
|
15035
15680
|
}
|
|
15036
15681
|
}
|
|
15037
15682
|
function createIdentifyCommand() {
|
|
15038
|
-
const cmd = new
|
|
15683
|
+
const cmd = new Command122("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) => {
|
|
15039
15684
|
const cwd = process.cwd();
|
|
15040
15685
|
if (opts.clear) {
|
|
15041
15686
|
writeTelemetryFile(cwd, { identity: {} });
|
|
@@ -15060,7 +15705,7 @@ function createIdentifyCommand() {
|
|
|
15060
15705
|
|
|
15061
15706
|
// src/commands/telemetry/status.ts
|
|
15062
15707
|
init_dist3();
|
|
15063
|
-
import { Command as
|
|
15708
|
+
import { Command as Command123 } from "commander";
|
|
15064
15709
|
function gatherEnvOverrides() {
|
|
15065
15710
|
const overrides = {};
|
|
15066
15711
|
if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
|
|
@@ -15102,7 +15747,7 @@ function printHumanStatus(result) {
|
|
|
15102
15747
|
printEnvOverrides(result.envOverrides);
|
|
15103
15748
|
}
|
|
15104
15749
|
function createStatusCommand2() {
|
|
15105
|
-
const cmd = new
|
|
15750
|
+
const cmd = new Command123("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
|
|
15106
15751
|
const cwd = process.cwd();
|
|
15107
15752
|
const envOverrides = gatherEnvOverrides();
|
|
15108
15753
|
const consent = resolveConsent(cwd, void 0);
|
|
@@ -15135,11 +15780,11 @@ function createStatusCommand2() {
|
|
|
15135
15780
|
|
|
15136
15781
|
// src/commands/telemetry/test.ts
|
|
15137
15782
|
init_dist3();
|
|
15138
|
-
import { Command as
|
|
15783
|
+
import { Command as Command124 } from "commander";
|
|
15139
15784
|
|
|
15140
15785
|
// src/bin/command-telemetry.ts
|
|
15141
|
-
import { existsSync as
|
|
15142
|
-
import { join as
|
|
15786
|
+
import { existsSync as existsSync45, readFileSync as readFileSync33, mkdirSync as mkdirSync24, appendFileSync as appendFileSync2, writeFileSync as writeFileSync29 } from "fs";
|
|
15787
|
+
import { join as join66, parse as parsePath, resolve as resolve46 } from "path";
|
|
15143
15788
|
import { spawn as spawn2 } from "child_process";
|
|
15144
15789
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
15145
15790
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
@@ -15147,7 +15792,7 @@ function findProjectRoot(cwd) {
|
|
|
15147
15792
|
let dir = resolve46(cwd);
|
|
15148
15793
|
const { root } = parsePath(dir);
|
|
15149
15794
|
while (dir !== root) {
|
|
15150
|
-
if (
|
|
15795
|
+
if (existsSync45(join66(dir, "harness.config.json"))) return dir;
|
|
15151
15796
|
dir = resolve46(dir, "..");
|
|
15152
15797
|
}
|
|
15153
15798
|
return cwd;
|
|
@@ -15185,7 +15830,7 @@ function resolveCommandName(cmd) {
|
|
|
15185
15830
|
}
|
|
15186
15831
|
function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
15187
15832
|
try {
|
|
15188
|
-
const metricsDir =
|
|
15833
|
+
const metricsDir = join66(cwd, ".harness", "metrics");
|
|
15189
15834
|
mkdirSync24(metricsDir, { recursive: true });
|
|
15190
15835
|
const record = {
|
|
15191
15836
|
skill: command,
|
|
@@ -15195,16 +15840,16 @@ function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
|
15195
15840
|
outcome,
|
|
15196
15841
|
phasesReached: []
|
|
15197
15842
|
};
|
|
15198
|
-
const adoptionFile =
|
|
15843
|
+
const adoptionFile = join66(metricsDir, "adoption.jsonl");
|
|
15199
15844
|
appendFileSync2(adoptionFile, JSON.stringify(record) + "\n");
|
|
15200
15845
|
} catch {
|
|
15201
15846
|
}
|
|
15202
15847
|
}
|
|
15203
15848
|
function isTelemetryDisabledByConfig(cwd) {
|
|
15204
|
-
const configPath =
|
|
15205
|
-
if (!
|
|
15849
|
+
const configPath = join66(cwd, "harness.config.json");
|
|
15850
|
+
if (!existsSync45(configPath)) return false;
|
|
15206
15851
|
try {
|
|
15207
|
-
const config = JSON.parse(
|
|
15852
|
+
const config = JSON.parse(readFileSync33(configPath, "utf-8"));
|
|
15208
15853
|
return config?.telemetry?.enabled === false;
|
|
15209
15854
|
} catch {
|
|
15210
15855
|
return false;
|
|
@@ -15216,11 +15861,11 @@ function isTelemetryOptedOut(cwd) {
|
|
|
15216
15861
|
}
|
|
15217
15862
|
function flushTelemetryBackground(cwd) {
|
|
15218
15863
|
try {
|
|
15219
|
-
const adoptionFile =
|
|
15220
|
-
if (!
|
|
15864
|
+
const adoptionFile = join66(cwd, ".harness", "metrics", "adoption.jsonl");
|
|
15865
|
+
if (!existsSync45(adoptionFile)) return;
|
|
15221
15866
|
if (isTelemetryOptedOut(cwd)) return;
|
|
15222
|
-
const reporterPath =
|
|
15223
|
-
if (!
|
|
15867
|
+
const reporterPath = join66(cwd, ".harness", "hooks", "telemetry-reporter.js");
|
|
15868
|
+
if (!existsSync45(reporterPath)) return;
|
|
15224
15869
|
const child = spawn2(process.execPath, [reporterPath], {
|
|
15225
15870
|
cwd,
|
|
15226
15871
|
stdio: ["pipe", "ignore", "ignore"],
|
|
@@ -15257,7 +15902,7 @@ function reportTestSuccess(status, distinctId, identity) {
|
|
|
15257
15902
|
if (identity.team) logger.info(` team: ${identity.team}`);
|
|
15258
15903
|
}
|
|
15259
15904
|
function createTestCommand() {
|
|
15260
|
-
return new
|
|
15905
|
+
return new Command124("test").description("Send a test event to PostHog and verify connectivity").action(async () => {
|
|
15261
15906
|
const cwd = process.cwd();
|
|
15262
15907
|
const consent = resolveConsent(cwd, void 0);
|
|
15263
15908
|
if (!consent.allowed) {
|
|
@@ -15294,7 +15939,7 @@ function createTestCommand() {
|
|
|
15294
15939
|
|
|
15295
15940
|
// src/commands/telemetry/index.ts
|
|
15296
15941
|
function createTelemetryCommand2() {
|
|
15297
|
-
const command = new
|
|
15942
|
+
const command = new Command125("telemetry").description("Telemetry identity and status management");
|
|
15298
15943
|
command.addCommand(createIdentifyCommand());
|
|
15299
15944
|
command.addCommand(createStatusCommand2());
|
|
15300
15945
|
command.addCommand(createTestCommand());
|
|
@@ -15302,9 +15947,9 @@ function createTelemetryCommand2() {
|
|
|
15302
15947
|
}
|
|
15303
15948
|
|
|
15304
15949
|
// src/commands/test-craft.ts
|
|
15305
|
-
import { Command as
|
|
15950
|
+
import { Command as Command126 } from "commander";
|
|
15306
15951
|
function createTestCraftCommand() {
|
|
15307
|
-
return new
|
|
15952
|
+
return new Command126("test-craft").description(
|
|
15308
15953
|
"LLM-judgment critique of test quality across vitest/jest/mocha/playwright. Fourth craft-pipeline ceiling skill. Per-test critique with best-effort source pairing."
|
|
15309
15954
|
).option("-f, --files <files...>", "Optional test file/glob scope").option("--frameworks <names...>", "Restrict to: vitest / jest / mocha / playwright").option("--max-files <n>", "Cap test file count (default: 100)").option("--max-tests-per-file <n>", "Cap per-file test critique (default: 20)").option("--no-source-pair", "Skip source-pairing resolution").action(async (opts, cmd) => {
|
|
15310
15955
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -15352,10 +15997,10 @@ function printResult7(result, mode, _formatter) {
|
|
|
15352
15997
|
list.push(f);
|
|
15353
15998
|
byFile.set(f.target.file, list);
|
|
15354
15999
|
}
|
|
15355
|
-
for (const [file,
|
|
16000
|
+
for (const [file, fs52] of byFile) {
|
|
15356
16001
|
console.log(`
|
|
15357
16002
|
${file}`);
|
|
15358
|
-
for (const f of
|
|
16003
|
+
for (const f of fs52) {
|
|
15359
16004
|
const nestingStr = f.target.nesting.length > 0 ? f.target.nesting.join(" > ") + " > " : "";
|
|
15360
16005
|
console.log(
|
|
15361
16006
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.framework}:${f.target.line}`
|
|
@@ -15374,7 +16019,7 @@ ${file}`);
|
|
|
15374
16019
|
}
|
|
15375
16020
|
|
|
15376
16021
|
// src/commands/traceability.ts
|
|
15377
|
-
import { Command as
|
|
16022
|
+
import { Command as Command127 } from "commander";
|
|
15378
16023
|
import chalk19 from "chalk";
|
|
15379
16024
|
function confidenceLabel(maxConfidence) {
|
|
15380
16025
|
if (maxConfidence >= 0.8) return "explicit";
|
|
@@ -15514,7 +16159,7 @@ async function runTraceability(options) {
|
|
|
15514
16159
|
process.exit(ExitCode.SUCCESS);
|
|
15515
16160
|
}
|
|
15516
16161
|
function createTraceabilityCommand() {
|
|
15517
|
-
return new
|
|
16162
|
+
return new Command127("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) => {
|
|
15518
16163
|
const globalOpts = cmd.optsWithGlobals();
|
|
15519
16164
|
await runTraceability({
|
|
15520
16165
|
spec: opts.spec,
|
|
@@ -15527,15 +16172,15 @@ function createTraceabilityCommand() {
|
|
|
15527
16172
|
}
|
|
15528
16173
|
|
|
15529
16174
|
// src/commands/uninstall.ts
|
|
15530
|
-
import * as
|
|
15531
|
-
import { Command as
|
|
16175
|
+
import * as path79 from "path";
|
|
16176
|
+
import { Command as Command128 } from "commander";
|
|
15532
16177
|
async function runUninstall(skillName, options) {
|
|
15533
16178
|
const packageName = resolvePackageName(skillName);
|
|
15534
16179
|
const shortName = extractSkillName(packageName);
|
|
15535
16180
|
const globalDir = resolveGlobalSkillsDir();
|
|
15536
|
-
const skillsDir =
|
|
15537
|
-
const communityBase =
|
|
15538
|
-
const lockfilePath =
|
|
16181
|
+
const skillsDir = path79.dirname(globalDir);
|
|
16182
|
+
const communityBase = path79.join(skillsDir, "community");
|
|
16183
|
+
const lockfilePath = path79.join(communityBase, "skills-lock.json");
|
|
15539
16184
|
const lockfile = readLockfile2(lockfilePath);
|
|
15540
16185
|
const entry = lockfile.skills[packageName];
|
|
15541
16186
|
if (!entry) {
|
|
@@ -15565,7 +16210,7 @@ async function runUninstall(skillName, options) {
|
|
|
15565
16210
|
return result;
|
|
15566
16211
|
}
|
|
15567
16212
|
function createUninstallCommand() {
|
|
15568
|
-
const cmd = new
|
|
16213
|
+
const cmd = new Command128("uninstall");
|
|
15569
16214
|
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) => {
|
|
15570
16215
|
try {
|
|
15571
16216
|
const result = await runUninstall(skill, opts);
|
|
@@ -15585,9 +16230,9 @@ function createUninstallCommand() {
|
|
|
15585
16230
|
|
|
15586
16231
|
// src/commands/uninstall-constraints.ts
|
|
15587
16232
|
init_dist3();
|
|
15588
|
-
import * as
|
|
15589
|
-
import * as
|
|
15590
|
-
import { Command as
|
|
16233
|
+
import * as fs50 from "fs/promises";
|
|
16234
|
+
import * as path80 from "path";
|
|
16235
|
+
import { Command as Command129 } from "commander";
|
|
15591
16236
|
async function runUninstallConstraints(options) {
|
|
15592
16237
|
const { packageName, configPath, lockfilePath } = options;
|
|
15593
16238
|
const lockfileResult = await readLockfile(lockfilePath);
|
|
@@ -15607,7 +16252,7 @@ async function runUninstallConstraints(options) {
|
|
|
15607
16252
|
}
|
|
15608
16253
|
let localConfig;
|
|
15609
16254
|
try {
|
|
15610
|
-
const raw = await
|
|
16255
|
+
const raw = await fs50.readFile(configPath, "utf-8");
|
|
15611
16256
|
localConfig = JSON.parse(raw);
|
|
15612
16257
|
} catch (err) {
|
|
15613
16258
|
return {
|
|
@@ -15644,11 +16289,11 @@ async function runUninstallConstraints(options) {
|
|
|
15644
16289
|
};
|
|
15645
16290
|
}
|
|
15646
16291
|
function createUninstallConstraintsCommand() {
|
|
15647
|
-
const cmd = new
|
|
16292
|
+
const cmd = new Command129("uninstall-constraints");
|
|
15648
16293
|
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) => {
|
|
15649
16294
|
let configPath;
|
|
15650
16295
|
if (opts.config) {
|
|
15651
|
-
configPath =
|
|
16296
|
+
configPath = path80.resolve(opts.config);
|
|
15652
16297
|
} else {
|
|
15653
16298
|
const found = findConfigFile();
|
|
15654
16299
|
if (!found.ok) {
|
|
@@ -15657,8 +16302,8 @@ function createUninstallConstraintsCommand() {
|
|
|
15657
16302
|
}
|
|
15658
16303
|
configPath = found.value;
|
|
15659
16304
|
}
|
|
15660
|
-
const projectRoot3 =
|
|
15661
|
-
const lockfilePath =
|
|
16305
|
+
const projectRoot3 = path80.dirname(configPath);
|
|
16306
|
+
const lockfilePath = path80.join(projectRoot3, ".harness", "constraints.lock.json");
|
|
15662
16307
|
const result = await runUninstallConstraints({
|
|
15663
16308
|
packageName: name,
|
|
15664
16309
|
configPath,
|
|
@@ -15684,10 +16329,10 @@ function createUninstallConstraintsCommand() {
|
|
|
15684
16329
|
|
|
15685
16330
|
// src/commands/update.ts
|
|
15686
16331
|
init_dist3();
|
|
15687
|
-
import { Command as
|
|
16332
|
+
import { Command as Command130 } from "commander";
|
|
15688
16333
|
import { execFile as execFile2, execFileSync as execFileSync13 } from "child_process";
|
|
15689
|
-
import { realpathSync, existsSync as
|
|
15690
|
-
import { dirname as dirname25, join as
|
|
16334
|
+
import { realpathSync, existsSync as existsSync46, readFileSync as readFileSync34 } from "fs";
|
|
16335
|
+
import { dirname as dirname25, join as join69 } from "path";
|
|
15691
16336
|
import { promisify as promisify2 } from "util";
|
|
15692
16337
|
import readline4 from "readline";
|
|
15693
16338
|
import chalk20 from "chalk";
|
|
@@ -15778,9 +16423,9 @@ function prompt3(question) {
|
|
|
15778
16423
|
function findPackageDir(resolvedBin) {
|
|
15779
16424
|
let dir = dirname25(resolvedBin);
|
|
15780
16425
|
for (let i = 0; i < 6; i++) {
|
|
15781
|
-
if (
|
|
16426
|
+
if (existsSync46(join69(dir, "package.json"))) {
|
|
15782
16427
|
try {
|
|
15783
|
-
const pkg = JSON.parse(
|
|
16428
|
+
const pkg = JSON.parse(readFileSync34(join69(dir, "package.json"), "utf-8"));
|
|
15784
16429
|
if (pkg?.name === CLI_PACKAGE) return dir;
|
|
15785
16430
|
} catch {
|
|
15786
16431
|
}
|
|
@@ -15825,7 +16470,7 @@ function findAllInstalls() {
|
|
|
15825
16470
|
if (!packageDir) continue;
|
|
15826
16471
|
let version = null;
|
|
15827
16472
|
try {
|
|
15828
|
-
const pkg = JSON.parse(
|
|
16473
|
+
const pkg = JSON.parse(readFileSync34(join69(packageDir, "package.json"), "utf-8"));
|
|
15829
16474
|
if (typeof pkg?.version === "string") version = pkg.version;
|
|
15830
16475
|
} catch {
|
|
15831
16476
|
}
|
|
@@ -15918,12 +16563,12 @@ async function ensureTelemetryIfNeeded() {
|
|
|
15918
16563
|
}
|
|
15919
16564
|
function refreshHooks() {
|
|
15920
16565
|
const cwd = process.cwd();
|
|
15921
|
-
const configPath =
|
|
15922
|
-
if (!
|
|
16566
|
+
const configPath = join69(cwd, "harness.config.json");
|
|
16567
|
+
if (!existsSync46(configPath)) return;
|
|
15923
16568
|
let profile = "standard";
|
|
15924
|
-
const profilePath =
|
|
16569
|
+
const profilePath = join69(cwd, ".harness", "hooks", "profile.json");
|
|
15925
16570
|
try {
|
|
15926
|
-
const data = JSON.parse(
|
|
16571
|
+
const data = JSON.parse(readFileSync34(profilePath, "utf-8"));
|
|
15927
16572
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
15928
16573
|
profile = data.profile;
|
|
15929
16574
|
}
|
|
@@ -16052,7 +16697,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
16052
16697
|
process.exit(ExitCode.SUCCESS);
|
|
16053
16698
|
}
|
|
16054
16699
|
function createUpdateCommand() {
|
|
16055
|
-
return new
|
|
16700
|
+
return new Command130("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(
|
|
16056
16701
|
"--regenerate",
|
|
16057
16702
|
"Only regenerate slash commands and agent definitions (skip package updates)"
|
|
16058
16703
|
).action(async (opts, cmd) => {
|
|
@@ -16062,9 +16707,9 @@ function createUpdateCommand() {
|
|
|
16062
16707
|
}
|
|
16063
16708
|
|
|
16064
16709
|
// src/commands/usage.ts
|
|
16065
|
-
import { Command as
|
|
16710
|
+
import { Command as Command131 } from "commander";
|
|
16066
16711
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
16067
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
16712
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-DTXVNQAY.js");
|
|
16068
16713
|
const records = readCostRecords(cwd);
|
|
16069
16714
|
if (includeClaudeSessions) {
|
|
16070
16715
|
const ccRecords = parseCCRecords();
|
|
@@ -16157,7 +16802,7 @@ function registerDailyCommand(usage) {
|
|
|
16157
16802
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
16158
16803
|
return;
|
|
16159
16804
|
}
|
|
16160
|
-
const { aggregateByDay } = await import("./dist-
|
|
16805
|
+
const { aggregateByDay } = await import("./dist-DTXVNQAY.js");
|
|
16161
16806
|
const limited = aggregateByDay(records).slice(0, days);
|
|
16162
16807
|
if (globalOpts.json) {
|
|
16163
16808
|
console.log(
|
|
@@ -16186,7 +16831,7 @@ function registerSessionsCommand(usage) {
|
|
|
16186
16831
|
}
|
|
16187
16832
|
return;
|
|
16188
16833
|
}
|
|
16189
|
-
const { aggregateBySession } = await import("./dist-
|
|
16834
|
+
const { aggregateBySession } = await import("./dist-DTXVNQAY.js");
|
|
16190
16835
|
const sessionData = aggregateBySession(records);
|
|
16191
16836
|
const limited = sessionData.slice(0, limit);
|
|
16192
16837
|
if (globalOpts.json) {
|
|
@@ -16266,7 +16911,7 @@ function registerSessionCommand(usage) {
|
|
|
16266
16911
|
cwd,
|
|
16267
16912
|
globalOpts.includeClaudeSessions
|
|
16268
16913
|
);
|
|
16269
|
-
const { aggregateBySession } = await import("./dist-
|
|
16914
|
+
const { aggregateBySession } = await import("./dist-DTXVNQAY.js");
|
|
16270
16915
|
const sessionData = aggregateBySession(records);
|
|
16271
16916
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
16272
16917
|
if (!match) {
|
|
@@ -16301,7 +16946,7 @@ function registerLatestCommand(usage) {
|
|
|
16301
16946
|
}
|
|
16302
16947
|
return;
|
|
16303
16948
|
}
|
|
16304
|
-
const { aggregateBySession } = await import("./dist-
|
|
16949
|
+
const { aggregateBySession } = await import("./dist-DTXVNQAY.js");
|
|
16305
16950
|
const sessionData = aggregateBySession(records);
|
|
16306
16951
|
const latest = sessionData[0];
|
|
16307
16952
|
if (!latest) {
|
|
@@ -16327,7 +16972,7 @@ function registerLatestCommand(usage) {
|
|
|
16327
16972
|
});
|
|
16328
16973
|
}
|
|
16329
16974
|
function createUsageCommand() {
|
|
16330
|
-
const usage = new
|
|
16975
|
+
const usage = new Command131("usage").description("Token usage and cost tracking");
|
|
16331
16976
|
usage.option(
|
|
16332
16977
|
"--include-claude-sessions",
|
|
16333
16978
|
"Include Claude Code session data from ~/.claude/projects/"
|
|
@@ -16342,9 +16987,9 @@ function createUsageCommand() {
|
|
|
16342
16987
|
// src/commands/validate.ts
|
|
16343
16988
|
init_dist3();
|
|
16344
16989
|
init_dist3();
|
|
16345
|
-
import { Command as
|
|
16346
|
-
import * as
|
|
16347
|
-
import * as
|
|
16990
|
+
import { Command as Command132 } from "commander";
|
|
16991
|
+
import * as fs51 from "fs";
|
|
16992
|
+
import * as path81 from "path";
|
|
16348
16993
|
import { execFileSync as execFileSync14 } from "child_process";
|
|
16349
16994
|
async function runValidate(options) {
|
|
16350
16995
|
const configResult = resolveConfig(options.configPath);
|
|
@@ -16352,7 +16997,7 @@ async function runValidate(options) {
|
|
|
16352
16997
|
return configResult;
|
|
16353
16998
|
}
|
|
16354
16999
|
const config = configResult.value;
|
|
16355
|
-
const cwd = options.cwd ?? (options.configPath ?
|
|
17000
|
+
const cwd = options.cwd ?? (options.configPath ? path81.dirname(path81.resolve(options.configPath)) : process.cwd());
|
|
16356
17001
|
const result = {
|
|
16357
17002
|
valid: true,
|
|
16358
17003
|
checks: {
|
|
@@ -16362,7 +17007,7 @@ async function runValidate(options) {
|
|
|
16362
17007
|
},
|
|
16363
17008
|
issues: []
|
|
16364
17009
|
};
|
|
16365
|
-
const agentsMapPath =
|
|
17010
|
+
const agentsMapPath = path81.resolve(cwd, config.agentsMapPath);
|
|
16366
17011
|
const agentsResult = await validateAgentsMap(agentsMapPath);
|
|
16367
17012
|
if (agentsResult.ok) {
|
|
16368
17013
|
result.checks.agentsMap = true;
|
|
@@ -16459,9 +17104,9 @@ async function runValidate(options) {
|
|
|
16459
17104
|
}
|
|
16460
17105
|
});
|
|
16461
17106
|
}
|
|
16462
|
-
const gitattributesPath =
|
|
16463
|
-
if (
|
|
16464
|
-
const gitattributesContent =
|
|
17107
|
+
const gitattributesPath = path81.join(cwd, ".gitattributes");
|
|
17108
|
+
if (fs51.existsSync(gitattributesPath)) {
|
|
17109
|
+
const gitattributesContent = fs51.readFileSync(gitattributesPath, "utf-8");
|
|
16465
17110
|
let driverConfigured = false;
|
|
16466
17111
|
try {
|
|
16467
17112
|
const out = execFileSync14("git", ["config", "--get", "merge.ours.driver"], {
|
|
@@ -16485,10 +17130,10 @@ async function runValidate(options) {
|
|
|
16485
17130
|
}
|
|
16486
17131
|
}
|
|
16487
17132
|
if (detectRoadmapStorageMode(cwd) === "sharded") {
|
|
16488
|
-
const shardDir =
|
|
17133
|
+
const shardDir = path81.join(cwd, "docs", "roadmap.d");
|
|
16489
17134
|
const regenerated = await regenerate(shardDir, createNodeRoadmapIO());
|
|
16490
|
-
const aggregatePath =
|
|
16491
|
-
const committedAggregate =
|
|
17135
|
+
const aggregatePath = path81.join(cwd, "docs", "roadmap.md");
|
|
17136
|
+
const committedAggregate = fs51.existsSync(aggregatePath) ? fs51.readFileSync(aggregatePath, "utf-8") : null;
|
|
16492
17137
|
const drift = checkRoadmapAggregateDrift({
|
|
16493
17138
|
shardDirExists: true,
|
|
16494
17139
|
committedAggregate,
|
|
@@ -16506,9 +17151,9 @@ async function runValidate(options) {
|
|
|
16506
17151
|
result.checks.roadmapAggregateDrift = true;
|
|
16507
17152
|
}
|
|
16508
17153
|
}
|
|
16509
|
-
const roadmapPath =
|
|
16510
|
-
if (
|
|
16511
|
-
const parsed = parseRoadmap(
|
|
17154
|
+
const roadmapPath = path81.join(cwd, "docs", "roadmap.md");
|
|
17155
|
+
if (fs51.existsSync(roadmapPath)) {
|
|
17156
|
+
const parsed = parseRoadmap(fs51.readFileSync(roadmapPath, "utf-8"));
|
|
16512
17157
|
if (parsed.ok) {
|
|
16513
17158
|
const findings = checkRoadmapHealth(parsed.value);
|
|
16514
17159
|
result.checks.roadmapHealth = !findings.some((f) => f.severity === "error");
|
|
@@ -16650,11 +17295,11 @@ function resolveValidateMode(globalOpts) {
|
|
|
16650
17295
|
return OutputMode.TEXT;
|
|
16651
17296
|
}
|
|
16652
17297
|
async function printCrossCheckWarnings(mode) {
|
|
16653
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
17298
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-QJGCDWRO.js");
|
|
16654
17299
|
const cwd = process.cwd();
|
|
16655
17300
|
const crossResult = await runCrossCheck2({
|
|
16656
|
-
specsDir:
|
|
16657
|
-
plansDir:
|
|
17301
|
+
specsDir: path81.join(cwd, "docs", "specs"),
|
|
17302
|
+
plansDir: path81.join(cwd, "docs", "plans"),
|
|
16658
17303
|
projectPath: cwd
|
|
16659
17304
|
});
|
|
16660
17305
|
if (!crossResult.ok || crossResult.value.warnings === 0) return;
|
|
@@ -16666,7 +17311,7 @@ async function printCrossCheckWarnings(mode) {
|
|
|
16666
17311
|
${crossResult.value.warnings} warnings`);
|
|
16667
17312
|
}
|
|
16668
17313
|
function createValidateCommand3() {
|
|
16669
|
-
const command = new
|
|
17314
|
+
const command = new Command132("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
|
|
16670
17315
|
"--agent-configs",
|
|
16671
17316
|
"Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
|
|
16672
17317
|
).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()));
|
|
@@ -16715,7 +17360,7 @@ Agent configs checked via ${engineLabel}${note}`);
|
|
|
16715
17360
|
|
|
16716
17361
|
// src/commands/verify.ts
|
|
16717
17362
|
init_dist3();
|
|
16718
|
-
import { Command as
|
|
17363
|
+
import { Command as Command133 } from "commander";
|
|
16719
17364
|
import { execSync as execSync6 } from "child_process";
|
|
16720
17365
|
function loadBranchingConfig(configPath) {
|
|
16721
17366
|
const shouldLoad = configPath !== void 0 || findConfigFile().ok;
|
|
@@ -16778,7 +17423,7 @@ async function runVerify(options) {
|
|
|
16778
17423
|
process.exit(ExitCode.VALIDATION_FAILED);
|
|
16779
17424
|
}
|
|
16780
17425
|
function createVerifyCommand() {
|
|
16781
|
-
return new
|
|
17426
|
+
return new Command133("verify").description(
|
|
16782
17427
|
"Verify project conventions (currently: branch naming). Works with or without a harness.config.json."
|
|
16783
17428
|
).option(
|
|
16784
17429
|
"--branch <name>",
|
|
@@ -16906,7 +17551,7 @@ function registerDeprecatedGraphAliases(program) {
|
|
|
16906
17551
|
|
|
16907
17552
|
// src/index.ts
|
|
16908
17553
|
function createProgram() {
|
|
16909
|
-
const program = new
|
|
17554
|
+
const program = new Command134();
|
|
16910
17555
|
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");
|
|
16911
17556
|
for (const creator of commandCreators) {
|
|
16912
17557
|
program.addCommand(creator());
|