@harness-engineering/cli 6.0.0 → 7.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-6Y6M3AZA.js → chunk-N3S6JTRS.js} +2 -2
- package/dist/{chunk-VGDZAWCJ.js → chunk-N4VEUBJ5.js} +934 -377
- 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 +745 -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,570 @@ 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
|
+
async function runTriageReport(roadmap, deps = {}) {
|
|
11991
|
+
const features = roadmap.milestones.flatMap((m) => m.features).filter(isActionable);
|
|
11992
|
+
const rows = [];
|
|
11993
|
+
for (const feature of features) {
|
|
11994
|
+
const verdict = await triageIssue(featureToIssue(feature), {
|
|
11995
|
+
...deps.graphStore ? { graphStore: deps.graphStore } : {},
|
|
11996
|
+
...deps.precedent ? { precedent: deps.precedent } : {},
|
|
11997
|
+
...deps.config ? { config: deps.config } : {}
|
|
11998
|
+
});
|
|
11999
|
+
rows.push({
|
|
12000
|
+
externalId: verdict.externalId,
|
|
12001
|
+
name: feature.name,
|
|
12002
|
+
status: feature.status,
|
|
12003
|
+
...rankingInputs(feature, verdict),
|
|
12004
|
+
verdict
|
|
12005
|
+
});
|
|
12006
|
+
}
|
|
12007
|
+
return rankTriageCandidates(rows);
|
|
12008
|
+
}
|
|
12009
|
+
function renderHuman(rows) {
|
|
12010
|
+
if (rows.length === 0) return "No actionable roadmap items to triage.";
|
|
12011
|
+
const lines = [];
|
|
12012
|
+
lines.push(`Triaged ${rows.length} actionable item(s):
|
|
12013
|
+
`);
|
|
12014
|
+
for (const row of rows) {
|
|
12015
|
+
const v = row.verdict;
|
|
12016
|
+
const badge = v.dispatchable ? "\u2713 DISPATCHABLE" : `\u2717 HELD (${v.holdReason})`;
|
|
12017
|
+
lines.push(`${badge} ${row.name}`);
|
|
12018
|
+
lines.push(
|
|
12019
|
+
` level=${v.verdict.level} confidence=${v.verdict.confidence} impact=${row.impact} effort=${row.effort}`
|
|
12020
|
+
);
|
|
12021
|
+
lines.push(` ${v.rationale}`);
|
|
12022
|
+
lines.push("");
|
|
12023
|
+
}
|
|
12024
|
+
const dispatchable = rows.filter((r) => r.verdict.dispatchable).length;
|
|
12025
|
+
lines.push(
|
|
12026
|
+
`${dispatchable}/${rows.length} dispatchable; ${rows.length - dispatchable} to human.`
|
|
12027
|
+
);
|
|
12028
|
+
return lines.join("\n");
|
|
12029
|
+
}
|
|
12030
|
+
function renderJson(rows) {
|
|
12031
|
+
return JSON.stringify(
|
|
12032
|
+
{
|
|
12033
|
+
count: rows.length,
|
|
12034
|
+
dispatchable: rows.filter((r) => r.verdict.dispatchable).length,
|
|
12035
|
+
items: rows.map((r) => ({
|
|
12036
|
+
externalId: r.externalId,
|
|
12037
|
+
name: r.name,
|
|
12038
|
+
status: r.status,
|
|
12039
|
+
dispatchable: r.verdict.dispatchable,
|
|
12040
|
+
holdReason: r.verdict.holdReason ?? null,
|
|
12041
|
+
level: r.verdict.verdict.level,
|
|
12042
|
+
confidence: r.verdict.verdict.confidence,
|
|
12043
|
+
impact: r.impact,
|
|
12044
|
+
effort: r.effort,
|
|
12045
|
+
pilotInputs: { impact: r.impact, confidence: r.confidence, effort: r.effort },
|
|
12046
|
+
levers: r.verdict.levers,
|
|
12047
|
+
rationale: r.verdict.rationale
|
|
12048
|
+
}))
|
|
12049
|
+
},
|
|
12050
|
+
null,
|
|
12051
|
+
2
|
|
12052
|
+
);
|
|
12053
|
+
}
|
|
12054
|
+
async function runBrainstormReport(roadmap, deps = {}) {
|
|
12055
|
+
const features = roadmap.milestones.flatMap((m) => m.features).filter(isActionable);
|
|
12056
|
+
const rows = [];
|
|
12057
|
+
for (const feature of features) {
|
|
12058
|
+
const issue = featureToIssue(feature);
|
|
12059
|
+
const verdict = await triageIssue(issue, {
|
|
12060
|
+
...deps.graphStore ? { graphStore: deps.graphStore } : {},
|
|
12061
|
+
...deps.provider ? { provider: deps.provider } : {},
|
|
12062
|
+
...deps.precedent ? { precedent: deps.precedent } : {},
|
|
12063
|
+
...deps.config ? { config: deps.config } : {}
|
|
12064
|
+
});
|
|
12065
|
+
const level = verdict.verdict.level;
|
|
12066
|
+
const wiringDeps = {
|
|
12067
|
+
...deps.generator ? { generator: deps.generator } : {},
|
|
12068
|
+
...deps.provider ? { provider: deps.provider } : {},
|
|
12069
|
+
...deps.generatorOptions ? { generatorOptions: deps.generatorOptions } : {},
|
|
12070
|
+
...deps.docsRoot ? { docsRoot: deps.docsRoot } : {},
|
|
12071
|
+
// Re-score the enriched item on completion (SC4), reusing the same seams — the
|
|
12072
|
+
// precedent lever included so the RE-SCORE (the prediction the marker records) is
|
|
12073
|
+
// graded against real base-rates too (SC5).
|
|
12074
|
+
rescore: {
|
|
12075
|
+
...deps.graphStore ? { graphStore: deps.graphStore } : {},
|
|
12076
|
+
...deps.provider ? { provider: deps.provider } : {},
|
|
12077
|
+
...deps.precedent ? { precedent: deps.precedent } : {},
|
|
12078
|
+
...deps.config ? { config: deps.config } : {}
|
|
12079
|
+
}
|
|
12080
|
+
};
|
|
12081
|
+
const result = await runBrainstormForIssue(issue, level, wiringDeps);
|
|
12082
|
+
rows.push({
|
|
12083
|
+
externalId: verdict.externalId,
|
|
12084
|
+
name: feature.name,
|
|
12085
|
+
status: feature.status,
|
|
12086
|
+
level,
|
|
12087
|
+
result
|
|
12088
|
+
});
|
|
12089
|
+
}
|
|
12090
|
+
return rows;
|
|
12091
|
+
}
|
|
12092
|
+
function renderBrainstormHuman(rows) {
|
|
12093
|
+
if (rows.length === 0) return "No actionable roadmap items to brainstorm.";
|
|
12094
|
+
const lines = [];
|
|
12095
|
+
lines.push(`Brainstormed ${rows.length} actionable item(s):
|
|
12096
|
+
`);
|
|
12097
|
+
for (const row of rows) {
|
|
12098
|
+
const o = row.result.outcome;
|
|
12099
|
+
if (o.kind === "completed") {
|
|
12100
|
+
const where = row.result.specPath ? ` \u2192 ${row.result.specPath}` : " (dry run \u2014 no docs root)";
|
|
12101
|
+
lines.push(`\u2713 SPEC DRAFTED ${row.name} [${row.level}]${where}`);
|
|
12102
|
+
lines.push(` ${o.spec.decisions.length} fork(s) resolved at high confidence`);
|
|
12103
|
+
if (row.result.rescore) {
|
|
12104
|
+
const rv = row.result.rescore;
|
|
12105
|
+
lines.push(
|
|
12106
|
+
` re-score: ${rv.dispatchable ? "DISPATCHABLE" : `held (${rv.holdReason})`} \u2014 ${rv.verdict.level}/${rv.verdict.confidence}`
|
|
12107
|
+
);
|
|
12108
|
+
}
|
|
12109
|
+
} else {
|
|
12110
|
+
lines.push(`\u2717 HALTED \u2192 HUMAN ${row.name} [${row.level}] (${o.reason})`);
|
|
12111
|
+
lines.push(` fork: ${o.fork.question || o.fork.id}`);
|
|
12112
|
+
lines.push(` ${o.detail}`);
|
|
12113
|
+
}
|
|
12114
|
+
lines.push("");
|
|
12115
|
+
}
|
|
12116
|
+
const completed = rows.filter((r) => r.result.outcome.kind === "completed").length;
|
|
12117
|
+
lines.push(
|
|
12118
|
+
`${completed}/${rows.length} spec(s) drafted; ${rows.length - completed} halted to human.`
|
|
12119
|
+
);
|
|
12120
|
+
return lines.join("\n");
|
|
12121
|
+
}
|
|
12122
|
+
function renderBrainstormJson(rows) {
|
|
12123
|
+
return JSON.stringify(
|
|
12124
|
+
{
|
|
12125
|
+
count: rows.length,
|
|
12126
|
+
drafted: rows.filter((r) => r.result.outcome.kind === "completed").length,
|
|
12127
|
+
items: rows.map((r) => {
|
|
12128
|
+
const o = r.result.outcome;
|
|
12129
|
+
return {
|
|
12130
|
+
externalId: r.externalId,
|
|
12131
|
+
name: r.name,
|
|
12132
|
+
status: r.status,
|
|
12133
|
+
level: r.level,
|
|
12134
|
+
outcome: o.kind,
|
|
12135
|
+
...o.kind === "completed" ? {
|
|
12136
|
+
specPath: r.result.specPath ?? null,
|
|
12137
|
+
forks: o.spec.decisions.map((d) => ({
|
|
12138
|
+
id: d.fork.id,
|
|
12139
|
+
question: d.fork.question,
|
|
12140
|
+
recommendation: d.recommendation,
|
|
12141
|
+
confidence: d.confidence
|
|
12142
|
+
})),
|
|
12143
|
+
rescore: r.result.rescore ? {
|
|
12144
|
+
dispatchable: r.result.rescore.dispatchable,
|
|
12145
|
+
holdReason: r.result.rescore.holdReason ?? null,
|
|
12146
|
+
level: r.result.rescore.verdict.level,
|
|
12147
|
+
confidence: r.result.rescore.verdict.confidence
|
|
12148
|
+
} : null
|
|
12149
|
+
} : {
|
|
12150
|
+
halt: {
|
|
12151
|
+
reason: o.reason,
|
|
12152
|
+
fork: { id: o.fork.id, question: o.fork.question },
|
|
12153
|
+
detail: o.detail
|
|
12154
|
+
}
|
|
12155
|
+
}
|
|
12156
|
+
};
|
|
12157
|
+
})
|
|
12158
|
+
},
|
|
12159
|
+
null,
|
|
12160
|
+
2
|
|
12161
|
+
);
|
|
12162
|
+
}
|
|
12163
|
+
function resolveBrainstormProvider(configPath, model) {
|
|
12164
|
+
const configResult = resolveConfig(configPath);
|
|
12165
|
+
if (!configResult.ok) return null;
|
|
12166
|
+
return resolveTriageProvider(configResult.value, model);
|
|
12167
|
+
}
|
|
12168
|
+
async function runApproveCommand(cwd, opts) {
|
|
12169
|
+
const configResult = resolveConfig(opts.config);
|
|
12170
|
+
const cfg = configResult.ok ? configResult.value : void 0;
|
|
12171
|
+
const autoTriage = cfg?.roadmap?.autoTriage;
|
|
12172
|
+
if (!autoTriage || autoTriage.enabled !== true) {
|
|
12173
|
+
return { marked: [], held: [], note: "disabled" };
|
|
12174
|
+
}
|
|
12175
|
+
const stage = autoTriage.ratchetStage ?? 1;
|
|
12176
|
+
const roadmapPath = path60.join(cwd, "docs", "roadmap.md");
|
|
12177
|
+
if (!fs36.existsSync(roadmapPath)) {
|
|
12178
|
+
return { marked: [], held: [], note: "no-roadmap" };
|
|
12179
|
+
}
|
|
12180
|
+
const parsed = parseRoadmap(fs36.readFileSync(roadmapPath, "utf-8"));
|
|
12181
|
+
if (!parsed.ok) return { marked: [], held: [], note: "roadmap-parse-error" };
|
|
12182
|
+
const graphStore = await loadGraphStore(cwd);
|
|
12183
|
+
const provider = resolveBrainstormProvider(opts.config);
|
|
12184
|
+
const precedent = await buildPrecedentLookup(cwd);
|
|
12185
|
+
const rows = await runBrainstormReport(parsed.value, {
|
|
12186
|
+
...graphStore ? { graphStore } : {},
|
|
12187
|
+
...provider ? { provider } : {},
|
|
12188
|
+
...precedent ? { precedent } : {},
|
|
12189
|
+
docsRoot: path60.join(cwd, "docs")
|
|
12190
|
+
});
|
|
12191
|
+
const ready = deriveReadyCandidates(rows, parsed.value);
|
|
12192
|
+
const approvedIds = new Set(
|
|
12193
|
+
(opts.approve ?? "").split(",").map((s) => s.trim()).filter((s) => s.length > 0)
|
|
12194
|
+
);
|
|
12195
|
+
const historyForShape = await buildShapeHistory(cwd);
|
|
12196
|
+
const plan = buildApprovalPlan(ready, {
|
|
12197
|
+
approvedIds,
|
|
12198
|
+
...opts.approveAll ? { approveAll: true } : {},
|
|
12199
|
+
stage,
|
|
12200
|
+
historyForShape
|
|
12201
|
+
});
|
|
12202
|
+
const store = resolveRoadmapStoreForFile({ roadmapPath });
|
|
12203
|
+
const recordPrediction = (payload) => event_sourcing_exports.recordTriagePrediction(cwd, payload);
|
|
12204
|
+
const result = await markApprovedForDispatch(plan.toMark, {
|
|
12205
|
+
store,
|
|
12206
|
+
config: { enabled: true, ratchetStage: stage },
|
|
12207
|
+
recordPrediction
|
|
12208
|
+
});
|
|
12209
|
+
if (!result.ok) {
|
|
12210
|
+
return { marked: [], held: plan.held, note: `mark-failed: ${result.error.message}` };
|
|
12211
|
+
}
|
|
12212
|
+
return { marked: result.value.marked, held: plan.held };
|
|
12213
|
+
}
|
|
12214
|
+
function renderApproveHuman(res) {
|
|
12215
|
+
if (res.note === "disabled") {
|
|
12216
|
+
return "Roadmap auto-triage is disabled (roadmap.autoTriage.enabled is not true). Enable it to use `triage approve`. No changes made.";
|
|
12217
|
+
}
|
|
12218
|
+
if (res.note === "no-roadmap") return "No roadmap aggregate at docs/roadmap.md. No changes made.";
|
|
12219
|
+
if (res.note === "roadmap-parse-error") return "Failed to parse roadmap. No changes made.";
|
|
12220
|
+
const lines = [];
|
|
12221
|
+
if (res.marked.length > 0) {
|
|
12222
|
+
lines.push(`Marked ${res.marked.length} item(s) eligible for orchestrator pickup:`);
|
|
12223
|
+
for (const id of res.marked) lines.push(` \u2713 ${id}`);
|
|
12224
|
+
} else {
|
|
12225
|
+
lines.push("No items marked.");
|
|
12226
|
+
}
|
|
12227
|
+
if (res.held.length > 0) {
|
|
12228
|
+
lines.push(`
|
|
12229
|
+
Held to human (${res.held.length}):`);
|
|
12230
|
+
for (const h of res.held) lines.push(` \u2717 ${h.externalId} \u2014 ${h.reason}`);
|
|
12231
|
+
}
|
|
12232
|
+
if (res.note && res.note.startsWith("mark-failed")) lines.push(`
|
|
12233
|
+
ERROR: ${res.note}`);
|
|
12234
|
+
lines.push(
|
|
12235
|
+
"\nApproved items are NOT dispatched here \u2014 the existing orchestrator pickup loop dispatches them through its normal gating on its next tick."
|
|
12236
|
+
);
|
|
12237
|
+
return lines.join("\n");
|
|
12238
|
+
}
|
|
12239
|
+
function createTriageApproveCommand() {
|
|
12240
|
+
return new Command89("approve").description(
|
|
12241
|
+
"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)."
|
|
12242
|
+
).option(
|
|
12243
|
+
"--approve <ids>",
|
|
12244
|
+
"Comma-separated externalIds to approve (the explicit human go). Omit to mark nothing."
|
|
12245
|
+
).option("--approve-all", "Approve every READY candidate (still subject to the category gate).").action(async (opts, cmd) => {
|
|
12246
|
+
const cwd = process.cwd();
|
|
12247
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
12248
|
+
const res = await runApproveCommand(cwd, {
|
|
12249
|
+
...opts,
|
|
12250
|
+
...globalOpts.config !== void 0 ? { config: globalOpts.config } : {}
|
|
12251
|
+
});
|
|
12252
|
+
if (globalOpts.json) {
|
|
12253
|
+
process.stdout.write(JSON.stringify(res, null, 2) + "\n");
|
|
12254
|
+
} else {
|
|
12255
|
+
logger.info(renderApproveHuman(res));
|
|
12256
|
+
}
|
|
12257
|
+
});
|
|
12258
|
+
}
|
|
12259
|
+
function createRoadmapTriageCommand() {
|
|
12260
|
+
return new Command89("triage").description(
|
|
12261
|
+
"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."
|
|
12262
|
+
).option(
|
|
12263
|
+
"--brainstorm",
|
|
12264
|
+
"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."
|
|
12265
|
+
).action(async (opts, cmd) => {
|
|
12266
|
+
const cwd = process.cwd();
|
|
12267
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
12268
|
+
const configResult = resolveConfig(globalOpts.config);
|
|
12269
|
+
const enabled = configResult.ok && configResult.value.roadmap?.autoTriage?.enabled === true;
|
|
12270
|
+
if (!enabled) {
|
|
12271
|
+
logger.info(
|
|
12272
|
+
"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."
|
|
12273
|
+
);
|
|
12274
|
+
return;
|
|
12275
|
+
}
|
|
12276
|
+
const roadmapPath = path60.join(cwd, "docs", "roadmap.md");
|
|
12277
|
+
if (!fs36.existsSync(roadmapPath)) {
|
|
12278
|
+
logger.error(
|
|
12279
|
+
`No roadmap aggregate at ${roadmapPath}. If your roadmap is sharded, run \`harness roadmap regen\` first, then re-run triage.`
|
|
12280
|
+
);
|
|
12281
|
+
process.exitCode = 1;
|
|
12282
|
+
return;
|
|
12283
|
+
}
|
|
12284
|
+
const parsed = parseRoadmap(fs36.readFileSync(roadmapPath, "utf-8"));
|
|
12285
|
+
if (!parsed.ok) {
|
|
12286
|
+
logger.error(`Failed to parse roadmap: ${parsed.error.message}`);
|
|
12287
|
+
process.exitCode = 1;
|
|
12288
|
+
return;
|
|
12289
|
+
}
|
|
12290
|
+
const graphStore = await loadGraphStore(cwd);
|
|
12291
|
+
const precedent = await buildPrecedentLookup(cwd);
|
|
12292
|
+
if (opts.brainstorm) {
|
|
12293
|
+
const provider = resolveBrainstormProvider(globalOpts.config);
|
|
12294
|
+
const rows2 = await runBrainstormReport(parsed.value, {
|
|
12295
|
+
...graphStore ? { graphStore } : {},
|
|
12296
|
+
...provider ? { provider } : {},
|
|
12297
|
+
...precedent ? { precedent } : {},
|
|
12298
|
+
// Specs are written under docs/changes/<slug>/proposal.md (docs only, no dispatch).
|
|
12299
|
+
docsRoot: path60.join(cwd, "docs")
|
|
12300
|
+
});
|
|
12301
|
+
if (globalOpts.json) {
|
|
12302
|
+
process.stdout.write(renderBrainstormJson(rows2) + "\n");
|
|
12303
|
+
} else {
|
|
12304
|
+
logger.info(renderBrainstormHuman(rows2));
|
|
12305
|
+
}
|
|
12306
|
+
return;
|
|
12307
|
+
}
|
|
12308
|
+
const rows = await runTriageReport(parsed.value, {
|
|
12309
|
+
...graphStore ? { graphStore } : {},
|
|
12310
|
+
...precedent ? { precedent } : {}
|
|
12311
|
+
});
|
|
12312
|
+
if (globalOpts.json) {
|
|
12313
|
+
process.stdout.write(renderJson(rows) + "\n");
|
|
12314
|
+
} else {
|
|
12315
|
+
logger.info(renderHuman(rows));
|
|
12316
|
+
}
|
|
12317
|
+
}).addCommand(createTriageApproveCommand());
|
|
12318
|
+
}
|
|
12319
|
+
|
|
11772
12320
|
// src/commands/roadmap/index.ts
|
|
11773
12321
|
function createRoadmapCommand() {
|
|
11774
|
-
const roadmap = new
|
|
12322
|
+
const roadmap = new Command90("roadmap").description("Roadmap management");
|
|
11775
12323
|
roadmap.addCommand(createRoadmapMigrateCommand());
|
|
11776
12324
|
roadmap.addCommand(createRoadmapShardCommand());
|
|
11777
12325
|
roadmap.addCommand(createRoadmapUnshardCommand());
|
|
11778
12326
|
roadmap.addCommand(createRoadmapRegenCommand());
|
|
11779
12327
|
roadmap.addCommand(createRoadmapReconcileCommand());
|
|
11780
12328
|
roadmap.addCommand(createRoadmapReferencedIssuesCommand());
|
|
12329
|
+
roadmap.addCommand(createRoadmapTriageCommand());
|
|
11781
12330
|
return roadmap;
|
|
11782
12331
|
}
|
|
11783
12332
|
|
|
11784
12333
|
// src/commands/rollback.ts
|
|
11785
12334
|
init_dist3();
|
|
11786
12335
|
import { execFileSync as execFileSync10 } from "child_process";
|
|
11787
|
-
import { Command as
|
|
12336
|
+
import { Command as Command91, Option as Option3, InvalidArgumentError } from "commander";
|
|
11788
12337
|
|
|
11789
12338
|
// src/rollback/compose.ts
|
|
11790
12339
|
var ROLLBACK_LABEL = "harness:rollback";
|
|
@@ -11842,12 +12391,12 @@ async function composeRevertPr(decision, originalTitle, opts) {
|
|
|
11842
12391
|
|
|
11843
12392
|
// src/rollback/breadcrumb.ts
|
|
11844
12393
|
import { appendFileSync, mkdirSync as mkdirSync19 } from "fs";
|
|
11845
|
-
import { dirname as dirname20, join as
|
|
11846
|
-
var ROLLBACK_EVENTS_FILE =
|
|
12394
|
+
import { dirname as dirname20, join as join51 } from "path";
|
|
12395
|
+
var ROLLBACK_EVENTS_FILE = join51(".harness", "signals", "rollback-events.jsonl");
|
|
11847
12396
|
async function appendRollbackEvent(event, opts = {}) {
|
|
11848
12397
|
const root = opts.root ?? process.cwd();
|
|
11849
12398
|
const ts = (opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))();
|
|
11850
|
-
const file =
|
|
12399
|
+
const file = join51(root, ROLLBACK_EVENTS_FILE);
|
|
11851
12400
|
mkdirSync19(dirname20(file), { recursive: true });
|
|
11852
12401
|
appendFileSync(file, `${JSON.stringify({ ...event, ts })}
|
|
11853
12402
|
`, "utf-8");
|
|
@@ -11874,7 +12423,7 @@ async function linkRollbackEventToGraph(event, opts = {}) {
|
|
|
11874
12423
|
if (target) {
|
|
11875
12424
|
store.addEdge({ from: nodeId, to: target.id, type: "references" });
|
|
11876
12425
|
}
|
|
11877
|
-
await store.save(
|
|
12426
|
+
await store.save(join51(root, ".harness", "graph"));
|
|
11878
12427
|
} catch {
|
|
11879
12428
|
}
|
|
11880
12429
|
}
|
|
@@ -11906,8 +12455,8 @@ function parseConflictPaths(stdout) {
|
|
|
11906
12455
|
if (line === void 0 || line.trim() === "") break;
|
|
11907
12456
|
const tab = line.indexOf(" ");
|
|
11908
12457
|
if (tab === -1) continue;
|
|
11909
|
-
const
|
|
11910
|
-
if (
|
|
12458
|
+
const path82 = line.slice(tab + 1).trim();
|
|
12459
|
+
if (path82) paths.add(path82);
|
|
11911
12460
|
}
|
|
11912
12461
|
return [...paths];
|
|
11913
12462
|
}
|
|
@@ -12153,7 +12702,7 @@ async function runRollbackSweepCommand(deps) {
|
|
|
12153
12702
|
});
|
|
12154
12703
|
}
|
|
12155
12704
|
function createRollbackCommand() {
|
|
12156
|
-
const rollback = new
|
|
12705
|
+
const rollback = new Command91("rollback").description(
|
|
12157
12706
|
"Post-ship revert circuit breaker (propose-only in v1)"
|
|
12158
12707
|
);
|
|
12159
12708
|
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 +12755,10 @@ function createRollbackCommand() {
|
|
|
12206
12755
|
}
|
|
12207
12756
|
|
|
12208
12757
|
// src/commands/routing/index.ts
|
|
12209
|
-
import { Command as
|
|
12758
|
+
import { Command as Command97 } from "commander";
|
|
12210
12759
|
|
|
12211
12760
|
// src/commands/routing/config.ts
|
|
12212
|
-
import { Command as
|
|
12761
|
+
import { Command as Command92 } from "commander";
|
|
12213
12762
|
|
|
12214
12763
|
// src/commands/routing/http-client.ts
|
|
12215
12764
|
function orchestratorBase() {
|
|
@@ -12219,9 +12768,9 @@ function authHeader() {
|
|
|
12219
12768
|
const tok = process.env["HARNESS_API_TOKEN"];
|
|
12220
12769
|
return tok ? { Authorization: `Bearer ${tok}` } : {};
|
|
12221
12770
|
}
|
|
12222
|
-
async function getJson(
|
|
12771
|
+
async function getJson(path82) {
|
|
12223
12772
|
try {
|
|
12224
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
12773
|
+
const res = await fetch(`${orchestratorBase()}${path82}`, {
|
|
12225
12774
|
headers: { ...authHeader() }
|
|
12226
12775
|
});
|
|
12227
12776
|
const text = await res.text();
|
|
@@ -12236,9 +12785,9 @@ async function getJson(path81) {
|
|
|
12236
12785
|
};
|
|
12237
12786
|
}
|
|
12238
12787
|
}
|
|
12239
|
-
async function postJson(
|
|
12788
|
+
async function postJson(path82, body) {
|
|
12240
12789
|
try {
|
|
12241
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
12790
|
+
const res = await fetch(`${orchestratorBase()}${path82}`, {
|
|
12242
12791
|
method: "POST",
|
|
12243
12792
|
headers: { "Content-Type": "application/json", ...authHeader() },
|
|
12244
12793
|
body: JSON.stringify(body)
|
|
@@ -12257,7 +12806,7 @@ async function postJson(path81, body) {
|
|
|
12257
12806
|
}
|
|
12258
12807
|
|
|
12259
12808
|
// src/commands/routing/config.ts
|
|
12260
|
-
function
|
|
12809
|
+
function renderHuman2(data) {
|
|
12261
12810
|
console.log("Backends:");
|
|
12262
12811
|
for (const b of data.backends) console.log(` - ${b}`);
|
|
12263
12812
|
console.log("");
|
|
@@ -12274,7 +12823,7 @@ function renderHuman(data) {
|
|
|
12274
12823
|
}
|
|
12275
12824
|
}
|
|
12276
12825
|
function createConfigCommand() {
|
|
12277
|
-
return new
|
|
12826
|
+
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
12827
|
const r = await getJson("/api/v1/routing/config");
|
|
12279
12828
|
if (!r.ok) {
|
|
12280
12829
|
if (r.status === 0) {
|
|
@@ -12295,12 +12844,12 @@ function createConfigCommand() {
|
|
|
12295
12844
|
console.log(JSON.stringify(r.body, null, 2));
|
|
12296
12845
|
return;
|
|
12297
12846
|
}
|
|
12298
|
-
if (r.body)
|
|
12847
|
+
if (r.body) renderHuman2(r.body);
|
|
12299
12848
|
});
|
|
12300
12849
|
}
|
|
12301
12850
|
|
|
12302
12851
|
// src/commands/routing/trace.ts
|
|
12303
|
-
import { Command as
|
|
12852
|
+
import { Command as Command93 } from "commander";
|
|
12304
12853
|
var COMPLEXITY_VALUES = ["trivial", "simple", "moderate", "complex"];
|
|
12305
12854
|
var RISK_VALUES = ["low", "high"];
|
|
12306
12855
|
function validateEnum(flag, value, allowed) {
|
|
@@ -12314,20 +12863,28 @@ function buildUseCase(opts) {
|
|
|
12314
12863
|
if (opts.mode) return { kind: "mode", cognitiveMode: opts.mode };
|
|
12315
12864
|
return null;
|
|
12316
12865
|
}
|
|
12317
|
-
function
|
|
12318
|
-
|
|
12866
|
+
function renderHuman3(r) {
|
|
12867
|
+
const amr = r.tierRequired !== void 0 && r.costedBackendName !== void 0;
|
|
12868
|
+
const backend = amr ? r.costedBackendName : r.decision.backendName;
|
|
12869
|
+
const type = amr && r.costedBackendType !== void 0 ? r.costedBackendType : r.def.type;
|
|
12870
|
+
console.log(`Backend: ${backend} (type: ${type})`);
|
|
12319
12871
|
console.log(`Duration: ${r.decision.durationMs.toFixed(2)} ms`);
|
|
12320
12872
|
console.log("Resolution path:");
|
|
12321
12873
|
if (r.decision.resolutionPath.length === 0) {
|
|
12322
12874
|
console.log(" (empty)");
|
|
12323
|
-
|
|
12324
|
-
|
|
12325
|
-
|
|
12326
|
-
|
|
12875
|
+
} else {
|
|
12876
|
+
for (const step of r.decision.resolutionPath) {
|
|
12877
|
+
console.log(` ${step.source}:${step.candidate} -> ${step.outcome}`);
|
|
12878
|
+
}
|
|
12327
12879
|
}
|
|
12328
12880
|
if (r.tierRequired !== void 0) {
|
|
12329
12881
|
console.log(`Tier required: ${r.tierRequired}`);
|
|
12330
12882
|
}
|
|
12883
|
+
if (amr && r.costedBackendName !== r.decision.backendName) {
|
|
12884
|
+
console.log(
|
|
12885
|
+
` (AMR overrides the identity pick '${r.decision.backendName}' \u2192 cheapest backend at/above the tier)`
|
|
12886
|
+
);
|
|
12887
|
+
}
|
|
12331
12888
|
if (r.estCostUsd !== void 0) {
|
|
12332
12889
|
console.log(`Est cost: $${r.estCostUsd.toFixed(4)}`);
|
|
12333
12890
|
}
|
|
@@ -12368,17 +12925,17 @@ async function runTrace(opts) {
|
|
|
12368
12925
|
console.log(JSON.stringify(r.body, null, 2));
|
|
12369
12926
|
return;
|
|
12370
12927
|
}
|
|
12371
|
-
if (r.body)
|
|
12928
|
+
if (r.body) renderHuman3(r.body);
|
|
12372
12929
|
}
|
|
12373
12930
|
function createTraceCommand() {
|
|
12374
|
-
return new
|
|
12931
|
+
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
12932
|
"--complexity <level>",
|
|
12376
12933
|
"Synthetic complexity (trivial|simple|moderate|complex) \u2014 dry-run only"
|
|
12377
12934
|
).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
12935
|
}
|
|
12379
12936
|
|
|
12380
12937
|
// src/commands/routing/decisions.ts
|
|
12381
|
-
import { Command as
|
|
12938
|
+
import { Command as Command94 } from "commander";
|
|
12382
12939
|
function summarizeUseCase(uc) {
|
|
12383
12940
|
switch (uc.kind) {
|
|
12384
12941
|
case "skill":
|
|
@@ -12402,7 +12959,7 @@ function shortIso(iso) {
|
|
|
12402
12959
|
const tail = parts[1] ?? iso;
|
|
12403
12960
|
return tail.replace("Z", "");
|
|
12404
12961
|
}
|
|
12405
|
-
function
|
|
12962
|
+
function renderHuman4(data) {
|
|
12406
12963
|
if (data.decisions.length === 0) {
|
|
12407
12964
|
console.log("(no decisions in buffer)");
|
|
12408
12965
|
return;
|
|
@@ -12426,7 +12983,7 @@ function buildQuery(opts) {
|
|
|
12426
12983
|
return q ? `?${q}` : "";
|
|
12427
12984
|
}
|
|
12428
12985
|
function createDecisionsCommand() {
|
|
12429
|
-
return new
|
|
12986
|
+
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
12987
|
async (opts) => {
|
|
12431
12988
|
const query = buildQuery(opts);
|
|
12432
12989
|
const r = await getJson(`/api/v1/routing/decisions${query}`);
|
|
@@ -12449,19 +13006,19 @@ function createDecisionsCommand() {
|
|
|
12449
13006
|
console.log(JSON.stringify(r.body, null, 2));
|
|
12450
13007
|
return;
|
|
12451
13008
|
}
|
|
12452
|
-
if (r.body)
|
|
13009
|
+
if (r.body) renderHuman4(r.body);
|
|
12453
13010
|
}
|
|
12454
13011
|
);
|
|
12455
13012
|
}
|
|
12456
13013
|
|
|
12457
13014
|
// src/commands/routing/telemetry.ts
|
|
12458
|
-
import { Command as
|
|
13015
|
+
import { Command as Command95 } from "commander";
|
|
12459
13016
|
var TIERS = ["fast", "standard", "strong"];
|
|
12460
13017
|
function shortIso2(iso) {
|
|
12461
13018
|
const tail = iso.split("T")[1] ?? iso;
|
|
12462
13019
|
return tail.replace("Z", "");
|
|
12463
13020
|
}
|
|
12464
|
-
function
|
|
13021
|
+
function renderHuman5(data, tableLimit) {
|
|
12465
13022
|
if (data.decisions.length === 0) {
|
|
12466
13023
|
console.log("(no routing telemetry \u2014 AMR is off or no decisions recorded yet)");
|
|
12467
13024
|
return;
|
|
@@ -12499,7 +13056,7 @@ function renderHuman4(data, tableLimit) {
|
|
|
12499
13056
|
}
|
|
12500
13057
|
}
|
|
12501
13058
|
function createTelemetryCommand() {
|
|
12502
|
-
return new
|
|
13059
|
+
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
13060
|
const r = await getJson("/api/v1/routing/telemetry");
|
|
12504
13061
|
if (!r.ok) {
|
|
12505
13062
|
if (r.status === 0) {
|
|
@@ -12518,18 +13075,18 @@ function createTelemetryCommand() {
|
|
|
12518
13075
|
return;
|
|
12519
13076
|
}
|
|
12520
13077
|
const tableLimit = opts.last && Number.isFinite(Number(opts.last)) && Number(opts.last) > 0 ? Math.floor(Number(opts.last)) : void 0;
|
|
12521
|
-
|
|
13078
|
+
renderHuman5(body, tableLimit);
|
|
12522
13079
|
});
|
|
12523
13080
|
}
|
|
12524
13081
|
|
|
12525
13082
|
// src/commands/routing/status.ts
|
|
12526
|
-
import { Command as
|
|
13083
|
+
import { Command as Command96 } from "commander";
|
|
12527
13084
|
function bar(pct, width = 24) {
|
|
12528
13085
|
const clamped = Math.max(0, Math.min(100, pct));
|
|
12529
13086
|
const filled = Math.round(clamped / 100 * width);
|
|
12530
13087
|
return `[${"#".repeat(filled)}${"-".repeat(width - filled)}]`;
|
|
12531
13088
|
}
|
|
12532
|
-
function
|
|
13089
|
+
function renderHuman6(s) {
|
|
12533
13090
|
if (!s.active) {
|
|
12534
13091
|
console.log("Adaptive routing: OFF (no routing.policy configured \u2014 dispatch is unchanged)");
|
|
12535
13092
|
return;
|
|
@@ -12563,7 +13120,7 @@ function renderHuman5(s) {
|
|
|
12563
13120
|
);
|
|
12564
13121
|
}
|
|
12565
13122
|
function createStatusCommand() {
|
|
12566
|
-
return new
|
|
13123
|
+
return new Command96("status").description(
|
|
12567
13124
|
"Live routing status: budget spend-vs-cap, escalated units, provider allowlist (AMR)"
|
|
12568
13125
|
).option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
12569
13126
|
const r = await getJson("/api/v1/routing/status");
|
|
@@ -12588,13 +13145,13 @@ function createStatusCommand() {
|
|
|
12588
13145
|
console.log(JSON.stringify(body, null, 2));
|
|
12589
13146
|
return;
|
|
12590
13147
|
}
|
|
12591
|
-
|
|
13148
|
+
renderHuman6(body);
|
|
12592
13149
|
});
|
|
12593
13150
|
}
|
|
12594
13151
|
|
|
12595
13152
|
// src/commands/routing/index.ts
|
|
12596
13153
|
function createRoutingCommand() {
|
|
12597
|
-
const cmd = new
|
|
13154
|
+
const cmd = new Command97("routing").description(
|
|
12598
13155
|
"Inspect routing config, trace decisions, and read AMR telemetry + status"
|
|
12599
13156
|
);
|
|
12600
13157
|
cmd.addCommand(createConfigCommand());
|
|
@@ -12607,9 +13164,9 @@ function createRoutingCommand() {
|
|
|
12607
13164
|
|
|
12608
13165
|
// src/commands/scan-config.ts
|
|
12609
13166
|
init_dist3();
|
|
12610
|
-
import { Command as
|
|
12611
|
-
import { existsSync as
|
|
12612
|
-
import { join as
|
|
13167
|
+
import { Command as Command98 } from "commander";
|
|
13168
|
+
import { existsSync as existsSync32, readFileSync as readFileSync23, writeFileSync as writeFileSync23 } from "fs";
|
|
13169
|
+
import { join as join52, relative as relative4 } from "path";
|
|
12613
13170
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
12614
13171
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
12615
13172
|
const highLines = /* @__PURE__ */ new Set();
|
|
@@ -12642,10 +13199,10 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
|
|
|
12642
13199
|
}
|
|
12643
13200
|
}
|
|
12644
13201
|
function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
12645
|
-
if (!
|
|
13202
|
+
if (!existsSync32(filePath)) return null;
|
|
12646
13203
|
let content;
|
|
12647
13204
|
try {
|
|
12648
|
-
content =
|
|
13205
|
+
content = readFileSync23(filePath, "utf8");
|
|
12649
13206
|
} catch {
|
|
12650
13207
|
return null;
|
|
12651
13208
|
}
|
|
@@ -12666,7 +13223,7 @@ async function runScanConfig(targetDir, options) {
|
|
|
12666
13223
|
const scanner = new SecurityScanner(parseSecurityConfig({}));
|
|
12667
13224
|
const results = [];
|
|
12668
13225
|
for (const configFile of CONFIG_FILES) {
|
|
12669
|
-
const result = scanSingleFile(
|
|
13226
|
+
const result = scanSingleFile(join52(targetDir, configFile), targetDir, scanner, options);
|
|
12670
13227
|
if (result) results.push(result);
|
|
12671
13228
|
}
|
|
12672
13229
|
return { exitCode: computeScanExitCode(results), results };
|
|
@@ -12696,7 +13253,7 @@ function formatTextOutput(result) {
|
|
|
12696
13253
|
}
|
|
12697
13254
|
}
|
|
12698
13255
|
function createScanConfigCommand() {
|
|
12699
|
-
const command = new
|
|
13256
|
+
const command = new Command98("scan-config").description(
|
|
12700
13257
|
"Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
|
|
12701
13258
|
).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
|
|
12702
13259
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -12714,7 +13271,7 @@ function createScanConfigCommand() {
|
|
|
12714
13271
|
|
|
12715
13272
|
// src/commands/search.ts
|
|
12716
13273
|
init_dist();
|
|
12717
|
-
import { Command as
|
|
13274
|
+
import { Command as Command99 } from "commander";
|
|
12718
13275
|
function parseFileKinds(raw) {
|
|
12719
13276
|
if (!raw) return void 0;
|
|
12720
13277
|
const set = /* @__PURE__ */ new Set();
|
|
@@ -12735,7 +13292,7 @@ function fmtBytes(n) {
|
|
|
12735
13292
|
return n.toFixed(0);
|
|
12736
13293
|
}
|
|
12737
13294
|
function createSearchCommand() {
|
|
12738
|
-
return new
|
|
13295
|
+
return new Command99("search").description("Full-text search over archived + live session content.").argument(
|
|
12739
13296
|
"<query>",
|
|
12740
13297
|
"FTS5 query (bare words AND-joined; quotes/AND/OR/NOT/column: for advanced syntax)"
|
|
12741
13298
|
).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 +13352,9 @@ function createSearchCommand() {
|
|
|
12795
13352
|
}
|
|
12796
13353
|
|
|
12797
13354
|
// src/commands/security-craft.ts
|
|
12798
|
-
import { Command as
|
|
13355
|
+
import { Command as Command100 } from "commander";
|
|
12799
13356
|
function createSecurityCraftCommand() {
|
|
12800
|
-
return new
|
|
13357
|
+
return new Command100("security-craft").description(
|
|
12801
13358
|
"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
13359
|
).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
13360
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -12844,10 +13401,10 @@ function printResult5(result, mode, _formatter) {
|
|
|
12844
13401
|
list.push(f);
|
|
12845
13402
|
byFile.set(f.target.file, list);
|
|
12846
13403
|
}
|
|
12847
|
-
for (const [file,
|
|
13404
|
+
for (const [file, fs52] of byFile) {
|
|
12848
13405
|
console.log(`
|
|
12849
13406
|
${file}`);
|
|
12850
|
-
for (const f of
|
|
13407
|
+
for (const f of fs52) {
|
|
12851
13408
|
console.log(
|
|
12852
13409
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.signal}:${f.target.line}`
|
|
12853
13410
|
);
|
|
@@ -12863,24 +13420,24 @@ ${file}`);
|
|
|
12863
13420
|
}
|
|
12864
13421
|
|
|
12865
13422
|
// src/commands/setup.ts
|
|
12866
|
-
import { Command as
|
|
12867
|
-
import * as
|
|
13423
|
+
import { Command as Command101 } from "commander";
|
|
13424
|
+
import * as fs39 from "fs";
|
|
12868
13425
|
import * as os9 from "os";
|
|
12869
|
-
import * as
|
|
13426
|
+
import * as path63 from "path";
|
|
12870
13427
|
import chalk16 from "chalk";
|
|
12871
13428
|
|
|
12872
13429
|
// src/utils/first-run.ts
|
|
12873
|
-
import * as
|
|
13430
|
+
import * as fs37 from "fs";
|
|
12874
13431
|
import * as os8 from "os";
|
|
12875
|
-
import * as
|
|
12876
|
-
var HARNESS_DIR =
|
|
12877
|
-
var MARKER_FILE =
|
|
13432
|
+
import * as path61 from "path";
|
|
13433
|
+
var HARNESS_DIR = path61.join(os8.homedir(), ".harness");
|
|
13434
|
+
var MARKER_FILE = path61.join(HARNESS_DIR, ".setup-complete");
|
|
12878
13435
|
function isFirstRun() {
|
|
12879
|
-
return !
|
|
13436
|
+
return !fs37.existsSync(MARKER_FILE);
|
|
12880
13437
|
}
|
|
12881
13438
|
function markSetupComplete() {
|
|
12882
|
-
|
|
12883
|
-
|
|
13439
|
+
fs37.mkdirSync(HARNESS_DIR, { recursive: true });
|
|
13440
|
+
fs37.writeFileSync(MARKER_FILE, "", "utf-8");
|
|
12884
13441
|
}
|
|
12885
13442
|
function printFirstRunWelcome() {
|
|
12886
13443
|
try {
|
|
@@ -12893,8 +13450,8 @@ function printFirstRunWelcome() {
|
|
|
12893
13450
|
}
|
|
12894
13451
|
|
|
12895
13452
|
// src/commands/telemetry-wizard.ts
|
|
12896
|
-
import * as
|
|
12897
|
-
import * as
|
|
13453
|
+
import * as fs38 from "fs";
|
|
13454
|
+
import * as path62 from "path";
|
|
12898
13455
|
import readline2 from "readline";
|
|
12899
13456
|
import chalk15 from "chalk";
|
|
12900
13457
|
function prompt2(question) {
|
|
@@ -12925,21 +13482,21 @@ function isNo(answer) {
|
|
|
12925
13482
|
return answer === "n" || answer === "no";
|
|
12926
13483
|
}
|
|
12927
13484
|
function isTelemetryConfigured(cwd) {
|
|
12928
|
-
const configPath =
|
|
13485
|
+
const configPath = path62.join(cwd, "harness.config.json");
|
|
12929
13486
|
try {
|
|
12930
|
-
const raw = JSON.parse(
|
|
13487
|
+
const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
|
|
12931
13488
|
return raw.telemetry !== void 0 && raw.adoption !== void 0;
|
|
12932
13489
|
} catch {
|
|
12933
13490
|
return false;
|
|
12934
13491
|
}
|
|
12935
13492
|
}
|
|
12936
13493
|
function writeTelemetryConfig(cwd, result) {
|
|
12937
|
-
const configPath =
|
|
13494
|
+
const configPath = path62.join(cwd, "harness.config.json");
|
|
12938
13495
|
try {
|
|
12939
|
-
const raw = JSON.parse(
|
|
13496
|
+
const raw = JSON.parse(fs38.readFileSync(configPath, "utf-8"));
|
|
12940
13497
|
raw.telemetry = { enabled: result.telemetryEnabled };
|
|
12941
13498
|
raw.adoption = { enabled: result.adoptionEnabled };
|
|
12942
|
-
|
|
13499
|
+
fs38.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
12943
13500
|
} catch (err) {
|
|
12944
13501
|
console.warn(
|
|
12945
13502
|
`\u26A0 Could not update ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -12947,10 +13504,10 @@ function writeTelemetryConfig(cwd, result) {
|
|
|
12947
13504
|
}
|
|
12948
13505
|
const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
|
|
12949
13506
|
if (hasIdentity) {
|
|
12950
|
-
const harnessDir =
|
|
12951
|
-
|
|
12952
|
-
const telemetryFile =
|
|
12953
|
-
|
|
13507
|
+
const harnessDir = path62.join(cwd, ".harness");
|
|
13508
|
+
fs38.mkdirSync(harnessDir, { recursive: true });
|
|
13509
|
+
const telemetryFile = path62.join(harnessDir, "telemetry.json");
|
|
13510
|
+
fs38.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
|
|
12954
13511
|
}
|
|
12955
13512
|
}
|
|
12956
13513
|
async function promptIdentity() {
|
|
@@ -12996,8 +13553,8 @@ async function runTelemetryWizard() {
|
|
|
12996
13553
|
return { telemetryEnabled, adoptionEnabled, identity };
|
|
12997
13554
|
}
|
|
12998
13555
|
async function ensureTelemetryConfigured(cwd) {
|
|
12999
|
-
const configPath =
|
|
13000
|
-
if (!
|
|
13556
|
+
const configPath = path62.join(cwd, "harness.config.json");
|
|
13557
|
+
if (!fs38.existsSync(configPath)) {
|
|
13001
13558
|
return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
|
|
13002
13559
|
}
|
|
13003
13560
|
if (isTelemetryConfigured(cwd)) {
|
|
@@ -13043,7 +13600,7 @@ function runSlashCommandGeneration() {
|
|
|
13043
13600
|
}
|
|
13044
13601
|
}
|
|
13045
13602
|
function detectClient(dirName) {
|
|
13046
|
-
return
|
|
13603
|
+
return fs39.existsSync(path63.join(os9.homedir(), dirName));
|
|
13047
13604
|
}
|
|
13048
13605
|
async function runMcpSetup(cwd) {
|
|
13049
13606
|
const results = [];
|
|
@@ -13059,7 +13616,7 @@ async function runMcpSetup(cwd) {
|
|
|
13059
13616
|
{ name: "Cursor", dir: ".cursor", client: "cursor", configTarget: ".cursor/mcp.json" },
|
|
13060
13617
|
{
|
|
13061
13618
|
name: "OpenCode",
|
|
13062
|
-
dir:
|
|
13619
|
+
dir: path63.join(".config", "opencode"),
|
|
13063
13620
|
client: "opencode",
|
|
13064
13621
|
configTarget: "opencode.json"
|
|
13065
13622
|
}
|
|
@@ -13091,7 +13648,7 @@ function formatStep(result) {
|
|
|
13091
13648
|
}
|
|
13092
13649
|
function configureTier0Integrations(cwd) {
|
|
13093
13650
|
try {
|
|
13094
|
-
const mcpPath =
|
|
13651
|
+
const mcpPath = path63.join(cwd, ".mcp.json");
|
|
13095
13652
|
const config = readMcpConfig(mcpPath);
|
|
13096
13653
|
const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
|
|
13097
13654
|
const added = [];
|
|
@@ -13100,9 +13657,9 @@ function configureTier0Integrations(cwd) {
|
|
|
13100
13657
|
writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
|
|
13101
13658
|
added.push(integration.displayName);
|
|
13102
13659
|
}
|
|
13103
|
-
const geminiDir =
|
|
13104
|
-
if (
|
|
13105
|
-
const geminiPath =
|
|
13660
|
+
const geminiDir = path63.join(cwd, ".gemini");
|
|
13661
|
+
if (fs39.existsSync(geminiDir)) {
|
|
13662
|
+
const geminiPath = path63.join(geminiDir, "settings.json");
|
|
13106
13663
|
const geminiConfig = readMcpConfig(geminiPath);
|
|
13107
13664
|
for (const integration of tier0) {
|
|
13108
13665
|
if (!geminiConfig.mcpServers[integration.name]) {
|
|
@@ -13110,9 +13667,9 @@ function configureTier0Integrations(cwd) {
|
|
|
13110
13667
|
}
|
|
13111
13668
|
}
|
|
13112
13669
|
}
|
|
13113
|
-
const opencodePath =
|
|
13114
|
-
const opencodeGlobalDir =
|
|
13115
|
-
if (
|
|
13670
|
+
const opencodePath = path63.join(cwd, "opencode.json");
|
|
13671
|
+
const opencodeGlobalDir = path63.join(os9.homedir(), ".config", "opencode");
|
|
13672
|
+
if (fs39.existsSync(opencodePath) || fs39.existsSync(opencodeGlobalDir)) {
|
|
13116
13673
|
for (const integration of tier0) {
|
|
13117
13674
|
writeOpencodeMcpEntry(opencodePath, integration.name, integration.mcpConfig);
|
|
13118
13675
|
}
|
|
@@ -13130,14 +13687,14 @@ function configureTier0Integrations(cwd) {
|
|
|
13130
13687
|
}
|
|
13131
13688
|
}
|
|
13132
13689
|
function ensureHooks(cwd) {
|
|
13133
|
-
const configPath =
|
|
13134
|
-
if (!
|
|
13690
|
+
const configPath = path63.join(cwd, "harness.config.json");
|
|
13691
|
+
if (!fs39.existsSync(configPath)) {
|
|
13135
13692
|
return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
|
|
13136
13693
|
}
|
|
13137
13694
|
let profile = "standard";
|
|
13138
|
-
const profilePath =
|
|
13695
|
+
const profilePath = path63.join(cwd, ".harness", "hooks", "profile.json");
|
|
13139
13696
|
try {
|
|
13140
|
-
const data = JSON.parse(
|
|
13697
|
+
const data = JSON.parse(fs39.readFileSync(profilePath, "utf-8"));
|
|
13141
13698
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
13142
13699
|
profile = data.profile;
|
|
13143
13700
|
}
|
|
@@ -13208,7 +13765,7 @@ async function runSetup(cwd) {
|
|
|
13208
13765
|
return { steps, success };
|
|
13209
13766
|
}
|
|
13210
13767
|
function createSetupCommand() {
|
|
13211
|
-
return new
|
|
13768
|
+
return new Command101("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
|
|
13212
13769
|
const cwd = process.cwd();
|
|
13213
13770
|
console.log("");
|
|
13214
13771
|
console.log(` ${chalk16.bold("harness setup")}`);
|
|
@@ -13231,15 +13788,15 @@ function createSetupCommand() {
|
|
|
13231
13788
|
|
|
13232
13789
|
// src/commands/share.ts
|
|
13233
13790
|
init_dist3();
|
|
13234
|
-
import { Command as
|
|
13235
|
-
import * as
|
|
13236
|
-
import * as
|
|
13791
|
+
import { Command as Command102 } from "commander";
|
|
13792
|
+
import * as fs40 from "fs";
|
|
13793
|
+
import * as path64 from "path";
|
|
13237
13794
|
import { parse as parseYaml3 } from "yaml";
|
|
13238
13795
|
var MANIFEST_FILENAME = "constraints.yaml";
|
|
13239
13796
|
async function runShareAction(projectPath, options) {
|
|
13240
|
-
const rootDir =
|
|
13241
|
-
const manifestPath =
|
|
13242
|
-
if (!
|
|
13797
|
+
const rootDir = path64.resolve(projectPath);
|
|
13798
|
+
const manifestPath = path64.join(rootDir, MANIFEST_FILENAME);
|
|
13799
|
+
if (!fs40.existsSync(manifestPath)) {
|
|
13243
13800
|
logger.error(
|
|
13244
13801
|
`No ${MANIFEST_FILENAME} found at ${manifestPath}.
|
|
13245
13802
|
Create a constraints.yaml in your project root to define what to share.`
|
|
@@ -13248,7 +13805,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
13248
13805
|
}
|
|
13249
13806
|
let parsed;
|
|
13250
13807
|
try {
|
|
13251
|
-
const raw =
|
|
13808
|
+
const raw = fs40.readFileSync(manifestPath, "utf-8");
|
|
13252
13809
|
parsed = parseYaml3(raw);
|
|
13253
13810
|
} catch (err) {
|
|
13254
13811
|
logger.error(
|
|
@@ -13262,7 +13819,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
13262
13819
|
process.exit(1);
|
|
13263
13820
|
}
|
|
13264
13821
|
const manifest = manifestResult.value;
|
|
13265
|
-
const configResult = resolveConfig(
|
|
13822
|
+
const configResult = resolveConfig(path64.join(rootDir, "harness.config.json"));
|
|
13266
13823
|
if (!configResult.ok) {
|
|
13267
13824
|
logger.error(configResult.error.message);
|
|
13268
13825
|
process.exit(1);
|
|
@@ -13280,8 +13837,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
13280
13837
|
);
|
|
13281
13838
|
process.exit(1);
|
|
13282
13839
|
}
|
|
13283
|
-
const outputDir =
|
|
13284
|
-
const outputPath =
|
|
13840
|
+
const outputDir = path64.resolve(options.output);
|
|
13841
|
+
const outputPath = path64.join(outputDir, `${manifest.name}.harness-constraints.json`);
|
|
13285
13842
|
const writeResult = await writeConfig(outputPath, bundle);
|
|
13286
13843
|
if (!writeResult.ok) {
|
|
13287
13844
|
logger.error(`Failed to write bundle: ${writeResult.error.message}`);
|
|
@@ -13290,28 +13847,28 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
13290
13847
|
logger.success(`Bundle written to ${outputPath}`);
|
|
13291
13848
|
}
|
|
13292
13849
|
function createShareCommand() {
|
|
13293
|
-
return new
|
|
13850
|
+
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
13851
|
await runShareAction(projectPath, options);
|
|
13295
13852
|
});
|
|
13296
13853
|
}
|
|
13297
13854
|
|
|
13298
13855
|
// src/commands/skill/index.ts
|
|
13299
|
-
import { Command as
|
|
13856
|
+
import { Command as Command110 } from "commander";
|
|
13300
13857
|
|
|
13301
13858
|
// src/commands/skill/list.ts
|
|
13302
|
-
import { Command as
|
|
13303
|
-
import * as
|
|
13304
|
-
import * as
|
|
13859
|
+
import { Command as Command103 } from "commander";
|
|
13860
|
+
import * as fs41 from "fs";
|
|
13861
|
+
import * as path65 from "path";
|
|
13305
13862
|
import { parse } from "yaml";
|
|
13306
13863
|
function scanDirectory(dirPath, source) {
|
|
13307
|
-
if (!
|
|
13308
|
-
const entries =
|
|
13864
|
+
if (!fs41.existsSync(dirPath)) return [];
|
|
13865
|
+
const entries = fs41.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
13309
13866
|
const skills = [];
|
|
13310
13867
|
for (const name of entries) {
|
|
13311
|
-
const yamlPath =
|
|
13312
|
-
if (!
|
|
13868
|
+
const yamlPath = path65.join(dirPath, name, "skill.yaml");
|
|
13869
|
+
if (!fs41.existsSync(yamlPath)) continue;
|
|
13313
13870
|
try {
|
|
13314
|
-
const raw =
|
|
13871
|
+
const raw = fs41.readFileSync(yamlPath, "utf-8");
|
|
13315
13872
|
const parsed = parse(raw);
|
|
13316
13873
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
13317
13874
|
if (result.success) {
|
|
@@ -13329,10 +13886,10 @@ function scanDirectory(dirPath, source) {
|
|
|
13329
13886
|
}
|
|
13330
13887
|
function collectCommunitySkills(seen, allSkills) {
|
|
13331
13888
|
const globalDir = resolveGlobalSkillsDir();
|
|
13332
|
-
const skillsDir =
|
|
13333
|
-
const communityBase =
|
|
13334
|
-
const communityPlatformDir =
|
|
13335
|
-
const lockfilePath =
|
|
13889
|
+
const skillsDir = path65.dirname(globalDir);
|
|
13890
|
+
const communityBase = path65.join(skillsDir, "community");
|
|
13891
|
+
const communityPlatformDir = path65.join(communityBase, "claude-code");
|
|
13892
|
+
const lockfilePath = path65.join(communityBase, "skills-lock.json");
|
|
13336
13893
|
const lockfile = readLockfile2(lockfilePath);
|
|
13337
13894
|
const communitySkills = scanDirectory(communityPlatformDir, "community");
|
|
13338
13895
|
for (const skill of communitySkills) {
|
|
@@ -13402,7 +13959,7 @@ function printSkillsVerbose(skills) {
|
|
|
13402
13959
|
for (const s of skills) printSkillEntry(s);
|
|
13403
13960
|
}
|
|
13404
13961
|
function createListCommand3() {
|
|
13405
|
-
return new
|
|
13962
|
+
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
13963
|
const globalOpts = cmd.optsWithGlobals();
|
|
13407
13964
|
const skills = collectSkills({ filter: resolveFilter(opts) });
|
|
13408
13965
|
if (globalOpts.json) {
|
|
@@ -13417,9 +13974,9 @@ function createListCommand3() {
|
|
|
13417
13974
|
}
|
|
13418
13975
|
|
|
13419
13976
|
// src/commands/skill/run.ts
|
|
13420
|
-
import { Command as
|
|
13421
|
-
import * as
|
|
13422
|
-
import * as
|
|
13977
|
+
import { Command as Command104 } from "commander";
|
|
13978
|
+
import * as fs42 from "fs";
|
|
13979
|
+
import * as path66 from "path";
|
|
13423
13980
|
import { parse as parse2 } from "yaml";
|
|
13424
13981
|
|
|
13425
13982
|
// src/skill/complexity.ts
|
|
@@ -13515,10 +14072,10 @@ ${options.principles}`);
|
|
|
13515
14072
|
|
|
13516
14073
|
// src/commands/skill/run.ts
|
|
13517
14074
|
function loadSkillMetadata(skillDir) {
|
|
13518
|
-
const yamlPath =
|
|
13519
|
-
if (!
|
|
14075
|
+
const yamlPath = path66.join(skillDir, "skill.yaml");
|
|
14076
|
+
if (!fs42.existsSync(yamlPath)) return null;
|
|
13520
14077
|
try {
|
|
13521
|
-
const result = SkillMetadataSchema.safeParse(parse2(
|
|
14078
|
+
const result = SkillMetadataSchema.safeParse(parse2(fs42.readFileSync(yamlPath, "utf-8")));
|
|
13522
14079
|
return result.success ? result.data : null;
|
|
13523
14080
|
} catch {
|
|
13524
14081
|
return null;
|
|
@@ -13530,22 +14087,22 @@ function resolveComplexity(metadata, requested, projectPath) {
|
|
|
13530
14087
|
return requested;
|
|
13531
14088
|
}
|
|
13532
14089
|
function loadPrinciples(projectPath) {
|
|
13533
|
-
const principlesPath =
|
|
13534
|
-
return
|
|
14090
|
+
const principlesPath = path66.join(projectPath, "docs", "principles.md");
|
|
14091
|
+
return fs42.existsSync(principlesPath) ? fs42.readFileSync(principlesPath, "utf-8") : void 0;
|
|
13535
14092
|
}
|
|
13536
14093
|
function readMostRecentFileInDir(dirPath) {
|
|
13537
|
-
const files =
|
|
13538
|
-
if (files.length > 0) return
|
|
14094
|
+
const files = fs42.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs42.statSync(path66.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
14095
|
+
if (files.length > 0) return fs42.readFileSync(path66.join(dirPath, files[0].name), "utf-8");
|
|
13539
14096
|
return void 0;
|
|
13540
14097
|
}
|
|
13541
14098
|
function loadPriorState(metadata, projectPath) {
|
|
13542
14099
|
if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
|
|
13543
14100
|
for (const stateFilePath of metadata.state.files) {
|
|
13544
|
-
const fullPath =
|
|
13545
|
-
if (!
|
|
13546
|
-
const stat =
|
|
14101
|
+
const fullPath = path66.join(projectPath, stateFilePath);
|
|
14102
|
+
if (!fs42.existsSync(fullPath)) continue;
|
|
14103
|
+
const stat = fs42.statSync(fullPath);
|
|
13547
14104
|
if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
|
|
13548
|
-
return
|
|
14105
|
+
return fs42.readFileSync(fullPath, "utf-8");
|
|
13549
14106
|
}
|
|
13550
14107
|
return void 0;
|
|
13551
14108
|
}
|
|
@@ -13565,7 +14122,7 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
13565
14122
|
}
|
|
13566
14123
|
async function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
13567
14124
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
13568
|
-
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-
|
|
14125
|
+
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-YLQEYOLI.js");
|
|
13569
14126
|
const stateResult = await readHarnessState2(projectPath);
|
|
13570
14127
|
if (!stateResult.ok || isEmptyHarnessState(stateResult.value)) return content;
|
|
13571
14128
|
const stateContent = JSON.stringify(stateResult.value, null, 2);
|
|
@@ -13586,7 +14143,7 @@ async function runSkill(name, opts) {
|
|
|
13586
14143
|
return;
|
|
13587
14144
|
}
|
|
13588
14145
|
const metadata = loadSkillMetadata(skillDir);
|
|
13589
|
-
const projectPath = opts.path ?
|
|
14146
|
+
const projectPath = opts.path ? path66.resolve(opts.path) : process.cwd();
|
|
13590
14147
|
const complexity = resolveComplexity(
|
|
13591
14148
|
metadata,
|
|
13592
14149
|
opts.complexity ?? "standard",
|
|
@@ -13613,14 +14170,14 @@ async function runSkill(name, opts) {
|
|
|
13613
14170
|
...stateWarning !== void 0 && { stateWarning },
|
|
13614
14171
|
...opts.party !== void 0 && { party: opts.party }
|
|
13615
14172
|
});
|
|
13616
|
-
const skillMdPath =
|
|
13617
|
-
if (!
|
|
14173
|
+
const skillMdPath = path66.join(skillDir, "SKILL.md");
|
|
14174
|
+
if (!fs42.existsSync(skillMdPath)) {
|
|
13618
14175
|
logger.error(`SKILL.md not found for skill: ${name}`);
|
|
13619
14176
|
process.exit(ExitCode.ERROR);
|
|
13620
14177
|
return;
|
|
13621
14178
|
}
|
|
13622
14179
|
const content = await appendProjectState(
|
|
13623
|
-
|
|
14180
|
+
fs42.readFileSync(skillMdPath, "utf-8"),
|
|
13624
14181
|
metadata,
|
|
13625
14182
|
projectPath,
|
|
13626
14183
|
!!opts.path
|
|
@@ -13631,16 +14188,16 @@ async function runSkill(name, opts) {
|
|
|
13631
14188
|
process.exit(ExitCode.SUCCESS);
|
|
13632
14189
|
}
|
|
13633
14190
|
function createRunCommand3() {
|
|
13634
|
-
return new
|
|
14191
|
+
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
14192
|
"--backend <name>",
|
|
13636
14193
|
"Spec B: one-shot routing override forwarded to the orchestrator as HARNESS_BACKEND_OVERRIDE"
|
|
13637
14194
|
).action(async (name, opts) => runSkill(name, opts));
|
|
13638
14195
|
}
|
|
13639
14196
|
|
|
13640
14197
|
// src/commands/skill/validate.ts
|
|
13641
|
-
import { Command as
|
|
13642
|
-
import * as
|
|
13643
|
-
import * as
|
|
14198
|
+
import { Command as Command105 } from "commander";
|
|
14199
|
+
import * as fs43 from "fs";
|
|
14200
|
+
import * as path67 from "path";
|
|
13644
14201
|
import { parse as parse3 } from "yaml";
|
|
13645
14202
|
var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
13646
14203
|
"## When to Use",
|
|
@@ -13652,11 +14209,11 @@ var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
|
13652
14209
|
];
|
|
13653
14210
|
var KNOWLEDGE_REQUIRED_SECTIONS = ["## Instructions"];
|
|
13654
14211
|
function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
13655
|
-
if (!
|
|
14212
|
+
if (!fs43.existsSync(skillMdPath)) {
|
|
13656
14213
|
errors.push(`${name}: missing SKILL.md`);
|
|
13657
14214
|
return;
|
|
13658
14215
|
}
|
|
13659
|
-
const mdContent =
|
|
14216
|
+
const mdContent = fs43.readFileSync(skillMdPath, "utf-8");
|
|
13660
14217
|
if (!mdContent.trim().startsWith("# ")) {
|
|
13661
14218
|
errors.push(`${name}/SKILL.md: must start with an h1 heading`);
|
|
13662
14219
|
}
|
|
@@ -13681,20 +14238,20 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
|
13681
14238
|
}
|
|
13682
14239
|
}
|
|
13683
14240
|
function validateSkillEntry(name, skillsDir, errors) {
|
|
13684
|
-
const skillDir =
|
|
13685
|
-
const yamlPath =
|
|
13686
|
-
if (!
|
|
14241
|
+
const skillDir = path67.join(skillsDir, name);
|
|
14242
|
+
const yamlPath = path67.join(skillDir, "skill.yaml");
|
|
14243
|
+
if (!fs43.existsSync(yamlPath)) {
|
|
13687
14244
|
errors.push(`${name}: missing skill.yaml`);
|
|
13688
14245
|
return false;
|
|
13689
14246
|
}
|
|
13690
14247
|
try {
|
|
13691
|
-
const raw =
|
|
14248
|
+
const raw = fs43.readFileSync(yamlPath, "utf-8");
|
|
13692
14249
|
const result = SkillMetadataSchema.safeParse(parse3(raw));
|
|
13693
14250
|
if (!result.success) {
|
|
13694
14251
|
errors.push(`${name}/skill.yaml: ${result.error.message}`);
|
|
13695
14252
|
return false;
|
|
13696
14253
|
}
|
|
13697
|
-
validateSkillMd(name,
|
|
14254
|
+
validateSkillMd(name, path67.join(skillDir, "SKILL.md"), result.data.type, errors);
|
|
13698
14255
|
return true;
|
|
13699
14256
|
} catch (e) {
|
|
13700
14257
|
errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -13702,15 +14259,15 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
13702
14259
|
}
|
|
13703
14260
|
}
|
|
13704
14261
|
function createValidateCommand2() {
|
|
13705
|
-
return new
|
|
14262
|
+
return new Command105("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
13706
14263
|
const globalOpts = cmd.optsWithGlobals();
|
|
13707
14264
|
const skillsDir = resolveSkillsDir();
|
|
13708
|
-
if (!
|
|
14265
|
+
if (!fs43.existsSync(skillsDir)) {
|
|
13709
14266
|
logger.info("No skills directory found.");
|
|
13710
14267
|
process.exit(ExitCode.SUCCESS);
|
|
13711
14268
|
return;
|
|
13712
14269
|
}
|
|
13713
|
-
const entries =
|
|
14270
|
+
const entries = fs43.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
13714
14271
|
const errors = [];
|
|
13715
14272
|
let validated = 0;
|
|
13716
14273
|
for (const name of entries) {
|
|
@@ -13732,9 +14289,9 @@ function createValidateCommand2() {
|
|
|
13732
14289
|
}
|
|
13733
14290
|
|
|
13734
14291
|
// src/commands/skill/info.ts
|
|
13735
|
-
import { Command as
|
|
13736
|
-
import * as
|
|
13737
|
-
import * as
|
|
14292
|
+
import { Command as Command106 } from "commander";
|
|
14293
|
+
import * as fs44 from "fs";
|
|
14294
|
+
import * as path68 from "path";
|
|
13738
14295
|
import { parse as parse4 } from "yaml";
|
|
13739
14296
|
function loadSkillMetadata2(name) {
|
|
13740
14297
|
const skillDir = resolveSkillDir(name);
|
|
@@ -13742,13 +14299,13 @@ function loadSkillMetadata2(name) {
|
|
|
13742
14299
|
logger.error(`Skill not found: ${name}`);
|
|
13743
14300
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
13744
14301
|
}
|
|
13745
|
-
const yamlPath =
|
|
13746
|
-
if (!
|
|
14302
|
+
const yamlPath = path68.join(skillDir, "skill.yaml");
|
|
14303
|
+
if (!fs44.existsSync(yamlPath)) {
|
|
13747
14304
|
logger.error(`skill.yaml not found for skill: ${name}`);
|
|
13748
14305
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
13749
14306
|
}
|
|
13750
14307
|
try {
|
|
13751
|
-
const raw =
|
|
14308
|
+
const raw = fs44.readFileSync(yamlPath, "utf-8");
|
|
13752
14309
|
const parsed = parse4(raw);
|
|
13753
14310
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
13754
14311
|
if (!result.success) {
|
|
@@ -13777,7 +14334,7 @@ function printSkillInfo(skill) {
|
|
|
13777
14334
|
console.log(`Persistent: ${skill.state.persistent}`);
|
|
13778
14335
|
}
|
|
13779
14336
|
function createInfoCommand() {
|
|
13780
|
-
return new
|
|
14337
|
+
return new Command106("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
|
|
13781
14338
|
const globalOpts = cmd.optsWithGlobals();
|
|
13782
14339
|
const loaded = loadSkillMetadata2(name);
|
|
13783
14340
|
if (!loaded.ok) {
|
|
@@ -13794,7 +14351,7 @@ function createInfoCommand() {
|
|
|
13794
14351
|
}
|
|
13795
14352
|
|
|
13796
14353
|
// src/commands/skill/search.ts
|
|
13797
|
-
import { Command as
|
|
14354
|
+
import { Command as Command107 } from "commander";
|
|
13798
14355
|
async function runSearch(query, opts) {
|
|
13799
14356
|
const results = await searchNpmRegistry(query, opts.registry);
|
|
13800
14357
|
return results.filter((r) => {
|
|
@@ -13839,15 +14396,15 @@ async function runSearchAction(query, opts, globalOpts) {
|
|
|
13839
14396
|
}
|
|
13840
14397
|
}
|
|
13841
14398
|
function createSearchCommand2() {
|
|
13842
|
-
return new
|
|
14399
|
+
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
14400
|
await runSearchAction(query, opts, cmd.optsWithGlobals());
|
|
13844
14401
|
});
|
|
13845
14402
|
}
|
|
13846
14403
|
|
|
13847
14404
|
// src/commands/skill/create.ts
|
|
13848
|
-
import { Command as
|
|
13849
|
-
import * as
|
|
13850
|
-
import * as
|
|
14405
|
+
import { Command as Command108 } from "commander";
|
|
14406
|
+
import * as path69 from "path";
|
|
14407
|
+
import * as fs45 from "fs";
|
|
13851
14408
|
import YAML from "yaml";
|
|
13852
14409
|
var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
13853
14410
|
function buildReadme(name, description) {
|
|
@@ -13931,22 +14488,22 @@ function runCreate(name, opts) {
|
|
|
13931
14488
|
if (!KEBAB_CASE_RE.test(name)) {
|
|
13932
14489
|
throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
|
|
13933
14490
|
}
|
|
13934
|
-
const baseDir = opts.outputDir ??
|
|
13935
|
-
const skillDir =
|
|
13936
|
-
if (
|
|
14491
|
+
const baseDir = opts.outputDir ?? path69.join(process.cwd(), "agents", "skills", "claude-code");
|
|
14492
|
+
const skillDir = path69.join(baseDir, name);
|
|
14493
|
+
if (fs45.existsSync(skillDir)) {
|
|
13937
14494
|
throw new Error(`Skill directory already exists: ${skillDir}`);
|
|
13938
14495
|
}
|
|
13939
|
-
|
|
14496
|
+
fs45.mkdirSync(skillDir, { recursive: true });
|
|
13940
14497
|
const description = opts.description || `A community skill: ${name}`;
|
|
13941
14498
|
const skillYaml = buildSkillYaml(name, opts);
|
|
13942
|
-
const skillYamlPath =
|
|
13943
|
-
|
|
14499
|
+
const skillYamlPath = path69.join(skillDir, "skill.yaml");
|
|
14500
|
+
fs45.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
|
|
13944
14501
|
const skillMd = buildSkillMd(name, description);
|
|
13945
|
-
const skillMdPath =
|
|
13946
|
-
|
|
14502
|
+
const skillMdPath = path69.join(skillDir, "SKILL.md");
|
|
14503
|
+
fs45.writeFileSync(skillMdPath, skillMd);
|
|
13947
14504
|
const readme = buildReadme(name, description);
|
|
13948
|
-
const readmePath =
|
|
13949
|
-
|
|
14505
|
+
const readmePath = path69.join(skillDir, "README.md");
|
|
14506
|
+
fs45.writeFileSync(readmePath, readme);
|
|
13950
14507
|
return {
|
|
13951
14508
|
name,
|
|
13952
14509
|
directory: skillDir,
|
|
@@ -13960,12 +14517,12 @@ function printCreateResult(name, result) {
|
|
|
13960
14517
|
}
|
|
13961
14518
|
logger.info(`
|
|
13962
14519
|
Next steps:`);
|
|
13963
|
-
logger.info(` 1. Edit ${
|
|
14520
|
+
logger.info(` 1. Edit ${path69.join(result.directory, "SKILL.md")} with your skill content`);
|
|
13964
14521
|
logger.info(` 2. Run: harness skill validate ${name}`);
|
|
13965
14522
|
logger.info(` 3. Run: harness skills publish`);
|
|
13966
14523
|
}
|
|
13967
14524
|
function createCreateCommand() {
|
|
13968
|
-
return new
|
|
14525
|
+
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
14526
|
const globalOpts = cmd.optsWithGlobals();
|
|
13970
14527
|
try {
|
|
13971
14528
|
const result = runCreate(name, {
|
|
@@ -13988,26 +14545,26 @@ function createCreateCommand() {
|
|
|
13988
14545
|
}
|
|
13989
14546
|
|
|
13990
14547
|
// src/commands/skill/publish.ts
|
|
13991
|
-
import { Command as
|
|
13992
|
-
import * as
|
|
13993
|
-
import * as
|
|
14548
|
+
import { Command as Command109 } from "commander";
|
|
14549
|
+
import * as fs47 from "fs";
|
|
14550
|
+
import * as path71 from "path";
|
|
13994
14551
|
import { execFileSync as execFileSync12 } from "child_process";
|
|
13995
14552
|
|
|
13996
14553
|
// src/registry/validator.ts
|
|
13997
|
-
import * as
|
|
13998
|
-
import * as
|
|
14554
|
+
import * as fs46 from "fs";
|
|
14555
|
+
import * as path70 from "path";
|
|
13999
14556
|
import { parse as parse5 } from "yaml";
|
|
14000
14557
|
import semver4 from "semver";
|
|
14001
14558
|
async function validateForPublish(skillDir, registryUrl) {
|
|
14002
14559
|
const errors = [];
|
|
14003
|
-
const skillYamlPath =
|
|
14004
|
-
if (!
|
|
14560
|
+
const skillYamlPath = path70.join(skillDir, "skill.yaml");
|
|
14561
|
+
if (!fs46.existsSync(skillYamlPath)) {
|
|
14005
14562
|
errors.push("skill.yaml not found. Create one with: harness skill create <name>");
|
|
14006
14563
|
return { valid: false, errors };
|
|
14007
14564
|
}
|
|
14008
14565
|
let skillMeta;
|
|
14009
14566
|
try {
|
|
14010
|
-
const raw =
|
|
14567
|
+
const raw = fs46.readFileSync(skillYamlPath, "utf-8");
|
|
14011
14568
|
const parsed = parse5(raw);
|
|
14012
14569
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
14013
14570
|
if (!result.success) {
|
|
@@ -14029,11 +14586,11 @@ async function validateForPublish(skillDir, registryUrl) {
|
|
|
14029
14586
|
if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
|
|
14030
14587
|
errors.push("At least one trigger is required. Add triggers to skill.yaml.");
|
|
14031
14588
|
}
|
|
14032
|
-
const skillMdPath =
|
|
14033
|
-
if (!
|
|
14589
|
+
const skillMdPath = path70.join(skillDir, "SKILL.md");
|
|
14590
|
+
if (!fs46.existsSync(skillMdPath)) {
|
|
14034
14591
|
errors.push("SKILL.md not found. Create it with content describing your skill.");
|
|
14035
14592
|
} else {
|
|
14036
|
-
const content =
|
|
14593
|
+
const content = fs46.readFileSync(skillMdPath, "utf-8");
|
|
14037
14594
|
if (!content.includes("## When to Use")) {
|
|
14038
14595
|
errors.push('SKILL.md must contain a "## When to Use" section.');
|
|
14039
14596
|
}
|
|
@@ -14113,11 +14670,11 @@ ${errorList}`);
|
|
|
14113
14670
|
}
|
|
14114
14671
|
const meta = validation.skillMeta;
|
|
14115
14672
|
const pkg = derivePackageJson(meta);
|
|
14116
|
-
const pkgPath =
|
|
14117
|
-
|
|
14118
|
-
const readmePath =
|
|
14119
|
-
if (!
|
|
14120
|
-
const skillMdContent =
|
|
14673
|
+
const pkgPath = path71.join(skillDir, "package.json");
|
|
14674
|
+
fs47.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
14675
|
+
const readmePath = path71.join(skillDir, "README.md");
|
|
14676
|
+
if (!fs47.existsSync(readmePath)) {
|
|
14677
|
+
const skillMdContent = fs47.readFileSync(path71.join(skillDir, "SKILL.md"), "utf-8");
|
|
14121
14678
|
const readme = `# ${pkg.name}
|
|
14122
14679
|
|
|
14123
14680
|
${meta.description}
|
|
@@ -14131,7 +14688,7 @@ harness install ${meta.name}
|
|
|
14131
14688
|
---
|
|
14132
14689
|
|
|
14133
14690
|
${skillMdContent}`;
|
|
14134
|
-
|
|
14691
|
+
fs47.writeFileSync(readmePath, readme);
|
|
14135
14692
|
}
|
|
14136
14693
|
if (opts.dryRun) {
|
|
14137
14694
|
return {
|
|
@@ -14157,7 +14714,7 @@ ${skillMdContent}`;
|
|
|
14157
14714
|
};
|
|
14158
14715
|
}
|
|
14159
14716
|
function createPublishCommand() {
|
|
14160
|
-
return new
|
|
14717
|
+
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
14718
|
const globalOpts = cmd.optsWithGlobals();
|
|
14162
14719
|
const skillDir = opts.dir || process.cwd();
|
|
14163
14720
|
try {
|
|
@@ -14182,7 +14739,7 @@ function createPublishCommand() {
|
|
|
14182
14739
|
|
|
14183
14740
|
// src/commands/skill/index.ts
|
|
14184
14741
|
function createSkillCommand() {
|
|
14185
|
-
const command = new
|
|
14742
|
+
const command = new Command110("skill").description("Skill management commands");
|
|
14186
14743
|
command.addCommand(createListCommand3());
|
|
14187
14744
|
command.addCommand(createRunCommand3());
|
|
14188
14745
|
command.addCommand(createValidateCommand2());
|
|
@@ -14195,7 +14752,7 @@ function createSkillCommand() {
|
|
|
14195
14752
|
|
|
14196
14753
|
// src/commands/snapshot.ts
|
|
14197
14754
|
init_dist3();
|
|
14198
|
-
import { Command as
|
|
14755
|
+
import { Command as Command111 } from "commander";
|
|
14199
14756
|
import { execSync as execSync5 } from "child_process";
|
|
14200
14757
|
import chalk17 from "chalk";
|
|
14201
14758
|
function getCommitHash3(cwd) {
|
|
@@ -14377,7 +14934,7 @@ function registerListCommand2(parent) {
|
|
|
14377
14934
|
});
|
|
14378
14935
|
}
|
|
14379
14936
|
function createSnapshotCommand() {
|
|
14380
|
-
const command = new
|
|
14937
|
+
const command = new Command111("snapshot").description("Architecture timeline snapshot commands");
|
|
14381
14938
|
registerCaptureCommand(command);
|
|
14382
14939
|
registerTrendsCommand(command);
|
|
14383
14940
|
registerListCommand2(command);
|
|
@@ -14385,9 +14942,9 @@ function createSnapshotCommand() {
|
|
|
14385
14942
|
}
|
|
14386
14943
|
|
|
14387
14944
|
// src/commands/spec-craft.ts
|
|
14388
|
-
import { Command as
|
|
14945
|
+
import { Command as Command112 } from "commander";
|
|
14389
14946
|
function createSpecCraftCommand() {
|
|
14390
|
-
return new
|
|
14947
|
+
return new Command112("spec-craft").description(
|
|
14391
14948
|
"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
14949
|
).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
14950
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -14435,10 +14992,10 @@ function printResult6(result, mode, _formatter) {
|
|
|
14435
14992
|
list.push(f);
|
|
14436
14993
|
byFile.set(f.target.file, list);
|
|
14437
14994
|
}
|
|
14438
|
-
for (const [file,
|
|
14995
|
+
for (const [file, fs52] of byFile) {
|
|
14439
14996
|
console.log(`
|
|
14440
14997
|
${file}`);
|
|
14441
|
-
for (const f of
|
|
14998
|
+
for (const f of fs52) {
|
|
14442
14999
|
console.log(
|
|
14443
15000
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ## ${f.target.section}:${f.target.line}`
|
|
14444
15001
|
);
|
|
@@ -14455,7 +15012,7 @@ ${file}`);
|
|
|
14455
15012
|
|
|
14456
15013
|
// src/commands/stale-constraints.ts
|
|
14457
15014
|
init_dist3();
|
|
14458
|
-
import { Command as
|
|
15015
|
+
import { Command as Command113 } from "commander";
|
|
14459
15016
|
import chalk18 from "chalk";
|
|
14460
15017
|
var CATEGORIES = [
|
|
14461
15018
|
"circular-deps",
|
|
@@ -14542,7 +15099,7 @@ async function runStaleConstraintsCommand(options) {
|
|
|
14542
15099
|
);
|
|
14543
15100
|
}
|
|
14544
15101
|
function createStaleConstraintsCommand() {
|
|
14545
|
-
return new
|
|
15102
|
+
return new Command113("stale-constraints").description(
|
|
14546
15103
|
"Detect architectural constraints not violated within a window (candidates for removal)"
|
|
14547
15104
|
).option(
|
|
14548
15105
|
"--window <days>",
|
|
@@ -14560,11 +15117,11 @@ function createStaleConstraintsCommand() {
|
|
|
14560
15117
|
}
|
|
14561
15118
|
|
|
14562
15119
|
// src/commands/state/index.ts
|
|
14563
|
-
import { Command as
|
|
15120
|
+
import { Command as Command118 } from "commander";
|
|
14564
15121
|
|
|
14565
15122
|
// src/commands/state/show.ts
|
|
14566
|
-
import { Command as
|
|
14567
|
-
import * as
|
|
15123
|
+
import { Command as Command114 } from "commander";
|
|
15124
|
+
import * as path72 from "path";
|
|
14568
15125
|
function printStateText(state, stream) {
|
|
14569
15126
|
if (stream) console.log(`Stream: ${stream}`);
|
|
14570
15127
|
console.log(`Schema Version: ${state.schemaVersion}`);
|
|
@@ -14588,9 +15145,9 @@ function printStateProgress(progress) {
|
|
|
14588
15145
|
}
|
|
14589
15146
|
}
|
|
14590
15147
|
function createShowCommand() {
|
|
14591
|
-
return new
|
|
15148
|
+
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
15149
|
const globalOpts = cmd.optsWithGlobals();
|
|
14593
|
-
const result = await readHarnessState(
|
|
15150
|
+
const result = await readHarnessState(path72.resolve(opts.path), { stream: opts.stream });
|
|
14594
15151
|
if (!result.ok) {
|
|
14595
15152
|
logger.error(result.error.message);
|
|
14596
15153
|
process.exit(ExitCode.ERROR);
|
|
@@ -14606,8 +15163,8 @@ function createShowCommand() {
|
|
|
14606
15163
|
|
|
14607
15164
|
// src/commands/state/reset.ts
|
|
14608
15165
|
init_dist3();
|
|
14609
|
-
import { Command as
|
|
14610
|
-
import * as
|
|
15166
|
+
import { Command as Command115 } from "commander";
|
|
15167
|
+
import * as path73 from "path";
|
|
14611
15168
|
import * as readline3 from "readline";
|
|
14612
15169
|
async function confirmReset() {
|
|
14613
15170
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -14618,7 +15175,7 @@ async function confirmReset() {
|
|
|
14618
15175
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
14619
15176
|
}
|
|
14620
15177
|
async function runReset(opts) {
|
|
14621
|
-
const projectPath =
|
|
15178
|
+
const projectPath = path73.resolve(opts.path);
|
|
14622
15179
|
if (!opts.yes && !await confirmReset()) {
|
|
14623
15180
|
logger.info("Reset cancelled.");
|
|
14624
15181
|
process.exit(ExitCode.SUCCESS);
|
|
@@ -14632,16 +15189,16 @@ async function runReset(opts) {
|
|
|
14632
15189
|
process.exit(ExitCode.SUCCESS);
|
|
14633
15190
|
}
|
|
14634
15191
|
function createResetCommand() {
|
|
14635
|
-
return new
|
|
15192
|
+
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
15193
|
}
|
|
14637
15194
|
|
|
14638
15195
|
// src/commands/state/learn.ts
|
|
14639
15196
|
init_dist3();
|
|
14640
|
-
import { Command as
|
|
14641
|
-
import * as
|
|
15197
|
+
import { Command as Command116 } from "commander";
|
|
15198
|
+
import * as path74 from "path";
|
|
14642
15199
|
function createLearnCommand() {
|
|
14643
|
-
return new
|
|
14644
|
-
const projectPath =
|
|
15200
|
+
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) => {
|
|
15201
|
+
const projectPath = path74.resolve(opts.path);
|
|
14645
15202
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
14646
15203
|
if (!result.ok) {
|
|
14647
15204
|
logger.error(result.error.message);
|
|
@@ -14655,10 +15212,10 @@ function createLearnCommand() {
|
|
|
14655
15212
|
|
|
14656
15213
|
// src/commands/state/streams.ts
|
|
14657
15214
|
init_dist3();
|
|
14658
|
-
import { Command as
|
|
14659
|
-
import * as
|
|
15215
|
+
import { Command as Command117 } from "commander";
|
|
15216
|
+
import * as path75 from "path";
|
|
14660
15217
|
async function runListStreams(opts, globalOpts) {
|
|
14661
|
-
const projectPath =
|
|
15218
|
+
const projectPath = path75.resolve(opts.path);
|
|
14662
15219
|
const indexResult = await loadStreamIndex(projectPath);
|
|
14663
15220
|
const result = await listStreams(projectPath);
|
|
14664
15221
|
if (!result.ok) {
|
|
@@ -14679,7 +15236,7 @@ async function runListStreams(opts, globalOpts) {
|
|
|
14679
15236
|
process.exit(ExitCode.SUCCESS);
|
|
14680
15237
|
}
|
|
14681
15238
|
async function runCreateStream(name, opts) {
|
|
14682
|
-
const result = await createStream(
|
|
15239
|
+
const result = await createStream(path75.resolve(opts.path), name, opts.branch);
|
|
14683
15240
|
if (!result.ok) {
|
|
14684
15241
|
logger.error(result.error.message);
|
|
14685
15242
|
process.exit(ExitCode.ERROR);
|
|
@@ -14688,7 +15245,7 @@ async function runCreateStream(name, opts) {
|
|
|
14688
15245
|
process.exit(ExitCode.SUCCESS);
|
|
14689
15246
|
}
|
|
14690
15247
|
async function runArchiveStream(name, opts) {
|
|
14691
|
-
const result = await archiveStream(
|
|
15248
|
+
const result = await archiveStream(path75.resolve(opts.path), name);
|
|
14692
15249
|
if (!result.ok) {
|
|
14693
15250
|
logger.error(result.error.message);
|
|
14694
15251
|
process.exit(ExitCode.ERROR);
|
|
@@ -14697,7 +15254,7 @@ async function runArchiveStream(name, opts) {
|
|
|
14697
15254
|
process.exit(ExitCode.SUCCESS);
|
|
14698
15255
|
}
|
|
14699
15256
|
async function runActivateStream(name, opts) {
|
|
14700
|
-
const result = await setActiveStream(
|
|
15257
|
+
const result = await setActiveStream(path75.resolve(opts.path), name);
|
|
14701
15258
|
if (!result.ok) {
|
|
14702
15259
|
logger.error(result.error.message);
|
|
14703
15260
|
process.exit(ExitCode.ERROR);
|
|
@@ -14706,7 +15263,7 @@ async function runActivateStream(name, opts) {
|
|
|
14706
15263
|
process.exit(ExitCode.SUCCESS);
|
|
14707
15264
|
}
|
|
14708
15265
|
function createStreamsCommand() {
|
|
14709
|
-
const command = new
|
|
15266
|
+
const command = new Command117("streams").description("Manage state streams");
|
|
14710
15267
|
command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
|
|
14711
15268
|
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
15269
|
command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
|
|
@@ -14716,7 +15273,7 @@ function createStreamsCommand() {
|
|
|
14716
15273
|
|
|
14717
15274
|
// src/commands/state/index.ts
|
|
14718
15275
|
function createStateCommand() {
|
|
14719
|
-
const command = new
|
|
15276
|
+
const command = new Command118("state").description("Project state management commands");
|
|
14720
15277
|
command.addCommand(createShowCommand());
|
|
14721
15278
|
command.addCommand(createResetCommand());
|
|
14722
15279
|
command.addCommand(createLearnCommand());
|
|
@@ -14725,9 +15282,9 @@ function createStateCommand() {
|
|
|
14725
15282
|
}
|
|
14726
15283
|
|
|
14727
15284
|
// src/commands/sync-analyses.ts
|
|
14728
|
-
import * as
|
|
14729
|
-
import * as
|
|
14730
|
-
import { Command as
|
|
15285
|
+
import * as fs48 from "fs";
|
|
15286
|
+
import * as path76 from "path";
|
|
15287
|
+
import { Command as Command119 } from "commander";
|
|
14731
15288
|
init_dist3();
|
|
14732
15289
|
function isValidRecord(record) {
|
|
14733
15290
|
return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
|
|
@@ -14753,7 +15310,7 @@ function extractAnalysisFromComments(comments) {
|
|
|
14753
15310
|
return null;
|
|
14754
15311
|
}
|
|
14755
15312
|
function bootstrapTrackerCommand2(opts, verb) {
|
|
14756
|
-
const projectPath =
|
|
15313
|
+
const projectPath = path76.resolve(opts.dir);
|
|
14757
15314
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
14758
15315
|
if (!trackerConfig) {
|
|
14759
15316
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -14765,8 +15322,8 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
14765
15322
|
);
|
|
14766
15323
|
return null;
|
|
14767
15324
|
}
|
|
14768
|
-
const projectEnvPath =
|
|
14769
|
-
if (
|
|
15325
|
+
const projectEnvPath = path76.join(projectPath, ".env");
|
|
15326
|
+
if (fs48.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
14770
15327
|
__require("dotenv").config({ path: projectEnvPath });
|
|
14771
15328
|
}
|
|
14772
15329
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -14852,9 +15409,9 @@ async function runSyncAnalyses(opts) {
|
|
|
14852
15409
|
return;
|
|
14853
15410
|
}
|
|
14854
15411
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
14855
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
15412
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-DTXVNQAY.js");
|
|
14856
15413
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
14857
|
-
const archive = new AnalysisArchive(
|
|
15414
|
+
const archive = new AnalysisArchive(path76.join(projectPath, ".harness", "analyses"));
|
|
14858
15415
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
14859
15416
|
const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
|
|
14860
15417
|
features,
|
|
@@ -14874,7 +15431,7 @@ async function runSyncAnalyses(opts) {
|
|
|
14874
15431
|
}
|
|
14875
15432
|
}
|
|
14876
15433
|
function createSyncAnalysesCommand() {
|
|
14877
|
-
const command = new
|
|
15434
|
+
const command = new Command119("sync-analyses").description(
|
|
14878
15435
|
"Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
|
|
14879
15436
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
14880
15437
|
try {
|
|
@@ -14888,8 +15445,8 @@ function createSyncAnalysesCommand() {
|
|
|
14888
15445
|
}
|
|
14889
15446
|
|
|
14890
15447
|
// src/commands/sync-main.ts
|
|
14891
|
-
import { Command as
|
|
14892
|
-
import * as
|
|
15448
|
+
import { Command as Command120 } from "commander";
|
|
15449
|
+
import * as path77 from "path";
|
|
14893
15450
|
import {
|
|
14894
15451
|
syncMain as defaultSyncMain
|
|
14895
15452
|
} from "@harness-engineering/orchestrator";
|
|
@@ -14919,8 +15476,8 @@ function formatHuman2(r) {
|
|
|
14919
15476
|
}
|
|
14920
15477
|
}
|
|
14921
15478
|
function createSyncMainCommand() {
|
|
14922
|
-
return new
|
|
14923
|
-
const cwd =
|
|
15479
|
+
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) => {
|
|
15480
|
+
const cwd = path77.resolve(opts.path ?? ".");
|
|
14924
15481
|
const exitCode = await runSyncMain({ cwd, json: Boolean(opts.json) });
|
|
14925
15482
|
process.exit(exitCode);
|
|
14926
15483
|
});
|
|
@@ -14928,7 +15485,7 @@ function createSyncMainCommand() {
|
|
|
14928
15485
|
|
|
14929
15486
|
// src/commands/taint.ts
|
|
14930
15487
|
init_dist3();
|
|
14931
|
-
import { Command as
|
|
15488
|
+
import { Command as Command121 } from "commander";
|
|
14932
15489
|
function getProjectRoot() {
|
|
14933
15490
|
return process.cwd();
|
|
14934
15491
|
}
|
|
@@ -15000,28 +15557,28 @@ function registerStatusCommand(taint) {
|
|
|
15000
15557
|
});
|
|
15001
15558
|
}
|
|
15002
15559
|
function createTaintCommand() {
|
|
15003
|
-
const taint = new
|
|
15560
|
+
const taint = new Command121("taint").description("Manage sentinel session taint state");
|
|
15004
15561
|
registerClearCommand(taint);
|
|
15005
15562
|
registerStatusCommand(taint);
|
|
15006
15563
|
return taint;
|
|
15007
15564
|
}
|
|
15008
15565
|
|
|
15009
15566
|
// src/commands/telemetry/index.ts
|
|
15010
|
-
import { Command as
|
|
15567
|
+
import { Command as Command125 } from "commander";
|
|
15011
15568
|
|
|
15012
15569
|
// src/commands/telemetry/identify.ts
|
|
15013
15570
|
init_dist3();
|
|
15014
|
-
import { Command as
|
|
15015
|
-
import * as
|
|
15016
|
-
import * as
|
|
15571
|
+
import { Command as Command122 } from "commander";
|
|
15572
|
+
import * as fs49 from "fs";
|
|
15573
|
+
import * as path78 from "path";
|
|
15017
15574
|
function telemetryFilePath(cwd) {
|
|
15018
|
-
return
|
|
15575
|
+
return path78.join(cwd, ".harness", "telemetry.json");
|
|
15019
15576
|
}
|
|
15020
15577
|
function writeTelemetryFile(cwd, data) {
|
|
15021
15578
|
const filePath = telemetryFilePath(cwd);
|
|
15022
|
-
const dir =
|
|
15023
|
-
|
|
15024
|
-
|
|
15579
|
+
const dir = path78.dirname(filePath);
|
|
15580
|
+
fs49.mkdirSync(dir, { recursive: true });
|
|
15581
|
+
fs49.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
15025
15582
|
}
|
|
15026
15583
|
var IDENTITY_FIELDS = ["project", "team", "alias"];
|
|
15027
15584
|
function applyIdentityFields(opts, identity) {
|
|
@@ -15035,7 +15592,7 @@ function printIdentity(identity) {
|
|
|
15035
15592
|
}
|
|
15036
15593
|
}
|
|
15037
15594
|
function createIdentifyCommand() {
|
|
15038
|
-
const cmd = new
|
|
15595
|
+
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
15596
|
const cwd = process.cwd();
|
|
15040
15597
|
if (opts.clear) {
|
|
15041
15598
|
writeTelemetryFile(cwd, { identity: {} });
|
|
@@ -15060,7 +15617,7 @@ function createIdentifyCommand() {
|
|
|
15060
15617
|
|
|
15061
15618
|
// src/commands/telemetry/status.ts
|
|
15062
15619
|
init_dist3();
|
|
15063
|
-
import { Command as
|
|
15620
|
+
import { Command as Command123 } from "commander";
|
|
15064
15621
|
function gatherEnvOverrides() {
|
|
15065
15622
|
const overrides = {};
|
|
15066
15623
|
if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
|
|
@@ -15102,7 +15659,7 @@ function printHumanStatus(result) {
|
|
|
15102
15659
|
printEnvOverrides(result.envOverrides);
|
|
15103
15660
|
}
|
|
15104
15661
|
function createStatusCommand2() {
|
|
15105
|
-
const cmd = new
|
|
15662
|
+
const cmd = new Command123("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
|
|
15106
15663
|
const cwd = process.cwd();
|
|
15107
15664
|
const envOverrides = gatherEnvOverrides();
|
|
15108
15665
|
const consent = resolveConsent(cwd, void 0);
|
|
@@ -15135,11 +15692,11 @@ function createStatusCommand2() {
|
|
|
15135
15692
|
|
|
15136
15693
|
// src/commands/telemetry/test.ts
|
|
15137
15694
|
init_dist3();
|
|
15138
|
-
import { Command as
|
|
15695
|
+
import { Command as Command124 } from "commander";
|
|
15139
15696
|
|
|
15140
15697
|
// src/bin/command-telemetry.ts
|
|
15141
|
-
import { existsSync as
|
|
15142
|
-
import { join as
|
|
15698
|
+
import { existsSync as existsSync45, readFileSync as readFileSync33, mkdirSync as mkdirSync24, appendFileSync as appendFileSync2, writeFileSync as writeFileSync29 } from "fs";
|
|
15699
|
+
import { join as join66, parse as parsePath, resolve as resolve46 } from "path";
|
|
15143
15700
|
import { spawn as spawn2 } from "child_process";
|
|
15144
15701
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
15145
15702
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
@@ -15147,7 +15704,7 @@ function findProjectRoot(cwd) {
|
|
|
15147
15704
|
let dir = resolve46(cwd);
|
|
15148
15705
|
const { root } = parsePath(dir);
|
|
15149
15706
|
while (dir !== root) {
|
|
15150
|
-
if (
|
|
15707
|
+
if (existsSync45(join66(dir, "harness.config.json"))) return dir;
|
|
15151
15708
|
dir = resolve46(dir, "..");
|
|
15152
15709
|
}
|
|
15153
15710
|
return cwd;
|
|
@@ -15185,7 +15742,7 @@ function resolveCommandName(cmd) {
|
|
|
15185
15742
|
}
|
|
15186
15743
|
function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
15187
15744
|
try {
|
|
15188
|
-
const metricsDir =
|
|
15745
|
+
const metricsDir = join66(cwd, ".harness", "metrics");
|
|
15189
15746
|
mkdirSync24(metricsDir, { recursive: true });
|
|
15190
15747
|
const record = {
|
|
15191
15748
|
skill: command,
|
|
@@ -15195,16 +15752,16 @@ function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
|
15195
15752
|
outcome,
|
|
15196
15753
|
phasesReached: []
|
|
15197
15754
|
};
|
|
15198
|
-
const adoptionFile =
|
|
15755
|
+
const adoptionFile = join66(metricsDir, "adoption.jsonl");
|
|
15199
15756
|
appendFileSync2(adoptionFile, JSON.stringify(record) + "\n");
|
|
15200
15757
|
} catch {
|
|
15201
15758
|
}
|
|
15202
15759
|
}
|
|
15203
15760
|
function isTelemetryDisabledByConfig(cwd) {
|
|
15204
|
-
const configPath =
|
|
15205
|
-
if (!
|
|
15761
|
+
const configPath = join66(cwd, "harness.config.json");
|
|
15762
|
+
if (!existsSync45(configPath)) return false;
|
|
15206
15763
|
try {
|
|
15207
|
-
const config = JSON.parse(
|
|
15764
|
+
const config = JSON.parse(readFileSync33(configPath, "utf-8"));
|
|
15208
15765
|
return config?.telemetry?.enabled === false;
|
|
15209
15766
|
} catch {
|
|
15210
15767
|
return false;
|
|
@@ -15216,11 +15773,11 @@ function isTelemetryOptedOut(cwd) {
|
|
|
15216
15773
|
}
|
|
15217
15774
|
function flushTelemetryBackground(cwd) {
|
|
15218
15775
|
try {
|
|
15219
|
-
const adoptionFile =
|
|
15220
|
-
if (!
|
|
15776
|
+
const adoptionFile = join66(cwd, ".harness", "metrics", "adoption.jsonl");
|
|
15777
|
+
if (!existsSync45(adoptionFile)) return;
|
|
15221
15778
|
if (isTelemetryOptedOut(cwd)) return;
|
|
15222
|
-
const reporterPath =
|
|
15223
|
-
if (!
|
|
15779
|
+
const reporterPath = join66(cwd, ".harness", "hooks", "telemetry-reporter.js");
|
|
15780
|
+
if (!existsSync45(reporterPath)) return;
|
|
15224
15781
|
const child = spawn2(process.execPath, [reporterPath], {
|
|
15225
15782
|
cwd,
|
|
15226
15783
|
stdio: ["pipe", "ignore", "ignore"],
|
|
@@ -15257,7 +15814,7 @@ function reportTestSuccess(status, distinctId, identity) {
|
|
|
15257
15814
|
if (identity.team) logger.info(` team: ${identity.team}`);
|
|
15258
15815
|
}
|
|
15259
15816
|
function createTestCommand() {
|
|
15260
|
-
return new
|
|
15817
|
+
return new Command124("test").description("Send a test event to PostHog and verify connectivity").action(async () => {
|
|
15261
15818
|
const cwd = process.cwd();
|
|
15262
15819
|
const consent = resolveConsent(cwd, void 0);
|
|
15263
15820
|
if (!consent.allowed) {
|
|
@@ -15294,7 +15851,7 @@ function createTestCommand() {
|
|
|
15294
15851
|
|
|
15295
15852
|
// src/commands/telemetry/index.ts
|
|
15296
15853
|
function createTelemetryCommand2() {
|
|
15297
|
-
const command = new
|
|
15854
|
+
const command = new Command125("telemetry").description("Telemetry identity and status management");
|
|
15298
15855
|
command.addCommand(createIdentifyCommand());
|
|
15299
15856
|
command.addCommand(createStatusCommand2());
|
|
15300
15857
|
command.addCommand(createTestCommand());
|
|
@@ -15302,9 +15859,9 @@ function createTelemetryCommand2() {
|
|
|
15302
15859
|
}
|
|
15303
15860
|
|
|
15304
15861
|
// src/commands/test-craft.ts
|
|
15305
|
-
import { Command as
|
|
15862
|
+
import { Command as Command126 } from "commander";
|
|
15306
15863
|
function createTestCraftCommand() {
|
|
15307
|
-
return new
|
|
15864
|
+
return new Command126("test-craft").description(
|
|
15308
15865
|
"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
15866
|
).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
15867
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -15352,10 +15909,10 @@ function printResult7(result, mode, _formatter) {
|
|
|
15352
15909
|
list.push(f);
|
|
15353
15910
|
byFile.set(f.target.file, list);
|
|
15354
15911
|
}
|
|
15355
|
-
for (const [file,
|
|
15912
|
+
for (const [file, fs52] of byFile) {
|
|
15356
15913
|
console.log(`
|
|
15357
15914
|
${file}`);
|
|
15358
|
-
for (const f of
|
|
15915
|
+
for (const f of fs52) {
|
|
15359
15916
|
const nestingStr = f.target.nesting.length > 0 ? f.target.nesting.join(" > ") + " > " : "";
|
|
15360
15917
|
console.log(
|
|
15361
15918
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.framework}:${f.target.line}`
|
|
@@ -15374,7 +15931,7 @@ ${file}`);
|
|
|
15374
15931
|
}
|
|
15375
15932
|
|
|
15376
15933
|
// src/commands/traceability.ts
|
|
15377
|
-
import { Command as
|
|
15934
|
+
import { Command as Command127 } from "commander";
|
|
15378
15935
|
import chalk19 from "chalk";
|
|
15379
15936
|
function confidenceLabel(maxConfidence) {
|
|
15380
15937
|
if (maxConfidence >= 0.8) return "explicit";
|
|
@@ -15514,7 +16071,7 @@ async function runTraceability(options) {
|
|
|
15514
16071
|
process.exit(ExitCode.SUCCESS);
|
|
15515
16072
|
}
|
|
15516
16073
|
function createTraceabilityCommand() {
|
|
15517
|
-
return new
|
|
16074
|
+
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
16075
|
const globalOpts = cmd.optsWithGlobals();
|
|
15519
16076
|
await runTraceability({
|
|
15520
16077
|
spec: opts.spec,
|
|
@@ -15527,15 +16084,15 @@ function createTraceabilityCommand() {
|
|
|
15527
16084
|
}
|
|
15528
16085
|
|
|
15529
16086
|
// src/commands/uninstall.ts
|
|
15530
|
-
import * as
|
|
15531
|
-
import { Command as
|
|
16087
|
+
import * as path79 from "path";
|
|
16088
|
+
import { Command as Command128 } from "commander";
|
|
15532
16089
|
async function runUninstall(skillName, options) {
|
|
15533
16090
|
const packageName = resolvePackageName(skillName);
|
|
15534
16091
|
const shortName = extractSkillName(packageName);
|
|
15535
16092
|
const globalDir = resolveGlobalSkillsDir();
|
|
15536
|
-
const skillsDir =
|
|
15537
|
-
const communityBase =
|
|
15538
|
-
const lockfilePath =
|
|
16093
|
+
const skillsDir = path79.dirname(globalDir);
|
|
16094
|
+
const communityBase = path79.join(skillsDir, "community");
|
|
16095
|
+
const lockfilePath = path79.join(communityBase, "skills-lock.json");
|
|
15539
16096
|
const lockfile = readLockfile2(lockfilePath);
|
|
15540
16097
|
const entry = lockfile.skills[packageName];
|
|
15541
16098
|
if (!entry) {
|
|
@@ -15565,7 +16122,7 @@ async function runUninstall(skillName, options) {
|
|
|
15565
16122
|
return result;
|
|
15566
16123
|
}
|
|
15567
16124
|
function createUninstallCommand() {
|
|
15568
|
-
const cmd = new
|
|
16125
|
+
const cmd = new Command128("uninstall");
|
|
15569
16126
|
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
16127
|
try {
|
|
15571
16128
|
const result = await runUninstall(skill, opts);
|
|
@@ -15585,9 +16142,9 @@ function createUninstallCommand() {
|
|
|
15585
16142
|
|
|
15586
16143
|
// src/commands/uninstall-constraints.ts
|
|
15587
16144
|
init_dist3();
|
|
15588
|
-
import * as
|
|
15589
|
-
import * as
|
|
15590
|
-
import { Command as
|
|
16145
|
+
import * as fs50 from "fs/promises";
|
|
16146
|
+
import * as path80 from "path";
|
|
16147
|
+
import { Command as Command129 } from "commander";
|
|
15591
16148
|
async function runUninstallConstraints(options) {
|
|
15592
16149
|
const { packageName, configPath, lockfilePath } = options;
|
|
15593
16150
|
const lockfileResult = await readLockfile(lockfilePath);
|
|
@@ -15607,7 +16164,7 @@ async function runUninstallConstraints(options) {
|
|
|
15607
16164
|
}
|
|
15608
16165
|
let localConfig;
|
|
15609
16166
|
try {
|
|
15610
|
-
const raw = await
|
|
16167
|
+
const raw = await fs50.readFile(configPath, "utf-8");
|
|
15611
16168
|
localConfig = JSON.parse(raw);
|
|
15612
16169
|
} catch (err) {
|
|
15613
16170
|
return {
|
|
@@ -15644,11 +16201,11 @@ async function runUninstallConstraints(options) {
|
|
|
15644
16201
|
};
|
|
15645
16202
|
}
|
|
15646
16203
|
function createUninstallConstraintsCommand() {
|
|
15647
|
-
const cmd = new
|
|
16204
|
+
const cmd = new Command129("uninstall-constraints");
|
|
15648
16205
|
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
16206
|
let configPath;
|
|
15650
16207
|
if (opts.config) {
|
|
15651
|
-
configPath =
|
|
16208
|
+
configPath = path80.resolve(opts.config);
|
|
15652
16209
|
} else {
|
|
15653
16210
|
const found = findConfigFile();
|
|
15654
16211
|
if (!found.ok) {
|
|
@@ -15657,8 +16214,8 @@ function createUninstallConstraintsCommand() {
|
|
|
15657
16214
|
}
|
|
15658
16215
|
configPath = found.value;
|
|
15659
16216
|
}
|
|
15660
|
-
const projectRoot3 =
|
|
15661
|
-
const lockfilePath =
|
|
16217
|
+
const projectRoot3 = path80.dirname(configPath);
|
|
16218
|
+
const lockfilePath = path80.join(projectRoot3, ".harness", "constraints.lock.json");
|
|
15662
16219
|
const result = await runUninstallConstraints({
|
|
15663
16220
|
packageName: name,
|
|
15664
16221
|
configPath,
|
|
@@ -15684,10 +16241,10 @@ function createUninstallConstraintsCommand() {
|
|
|
15684
16241
|
|
|
15685
16242
|
// src/commands/update.ts
|
|
15686
16243
|
init_dist3();
|
|
15687
|
-
import { Command as
|
|
16244
|
+
import { Command as Command130 } from "commander";
|
|
15688
16245
|
import { execFile as execFile2, execFileSync as execFileSync13 } from "child_process";
|
|
15689
|
-
import { realpathSync, existsSync as
|
|
15690
|
-
import { dirname as dirname25, join as
|
|
16246
|
+
import { realpathSync, existsSync as existsSync46, readFileSync as readFileSync34 } from "fs";
|
|
16247
|
+
import { dirname as dirname25, join as join69 } from "path";
|
|
15691
16248
|
import { promisify as promisify2 } from "util";
|
|
15692
16249
|
import readline4 from "readline";
|
|
15693
16250
|
import chalk20 from "chalk";
|
|
@@ -15778,9 +16335,9 @@ function prompt3(question) {
|
|
|
15778
16335
|
function findPackageDir(resolvedBin) {
|
|
15779
16336
|
let dir = dirname25(resolvedBin);
|
|
15780
16337
|
for (let i = 0; i < 6; i++) {
|
|
15781
|
-
if (
|
|
16338
|
+
if (existsSync46(join69(dir, "package.json"))) {
|
|
15782
16339
|
try {
|
|
15783
|
-
const pkg = JSON.parse(
|
|
16340
|
+
const pkg = JSON.parse(readFileSync34(join69(dir, "package.json"), "utf-8"));
|
|
15784
16341
|
if (pkg?.name === CLI_PACKAGE) return dir;
|
|
15785
16342
|
} catch {
|
|
15786
16343
|
}
|
|
@@ -15825,7 +16382,7 @@ function findAllInstalls() {
|
|
|
15825
16382
|
if (!packageDir) continue;
|
|
15826
16383
|
let version = null;
|
|
15827
16384
|
try {
|
|
15828
|
-
const pkg = JSON.parse(
|
|
16385
|
+
const pkg = JSON.parse(readFileSync34(join69(packageDir, "package.json"), "utf-8"));
|
|
15829
16386
|
if (typeof pkg?.version === "string") version = pkg.version;
|
|
15830
16387
|
} catch {
|
|
15831
16388
|
}
|
|
@@ -15918,12 +16475,12 @@ async function ensureTelemetryIfNeeded() {
|
|
|
15918
16475
|
}
|
|
15919
16476
|
function refreshHooks() {
|
|
15920
16477
|
const cwd = process.cwd();
|
|
15921
|
-
const configPath =
|
|
15922
|
-
if (!
|
|
16478
|
+
const configPath = join69(cwd, "harness.config.json");
|
|
16479
|
+
if (!existsSync46(configPath)) return;
|
|
15923
16480
|
let profile = "standard";
|
|
15924
|
-
const profilePath =
|
|
16481
|
+
const profilePath = join69(cwd, ".harness", "hooks", "profile.json");
|
|
15925
16482
|
try {
|
|
15926
|
-
const data = JSON.parse(
|
|
16483
|
+
const data = JSON.parse(readFileSync34(profilePath, "utf-8"));
|
|
15927
16484
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
15928
16485
|
profile = data.profile;
|
|
15929
16486
|
}
|
|
@@ -16052,7 +16609,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
16052
16609
|
process.exit(ExitCode.SUCCESS);
|
|
16053
16610
|
}
|
|
16054
16611
|
function createUpdateCommand() {
|
|
16055
|
-
return new
|
|
16612
|
+
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
16613
|
"--regenerate",
|
|
16057
16614
|
"Only regenerate slash commands and agent definitions (skip package updates)"
|
|
16058
16615
|
).action(async (opts, cmd) => {
|
|
@@ -16062,9 +16619,9 @@ function createUpdateCommand() {
|
|
|
16062
16619
|
}
|
|
16063
16620
|
|
|
16064
16621
|
// src/commands/usage.ts
|
|
16065
|
-
import { Command as
|
|
16622
|
+
import { Command as Command131 } from "commander";
|
|
16066
16623
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
16067
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
16624
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-DTXVNQAY.js");
|
|
16068
16625
|
const records = readCostRecords(cwd);
|
|
16069
16626
|
if (includeClaudeSessions) {
|
|
16070
16627
|
const ccRecords = parseCCRecords();
|
|
@@ -16157,7 +16714,7 @@ function registerDailyCommand(usage) {
|
|
|
16157
16714
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
16158
16715
|
return;
|
|
16159
16716
|
}
|
|
16160
|
-
const { aggregateByDay } = await import("./dist-
|
|
16717
|
+
const { aggregateByDay } = await import("./dist-DTXVNQAY.js");
|
|
16161
16718
|
const limited = aggregateByDay(records).slice(0, days);
|
|
16162
16719
|
if (globalOpts.json) {
|
|
16163
16720
|
console.log(
|
|
@@ -16186,7 +16743,7 @@ function registerSessionsCommand(usage) {
|
|
|
16186
16743
|
}
|
|
16187
16744
|
return;
|
|
16188
16745
|
}
|
|
16189
|
-
const { aggregateBySession } = await import("./dist-
|
|
16746
|
+
const { aggregateBySession } = await import("./dist-DTXVNQAY.js");
|
|
16190
16747
|
const sessionData = aggregateBySession(records);
|
|
16191
16748
|
const limited = sessionData.slice(0, limit);
|
|
16192
16749
|
if (globalOpts.json) {
|
|
@@ -16266,7 +16823,7 @@ function registerSessionCommand(usage) {
|
|
|
16266
16823
|
cwd,
|
|
16267
16824
|
globalOpts.includeClaudeSessions
|
|
16268
16825
|
);
|
|
16269
|
-
const { aggregateBySession } = await import("./dist-
|
|
16826
|
+
const { aggregateBySession } = await import("./dist-DTXVNQAY.js");
|
|
16270
16827
|
const sessionData = aggregateBySession(records);
|
|
16271
16828
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
16272
16829
|
if (!match) {
|
|
@@ -16301,7 +16858,7 @@ function registerLatestCommand(usage) {
|
|
|
16301
16858
|
}
|
|
16302
16859
|
return;
|
|
16303
16860
|
}
|
|
16304
|
-
const { aggregateBySession } = await import("./dist-
|
|
16861
|
+
const { aggregateBySession } = await import("./dist-DTXVNQAY.js");
|
|
16305
16862
|
const sessionData = aggregateBySession(records);
|
|
16306
16863
|
const latest = sessionData[0];
|
|
16307
16864
|
if (!latest) {
|
|
@@ -16327,7 +16884,7 @@ function registerLatestCommand(usage) {
|
|
|
16327
16884
|
});
|
|
16328
16885
|
}
|
|
16329
16886
|
function createUsageCommand() {
|
|
16330
|
-
const usage = new
|
|
16887
|
+
const usage = new Command131("usage").description("Token usage and cost tracking");
|
|
16331
16888
|
usage.option(
|
|
16332
16889
|
"--include-claude-sessions",
|
|
16333
16890
|
"Include Claude Code session data from ~/.claude/projects/"
|
|
@@ -16342,9 +16899,9 @@ function createUsageCommand() {
|
|
|
16342
16899
|
// src/commands/validate.ts
|
|
16343
16900
|
init_dist3();
|
|
16344
16901
|
init_dist3();
|
|
16345
|
-
import { Command as
|
|
16346
|
-
import * as
|
|
16347
|
-
import * as
|
|
16902
|
+
import { Command as Command132 } from "commander";
|
|
16903
|
+
import * as fs51 from "fs";
|
|
16904
|
+
import * as path81 from "path";
|
|
16348
16905
|
import { execFileSync as execFileSync14 } from "child_process";
|
|
16349
16906
|
async function runValidate(options) {
|
|
16350
16907
|
const configResult = resolveConfig(options.configPath);
|
|
@@ -16352,7 +16909,7 @@ async function runValidate(options) {
|
|
|
16352
16909
|
return configResult;
|
|
16353
16910
|
}
|
|
16354
16911
|
const config = configResult.value;
|
|
16355
|
-
const cwd = options.cwd ?? (options.configPath ?
|
|
16912
|
+
const cwd = options.cwd ?? (options.configPath ? path81.dirname(path81.resolve(options.configPath)) : process.cwd());
|
|
16356
16913
|
const result = {
|
|
16357
16914
|
valid: true,
|
|
16358
16915
|
checks: {
|
|
@@ -16362,7 +16919,7 @@ async function runValidate(options) {
|
|
|
16362
16919
|
},
|
|
16363
16920
|
issues: []
|
|
16364
16921
|
};
|
|
16365
|
-
const agentsMapPath =
|
|
16922
|
+
const agentsMapPath = path81.resolve(cwd, config.agentsMapPath);
|
|
16366
16923
|
const agentsResult = await validateAgentsMap(agentsMapPath);
|
|
16367
16924
|
if (agentsResult.ok) {
|
|
16368
16925
|
result.checks.agentsMap = true;
|
|
@@ -16459,9 +17016,9 @@ async function runValidate(options) {
|
|
|
16459
17016
|
}
|
|
16460
17017
|
});
|
|
16461
17018
|
}
|
|
16462
|
-
const gitattributesPath =
|
|
16463
|
-
if (
|
|
16464
|
-
const gitattributesContent =
|
|
17019
|
+
const gitattributesPath = path81.join(cwd, ".gitattributes");
|
|
17020
|
+
if (fs51.existsSync(gitattributesPath)) {
|
|
17021
|
+
const gitattributesContent = fs51.readFileSync(gitattributesPath, "utf-8");
|
|
16465
17022
|
let driverConfigured = false;
|
|
16466
17023
|
try {
|
|
16467
17024
|
const out = execFileSync14("git", ["config", "--get", "merge.ours.driver"], {
|
|
@@ -16485,10 +17042,10 @@ async function runValidate(options) {
|
|
|
16485
17042
|
}
|
|
16486
17043
|
}
|
|
16487
17044
|
if (detectRoadmapStorageMode(cwd) === "sharded") {
|
|
16488
|
-
const shardDir =
|
|
17045
|
+
const shardDir = path81.join(cwd, "docs", "roadmap.d");
|
|
16489
17046
|
const regenerated = await regenerate(shardDir, createNodeRoadmapIO());
|
|
16490
|
-
const aggregatePath =
|
|
16491
|
-
const committedAggregate =
|
|
17047
|
+
const aggregatePath = path81.join(cwd, "docs", "roadmap.md");
|
|
17048
|
+
const committedAggregate = fs51.existsSync(aggregatePath) ? fs51.readFileSync(aggregatePath, "utf-8") : null;
|
|
16492
17049
|
const drift = checkRoadmapAggregateDrift({
|
|
16493
17050
|
shardDirExists: true,
|
|
16494
17051
|
committedAggregate,
|
|
@@ -16506,9 +17063,9 @@ async function runValidate(options) {
|
|
|
16506
17063
|
result.checks.roadmapAggregateDrift = true;
|
|
16507
17064
|
}
|
|
16508
17065
|
}
|
|
16509
|
-
const roadmapPath =
|
|
16510
|
-
if (
|
|
16511
|
-
const parsed = parseRoadmap(
|
|
17066
|
+
const roadmapPath = path81.join(cwd, "docs", "roadmap.md");
|
|
17067
|
+
if (fs51.existsSync(roadmapPath)) {
|
|
17068
|
+
const parsed = parseRoadmap(fs51.readFileSync(roadmapPath, "utf-8"));
|
|
16512
17069
|
if (parsed.ok) {
|
|
16513
17070
|
const findings = checkRoadmapHealth(parsed.value);
|
|
16514
17071
|
result.checks.roadmapHealth = !findings.some((f) => f.severity === "error");
|
|
@@ -16650,11 +17207,11 @@ function resolveValidateMode(globalOpts) {
|
|
|
16650
17207
|
return OutputMode.TEXT;
|
|
16651
17208
|
}
|
|
16652
17209
|
async function printCrossCheckWarnings(mode) {
|
|
16653
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
17210
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-QJGCDWRO.js");
|
|
16654
17211
|
const cwd = process.cwd();
|
|
16655
17212
|
const crossResult = await runCrossCheck2({
|
|
16656
|
-
specsDir:
|
|
16657
|
-
plansDir:
|
|
17213
|
+
specsDir: path81.join(cwd, "docs", "specs"),
|
|
17214
|
+
plansDir: path81.join(cwd, "docs", "plans"),
|
|
16658
17215
|
projectPath: cwd
|
|
16659
17216
|
});
|
|
16660
17217
|
if (!crossResult.ok || crossResult.value.warnings === 0) return;
|
|
@@ -16666,7 +17223,7 @@ async function printCrossCheckWarnings(mode) {
|
|
|
16666
17223
|
${crossResult.value.warnings} warnings`);
|
|
16667
17224
|
}
|
|
16668
17225
|
function createValidateCommand3() {
|
|
16669
|
-
const command = new
|
|
17226
|
+
const command = new Command132("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
|
|
16670
17227
|
"--agent-configs",
|
|
16671
17228
|
"Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
|
|
16672
17229
|
).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 +17272,7 @@ Agent configs checked via ${engineLabel}${note}`);
|
|
|
16715
17272
|
|
|
16716
17273
|
// src/commands/verify.ts
|
|
16717
17274
|
init_dist3();
|
|
16718
|
-
import { Command as
|
|
17275
|
+
import { Command as Command133 } from "commander";
|
|
16719
17276
|
import { execSync as execSync6 } from "child_process";
|
|
16720
17277
|
function loadBranchingConfig(configPath) {
|
|
16721
17278
|
const shouldLoad = configPath !== void 0 || findConfigFile().ok;
|
|
@@ -16778,7 +17335,7 @@ async function runVerify(options) {
|
|
|
16778
17335
|
process.exit(ExitCode.VALIDATION_FAILED);
|
|
16779
17336
|
}
|
|
16780
17337
|
function createVerifyCommand() {
|
|
16781
|
-
return new
|
|
17338
|
+
return new Command133("verify").description(
|
|
16782
17339
|
"Verify project conventions (currently: branch naming). Works with or without a harness.config.json."
|
|
16783
17340
|
).option(
|
|
16784
17341
|
"--branch <name>",
|
|
@@ -16906,7 +17463,7 @@ function registerDeprecatedGraphAliases(program) {
|
|
|
16906
17463
|
|
|
16907
17464
|
// src/index.ts
|
|
16908
17465
|
function createProgram() {
|
|
16909
|
-
const program = new
|
|
17466
|
+
const program = new Command134();
|
|
16910
17467
|
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
17468
|
for (const creator of commandCreators) {
|
|
16912
17469
|
program.addCommand(creator());
|