@harness-engineering/cli 2.7.0 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/claude-code/naming-craft/SKILL.md +74 -2
- package/dist/agents/skills/codex/naming-craft/SKILL.md +74 -2
- package/dist/agents/skills/cursor/naming-craft/SKILL.md +74 -2
- package/dist/agents/skills/gemini-cli/naming-craft/SKILL.md +74 -2
- package/dist/{agents-md-267R2NUJ.js → agents-md-FKMKH6ZF.js} +3 -3
- package/dist/{analyzer-VY6DJVOU-WYKV3TTW.js → analyzer-H3AHBFSL-KPOPJYRB.js} +7 -7
- package/dist/{architecture-ZTEHGIHD.js → architecture-LUR2I67H.js} +4 -4
- package/dist/{assess-project-OKDW3YSK.js → assess-project-OJWECOP2.js} +1 -1
- package/dist/bin/harness-mcp.js +14 -14
- package/dist/bin/harness.js +23 -23
- package/dist/{check-phase-gate-YJWFDW66.js → check-phase-gate-HS5KYLRO.js} +4 -4
- package/dist/{chunk-MQG7FHXX.js → chunk-7AF6C5GE.js} +3 -3
- package/dist/{chunk-4EGPFB7V.js → chunk-7PLIWP7U.js} +961 -256
- package/dist/{chunk-WOXR6I3R.js → chunk-B2VAYLYI.js} +1 -1
- package/dist/{chunk-67RP7MLR.js → chunk-CZPOPMMI.js} +2 -2
- package/dist/{chunk-OLTVDPA2.js → chunk-FC4JPM6W.js} +2 -2
- package/dist/{chunk-CXAHK5PJ.js → chunk-FEKBXX2J.js} +6 -6
- package/dist/{chunk-B4OLYXZ4.js → chunk-FXZI6NJ2.js} +39 -4
- package/dist/{chunk-6T5AYG5S.js → chunk-GGSNWH7P.js} +2 -2
- package/dist/{chunk-ZKA3TGYS.js → chunk-INBTDBV2.js} +1 -1
- package/dist/{chunk-HMMSWRWR.js → chunk-KN3EMDZ5.js} +2 -2
- package/dist/{chunk-OQE6ME2Z.js → chunk-LECXPXS3.js} +1 -1
- package/dist/{chunk-HLPY3RHE.js → chunk-LGYXR2KZ.js} +799 -515
- package/dist/{chunk-QUPC37UB.js → chunk-LYIPI2SJ.js} +3 -3
- package/dist/{chunk-S7GEONXL.js → chunk-N5NSSLMU.js} +1 -1
- package/dist/{chunk-P7EO7USC.js → chunk-NIVWM7OW.js} +5 -5
- package/dist/{chunk-PARV3G2F.js → chunk-O2ASYKA6.js} +1 -1
- package/dist/{chunk-XTIPZUPR.js → chunk-PTHX545Q.js} +1 -1
- package/dist/{chunk-B3B64OPD.js → chunk-RJ2WEES5.js} +1 -1
- package/dist/{chunk-TIZ3L6CU.js → chunk-VI4IS55S.js} +6 -6
- package/dist/{chunk-VG6UK6G6.js → chunk-VSIRUZQP.js} +1 -1
- package/dist/{chunk-IY2TZLY5.js → chunk-WZY5U6NS.js} +4 -4
- package/dist/{chunk-W3PDVZ4I.js → chunk-Y4T6ENKQ.js} +8 -8
- package/dist/{chunk-Z2GUITAS.js → chunk-Z3TMCCZB.js} +3 -3
- package/dist/{ci-workflow-YGTQTNVC.js → ci-workflow-MDSTHJOY.js} +3 -3
- package/dist/{dist-XIJWWQ5N.js → dist-72ID44UE.js} +2 -2
- package/dist/{docs-KCTLXK2G.js → docs-2RFOJ6XY.js} +4 -4
- package/dist/{engine-J64WPH5K.js → engine-BSFKOGPN.js} +3 -3
- package/dist/{entropy-M27JUL3V.js → entropy-3UX6644G.js} +3 -3
- package/dist/{feedback-W25QO3OL.js → feedback-CVCFYND4.js} +1 -1
- package/dist/{generate-agent-definitions-RNMU5U5R.js → generate-agent-definitions-HZ3XENWO.js} +4 -4
- package/dist/index.d.ts +425 -6
- package/dist/index.js +23 -23
- package/dist/{loader-HZKJ5ABV.js → loader-6RHWU5FH.js} +3 -3
- package/dist/{mcp-NEAPIDTT.js → mcp-OYR26JDI.js} +14 -14
- package/dist/{performance-3AVQUDRG.js → performance-JJMIRXCH.js} +4 -4
- package/dist/{review-pipeline-KA7SGUXJ.js → review-pipeline-BHQZIXWZ.js} +3 -3
- package/dist/{runtime-IP6FVV5M.js → runtime-DALTAVTN.js} +3 -3
- package/dist/{security-EAR4FJWI.js → security-RMLMYFPB.js} +1 -1
- package/dist/{validate-EVH3UXIC.js → validate-R7DZRDFK.js} +4 -4
- package/dist/{validate-cross-check-SNE3SQOB.js → validate-cross-check-AUUZFNVL.js} +3 -3
- package/package.json +4 -4
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-N5NSSLMU.js";
|
|
4
4
|
import {
|
|
5
5
|
generateAgentsMd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-LECXPXS3.js";
|
|
7
7
|
import {
|
|
8
8
|
generateCIWorkflow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-RJ2WEES5.js";
|
|
10
10
|
import {
|
|
11
11
|
createGenerateAgentDefinitionsCommand,
|
|
12
12
|
generateAgentDefinitions
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-INBTDBV2.js";
|
|
14
14
|
import {
|
|
15
15
|
toKebabCase
|
|
16
16
|
} from "./chunk-KET4QQZB.js";
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
} from "./chunk-6UHDQP2N.js";
|
|
21
21
|
import {
|
|
22
22
|
TemplateEngine
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-B2VAYLYI.js";
|
|
24
24
|
import {
|
|
25
25
|
generate,
|
|
26
26
|
validate
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
import {
|
|
32
32
|
listPersonas,
|
|
33
33
|
loadPersona
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-PTHX545Q.js";
|
|
35
35
|
import {
|
|
36
36
|
runPersona
|
|
37
37
|
} from "./chunk-GISMXMVL.js";
|
|
@@ -46,19 +46,21 @@ import {
|
|
|
46
46
|
OutputMode,
|
|
47
47
|
createCheckPhaseGateCommand,
|
|
48
48
|
findFiles
|
|
49
|
-
} from "./chunk-
|
|
49
|
+
} from "./chunk-FC4JPM6W.js";
|
|
50
50
|
import {
|
|
51
51
|
appendFrameworkAgents,
|
|
52
52
|
captureHealthSnapshot,
|
|
53
53
|
createAdviseSkillsCommand,
|
|
54
54
|
createGenerateSlashCommandsCommand,
|
|
55
55
|
ensureHarnessGitignore,
|
|
56
|
+
findOrchestratorMd,
|
|
56
57
|
generateSlashCommands,
|
|
57
58
|
handleGetImpact,
|
|
58
59
|
handleOrphanDeletion,
|
|
59
60
|
isSnapshotFresh,
|
|
60
61
|
loadCachedSnapshot,
|
|
61
62
|
persistToolingConfig,
|
|
63
|
+
readBackendsFromOrchestratorMd,
|
|
62
64
|
recommend,
|
|
63
65
|
runAlignDesignSystem,
|
|
64
66
|
runAudit,
|
|
@@ -72,7 +74,7 @@ import {
|
|
|
72
74
|
runSecurityCraft,
|
|
73
75
|
runSpecCraft,
|
|
74
76
|
runTestCraft
|
|
75
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-7PLIWP7U.js";
|
|
76
78
|
import {
|
|
77
79
|
VALID_PLATFORMS
|
|
78
80
|
} from "./chunk-3ISINLYT.js";
|
|
@@ -97,7 +99,7 @@ import {
|
|
|
97
99
|
BranchingConfigSchema,
|
|
98
100
|
findConfigFile,
|
|
99
101
|
resolveConfig
|
|
100
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-FXZI6NJ2.js";
|
|
101
103
|
import {
|
|
102
104
|
logger
|
|
103
105
|
} from "./chunk-EBJQ6N4M.js";
|
|
@@ -191,7 +193,7 @@ import {
|
|
|
191
193
|
validateSolutionsDir,
|
|
192
194
|
writeConfig,
|
|
193
195
|
writeLockfile
|
|
194
|
-
} from "./chunk-
|
|
196
|
+
} from "./chunk-WZY5U6NS.js";
|
|
195
197
|
import {
|
|
196
198
|
CLI_VERSION
|
|
197
199
|
} from "./chunk-BM3PWGXQ.js";
|
|
@@ -204,7 +206,7 @@ import {
|
|
|
204
206
|
detectDocDrift,
|
|
205
207
|
generateSuggestions,
|
|
206
208
|
validateDependencies
|
|
207
|
-
} from "./chunk-
|
|
209
|
+
} from "./chunk-Y4T6ENKQ.js";
|
|
208
210
|
import {
|
|
209
211
|
Err,
|
|
210
212
|
INDEXED_FILE_KINDS,
|
|
@@ -228,7 +230,7 @@ import {
|
|
|
228
230
|
} from "./chunk-KFQGP6VL.js";
|
|
229
231
|
|
|
230
232
|
// src/index.ts
|
|
231
|
-
import { Command as
|
|
233
|
+
import { Command as Command120 } from "commander";
|
|
232
234
|
|
|
233
235
|
// src/commands/add.ts
|
|
234
236
|
init_dist3();
|
|
@@ -417,7 +419,7 @@ function registerSkillsCommand(adoption) {
|
|
|
417
419
|
const globalOpts = cmd.optsWithGlobals();
|
|
418
420
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
419
421
|
const cwd = process.cwd();
|
|
420
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
422
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-72ID44UE.js");
|
|
421
423
|
const records = readAdoptionRecords(cwd);
|
|
422
424
|
if (records.length === 0) {
|
|
423
425
|
if (globalOpts.json) {
|
|
@@ -453,7 +455,7 @@ function registerRecentCommand(adoption) {
|
|
|
453
455
|
const globalOpts = cmd.optsWithGlobals();
|
|
454
456
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
455
457
|
const cwd = process.cwd();
|
|
456
|
-
const { readAdoptionRecords } = await import("./dist-
|
|
458
|
+
const { readAdoptionRecords } = await import("./dist-72ID44UE.js");
|
|
457
459
|
const records = readAdoptionRecords(cwd);
|
|
458
460
|
if (records.length === 0) {
|
|
459
461
|
if (globalOpts.json) {
|
|
@@ -531,7 +533,7 @@ function registerSkillCommand(adoption) {
|
|
|
531
533
|
adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
|
|
532
534
|
const globalOpts = cmd.optsWithGlobals();
|
|
533
535
|
const cwd = process.cwd();
|
|
534
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
536
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-72ID44UE.js");
|
|
535
537
|
const records = readAdoptionRecords(cwd);
|
|
536
538
|
const skillRecords = records.filter((r) => r.skill === name);
|
|
537
539
|
if (skillRecords.length === 0) {
|
|
@@ -1124,7 +1126,7 @@ function runBackfillSkillProvenance(projectRoot2) {
|
|
|
1124
1126
|
}
|
|
1125
1127
|
function createBackfillSkillProvenanceCommand() {
|
|
1126
1128
|
const cmd = new Command8("backfill-skill-provenance").description(
|
|
1127
|
-
"Stamp `provenance: user-authored` on every catalog skill missing the field (
|
|
1129
|
+
"Stamp `provenance: user-authored` on every catalog skill missing the field (one-shot migration)"
|
|
1128
1130
|
).option("--root <path>", "Project root containing agents/skills/", process.cwd()).action((opts) => {
|
|
1129
1131
|
const result = runBackfillSkillProvenance(opts.root);
|
|
1130
1132
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -1652,9 +1654,9 @@ ${name} (${count} finding${count === 1 ? "" : "s"})`);
|
|
|
1652
1654
|
}
|
|
1653
1655
|
function printAnatomyFindings(findings, verbose) {
|
|
1654
1656
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1655
|
-
for (const [file,
|
|
1657
|
+
for (const [file, fs48] of byFile) {
|
|
1656
1658
|
console.log(` ${file}`);
|
|
1657
|
-
for (const f of
|
|
1659
|
+
for (const f of fs48) {
|
|
1658
1660
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1659
1661
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1660
1662
|
if (verbose) {
|
|
@@ -1665,9 +1667,9 @@ function printAnatomyFindings(findings, verbose) {
|
|
|
1665
1667
|
}
|
|
1666
1668
|
function printCraftFindings(findings, verbose) {
|
|
1667
1669
|
const byFile = groupByFile(findings, (f) => f.target.file);
|
|
1668
|
-
for (const [file,
|
|
1670
|
+
for (const [file, fs48] of byFile) {
|
|
1669
1671
|
console.log(` ${file}`);
|
|
1670
|
-
for (const f of
|
|
1672
|
+
for (const f of fs48) {
|
|
1671
1673
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
1672
1674
|
const sev = craftTierToSeverity(f.tier);
|
|
1673
1675
|
console.log(
|
|
@@ -1681,9 +1683,9 @@ function printCraftFindings(findings, verbose) {
|
|
|
1681
1683
|
}
|
|
1682
1684
|
function printDriftFindings(findings, verbose) {
|
|
1683
1685
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1684
|
-
for (const [file,
|
|
1686
|
+
for (const [file, fs48] of byFile) {
|
|
1685
1687
|
console.log(` ${file}`);
|
|
1686
|
-
for (const f of
|
|
1688
|
+
for (const f of fs48) {
|
|
1687
1689
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1688
1690
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1689
1691
|
if (verbose) {
|
|
@@ -1694,9 +1696,9 @@ function printDriftFindings(findings, verbose) {
|
|
|
1694
1696
|
}
|
|
1695
1697
|
function printBrandFindings(findings, verbose) {
|
|
1696
1698
|
const byFile = groupByFile(findings, (f) => f.file);
|
|
1697
|
-
for (const [file,
|
|
1699
|
+
for (const [file, fs48] of byFile) {
|
|
1698
1700
|
console.log(` ${file}`);
|
|
1699
|
-
for (const f of
|
|
1701
|
+
for (const f of fs48) {
|
|
1700
1702
|
const line = f.line !== null && f.line !== void 0 ? `:${f.line}` : "";
|
|
1701
1703
|
console.log(` ${f.code} [${f.severity}] line${line}: ${f.message}`);
|
|
1702
1704
|
if (verbose) {
|
|
@@ -2015,10 +2017,10 @@ async function runCheckSecurity(cwd, options) {
|
|
|
2015
2017
|
const projectRoot2 = path9.resolve(cwd);
|
|
2016
2018
|
let configData = {};
|
|
2017
2019
|
try {
|
|
2018
|
-
const
|
|
2020
|
+
const fs48 = await import("fs");
|
|
2019
2021
|
const configPath = path9.join(projectRoot2, "harness.config.json");
|
|
2020
|
-
if (
|
|
2021
|
-
const raw =
|
|
2022
|
+
if (fs48.existsSync(configPath)) {
|
|
2023
|
+
const raw = fs48.readFileSync(configPath, "utf-8");
|
|
2022
2024
|
const parsed = JSON.parse(raw);
|
|
2023
2025
|
configData = parsed.security ?? {};
|
|
2024
2026
|
}
|
|
@@ -2786,10 +2788,10 @@ ${dryRun ? "Would remove" : "Removed"} ${totalRemoved} stale entry(ies) across t
|
|
|
2786
2788
|
function createCleanupSessionsCommand() {
|
|
2787
2789
|
const command = new Command21("cleanup-sessions").description(
|
|
2788
2790
|
"Remove stale entries from .harness/. Default: only .harness/sessions/ (no write in 24h)."
|
|
2789
|
-
).option("--dry-run", "List stale entries without deleting them", false).option("--path <path>", "Project root path", ".").option("--all", "
|
|
2791
|
+
).option("--dry-run", "List stale entries without deleting them", false).option("--path <path>", "Project root path", ".").option("--all", "Sweep every registered .harness/ target").option(
|
|
2790
2792
|
"--include <list>",
|
|
2791
|
-
"
|
|
2792
|
-
).option("--exclude <list>", "
|
|
2793
|
+
"Comma-separated target names (mutually-exclusive with --exclude/--all)"
|
|
2794
|
+
).option("--exclude <list>", "Comma-separated target names to skip").action(async (opts, cmd) => {
|
|
2793
2795
|
const globalOpts = cmd.optsWithGlobals();
|
|
2794
2796
|
const cwd = path12.resolve(opts.path);
|
|
2795
2797
|
const asJson = Boolean(globalOpts.json);
|
|
@@ -2979,10 +2981,10 @@ function printResult2(result, mode, _formatter) {
|
|
|
2979
2981
|
list.push(f);
|
|
2980
2982
|
bySurface.set(f.target.surface, list);
|
|
2981
2983
|
}
|
|
2982
|
-
for (const [surface,
|
|
2984
|
+
for (const [surface, fs48] of bySurface) {
|
|
2983
2985
|
console.log(`
|
|
2984
2986
|
[${surface}]`);
|
|
2985
|
-
for (const f of
|
|
2987
|
+
for (const f of fs48) {
|
|
2986
2988
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
2987
2989
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.file}${line}`);
|
|
2988
2990
|
console.log(` "${truncate(f.target.snippet, 80)}"`);
|
|
@@ -4210,11 +4212,11 @@ import { Command as Command35 } from "commander";
|
|
|
4210
4212
|
// src/commands/graph/status.ts
|
|
4211
4213
|
import * as path16 from "path";
|
|
4212
4214
|
async function readConnectorSyncStatus(graphDir) {
|
|
4213
|
-
const
|
|
4215
|
+
const fs48 = await import("fs/promises");
|
|
4214
4216
|
const result = {};
|
|
4215
4217
|
try {
|
|
4216
4218
|
const syncMetaPath = path16.join(graphDir, "sync-metadata.json");
|
|
4217
|
-
const syncMeta = JSON.parse(await
|
|
4219
|
+
const syncMeta = JSON.parse(await fs48.readFile(syncMetaPath, "utf-8"));
|
|
4218
4220
|
for (const [name, data] of Object.entries(syncMeta.connectors ?? {})) {
|
|
4219
4221
|
result[name] = data.lastSyncTimestamp;
|
|
4220
4222
|
}
|
|
@@ -4364,9 +4366,9 @@ import { Command as Command34 } from "commander";
|
|
|
4364
4366
|
import * as path19 from "path";
|
|
4365
4367
|
async function loadConnectorConfig(projectPath, source) {
|
|
4366
4368
|
try {
|
|
4367
|
-
const
|
|
4369
|
+
const fs48 = await import("fs/promises");
|
|
4368
4370
|
const configPath = path19.join(projectPath, "harness.config.json");
|
|
4369
|
-
const config = JSON.parse(await
|
|
4371
|
+
const config = JSON.parse(await fs48.readFile(configPath, "utf-8"));
|
|
4370
4372
|
const connector = config.graph?.connectors?.[source];
|
|
4371
4373
|
return connector ?? {};
|
|
4372
4374
|
} catch {
|
|
@@ -5356,6 +5358,7 @@ var ALL_MCP_TOOLS = [
|
|
|
5356
5358
|
"run_design_pipeline",
|
|
5357
5359
|
// craft-pipeline #1 — naming-craft LLM-judgment ceiling skill
|
|
5358
5360
|
"naming_craft",
|
|
5361
|
+
"naming_craft_finalize",
|
|
5359
5362
|
// craft-pipeline #6 — spec-craft LLM-judgment ceiling skill
|
|
5360
5363
|
"spec_craft",
|
|
5361
5364
|
// craft-pipeline #5 — copy-craft LLM-judgment ceiling skill (6 surfaces)
|
|
@@ -5738,12 +5741,10 @@ function renderPretty(report) {
|
|
|
5738
5741
|
}
|
|
5739
5742
|
}
|
|
5740
5743
|
function createInsightsCommand() {
|
|
5741
|
-
return new Command44("insights").description(
|
|
5742
|
-
"Composite project report \u2014 health, entropy, decay, attention, impact (Hermes Phase 1)."
|
|
5743
|
-
).option("--json", "Emit JSON to stdout instead of pretty text").option("--skip <list>", `Comma-separated keys to skip (${INSIGHTS_KEYS.join(",")})`).action(async (opts) => {
|
|
5744
|
+
return new Command44("insights").description("Composite project report \u2014 health, entropy, decay, attention, impact.").option("--json", "Emit JSON to stdout instead of pretty text").option("--skip <list>", `Comma-separated keys to skip (${INSIGHTS_KEYS.join(",")})`).action(async (opts) => {
|
|
5744
5745
|
const cwd = process.cwd();
|
|
5745
5746
|
const skip = parseSkip2(opts.skip);
|
|
5746
|
-
const { composeInsights } = await import("./dist-
|
|
5747
|
+
const { composeInsights } = await import("./dist-72ID44UE.js");
|
|
5747
5748
|
const report = await composeInsights(cwd, { skip });
|
|
5748
5749
|
if (opts.json) {
|
|
5749
5750
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -6915,10 +6916,10 @@ function printResult3(result, mode, _formatter) {
|
|
|
6915
6916
|
list.push(f);
|
|
6916
6917
|
byFile.set(f.target.file, list);
|
|
6917
6918
|
}
|
|
6918
|
-
for (const [file,
|
|
6919
|
+
for (const [file, fs48] of byFile) {
|
|
6919
6920
|
console.log(`
|
|
6920
6921
|
${file}`);
|
|
6921
|
-
for (const f of
|
|
6922
|
+
for (const f of fs48) {
|
|
6922
6923
|
console.log(` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.relative}`);
|
|
6923
6924
|
console.log(` ${f.message}`);
|
|
6924
6925
|
if (verbose) console.log(` source: ${f.cite.source}`);
|
|
@@ -6941,8 +6942,8 @@ function createKnowledgePipelineCommand() {
|
|
|
6941
6942
|
const projectDir = process.cwd();
|
|
6942
6943
|
try {
|
|
6943
6944
|
const graphDir = path39.join(projectDir, ".harness", "graph");
|
|
6944
|
-
const
|
|
6945
|
-
await
|
|
6945
|
+
const fs48 = await import("fs/promises");
|
|
6946
|
+
await fs48.mkdir(graphDir, { recursive: true });
|
|
6946
6947
|
const { GraphStore: GraphStore2, KnowledgePipelineRunner } = await import("./dist-R3TOOPJ7.js");
|
|
6947
6948
|
const store = new GraphStore2();
|
|
6948
6949
|
try {
|
|
@@ -7351,7 +7352,7 @@ function printListTable(rows) {
|
|
|
7351
7352
|
}
|
|
7352
7353
|
function createMaintenanceCommand() {
|
|
7353
7354
|
const command = new Command59("maintenance").description(
|
|
7354
|
-
"
|
|
7355
|
+
"Inspect built-in + custom maintenance tasks and their persisted outputs"
|
|
7355
7356
|
);
|
|
7356
7357
|
command.command("list").description("List all resolved maintenance tasks (built-in + customTasks)").option("--json", "Emit machine-readable JSON", false).option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
7357
7358
|
const cwd = path41.resolve(opts.path);
|
|
@@ -7445,7 +7446,7 @@ function createMcpCommand() {
|
|
|
7445
7446
|
parseBudget
|
|
7446
7447
|
).action(async (opts) => {
|
|
7447
7448
|
const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
|
|
7448
|
-
import("./mcp-
|
|
7449
|
+
import("./mcp-OYR26JDI.js"),
|
|
7449
7450
|
import("./tool-tiers-B7JC2XC3.js")
|
|
7450
7451
|
]);
|
|
7451
7452
|
if (opts.tools && opts.tools.length > 0) {
|
|
@@ -7606,14 +7607,150 @@ function createMcpGuardCommand() {
|
|
|
7606
7607
|
}
|
|
7607
7608
|
|
|
7608
7609
|
// src/commands/migrate.ts
|
|
7609
|
-
|
|
7610
|
-
|
|
7610
|
+
import { Command as Command63 } from "commander";
|
|
7611
|
+
|
|
7612
|
+
// src/commands/migrate-backends.ts
|
|
7611
7613
|
import { Command as Command62 } from "commander";
|
|
7612
7614
|
import * as fs27 from "fs";
|
|
7613
7615
|
import * as path43 from "path";
|
|
7616
|
+
import chalk9 from "chalk";
|
|
7617
|
+
import { parse as parseYaml2 } from "yaml";
|
|
7618
|
+
async function runMigrateBackends(opts = {}) {
|
|
7619
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
7620
|
+
const mdPath = findOrchestratorMd(cwd);
|
|
7621
|
+
if (mdPath === null) {
|
|
7622
|
+
return {
|
|
7623
|
+
status: "noop",
|
|
7624
|
+
message: "No harness.orchestrator.md found \u2014 nothing to migrate.",
|
|
7625
|
+
exitCode: ExitCode.SUCCESS
|
|
7626
|
+
};
|
|
7627
|
+
}
|
|
7628
|
+
const backends = readBackendsFromOrchestratorMd(cwd);
|
|
7629
|
+
if (backends === null) {
|
|
7630
|
+
return {
|
|
7631
|
+
status: "noop",
|
|
7632
|
+
message: `Found ${mdPath} but no agent.backends to migrate.`,
|
|
7633
|
+
exitCode: ExitCode.SUCCESS
|
|
7634
|
+
};
|
|
7635
|
+
}
|
|
7636
|
+
const routing = readRoutingFromOrchestratorMd(mdPath);
|
|
7637
|
+
const jsonPath = path43.join(path43.dirname(mdPath), "harness.config.json");
|
|
7638
|
+
if (!fs27.existsSync(jsonPath)) {
|
|
7639
|
+
return {
|
|
7640
|
+
status: "error",
|
|
7641
|
+
message: `harness.config.json not found at ${jsonPath}. Run \`harness init\` first, then re-run this migration.`,
|
|
7642
|
+
exitCode: ExitCode.ERROR
|
|
7643
|
+
};
|
|
7644
|
+
}
|
|
7645
|
+
let configRaw;
|
|
7646
|
+
try {
|
|
7647
|
+
configRaw = fs27.readFileSync(jsonPath, "utf-8");
|
|
7648
|
+
} catch (err) {
|
|
7649
|
+
return {
|
|
7650
|
+
status: "error",
|
|
7651
|
+
message: `Failed to read ${jsonPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
7652
|
+
exitCode: ExitCode.ERROR
|
|
7653
|
+
};
|
|
7654
|
+
}
|
|
7655
|
+
let configObj;
|
|
7656
|
+
try {
|
|
7657
|
+
configObj = JSON.parse(configRaw);
|
|
7658
|
+
} catch (err) {
|
|
7659
|
+
return {
|
|
7660
|
+
status: "error",
|
|
7661
|
+
message: `${jsonPath} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
7662
|
+
exitCode: ExitCode.ERROR
|
|
7663
|
+
};
|
|
7664
|
+
}
|
|
7665
|
+
const agent = configObj.agent ?? {};
|
|
7666
|
+
const existingBackends = agent.backends;
|
|
7667
|
+
if (existingBackends !== void 0 && !opts.force) {
|
|
7668
|
+
return {
|
|
7669
|
+
status: "error",
|
|
7670
|
+
message: `${jsonPath} already declares agent.backends. Re-run with --force to overwrite, or merge manually.`,
|
|
7671
|
+
exitCode: ExitCode.ERROR
|
|
7672
|
+
};
|
|
7673
|
+
}
|
|
7674
|
+
const nextAgent = { ...agent, backends };
|
|
7675
|
+
if (routing !== null) nextAgent.routing = routing;
|
|
7676
|
+
const nextConfig = { ...configObj, agent: nextAgent };
|
|
7677
|
+
const nextJson = JSON.stringify(nextConfig, null, 2) + "\n";
|
|
7678
|
+
if (opts.dryRun) {
|
|
7679
|
+
logger.info(chalk9.bold("--- dry run ---"));
|
|
7680
|
+
logger.info(`Would write ${Object.keys(backends).length} backend(s) to ${jsonPath}:`);
|
|
7681
|
+
for (const name of Object.keys(backends)) {
|
|
7682
|
+
logger.info(` \u2022 ${name}`);
|
|
7683
|
+
}
|
|
7684
|
+
if (routing !== null) {
|
|
7685
|
+
logger.info("Would also copy agent.routing.");
|
|
7686
|
+
}
|
|
7687
|
+
return {
|
|
7688
|
+
status: "ok",
|
|
7689
|
+
message: "(dry-run) migration plan above \u2014 re-run without --dry-run to apply.",
|
|
7690
|
+
exitCode: ExitCode.SUCCESS
|
|
7691
|
+
};
|
|
7692
|
+
}
|
|
7693
|
+
try {
|
|
7694
|
+
fs27.writeFileSync(jsonPath, nextJson);
|
|
7695
|
+
} catch (err) {
|
|
7696
|
+
return {
|
|
7697
|
+
status: "error",
|
|
7698
|
+
message: `Failed to write ${jsonPath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
7699
|
+
exitCode: ExitCode.ERROR
|
|
7700
|
+
};
|
|
7701
|
+
}
|
|
7702
|
+
return {
|
|
7703
|
+
status: "ok",
|
|
7704
|
+
message: `Migrated ${Object.keys(backends).length} backend(s)${routing !== null ? " + routing" : ""} from ${mdPath} to ${jsonPath}. You can leave the entries in harness.orchestrator.md (orchestrator still reads them) or remove them once the orchestrator config picks up the JSON source.`,
|
|
7705
|
+
exitCode: ExitCode.SUCCESS
|
|
7706
|
+
};
|
|
7707
|
+
}
|
|
7708
|
+
function readRoutingFromOrchestratorMd(mdPath) {
|
|
7709
|
+
let raw;
|
|
7710
|
+
try {
|
|
7711
|
+
raw = fs27.readFileSync(mdPath, "utf-8");
|
|
7712
|
+
} catch {
|
|
7713
|
+
return null;
|
|
7714
|
+
}
|
|
7715
|
+
const parts = raw.split("---");
|
|
7716
|
+
if (parts.length < 3) return null;
|
|
7717
|
+
let parsed;
|
|
7718
|
+
try {
|
|
7719
|
+
parsed = parseYaml2(parts[1].trim());
|
|
7720
|
+
} catch {
|
|
7721
|
+
return null;
|
|
7722
|
+
}
|
|
7723
|
+
if (parsed === null || typeof parsed !== "object") return null;
|
|
7724
|
+
const agent = parsed.agent;
|
|
7725
|
+
if (agent === null || typeof agent !== "object") return null;
|
|
7726
|
+
const routing = agent.routing;
|
|
7727
|
+
return routing ?? null;
|
|
7728
|
+
}
|
|
7729
|
+
function createBackendsSubcommand() {
|
|
7730
|
+
return new Command62("backends").description(
|
|
7731
|
+
"Copy agent.backends (and routing) from harness.orchestrator.md into harness.config.json."
|
|
7732
|
+
).option("--dry-run", "Show what would change without writing", false).option("--force", "Overwrite existing agent.backends in harness.config.json", false).action(async (options) => {
|
|
7733
|
+
const result = await runMigrateBackends({
|
|
7734
|
+
dryRun: options.dryRun ?? false,
|
|
7735
|
+
force: options.force ?? false
|
|
7736
|
+
});
|
|
7737
|
+
if (result.status === "error") {
|
|
7738
|
+
logger.error(result.message);
|
|
7739
|
+
process.exit(result.exitCode);
|
|
7740
|
+
}
|
|
7741
|
+
logger.info(result.message);
|
|
7742
|
+
process.exit(result.exitCode);
|
|
7743
|
+
});
|
|
7744
|
+
}
|
|
7745
|
+
|
|
7746
|
+
// src/commands/migrate.ts
|
|
7747
|
+
init_dist3();
|
|
7748
|
+
init_dist2();
|
|
7749
|
+
import * as fs28 from "fs";
|
|
7750
|
+
import * as path44 from "path";
|
|
7614
7751
|
import * as readline from "readline";
|
|
7615
7752
|
import { execFileSync as execFileSync4 } from "child_process";
|
|
7616
|
-
import
|
|
7753
|
+
import chalk10 from "chalk";
|
|
7617
7754
|
function isGitRepo(cwd) {
|
|
7618
7755
|
try {
|
|
7619
7756
|
execFileSync4("git", ["rev-parse", "--is-inside-work-tree"], { cwd, stdio: "pipe" });
|
|
@@ -7624,25 +7761,25 @@ function isGitRepo(cwd) {
|
|
|
7624
7761
|
}
|
|
7625
7762
|
function readJson2(p) {
|
|
7626
7763
|
try {
|
|
7627
|
-
return JSON.parse(
|
|
7764
|
+
return JSON.parse(fs28.readFileSync(p, "utf8"));
|
|
7628
7765
|
} catch {
|
|
7629
7766
|
return null;
|
|
7630
7767
|
}
|
|
7631
7768
|
}
|
|
7632
7769
|
function listDirs(p) {
|
|
7633
|
-
if (!
|
|
7634
|
-
return
|
|
7770
|
+
if (!fs28.existsSync(p)) return [];
|
|
7771
|
+
return fs28.readdirSync(p, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
7635
7772
|
}
|
|
7636
7773
|
function listFiles(p, predicate) {
|
|
7637
|
-
if (!
|
|
7638
|
-
return
|
|
7774
|
+
if (!fs28.existsSync(p)) return [];
|
|
7775
|
+
return fs28.readdirSync(p, { withFileTypes: true }).filter((e) => e.isFile() && predicate(e.name)).map((e) => e.name);
|
|
7639
7776
|
}
|
|
7640
7777
|
function walk(dir, predicate) {
|
|
7641
|
-
if (!
|
|
7778
|
+
if (!fs28.existsSync(dir)) return [];
|
|
7642
7779
|
const out = [];
|
|
7643
|
-
for (const entry of
|
|
7780
|
+
for (const entry of fs28.readdirSync(dir, { withFileTypes: true })) {
|
|
7644
7781
|
if (DEFAULT_SKIP_DIRS.has(entry.name)) continue;
|
|
7645
|
-
const full =
|
|
7782
|
+
const full = path44.join(dir, entry.name);
|
|
7646
7783
|
if (entry.isDirectory()) out.push(...walk(full, predicate));
|
|
7647
7784
|
else if (predicate(full)) out.push(full);
|
|
7648
7785
|
}
|
|
@@ -7654,11 +7791,11 @@ function extractAutopilotTopic(state) {
|
|
|
7654
7791
|
}
|
|
7655
7792
|
function buildAutopilotMap(cwd) {
|
|
7656
7793
|
const map = /* @__PURE__ */ new Map();
|
|
7657
|
-
const sessionsDir =
|
|
7658
|
-
if (!
|
|
7794
|
+
const sessionsDir = path44.join(cwd, ".harness", "sessions");
|
|
7795
|
+
if (!fs28.existsSync(sessionsDir)) return map;
|
|
7659
7796
|
for (const session of listDirs(sessionsDir)) {
|
|
7660
7797
|
if (!session.startsWith("changes--")) continue;
|
|
7661
|
-
const state = readJson2(
|
|
7798
|
+
const state = readJson2(path44.join(sessionsDir, session, "autopilot-state.json"));
|
|
7662
7799
|
if (!state) continue;
|
|
7663
7800
|
const topic = extractAutopilotTopic(state);
|
|
7664
7801
|
if (!topic) continue;
|
|
@@ -7671,7 +7808,7 @@ function buildAutopilotMap(cwd) {
|
|
|
7671
7808
|
function extractTopicFromHeader(planAbs, proposalTopics) {
|
|
7672
7809
|
let text;
|
|
7673
7810
|
try {
|
|
7674
|
-
text =
|
|
7811
|
+
text = fs28.readFileSync(planAbs, "utf8").slice(0, 4e3);
|
|
7675
7812
|
} catch {
|
|
7676
7813
|
return null;
|
|
7677
7814
|
}
|
|
@@ -7696,8 +7833,8 @@ function extractTopicFromFilename(filename, proposalTopics) {
|
|
|
7696
7833
|
return best;
|
|
7697
7834
|
}
|
|
7698
7835
|
function resolveDocsDir(cwd) {
|
|
7699
|
-
const configPath =
|
|
7700
|
-
const configResult =
|
|
7836
|
+
const configPath = path44.join(cwd, "harness.config.json");
|
|
7837
|
+
const configResult = fs28.existsSync(configPath) ? resolveConfig(configPath) : null;
|
|
7701
7838
|
let docsDir;
|
|
7702
7839
|
if (configResult?.ok) {
|
|
7703
7840
|
docsDir = configResult.value.docsDir;
|
|
@@ -7709,16 +7846,16 @@ function resolveDocsDir(cwd) {
|
|
|
7709
7846
|
} else {
|
|
7710
7847
|
docsDir = "./docs";
|
|
7711
7848
|
}
|
|
7712
|
-
const docsAbs =
|
|
7713
|
-
const relRaw =
|
|
7849
|
+
const docsAbs = path44.resolve(cwd, docsDir);
|
|
7850
|
+
const relRaw = path44.relative(cwd, docsAbs).replaceAll("\\", "/");
|
|
7714
7851
|
return relRaw === "" ? "docs" : relRaw;
|
|
7715
7852
|
}
|
|
7716
7853
|
function collectAdrMoves(cwd, docsRel) {
|
|
7717
7854
|
const moves = [];
|
|
7718
|
-
const legacyAdrDir =
|
|
7719
|
-
if (!
|
|
7855
|
+
const legacyAdrDir = path44.join(cwd, ".harness", "architecture");
|
|
7856
|
+
if (!fs28.existsSync(legacyAdrDir)) return moves;
|
|
7720
7857
|
for (const topic of listDirs(legacyAdrDir)) {
|
|
7721
|
-
const topicSrc =
|
|
7858
|
+
const topicSrc = path44.join(legacyAdrDir, topic);
|
|
7722
7859
|
for (const file of listFiles(topicSrc, (n) => n.endsWith(".md"))) {
|
|
7723
7860
|
moves.push({
|
|
7724
7861
|
src: `.harness/architecture/${topic}/${file}`,
|
|
@@ -7730,9 +7867,9 @@ function collectAdrMoves(cwd, docsRel) {
|
|
|
7730
7867
|
}
|
|
7731
7868
|
function collectProposalTopics(cwd) {
|
|
7732
7869
|
const topics = /* @__PURE__ */ new Set();
|
|
7733
|
-
const changesDir =
|
|
7870
|
+
const changesDir = path44.join(cwd, "docs", "changes");
|
|
7734
7871
|
for (const topic of listDirs(changesDir)) {
|
|
7735
|
-
if (
|
|
7872
|
+
if (fs28.existsSync(path44.join(changesDir, topic, "proposal.md"))) {
|
|
7736
7873
|
topics.add(topic);
|
|
7737
7874
|
}
|
|
7738
7875
|
}
|
|
@@ -7759,12 +7896,12 @@ async function buildPlan(opts) {
|
|
|
7759
7896
|
orphanPlans: [],
|
|
7760
7897
|
proposalTopics: collectProposalTopics(cwd)
|
|
7761
7898
|
};
|
|
7762
|
-
const legacyPlanDir =
|
|
7763
|
-
if (!
|
|
7899
|
+
const legacyPlanDir = path44.join(cwd, "docs", "plans");
|
|
7900
|
+
if (!fs28.existsSync(legacyPlanDir)) return plan;
|
|
7764
7901
|
const autopilotMap = buildAutopilotMap(cwd);
|
|
7765
7902
|
for (const file of listFiles(legacyPlanDir, (n) => n.endsWith(".md") && n !== "index.md")) {
|
|
7766
7903
|
const planRel = `docs/plans/${file}`;
|
|
7767
|
-
const planAbs =
|
|
7904
|
+
const planAbs = path44.join(legacyPlanDir, file);
|
|
7768
7905
|
const subdir = file.includes("VERIFICATION") ? "verifications" : "plans";
|
|
7769
7906
|
const classified = classifyPlan(planRel, planAbs, file, autopilotMap, plan.proposalTopics);
|
|
7770
7907
|
if (classified) {
|
|
@@ -7782,8 +7919,8 @@ async function buildPlan(opts) {
|
|
|
7782
7919
|
return plan;
|
|
7783
7920
|
}
|
|
7784
7921
|
function gitMv(cwd, src, dest, useGit) {
|
|
7785
|
-
const destAbs =
|
|
7786
|
-
|
|
7922
|
+
const destAbs = path44.join(cwd, dest);
|
|
7923
|
+
fs28.mkdirSync(path44.dirname(destAbs), { recursive: true });
|
|
7787
7924
|
if (useGit) {
|
|
7788
7925
|
try {
|
|
7789
7926
|
execFileSync4("git", ["mv", src, dest], { cwd, stdio: "pipe" });
|
|
@@ -7791,7 +7928,7 @@ function gitMv(cwd, src, dest, useGit) {
|
|
|
7791
7928
|
} catch {
|
|
7792
7929
|
}
|
|
7793
7930
|
}
|
|
7794
|
-
|
|
7931
|
+
fs28.renameSync(path44.join(cwd, src), destAbs);
|
|
7795
7932
|
}
|
|
7796
7933
|
function executeMigration(plan) {
|
|
7797
7934
|
let moved = 0;
|
|
@@ -7823,13 +7960,13 @@ function updateReferences(plan) {
|
|
|
7823
7960
|
let files = 0;
|
|
7824
7961
|
let replacements = 0;
|
|
7825
7962
|
const targets = [
|
|
7826
|
-
...walk(
|
|
7827
|
-
...walk(
|
|
7963
|
+
...walk(path44.join(plan.cwd, "docs"), (p) => p.endsWith(".md")),
|
|
7964
|
+
...walk(path44.join(plan.cwd, ".harness", "sessions"), (p) => p.endsWith(".json"))
|
|
7828
7965
|
];
|
|
7829
7966
|
for (const target of targets) {
|
|
7830
7967
|
let text;
|
|
7831
7968
|
try {
|
|
7832
|
-
text =
|
|
7969
|
+
text = fs28.readFileSync(target, "utf8");
|
|
7833
7970
|
} catch {
|
|
7834
7971
|
continue;
|
|
7835
7972
|
}
|
|
@@ -7842,7 +7979,7 @@ function updateReferences(plan) {
|
|
|
7842
7979
|
count += occurrences;
|
|
7843
7980
|
}
|
|
7844
7981
|
if (count > 0 && result !== text) {
|
|
7845
|
-
|
|
7982
|
+
fs28.writeFileSync(target, result);
|
|
7846
7983
|
files++;
|
|
7847
7984
|
replacements += count;
|
|
7848
7985
|
}
|
|
@@ -7901,7 +8038,7 @@ async function resolveOrphans(plan, opts) {
|
|
|
7901
8038
|
}
|
|
7902
8039
|
function bucketOrphans(plan, topic) {
|
|
7903
8040
|
for (const src of plan.orphanPlans) {
|
|
7904
|
-
const file =
|
|
8041
|
+
const file = path44.basename(src);
|
|
7905
8042
|
const isVerification = file.includes("VERIFICATION");
|
|
7906
8043
|
const subdir = isVerification ? "verifications" : "plans";
|
|
7907
8044
|
plan.planMoves.push({
|
|
@@ -7916,7 +8053,7 @@ function bucketOrphans(plan, topic) {
|
|
|
7916
8053
|
}
|
|
7917
8054
|
function summarize(plan) {
|
|
7918
8055
|
console.log("");
|
|
7919
|
-
console.log(
|
|
8056
|
+
console.log(chalk10.bold(" Migration plan"));
|
|
7920
8057
|
console.log("");
|
|
7921
8058
|
if (plan.adrMoves.length === 0 && plan.planMoves.length === 0 && plan.orphanPlans.length === 0) {
|
|
7922
8059
|
logger.success(" Nothing to migrate. Layout is already up to date.");
|
|
@@ -7935,7 +8072,7 @@ function summarize(plan) {
|
|
|
7935
8072
|
logger.info(
|
|
7936
8073
|
` Plan moves: ${plan.planMoves.length} file(s) \u2014 docs/plans/ \u2192 docs/changes/<topic>/{plans,verifications}/`
|
|
7937
8074
|
);
|
|
7938
|
-
for (const [via, n] of Object.entries(byVia)) console.log(
|
|
8075
|
+
for (const [via, n] of Object.entries(byVia)) console.log(chalk10.dim(` via ${via}: ${n}`));
|
|
7939
8076
|
}
|
|
7940
8077
|
if (plan.orphanPlans.length > 0) {
|
|
7941
8078
|
logger.warn(` Orphan plans: ${plan.orphanPlans.length} (no proposal match \u2014 needs decision)`);
|
|
@@ -7943,10 +8080,10 @@ function summarize(plan) {
|
|
|
7943
8080
|
console.log("");
|
|
7944
8081
|
}
|
|
7945
8082
|
async function detectLegacyArtifacts(cwd) {
|
|
7946
|
-
const adrLegacyDir =
|
|
7947
|
-
const adrLegacy =
|
|
7948
|
-
const planLegacyDir =
|
|
7949
|
-
const planLegacy =
|
|
8083
|
+
const adrLegacyDir = path44.join(cwd, ".harness", "architecture");
|
|
8084
|
+
const adrLegacy = fs28.existsSync(adrLegacyDir) && listDirs(adrLegacyDir).length > 0;
|
|
8085
|
+
const planLegacyDir = path44.join(cwd, "docs", "plans");
|
|
8086
|
+
const planLegacy = fs28.existsSync(planLegacyDir) && listFiles(planLegacyDir, (n) => n.endsWith(".md") && n !== "index.md").length > 0;
|
|
7950
8087
|
return { adrLegacy, planLegacy };
|
|
7951
8088
|
}
|
|
7952
8089
|
async function confirmApply(opts) {
|
|
@@ -8014,7 +8151,7 @@ async function runMigrate(opts) {
|
|
|
8014
8151
|
});
|
|
8015
8152
|
}
|
|
8016
8153
|
function createMigrateCommand() {
|
|
8017
|
-
|
|
8154
|
+
const cmd = new Command63("migrate").description("Migrate legacy harness artifact locations to current layout").option("--dry-run", "Show the migration plan without moving files", false).option("--yes", "Skip confirmation prompt", false).option(
|
|
8018
8155
|
"--skip-references",
|
|
8019
8156
|
"Do not update path references in docs/sessions after moves",
|
|
8020
8157
|
false
|
|
@@ -8033,12 +8170,158 @@ function createMigrateCommand() {
|
|
|
8033
8170
|
}
|
|
8034
8171
|
process.exit(result.value.movesFailed === 0 ? ExitCode.SUCCESS : ExitCode.ERROR);
|
|
8035
8172
|
});
|
|
8173
|
+
cmd.addCommand(createBackendsSubcommand());
|
|
8174
|
+
return cmd;
|
|
8175
|
+
}
|
|
8176
|
+
|
|
8177
|
+
// src/commands/models.ts
|
|
8178
|
+
import { Command as Command64 } from "commander";
|
|
8179
|
+
import { defaultFetchModels } from "@harness-engineering/orchestrator";
|
|
8180
|
+
function errorResult(message, partial) {
|
|
8181
|
+
return {
|
|
8182
|
+
status: "error",
|
|
8183
|
+
endpoint: "",
|
|
8184
|
+
configured: [],
|
|
8185
|
+
detected: [],
|
|
8186
|
+
resolved: null,
|
|
8187
|
+
error: message,
|
|
8188
|
+
exitCode: ExitCode.ERROR,
|
|
8189
|
+
...partial
|
|
8190
|
+
};
|
|
8191
|
+
}
|
|
8192
|
+
function pickConfiguredList(model) {
|
|
8193
|
+
if (typeof model === "string") return [model];
|
|
8194
|
+
if (Array.isArray(model)) return model.filter((m) => typeof m === "string");
|
|
8195
|
+
return [];
|
|
8196
|
+
}
|
|
8197
|
+
function locateBackend(opts) {
|
|
8198
|
+
const resolved = resolveConfig(opts.configPath);
|
|
8199
|
+
if (!resolved.ok) {
|
|
8200
|
+
return errorResult(`Could not load harness.config.json: ${resolved.error.message}`);
|
|
8201
|
+
}
|
|
8202
|
+
const backends = resolved.value.agent?.backends;
|
|
8203
|
+
const name = opts.backend ?? defaultLocalBackend(backends);
|
|
8204
|
+
if (name === null) {
|
|
8205
|
+
return errorResult(
|
|
8206
|
+
'No local backend in agent.backends. Declare one with type "local" or "pi", or pass --endpoint <url>.'
|
|
8207
|
+
);
|
|
8208
|
+
}
|
|
8209
|
+
const def = backends?.[name];
|
|
8210
|
+
if (def === void 0) {
|
|
8211
|
+
return errorResult(`agent.backends["${name}"] not found.`);
|
|
8212
|
+
}
|
|
8213
|
+
return { name, def };
|
|
8214
|
+
}
|
|
8215
|
+
function resolveProbeContext(opts) {
|
|
8216
|
+
if (opts.endpoint !== void 0) {
|
|
8217
|
+
return {
|
|
8218
|
+
endpoint: opts.endpoint,
|
|
8219
|
+
configured: [],
|
|
8220
|
+
apiKey: opts.apiKey ?? "local",
|
|
8221
|
+
backendLabel: opts.backend
|
|
8222
|
+
};
|
|
8223
|
+
}
|
|
8224
|
+
const located = locateBackend(opts);
|
|
8225
|
+
if ("status" in located) return located;
|
|
8226
|
+
const ep = located.def.endpoint;
|
|
8227
|
+
if (typeof ep !== "string") {
|
|
8228
|
+
return errorResult(`agent.backends["${located.name}"]: missing or invalid 'endpoint'.`);
|
|
8229
|
+
}
|
|
8230
|
+
const explicitKey = typeof located.def.apiKey === "string" ? located.def.apiKey : void 0;
|
|
8231
|
+
return {
|
|
8232
|
+
endpoint: ep,
|
|
8233
|
+
configured: pickConfiguredList(located.def.model),
|
|
8234
|
+
apiKey: opts.apiKey ?? explicitKey ?? "local",
|
|
8235
|
+
backendLabel: located.name
|
|
8236
|
+
};
|
|
8237
|
+
}
|
|
8238
|
+
async function runModelsProbe(opts) {
|
|
8239
|
+
const ctx = resolveProbeContext(opts);
|
|
8240
|
+
if ("status" in ctx) return ctx;
|
|
8241
|
+
let detected;
|
|
8242
|
+
try {
|
|
8243
|
+
detected = await defaultFetchModels(ctx.endpoint, ctx.apiKey);
|
|
8244
|
+
} catch (err) {
|
|
8245
|
+
return {
|
|
8246
|
+
status: "error",
|
|
8247
|
+
...ctx.backendLabel !== void 0 ? { backend: ctx.backendLabel } : {},
|
|
8248
|
+
endpoint: ctx.endpoint,
|
|
8249
|
+
configured: ctx.configured,
|
|
8250
|
+
detected: [],
|
|
8251
|
+
resolved: null,
|
|
8252
|
+
error: err instanceof Error ? err.message : String(err),
|
|
8253
|
+
exitCode: ExitCode.ERROR
|
|
8254
|
+
};
|
|
8255
|
+
}
|
|
8256
|
+
const match = ctx.configured.find((id) => detected.includes(id)) ?? null;
|
|
8257
|
+
return {
|
|
8258
|
+
status: match !== null ? "ok" : "no-match",
|
|
8259
|
+
...ctx.backendLabel !== void 0 ? { backend: ctx.backendLabel } : {},
|
|
8260
|
+
endpoint: ctx.endpoint,
|
|
8261
|
+
configured: ctx.configured,
|
|
8262
|
+
detected,
|
|
8263
|
+
resolved: match,
|
|
8264
|
+
exitCode: match !== null ? ExitCode.SUCCESS : ExitCode.ERROR
|
|
8265
|
+
};
|
|
8266
|
+
}
|
|
8267
|
+
function defaultLocalBackend(backends) {
|
|
8268
|
+
if (backends === void 0) return null;
|
|
8269
|
+
for (const [name, def] of Object.entries(backends)) {
|
|
8270
|
+
const t = def.type;
|
|
8271
|
+
if (t === "local" || t === "pi") return name;
|
|
8272
|
+
}
|
|
8273
|
+
return null;
|
|
8274
|
+
}
|
|
8275
|
+
function formatProbe(result) {
|
|
8276
|
+
if (result.status === "error") {
|
|
8277
|
+
return `error probing ${result.endpoint || "(no endpoint)"}: ${result.error}`;
|
|
8278
|
+
}
|
|
8279
|
+
const lines = [];
|
|
8280
|
+
lines.push(
|
|
8281
|
+
`endpoint: ${result.endpoint}${result.backend !== void 0 ? ` (agent.backends["${result.backend}"])` : ""}`
|
|
8282
|
+
);
|
|
8283
|
+
lines.push(`configured: [${result.configured.join(", ")}]`);
|
|
8284
|
+
lines.push(`detected: [${result.detected.join(", ")}]`);
|
|
8285
|
+
if (result.resolved !== null) {
|
|
8286
|
+
lines.push(`resolved: ${result.resolved}`);
|
|
8287
|
+
} else {
|
|
8288
|
+
lines.push("resolved: (no configured model is loaded)");
|
|
8289
|
+
lines.push(
|
|
8290
|
+
"hint: load one of the configured models or update agent.backends.<name>.model to include a detected id."
|
|
8291
|
+
);
|
|
8292
|
+
}
|
|
8293
|
+
return lines.join("\n");
|
|
8294
|
+
}
|
|
8295
|
+
function createModelsCommand() {
|
|
8296
|
+
const cmd = new Command64("models").description(
|
|
8297
|
+
"Inspect and manage local LLM backends. Currently ships `probe`; LMLM phases add status/suggest/pool/proposals."
|
|
8298
|
+
);
|
|
8299
|
+
cmd.command("probe").description(
|
|
8300
|
+
"Probe a local backend's /v1/models endpoint and report which configured model is loaded."
|
|
8301
|
+
).option(
|
|
8302
|
+
"--backend <name>",
|
|
8303
|
+
"Name of an entry in agent.backends. Defaults to the first local/pi entry."
|
|
8304
|
+
).option("--endpoint <url>", "Override the backend endpoint (bypasses harness.config.json).").option("--api-key <key>", "Override the API key.").option("--json", "Print machine-readable JSON instead of a human summary.", false).action(async (options) => {
|
|
8305
|
+
const result = await runModelsProbe(options);
|
|
8306
|
+
if (options.json) {
|
|
8307
|
+
console.log(JSON.stringify(result, null, 2));
|
|
8308
|
+
} else {
|
|
8309
|
+
const text = formatProbe(result);
|
|
8310
|
+
if (result.status === "error" || result.status === "no-match") {
|
|
8311
|
+
logger.error(text);
|
|
8312
|
+
} else {
|
|
8313
|
+
logger.info(text);
|
|
8314
|
+
}
|
|
8315
|
+
}
|
|
8316
|
+
process.exit(result.exitCode);
|
|
8317
|
+
});
|
|
8318
|
+
return cmd;
|
|
8036
8319
|
}
|
|
8037
8320
|
|
|
8038
8321
|
// src/commands/naming-craft.ts
|
|
8039
|
-
import { Command as
|
|
8322
|
+
import { Command as Command65 } from "commander";
|
|
8040
8323
|
function createNamingCraftCommand() {
|
|
8041
|
-
return new
|
|
8324
|
+
return new Command65("naming-craft").description(
|
|
8042
8325
|
"LLM-judgment critique of identifier names (variables, functions, types, files). First craft-pipeline ceiling skill; uses curated rubric catalog from Martin/Beck/Karlton."
|
|
8043
8326
|
).option("-f, --files <files...>", "Optional file/glob scope").option(
|
|
8044
8327
|
"-k, --kinds <kinds...>",
|
|
@@ -8088,10 +8371,10 @@ function printResult4(result, mode, _formatter) {
|
|
|
8088
8371
|
list.push(f);
|
|
8089
8372
|
byFile.set(f.target.file, list);
|
|
8090
8373
|
}
|
|
8091
|
-
for (const [file,
|
|
8374
|
+
for (const [file, fs48] of byFile) {
|
|
8092
8375
|
console.log(`
|
|
8093
8376
|
${file}`);
|
|
8094
|
-
for (const f of
|
|
8377
|
+
for (const f of fs48) {
|
|
8095
8378
|
const line = f.target.line !== void 0 ? `:${f.target.line}` : "";
|
|
8096
8379
|
console.log(
|
|
8097
8380
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.kind} ${f.target.identifier}${line}`
|
|
@@ -8111,11 +8394,11 @@ ${file}`);
|
|
|
8111
8394
|
}
|
|
8112
8395
|
|
|
8113
8396
|
// src/commands/notifications/index.ts
|
|
8114
|
-
import { Command as
|
|
8397
|
+
import { Command as Command67 } from "commander";
|
|
8115
8398
|
|
|
8116
8399
|
// src/commands/notifications/test.ts
|
|
8117
8400
|
init_dist3();
|
|
8118
|
-
import { Command as
|
|
8401
|
+
import { Command as Command66 } from "commander";
|
|
8119
8402
|
import { randomBytes } from "crypto";
|
|
8120
8403
|
import { resolve as resolve28 } from "path";
|
|
8121
8404
|
import { SinkRegistry, SinkConfigError, wrapAsEnvelope } from "@harness-engineering/orchestrator";
|
|
@@ -8167,7 +8450,7 @@ async function runNotificationsTest(sinkId, opts, projectRoot2 = process.cwd())
|
|
|
8167
8450
|
return { ok: false, error: result.error };
|
|
8168
8451
|
}
|
|
8169
8452
|
function createNotificationsTestSubcommand() {
|
|
8170
|
-
return new
|
|
8453
|
+
return new Command66("test").description("Send a synthetic notification.test event through the named sink").argument("<sink-id>", "Sink id from harness.config.json `notifications.sinks[].id`").option("--message <text>", "Override the default test message").action(async (sinkId, opts, cmd) => {
|
|
8171
8454
|
const globalOpts = cmd.optsWithGlobals();
|
|
8172
8455
|
const projectRoot2 = typeof globalOpts["cwd"] === "string" ? resolve28(globalOpts["cwd"]) : process.cwd();
|
|
8173
8456
|
const result = await runNotificationsTest(sinkId, opts, projectRoot2);
|
|
@@ -8184,7 +8467,7 @@ function createNotificationsTestSubcommand() {
|
|
|
8184
8467
|
|
|
8185
8468
|
// src/commands/notifications/index.ts
|
|
8186
8469
|
function createNotificationsCommand() {
|
|
8187
|
-
const cmd = new
|
|
8470
|
+
const cmd = new Command67("notifications").description(
|
|
8188
8471
|
"Manage notification sinks (Slack and others)"
|
|
8189
8472
|
);
|
|
8190
8473
|
cmd.addCommand(createNotificationsTestSubcommand());
|
|
@@ -8192,13 +8475,13 @@ function createNotificationsCommand() {
|
|
|
8192
8475
|
}
|
|
8193
8476
|
|
|
8194
8477
|
// src/commands/orchestrator.ts
|
|
8195
|
-
import { Command as
|
|
8196
|
-
import * as
|
|
8478
|
+
import { Command as Command68 } from "commander";
|
|
8479
|
+
import * as path45 from "path";
|
|
8197
8480
|
import { Orchestrator, WorkflowLoader as WorkflowLoader2, launchTUI } from "@harness-engineering/orchestrator";
|
|
8198
8481
|
function createOrchestratorCommand() {
|
|
8199
|
-
const orchestrator = new
|
|
8482
|
+
const orchestrator = new Command68("orchestrator");
|
|
8200
8483
|
orchestrator.command("run").description("Run the orchestrator daemon").option("-w, --workflow <path>", "Path to harness.orchestrator.md", "harness.orchestrator.md").option("--headless", "Run without TUI (server-only mode for use with web dashboard)").action(async (opts) => {
|
|
8201
|
-
const workflowPath =
|
|
8484
|
+
const workflowPath = path45.resolve(process.cwd(), opts.workflow);
|
|
8202
8485
|
const loader = new WorkflowLoader2();
|
|
8203
8486
|
const result = await loader.loadWorkflow(workflowPath);
|
|
8204
8487
|
if (!result.ok) {
|
|
@@ -8232,13 +8515,13 @@ function createOrchestratorCommand() {
|
|
|
8232
8515
|
|
|
8233
8516
|
// src/commands/perf.ts
|
|
8234
8517
|
init_dist3();
|
|
8235
|
-
import { Command as
|
|
8236
|
-
import * as
|
|
8518
|
+
import { Command as Command69 } from "commander";
|
|
8519
|
+
import * as path46 from "path";
|
|
8237
8520
|
function registerBenchCommand(perf) {
|
|
8238
8521
|
perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
|
|
8239
8522
|
const globalOpts = cmd.optsWithGlobals();
|
|
8240
8523
|
const cwd = process.cwd();
|
|
8241
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
8524
|
+
const { BenchmarkRunner } = await import("./dist-72ID44UE.js");
|
|
8242
8525
|
const runner = new BenchmarkRunner();
|
|
8243
8526
|
const benchFiles = runner.discover(cwd, glob2);
|
|
8244
8527
|
if (benchFiles.length === 0) {
|
|
@@ -8302,7 +8585,7 @@ async function runBaselinesShow(globalOpts) {
|
|
|
8302
8585
|
}
|
|
8303
8586
|
async function runBaselinesUpdate(globalOpts) {
|
|
8304
8587
|
const cwd = process.cwd();
|
|
8305
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
8588
|
+
const { BenchmarkRunner } = await import("./dist-72ID44UE.js");
|
|
8306
8589
|
const runner = new BenchmarkRunner();
|
|
8307
8590
|
const manager = new BaselineManager(cwd);
|
|
8308
8591
|
logger.info("Running benchmarks to update baselines...");
|
|
@@ -8343,9 +8626,9 @@ function registerReportCommand(perf) {
|
|
|
8343
8626
|
perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
|
|
8344
8627
|
const globalOpts = cmd.optsWithGlobals();
|
|
8345
8628
|
const cwd = process.cwd();
|
|
8346
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-
|
|
8629
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-72ID44UE.js");
|
|
8347
8630
|
const analyzer = new EntropyAnalyzer2({
|
|
8348
|
-
rootDir:
|
|
8631
|
+
rootDir: path46.resolve(cwd),
|
|
8349
8632
|
analyze: { complexity: true, coupling: true }
|
|
8350
8633
|
});
|
|
8351
8634
|
const result = await analyzer.analyze();
|
|
@@ -8404,7 +8687,7 @@ function registerCriticalPathsCommand(perf) {
|
|
|
8404
8687
|
});
|
|
8405
8688
|
}
|
|
8406
8689
|
function createPerfCommand() {
|
|
8407
|
-
const perf = new
|
|
8690
|
+
const perf = new Command69("perf").description("Performance benchmark and baseline management");
|
|
8408
8691
|
registerBenchCommand(perf);
|
|
8409
8692
|
registerBaselinesCommands(perf);
|
|
8410
8693
|
registerReportCommand(perf);
|
|
@@ -8413,10 +8696,10 @@ function createPerfCommand() {
|
|
|
8413
8696
|
}
|
|
8414
8697
|
|
|
8415
8698
|
// src/commands/persona/index.ts
|
|
8416
|
-
import { Command as
|
|
8699
|
+
import { Command as Command72 } from "commander";
|
|
8417
8700
|
|
|
8418
8701
|
// src/commands/persona/list.ts
|
|
8419
|
-
import { Command as
|
|
8702
|
+
import { Command as Command70 } from "commander";
|
|
8420
8703
|
function printPersonaList(personas) {
|
|
8421
8704
|
if (personas.length === 0) {
|
|
8422
8705
|
logger.info("No personas found.");
|
|
@@ -8430,7 +8713,7 @@ function printPersonaList(personas) {
|
|
|
8430
8713
|
}
|
|
8431
8714
|
}
|
|
8432
8715
|
function createListCommand2() {
|
|
8433
|
-
return new
|
|
8716
|
+
return new Command70("list").description("List available agent personas").action(async (_opts, cmd) => {
|
|
8434
8717
|
const globalOpts = cmd.optsWithGlobals();
|
|
8435
8718
|
const personasDir = resolvePersonasDir();
|
|
8436
8719
|
const result = listPersonas(personasDir);
|
|
@@ -8450,51 +8733,51 @@ function createListCommand2() {
|
|
|
8450
8733
|
}
|
|
8451
8734
|
|
|
8452
8735
|
// src/commands/persona/generate.ts
|
|
8453
|
-
import { Command as
|
|
8454
|
-
import * as
|
|
8455
|
-
import * as
|
|
8736
|
+
import { Command as Command71 } from "commander";
|
|
8737
|
+
import * as fs29 from "fs";
|
|
8738
|
+
import * as path47 from "path";
|
|
8456
8739
|
function generatePersonaArtifacts(persona, outputDir, only) {
|
|
8457
8740
|
const slug = toKebabCase(persona.name);
|
|
8458
8741
|
const generated = [];
|
|
8459
8742
|
if (!only || only === "runtime") {
|
|
8460
8743
|
const result = generateRuntime(persona);
|
|
8461
8744
|
if (result.ok) {
|
|
8462
|
-
const outPath =
|
|
8463
|
-
|
|
8464
|
-
|
|
8745
|
+
const outPath = path47.join(outputDir, `${slug}.runtime.json`);
|
|
8746
|
+
fs29.mkdirSync(path47.dirname(outPath), { recursive: true });
|
|
8747
|
+
fs29.writeFileSync(outPath, result.value);
|
|
8465
8748
|
generated.push(outPath);
|
|
8466
8749
|
}
|
|
8467
8750
|
}
|
|
8468
8751
|
if (!only || only === "agents-md") {
|
|
8469
8752
|
const result = generateAgentsMd(persona);
|
|
8470
8753
|
if (result.ok) {
|
|
8471
|
-
const outPath =
|
|
8472
|
-
|
|
8754
|
+
const outPath = path47.join(outputDir, `${slug}.agents.md`);
|
|
8755
|
+
fs29.writeFileSync(outPath, result.value);
|
|
8473
8756
|
generated.push(outPath);
|
|
8474
8757
|
}
|
|
8475
8758
|
}
|
|
8476
8759
|
if (!only || only === "ci") {
|
|
8477
8760
|
const result = generateCIWorkflow(persona, "github");
|
|
8478
8761
|
if (result.ok) {
|
|
8479
|
-
const outPath =
|
|
8480
|
-
|
|
8481
|
-
|
|
8762
|
+
const outPath = path47.join(outputDir, ".github", "workflows", `${slug}.yml`);
|
|
8763
|
+
fs29.mkdirSync(path47.dirname(outPath), { recursive: true });
|
|
8764
|
+
fs29.writeFileSync(outPath, result.value);
|
|
8482
8765
|
generated.push(outPath);
|
|
8483
8766
|
}
|
|
8484
8767
|
}
|
|
8485
8768
|
return generated;
|
|
8486
8769
|
}
|
|
8487
8770
|
function createGenerateCommand3() {
|
|
8488
|
-
return new
|
|
8771
|
+
return new Command71("generate").description("Generate artifacts from a persona config").argument("<name>", "Persona name (e.g., architecture-enforcer)").option("--output-dir <dir>", "Output directory", ".").option("--only <type>", "Generate only: ci, agents-md, runtime").action(async (name, opts, cmd) => {
|
|
8489
8772
|
const globalOpts = cmd.optsWithGlobals();
|
|
8490
|
-
const personaResult = loadPersona(
|
|
8773
|
+
const personaResult = loadPersona(path47.join(resolvePersonasDir(), `${name}.yaml`));
|
|
8491
8774
|
if (!personaResult.ok) {
|
|
8492
8775
|
logger.error(personaResult.error.message);
|
|
8493
8776
|
process.exit(ExitCode.ERROR);
|
|
8494
8777
|
}
|
|
8495
8778
|
const generated = generatePersonaArtifacts(
|
|
8496
8779
|
personaResult.value,
|
|
8497
|
-
|
|
8780
|
+
path47.resolve(opts.outputDir),
|
|
8498
8781
|
opts.only
|
|
8499
8782
|
);
|
|
8500
8783
|
if (!globalOpts.quiet) {
|
|
@@ -8507,7 +8790,7 @@ function createGenerateCommand3() {
|
|
|
8507
8790
|
|
|
8508
8791
|
// src/commands/persona/index.ts
|
|
8509
8792
|
function createPersonaCommand() {
|
|
8510
|
-
const command = new
|
|
8793
|
+
const command = new Command72("persona").description("Agent persona management commands");
|
|
8511
8794
|
command.addCommand(createListCommand2());
|
|
8512
8795
|
command.addCommand(createGenerateCommand3());
|
|
8513
8796
|
return command;
|
|
@@ -8515,8 +8798,8 @@ function createPersonaCommand() {
|
|
|
8515
8798
|
|
|
8516
8799
|
// src/commands/predict.ts
|
|
8517
8800
|
init_dist3();
|
|
8518
|
-
import { Command as
|
|
8519
|
-
import
|
|
8801
|
+
import { Command as Command73 } from "commander";
|
|
8802
|
+
import chalk11 from "chalk";
|
|
8520
8803
|
var CATEGORY_ORDER = [
|
|
8521
8804
|
"circular-deps",
|
|
8522
8805
|
"layer-violations",
|
|
@@ -8537,11 +8820,11 @@ function crossingLabel(weeks) {
|
|
|
8537
8820
|
function severityPrefix(severity) {
|
|
8538
8821
|
switch (severity) {
|
|
8539
8822
|
case "critical":
|
|
8540
|
-
return
|
|
8823
|
+
return chalk11.red("[critical]");
|
|
8541
8824
|
case "warning":
|
|
8542
|
-
return
|
|
8825
|
+
return chalk11.yellow("[warning]");
|
|
8543
8826
|
case "info":
|
|
8544
|
-
return
|
|
8827
|
+
return chalk11.blue("[info]");
|
|
8545
8828
|
}
|
|
8546
8829
|
}
|
|
8547
8830
|
function printCategoryTable(result) {
|
|
@@ -8621,7 +8904,7 @@ function handlePredictError(err, mode) {
|
|
|
8621
8904
|
process.exit(ExitCode.ERROR);
|
|
8622
8905
|
}
|
|
8623
8906
|
function createPredictCommand() {
|
|
8624
|
-
const command = new
|
|
8907
|
+
const command = new Command73("predict").description("Predict which architectural constraints will break and when").option("--category <name>", "Filter to a single metric category").option("--no-roadmap", "Baseline only \u2014 skip roadmap spec impact").option("--horizon <weeks>", "Forecast horizon in weeks (default: 12)", "12").action(async (opts, cmd) => {
|
|
8625
8908
|
const globalOpts = cmd.optsWithGlobals();
|
|
8626
8909
|
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
8627
8910
|
try {
|
|
@@ -8645,7 +8928,7 @@ function createPredictCommand() {
|
|
|
8645
8928
|
|
|
8646
8929
|
// src/commands/proposals.ts
|
|
8647
8930
|
init_dist3();
|
|
8648
|
-
import { Command as
|
|
8931
|
+
import { Command as Command74 } from "commander";
|
|
8649
8932
|
import { resolve as resolve32 } from "path";
|
|
8650
8933
|
function projectRoot() {
|
|
8651
8934
|
return resolve32(process.env["HARNESS_PROJECT_ROOT"] ?? process.cwd());
|
|
@@ -8740,7 +9023,7 @@ async function actApproveCommand(id) {
|
|
|
8740
9023
|
}
|
|
8741
9024
|
}
|
|
8742
9025
|
function createProposalsCommand() {
|
|
8743
|
-
const cmd = new
|
|
9026
|
+
const cmd = new Command74("proposals").description("Skill-proposal review queue");
|
|
8744
9027
|
cmd.command("list").description("List skill proposals in the local queue").option(
|
|
8745
9028
|
"--status <status>",
|
|
8746
9029
|
`Filter by status \u2014 one of ${ALLOWED_STATUSES.join(" | ")}`,
|
|
@@ -8755,9 +9038,9 @@ function createProposalsCommand() {
|
|
|
8755
9038
|
}
|
|
8756
9039
|
|
|
8757
9040
|
// src/commands/publish-analyses.ts
|
|
8758
|
-
import * as
|
|
8759
|
-
import * as
|
|
8760
|
-
import { Command as
|
|
9041
|
+
import * as fs30 from "fs";
|
|
9042
|
+
import * as path48 from "path";
|
|
9043
|
+
import { Command as Command75 } from "commander";
|
|
8761
9044
|
init_dist3();
|
|
8762
9045
|
import {
|
|
8763
9046
|
renderAnalysisComment,
|
|
@@ -8765,7 +9048,7 @@ import {
|
|
|
8765
9048
|
savePublishedIndex
|
|
8766
9049
|
} from "@harness-engineering/orchestrator";
|
|
8767
9050
|
function bootstrapTrackerCommand(opts, verb) {
|
|
8768
|
-
const projectPath =
|
|
9051
|
+
const projectPath = path48.resolve(opts.dir);
|
|
8769
9052
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
8770
9053
|
if (!trackerConfig) {
|
|
8771
9054
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -8777,8 +9060,8 @@ function bootstrapTrackerCommand(opts, verb) {
|
|
|
8777
9060
|
);
|
|
8778
9061
|
return null;
|
|
8779
9062
|
}
|
|
8780
|
-
const projectEnvPath =
|
|
8781
|
-
if (
|
|
9063
|
+
const projectEnvPath = path48.join(projectPath, ".env");
|
|
9064
|
+
if (fs30.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
8782
9065
|
__require("dotenv").config({ path: projectEnvPath });
|
|
8783
9066
|
}
|
|
8784
9067
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -8814,13 +9097,13 @@ async function buildNameToExternalIdMap(projectPath) {
|
|
|
8814
9097
|
}
|
|
8815
9098
|
return fileLessMap;
|
|
8816
9099
|
}
|
|
8817
|
-
const roadmapFile =
|
|
8818
|
-
if (!
|
|
9100
|
+
const roadmapFile = path48.join(projectPath, "docs", "roadmap.md");
|
|
9101
|
+
if (!fs30.existsSync(roadmapFile)) {
|
|
8819
9102
|
logger.error("No docs/roadmap.md found. Cannot map issue hashes to external IDs.");
|
|
8820
9103
|
return null;
|
|
8821
9104
|
}
|
|
8822
9105
|
const { parseRoadmap: parseRoadmap2 } = (init_dist3(), __toCommonJS(dist_exports));
|
|
8823
|
-
const roadmapParsed = parseRoadmap2(
|
|
9106
|
+
const roadmapParsed = parseRoadmap2(fs30.readFileSync(roadmapFile, "utf-8"));
|
|
8824
9107
|
if (!roadmapParsed.ok) {
|
|
8825
9108
|
logger.error("Failed to parse docs/roadmap.md");
|
|
8826
9109
|
return null;
|
|
@@ -8870,8 +9153,8 @@ async function runPublishAnalyses(opts) {
|
|
|
8870
9153
|
const nameToExternalId = await buildNameToExternalIdMap(projectPath);
|
|
8871
9154
|
if (!nameToExternalId) return process.exit(1);
|
|
8872
9155
|
const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
|
|
8873
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
8874
|
-
const archive = new AnalysisArchive(
|
|
9156
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-72ID44UE.js");
|
|
9157
|
+
const archive = new AnalysisArchive(path48.join(projectPath, ".harness", "analyses"));
|
|
8875
9158
|
const analyses = await archive.list();
|
|
8876
9159
|
if (analyses.length === 0) {
|
|
8877
9160
|
logger.info("No analyses found to publish.");
|
|
@@ -8893,7 +9176,7 @@ async function runPublishAnalyses(opts) {
|
|
|
8893
9176
|
}
|
|
8894
9177
|
}
|
|
8895
9178
|
function createPublishAnalysesCommand() {
|
|
8896
|
-
const command = new
|
|
9179
|
+
const command = new Command75("publish-analyses").description(
|
|
8897
9180
|
"Publishes locally generated intelligence analyses to the external issue tracker (e.g., GitHub)"
|
|
8898
9181
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
8899
9182
|
try {
|
|
@@ -8909,18 +9192,18 @@ function createPublishAnalysesCommand() {
|
|
|
8909
9192
|
}
|
|
8910
9193
|
|
|
8911
9194
|
// src/commands/pulse/index.ts
|
|
8912
|
-
import { Command as
|
|
9195
|
+
import { Command as Command77 } from "commander";
|
|
8913
9196
|
|
|
8914
9197
|
// src/commands/pulse/run.ts
|
|
8915
9198
|
init_dist3();
|
|
8916
|
-
import { readFileSync as
|
|
8917
|
-
import { dirname as
|
|
8918
|
-
import { Command as
|
|
9199
|
+
import { readFileSync as readFileSync19, mkdirSync as mkdirSync13, writeFileSync as writeFileSync16 } from "fs";
|
|
9200
|
+
import { dirname as dirname18, join as join39, resolve as resolve34 } from "path";
|
|
9201
|
+
import { Command as Command76 } from "commander";
|
|
8919
9202
|
async function runPulseRunCommand(opts) {
|
|
8920
9203
|
const startedAt = Date.now();
|
|
8921
9204
|
let raw;
|
|
8922
9205
|
try {
|
|
8923
|
-
raw = JSON.parse(
|
|
9206
|
+
raw = JSON.parse(readFileSync19(opts.configPath, "utf-8"));
|
|
8924
9207
|
} catch (err) {
|
|
8925
9208
|
return emit2(
|
|
8926
9209
|
{
|
|
@@ -8946,9 +9229,9 @@ async function runPulseRunCommand(opts) {
|
|
|
8946
9229
|
const report = assembleReport(result, productName, lookback);
|
|
8947
9230
|
const now = /* @__PURE__ */ new Date();
|
|
8948
9231
|
const ts = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}_${String(now.getHours()).padStart(2, "0")}-${String(now.getMinutes()).padStart(2, "0")}`;
|
|
8949
|
-
const filePath =
|
|
8950
|
-
mkdirSync13(
|
|
8951
|
-
|
|
9232
|
+
const filePath = join39(opts.outputDir, `${ts}.md`);
|
|
9233
|
+
mkdirSync13(dirname18(filePath), { recursive: true });
|
|
9234
|
+
writeFileSync16(filePath, report);
|
|
8952
9235
|
const headlines = extractHeadlines(report);
|
|
8953
9236
|
return emit2(
|
|
8954
9237
|
{
|
|
@@ -8980,7 +9263,7 @@ function emit2(status, nonInteractive) {
|
|
|
8980
9263
|
return status;
|
|
8981
9264
|
}
|
|
8982
9265
|
function createRunCommand2() {
|
|
8983
|
-
return new
|
|
9266
|
+
return new Command76("run").description("Run a pulse: query configured adapters, sanitize, assemble single-page report").option(
|
|
8984
9267
|
"--lookback <window>",
|
|
8985
9268
|
"Lookback window (e.g. 24h, 7d). Defaults to pulse.lookbackDefault."
|
|
8986
9269
|
).option(
|
|
@@ -9004,14 +9287,14 @@ function createRunCommand2() {
|
|
|
9004
9287
|
|
|
9005
9288
|
// src/commands/pulse/index.ts
|
|
9006
9289
|
function createPulseCommand() {
|
|
9007
|
-
const command = new
|
|
9290
|
+
const command = new Command77("pulse").description("Pulse (read-side observability) commands");
|
|
9008
9291
|
command.addCommand(createRunCommand2());
|
|
9009
9292
|
return command;
|
|
9010
9293
|
}
|
|
9011
9294
|
|
|
9012
9295
|
// src/commands/recommend.ts
|
|
9013
|
-
import { Command as
|
|
9014
|
-
import
|
|
9296
|
+
import { Command as Command78 } from "commander";
|
|
9297
|
+
import chalk12 from "chalk";
|
|
9015
9298
|
async function resolveSnapshot(cwd, noCache) {
|
|
9016
9299
|
if (!noCache) {
|
|
9017
9300
|
const cached = loadCachedSnapshot(cwd);
|
|
@@ -9045,12 +9328,12 @@ async function runRecommend(options) {
|
|
|
9045
9328
|
function formatRecommendation(rec) {
|
|
9046
9329
|
const lines = [];
|
|
9047
9330
|
if (rec.urgency === "critical") {
|
|
9048
|
-
lines.push(` ${
|
|
9331
|
+
lines.push(` ${chalk12.red("[CRITICAL]")} ${rec.sequence}. ${rec.skillName}`);
|
|
9049
9332
|
} else {
|
|
9050
9333
|
lines.push(` ${rec.sequence}. ${rec.skillName} (${rec.score.toFixed(2)})`);
|
|
9051
9334
|
}
|
|
9052
9335
|
for (const reason of rec.reasons) {
|
|
9053
|
-
lines.push(` ${
|
|
9336
|
+
lines.push(` ${chalk12.dim("\u2192")} ${reason}`);
|
|
9054
9337
|
}
|
|
9055
9338
|
return lines.join("\n");
|
|
9056
9339
|
}
|
|
@@ -9074,7 +9357,7 @@ function printRecommendations(result) {
|
|
|
9074
9357
|
console.log("");
|
|
9075
9358
|
}
|
|
9076
9359
|
function createRecommendCommand() {
|
|
9077
|
-
const command = new
|
|
9360
|
+
const command = new Command78("recommend").description("Recommend skills based on codebase health analysis").option("--no-cache", "Force fresh health snapshot").option("--top <n>", "Max recommendations (default 5)", "5").action(async (opts, cmd) => {
|
|
9078
9361
|
const globalOpts = cmd.optsWithGlobals();
|
|
9079
9362
|
const mode = globalOpts.json ? OutputMode.JSON : OutputMode.TEXT;
|
|
9080
9363
|
try {
|
|
@@ -9110,18 +9393,18 @@ function createRecommendCommand() {
|
|
|
9110
9393
|
}
|
|
9111
9394
|
|
|
9112
9395
|
// src/commands/roadmap/index.ts
|
|
9113
|
-
import { Command as
|
|
9396
|
+
import { Command as Command80 } from "commander";
|
|
9114
9397
|
|
|
9115
9398
|
// src/commands/roadmap/migrate.ts
|
|
9116
9399
|
init_dist3();
|
|
9117
|
-
import { Command as
|
|
9118
|
-
import * as
|
|
9119
|
-
import * as
|
|
9120
|
-
import
|
|
9400
|
+
import { Command as Command79 } from "commander";
|
|
9401
|
+
import * as fs32 from "fs";
|
|
9402
|
+
import * as path50 from "path";
|
|
9403
|
+
import chalk13 from "chalk";
|
|
9121
9404
|
|
|
9122
9405
|
// src/commands/roadmap/migrate-lock.ts
|
|
9123
|
-
import * as
|
|
9124
|
-
import * as
|
|
9406
|
+
import * as fs31 from "fs";
|
|
9407
|
+
import * as path49 from "path";
|
|
9125
9408
|
import * as os6 from "os";
|
|
9126
9409
|
var LOCK_FILE_NAME = "migrate.lock";
|
|
9127
9410
|
var STALE_LOCK_MS = 30 * 60 * 1e3;
|
|
@@ -9135,13 +9418,13 @@ function isPidAlive(pid) {
|
|
|
9135
9418
|
}
|
|
9136
9419
|
}
|
|
9137
9420
|
function acquireMigrateLock(projectRoot2) {
|
|
9138
|
-
const harnessDir =
|
|
9139
|
-
const lockPath =
|
|
9140
|
-
if (!
|
|
9141
|
-
if (
|
|
9421
|
+
const harnessDir = path49.join(projectRoot2, ".harness");
|
|
9422
|
+
const lockPath = path49.join(harnessDir, LOCK_FILE_NAME);
|
|
9423
|
+
if (!fs31.existsSync(harnessDir)) fs31.mkdirSync(harnessDir, { recursive: true });
|
|
9424
|
+
if (fs31.existsSync(lockPath)) {
|
|
9142
9425
|
const existingRaw = (() => {
|
|
9143
9426
|
try {
|
|
9144
|
-
return
|
|
9427
|
+
return fs31.readFileSync(lockPath, "utf-8");
|
|
9145
9428
|
} catch {
|
|
9146
9429
|
return null;
|
|
9147
9430
|
}
|
|
@@ -9171,11 +9454,11 @@ function acquireMigrateLock(projectRoot2) {
|
|
|
9171
9454
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9172
9455
|
hostname: os6.hostname()
|
|
9173
9456
|
};
|
|
9174
|
-
|
|
9457
|
+
fs31.writeFileSync(lockPath, JSON.stringify(payload, null, 2) + "\n");
|
|
9175
9458
|
return {
|
|
9176
9459
|
release: () => {
|
|
9177
9460
|
try {
|
|
9178
|
-
|
|
9461
|
+
fs31.unlinkSync(lockPath);
|
|
9179
9462
|
} catch {
|
|
9180
9463
|
}
|
|
9181
9464
|
}
|
|
@@ -9206,7 +9489,7 @@ function reportToExitCode(report) {
|
|
|
9206
9489
|
return MigrateExitCode.GENERIC_FAILURE;
|
|
9207
9490
|
}
|
|
9208
9491
|
function printPlanSummary(plan, dryRun) {
|
|
9209
|
-
const banner = dryRun ?
|
|
9492
|
+
const banner = dryRun ? chalk13.cyan("DRY RUN ") : "";
|
|
9210
9493
|
console.log(`${banner}Migration plan:`);
|
|
9211
9494
|
console.log(` Would create: ${plan.toCreate.length}`);
|
|
9212
9495
|
for (const e of plan.toCreate) console.log(` - ${e.name}`);
|
|
@@ -9335,11 +9618,11 @@ async function runRoadmapMigrate(opts) {
|
|
|
9335
9618
|
if (!clientR.ok) return Err(new CLIError(clientR.error.message));
|
|
9336
9619
|
client = clientR.value;
|
|
9337
9620
|
}
|
|
9338
|
-
const roadmapPath =
|
|
9339
|
-
if (!
|
|
9621
|
+
const roadmapPath = path50.join(cwd, "docs", "roadmap.md");
|
|
9622
|
+
if (!fs32.existsSync(roadmapPath)) {
|
|
9340
9623
|
return Err(new CLIError(`docs/roadmap.md not found in ${cwd}`));
|
|
9341
9624
|
}
|
|
9342
|
-
const roadmapR = parseRoadmap(
|
|
9625
|
+
const roadmapR = parseRoadmap(fs32.readFileSync(roadmapPath, "utf-8"));
|
|
9343
9626
|
if (!roadmapR.ok) {
|
|
9344
9627
|
return Err(new CLIError(`failed to parse docs/roadmap.md: ${roadmapR.error.message}`));
|
|
9345
9628
|
}
|
|
@@ -9360,10 +9643,10 @@ async function runRoadmapMigrate(opts) {
|
|
|
9360
9643
|
if (!isJson) printPlanSummary(plan, opts.dryRun);
|
|
9361
9644
|
const deps = {
|
|
9362
9645
|
client,
|
|
9363
|
-
readFile: (p) =>
|
|
9364
|
-
writeFile: (p, b) =>
|
|
9365
|
-
renameFile: (from, to) =>
|
|
9366
|
-
existsFile: (p) =>
|
|
9646
|
+
readFile: (p) => fs32.readFileSync(p, "utf-8"),
|
|
9647
|
+
writeFile: (p, b) => fs32.writeFileSync(p, b),
|
|
9648
|
+
renameFile: (from, to) => fs32.renameSync(from, to),
|
|
9649
|
+
existsFile: (p) => fs32.existsSync(p)
|
|
9367
9650
|
};
|
|
9368
9651
|
const reportR = await migrate_exports.runMigrationPlan(plan, deps, {
|
|
9369
9652
|
projectRoot: cwd,
|
|
@@ -9384,7 +9667,7 @@ async function runRoadmapMigrate(opts) {
|
|
|
9384
9667
|
}
|
|
9385
9668
|
}
|
|
9386
9669
|
function createRoadmapMigrateCommand() {
|
|
9387
|
-
return new
|
|
9670
|
+
return new Command79("migrate").description("Migrate the project roadmap to a different storage mode").requiredOption("--to <target>", 'Migration target (only "file-less" supported today)').option("--dry-run", "Print the migration plan without making any changes", false).option(
|
|
9388
9671
|
"--format <fmt>",
|
|
9389
9672
|
'Output format: "human" (default) or "json" (single JSON object for CI consumers)',
|
|
9390
9673
|
"human"
|
|
@@ -9419,16 +9702,16 @@ function createRoadmapMigrateCommand() {
|
|
|
9419
9702
|
|
|
9420
9703
|
// src/commands/roadmap/index.ts
|
|
9421
9704
|
function createRoadmapCommand() {
|
|
9422
|
-
const roadmap = new
|
|
9705
|
+
const roadmap = new Command80("roadmap").description("Roadmap management");
|
|
9423
9706
|
roadmap.addCommand(createRoadmapMigrateCommand());
|
|
9424
9707
|
return roadmap;
|
|
9425
9708
|
}
|
|
9426
9709
|
|
|
9427
9710
|
// src/commands/routing/index.ts
|
|
9428
|
-
import { Command as
|
|
9711
|
+
import { Command as Command84 } from "commander";
|
|
9429
9712
|
|
|
9430
9713
|
// src/commands/routing/config.ts
|
|
9431
|
-
import { Command as
|
|
9714
|
+
import { Command as Command81 } from "commander";
|
|
9432
9715
|
|
|
9433
9716
|
// src/commands/routing/http-client.ts
|
|
9434
9717
|
function orchestratorBase() {
|
|
@@ -9438,9 +9721,9 @@ function authHeader() {
|
|
|
9438
9721
|
const tok = process.env["HARNESS_API_TOKEN"];
|
|
9439
9722
|
return tok ? { Authorization: `Bearer ${tok}` } : {};
|
|
9440
9723
|
}
|
|
9441
|
-
async function getJson(
|
|
9724
|
+
async function getJson(path72) {
|
|
9442
9725
|
try {
|
|
9443
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
9726
|
+
const res = await fetch(`${orchestratorBase()}${path72}`, {
|
|
9444
9727
|
headers: { ...authHeader() }
|
|
9445
9728
|
});
|
|
9446
9729
|
const text = await res.text();
|
|
@@ -9455,9 +9738,9 @@ async function getJson(path71) {
|
|
|
9455
9738
|
};
|
|
9456
9739
|
}
|
|
9457
9740
|
}
|
|
9458
|
-
async function postJson(
|
|
9741
|
+
async function postJson(path72, body) {
|
|
9459
9742
|
try {
|
|
9460
|
-
const res = await fetch(`${orchestratorBase()}${
|
|
9743
|
+
const res = await fetch(`${orchestratorBase()}${path72}`, {
|
|
9461
9744
|
method: "POST",
|
|
9462
9745
|
headers: { "Content-Type": "application/json", ...authHeader() },
|
|
9463
9746
|
body: JSON.stringify(body)
|
|
@@ -9493,7 +9776,7 @@ function renderHuman(data) {
|
|
|
9493
9776
|
}
|
|
9494
9777
|
}
|
|
9495
9778
|
function createConfigCommand() {
|
|
9496
|
-
return new
|
|
9779
|
+
return new Command81("config").description("Print active routing config and resolved fallback chains").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
9497
9780
|
const r = await getJson("/api/v1/routing/config");
|
|
9498
9781
|
if (!r.ok) {
|
|
9499
9782
|
if (r.status === 0) {
|
|
@@ -9519,7 +9802,7 @@ function createConfigCommand() {
|
|
|
9519
9802
|
}
|
|
9520
9803
|
|
|
9521
9804
|
// src/commands/routing/trace.ts
|
|
9522
|
-
import { Command as
|
|
9805
|
+
import { Command as Command82 } from "commander";
|
|
9523
9806
|
function buildUseCase(opts) {
|
|
9524
9807
|
if (opts.skill) {
|
|
9525
9808
|
return opts.mode ? { kind: "skill", skillName: opts.skill, cognitiveMode: opts.mode } : { kind: "skill", skillName: opts.skill };
|
|
@@ -9540,7 +9823,7 @@ function renderHuman2(r) {
|
|
|
9540
9823
|
}
|
|
9541
9824
|
}
|
|
9542
9825
|
function createTraceCommand() {
|
|
9543
|
-
return new
|
|
9826
|
+
return new Command82("trace").description("Dry-run a routing decision without dispatching (Spec B F7)").option("--skill <name>", "Skill name to trace").option("--mode <m>", "Cognitive mode to trace (or attach to --skill per spec D12)").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
9544
9827
|
const useCase = buildUseCase(opts);
|
|
9545
9828
|
if (!useCase) {
|
|
9546
9829
|
logger.error("Either --skill <name> or --mode <m> is required");
|
|
@@ -9572,7 +9855,7 @@ function createTraceCommand() {
|
|
|
9572
9855
|
}
|
|
9573
9856
|
|
|
9574
9857
|
// src/commands/routing/decisions.ts
|
|
9575
|
-
import { Command as
|
|
9858
|
+
import { Command as Command83 } from "commander";
|
|
9576
9859
|
function summarizeUseCase(uc) {
|
|
9577
9860
|
switch (uc.kind) {
|
|
9578
9861
|
case "skill":
|
|
@@ -9620,7 +9903,7 @@ function buildQuery(opts) {
|
|
|
9620
9903
|
return q ? `?${q}` : "";
|
|
9621
9904
|
}
|
|
9622
9905
|
function createDecisionsCommand() {
|
|
9623
|
-
return new
|
|
9906
|
+
return new Command83("decisions").description("List recent routing decisions from the orchestrator ring buffer (Spec B F8)").option("--skill <name>", "Filter by useCase.skillName").option("--mode <m>", "Filter by useCase.cognitiveMode").option("--backend <name>", "Filter by chosen backendName").option("--last <N>", "Limit to the N most recent decisions").option("--json", "Emit JSON to stdout instead of human-readable text").action(
|
|
9624
9907
|
async (opts) => {
|
|
9625
9908
|
const query = buildQuery(opts);
|
|
9626
9909
|
const r = await getJson(`/api/v1/routing/decisions${query}`);
|
|
@@ -9650,7 +9933,7 @@ function createDecisionsCommand() {
|
|
|
9650
9933
|
|
|
9651
9934
|
// src/commands/routing/index.ts
|
|
9652
9935
|
function createRoutingCommand() {
|
|
9653
|
-
const cmd = new
|
|
9936
|
+
const cmd = new Command84("routing").description(
|
|
9654
9937
|
"Inspect routing config, trace decisions, and read recent dispatches"
|
|
9655
9938
|
);
|
|
9656
9939
|
cmd.addCommand(createConfigCommand());
|
|
@@ -9661,9 +9944,9 @@ function createRoutingCommand() {
|
|
|
9661
9944
|
|
|
9662
9945
|
// src/commands/scan-config.ts
|
|
9663
9946
|
init_dist3();
|
|
9664
|
-
import { Command as
|
|
9665
|
-
import { existsSync as
|
|
9666
|
-
import { join as
|
|
9947
|
+
import { Command as Command85 } from "commander";
|
|
9948
|
+
import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync19 } from "fs";
|
|
9949
|
+
import { join as join42, relative as relative3 } from "path";
|
|
9667
9950
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
9668
9951
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
9669
9952
|
const highLines = /* @__PURE__ */ new Set();
|
|
@@ -9689,17 +9972,17 @@ function applyFix(filePath, targetDir, content, injectionFindings) {
|
|
|
9689
9972
|
if (!hasHighSeverity) return;
|
|
9690
9973
|
const { cleaned, linesStripped } = stripHighSeverityPatterns(content, injectionFindings);
|
|
9691
9974
|
if (linesStripped > 0) {
|
|
9692
|
-
|
|
9975
|
+
writeFileSync19(filePath, cleaned);
|
|
9693
9976
|
logger.info(
|
|
9694
9977
|
`scan-config --fix: stripped ${linesStripped} high-severity line(s) from ${relative3(targetDir, filePath).replaceAll("\\", "/")}`
|
|
9695
9978
|
);
|
|
9696
9979
|
}
|
|
9697
9980
|
}
|
|
9698
9981
|
function scanSingleFile(filePath, targetDir, scanner, options) {
|
|
9699
|
-
if (!
|
|
9982
|
+
if (!existsSync29(filePath)) return null;
|
|
9700
9983
|
let content;
|
|
9701
9984
|
try {
|
|
9702
|
-
content =
|
|
9985
|
+
content = readFileSync22(filePath, "utf8");
|
|
9703
9986
|
} catch {
|
|
9704
9987
|
return null;
|
|
9705
9988
|
}
|
|
@@ -9720,7 +10003,7 @@ async function runScanConfig(targetDir, options) {
|
|
|
9720
10003
|
const scanner = new SecurityScanner(parseSecurityConfig({}));
|
|
9721
10004
|
const results = [];
|
|
9722
10005
|
for (const configFile of CONFIG_FILES) {
|
|
9723
|
-
const result = scanSingleFile(
|
|
10006
|
+
const result = scanSingleFile(join42(targetDir, configFile), targetDir, scanner, options);
|
|
9724
10007
|
if (result) results.push(result);
|
|
9725
10008
|
}
|
|
9726
10009
|
return { exitCode: computeScanExitCode(results), results };
|
|
@@ -9750,7 +10033,7 @@ function formatTextOutput(result) {
|
|
|
9750
10033
|
}
|
|
9751
10034
|
}
|
|
9752
10035
|
function createScanConfigCommand() {
|
|
9753
|
-
const command = new
|
|
10036
|
+
const command = new Command85("scan-config").description(
|
|
9754
10037
|
"Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
|
|
9755
10038
|
).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
|
|
9756
10039
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -9768,7 +10051,7 @@ function createScanConfigCommand() {
|
|
|
9768
10051
|
|
|
9769
10052
|
// src/commands/search.ts
|
|
9770
10053
|
init_dist();
|
|
9771
|
-
import { Command as
|
|
10054
|
+
import { Command as Command86 } from "commander";
|
|
9772
10055
|
function parseFileKinds(raw) {
|
|
9773
10056
|
if (!raw) return void 0;
|
|
9774
10057
|
const set = /* @__PURE__ */ new Set();
|
|
@@ -9789,7 +10072,7 @@ function fmtBytes(n) {
|
|
|
9789
10072
|
return n.toFixed(0);
|
|
9790
10073
|
}
|
|
9791
10074
|
function createSearchCommand() {
|
|
9792
|
-
return new
|
|
10075
|
+
return new Command86("search").description("Full-text search over archived + live session content.").argument(
|
|
9793
10076
|
"<query>",
|
|
9794
10077
|
"FTS5 query (bare words AND-joined; quotes/AND/OR/NOT/column: for advanced syntax)"
|
|
9795
10078
|
).option("-n, --limit <n>", "Max results", "20").option("--archived-only", "Skip live sessions, only search archived ones").option("--json", "Emit JSON to stdout instead of pretty text").option(
|
|
@@ -9849,9 +10132,9 @@ function createSearchCommand() {
|
|
|
9849
10132
|
}
|
|
9850
10133
|
|
|
9851
10134
|
// src/commands/security-craft.ts
|
|
9852
|
-
import { Command as
|
|
10135
|
+
import { Command as Command87 } from "commander";
|
|
9853
10136
|
function createSecurityCraftCommand() {
|
|
9854
|
-
return new
|
|
10137
|
+
return new Command87("security-craft").description(
|
|
9855
10138
|
"LLM-judgment critique of security posture (TS/JS source). Sixth non-design craft-pipeline ceiling skill (the final sub-project). AST-driven signal detection fires only on files with security-relevant constructs; conservative confidence defaults manage the FP risk inherent in judgment-based security."
|
|
9856
10139
|
).option("-f, --files <files...>", "Optional file scope (overrides discovery)").option("-p, --packages <names...>", "Restrict to specific packages under packages/").option("--max-files <n>", "Cap source-file count (default: 100)").option("--max-signals-per-file <n>", "Cap per-file signal critique (default: 10)").action(async (opts, cmd) => {
|
|
9857
10140
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -9898,10 +10181,10 @@ function printResult5(result, mode, _formatter) {
|
|
|
9898
10181
|
list.push(f);
|
|
9899
10182
|
byFile.set(f.target.file, list);
|
|
9900
10183
|
}
|
|
9901
|
-
for (const [file,
|
|
10184
|
+
for (const [file, fs48] of byFile) {
|
|
9902
10185
|
console.log(`
|
|
9903
10186
|
${file}`);
|
|
9904
|
-
for (const f of
|
|
10187
|
+
for (const f of fs48) {
|
|
9905
10188
|
console.log(
|
|
9906
10189
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.signal}:${f.target.line}`
|
|
9907
10190
|
);
|
|
@@ -9917,24 +10200,24 @@ ${file}`);
|
|
|
9917
10200
|
}
|
|
9918
10201
|
|
|
9919
10202
|
// src/commands/setup.ts
|
|
9920
|
-
import { Command as
|
|
9921
|
-
import * as
|
|
10203
|
+
import { Command as Command88 } from "commander";
|
|
10204
|
+
import * as fs35 from "fs";
|
|
9922
10205
|
import * as os8 from "os";
|
|
9923
|
-
import * as
|
|
9924
|
-
import
|
|
10206
|
+
import * as path53 from "path";
|
|
10207
|
+
import chalk15 from "chalk";
|
|
9925
10208
|
|
|
9926
10209
|
// src/utils/first-run.ts
|
|
9927
|
-
import * as
|
|
10210
|
+
import * as fs33 from "fs";
|
|
9928
10211
|
import * as os7 from "os";
|
|
9929
|
-
import * as
|
|
9930
|
-
var HARNESS_DIR =
|
|
9931
|
-
var MARKER_FILE =
|
|
10212
|
+
import * as path51 from "path";
|
|
10213
|
+
var HARNESS_DIR = path51.join(os7.homedir(), ".harness");
|
|
10214
|
+
var MARKER_FILE = path51.join(HARNESS_DIR, ".setup-complete");
|
|
9932
10215
|
function isFirstRun() {
|
|
9933
|
-
return !
|
|
10216
|
+
return !fs33.existsSync(MARKER_FILE);
|
|
9934
10217
|
}
|
|
9935
10218
|
function markSetupComplete() {
|
|
9936
|
-
|
|
9937
|
-
|
|
10219
|
+
fs33.mkdirSync(HARNESS_DIR, { recursive: true });
|
|
10220
|
+
fs33.writeFileSync(MARKER_FILE, "", "utf-8");
|
|
9938
10221
|
}
|
|
9939
10222
|
function printFirstRunWelcome() {
|
|
9940
10223
|
try {
|
|
@@ -9947,10 +10230,10 @@ function printFirstRunWelcome() {
|
|
|
9947
10230
|
}
|
|
9948
10231
|
|
|
9949
10232
|
// src/commands/telemetry-wizard.ts
|
|
9950
|
-
import * as
|
|
9951
|
-
import * as
|
|
10233
|
+
import * as fs34 from "fs";
|
|
10234
|
+
import * as path52 from "path";
|
|
9952
10235
|
import readline2 from "readline";
|
|
9953
|
-
import
|
|
10236
|
+
import chalk14 from "chalk";
|
|
9954
10237
|
function prompt2(question) {
|
|
9955
10238
|
const rl = readline2.createInterface({
|
|
9956
10239
|
input: process.stdin,
|
|
@@ -9979,21 +10262,21 @@ function isNo(answer) {
|
|
|
9979
10262
|
return answer === "n" || answer === "no";
|
|
9980
10263
|
}
|
|
9981
10264
|
function isTelemetryConfigured(cwd) {
|
|
9982
|
-
const configPath =
|
|
10265
|
+
const configPath = path52.join(cwd, "harness.config.json");
|
|
9983
10266
|
try {
|
|
9984
|
-
const raw = JSON.parse(
|
|
10267
|
+
const raw = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
|
|
9985
10268
|
return raw.telemetry !== void 0 && raw.adoption !== void 0;
|
|
9986
10269
|
} catch {
|
|
9987
10270
|
return false;
|
|
9988
10271
|
}
|
|
9989
10272
|
}
|
|
9990
10273
|
function writeTelemetryConfig(cwd, result) {
|
|
9991
|
-
const configPath =
|
|
10274
|
+
const configPath = path52.join(cwd, "harness.config.json");
|
|
9992
10275
|
try {
|
|
9993
|
-
const raw = JSON.parse(
|
|
10276
|
+
const raw = JSON.parse(fs34.readFileSync(configPath, "utf-8"));
|
|
9994
10277
|
raw.telemetry = { enabled: result.telemetryEnabled };
|
|
9995
10278
|
raw.adoption = { enabled: result.adoptionEnabled };
|
|
9996
|
-
|
|
10279
|
+
fs34.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
|
|
9997
10280
|
} catch (err) {
|
|
9998
10281
|
console.warn(
|
|
9999
10282
|
`\u26A0 Could not update ${configPath}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -10001,15 +10284,15 @@ function writeTelemetryConfig(cwd, result) {
|
|
|
10001
10284
|
}
|
|
10002
10285
|
const hasIdentity = result.identity.project || result.identity.team || result.identity.alias;
|
|
10003
10286
|
if (hasIdentity) {
|
|
10004
|
-
const harnessDir =
|
|
10005
|
-
|
|
10006
|
-
const telemetryFile =
|
|
10007
|
-
|
|
10287
|
+
const harnessDir = path52.join(cwd, ".harness");
|
|
10288
|
+
fs34.mkdirSync(harnessDir, { recursive: true });
|
|
10289
|
+
const telemetryFile = path52.join(harnessDir, "telemetry.json");
|
|
10290
|
+
fs34.writeFileSync(telemetryFile, JSON.stringify({ identity: result.identity }, null, 2) + "\n");
|
|
10008
10291
|
}
|
|
10009
10292
|
}
|
|
10010
10293
|
async function promptIdentity() {
|
|
10011
10294
|
console.log(
|
|
10012
|
-
` ${
|
|
10295
|
+
` ${chalk14.cyan("Identity")} (optional) \u2014 tag telemetry with project/team/alias for filtering.`
|
|
10013
10296
|
);
|
|
10014
10297
|
console.log(" Leave blank to keep anonymous.");
|
|
10015
10298
|
const answer = await prompt2(" Set identity fields? (y/N) ");
|
|
@@ -10027,20 +10310,20 @@ async function promptIdentity() {
|
|
|
10027
10310
|
async function runTelemetryWizard() {
|
|
10028
10311
|
if (!process.stdin.isTTY) return null;
|
|
10029
10312
|
console.log("");
|
|
10030
|
-
console.log(` ${
|
|
10313
|
+
console.log(` ${chalk14.bold("Telemetry Configuration")}`);
|
|
10031
10314
|
console.log("");
|
|
10032
10315
|
console.log(" Harness can collect anonymous usage data to help improve the tool.");
|
|
10033
10316
|
console.log(" You can change these settings anytime in harness.config.json.");
|
|
10034
10317
|
console.log("");
|
|
10035
10318
|
console.log(
|
|
10036
|
-
` ${
|
|
10319
|
+
` ${chalk14.cyan("Anonymous telemetry")} \u2014 error rates, command usage, performance metrics.`
|
|
10037
10320
|
);
|
|
10038
10321
|
console.log(" No code, file contents, or personally identifiable info is sent.");
|
|
10039
10322
|
const telemetryAnswer = await prompt2(" Enable anonymous telemetry? (Y/n) ");
|
|
10040
10323
|
const telemetryEnabled = !isNo(telemetryAnswer);
|
|
10041
10324
|
console.log("");
|
|
10042
10325
|
console.log(
|
|
10043
|
-
` ${
|
|
10326
|
+
` ${chalk14.cyan("Adoption tracking")} \u2014 which skills are used, how often, and success rates.`
|
|
10044
10327
|
);
|
|
10045
10328
|
console.log(" Stored locally in .harness/adoption/. Never sent externally.");
|
|
10046
10329
|
const adoptionAnswer = await prompt2(" Enable local adoption tracking? (Y/n) ");
|
|
@@ -10050,8 +10333,8 @@ async function runTelemetryWizard() {
|
|
|
10050
10333
|
return { telemetryEnabled, adoptionEnabled, identity };
|
|
10051
10334
|
}
|
|
10052
10335
|
async function ensureTelemetryConfigured(cwd) {
|
|
10053
|
-
const configPath =
|
|
10054
|
-
if (!
|
|
10336
|
+
const configPath = path52.join(cwd, "harness.config.json");
|
|
10337
|
+
if (!fs34.existsSync(configPath)) {
|
|
10055
10338
|
return { status: "warn", message: "Not a harness project \u2014 skipped telemetry configuration" };
|
|
10056
10339
|
}
|
|
10057
10340
|
if (isTelemetryConfigured(cwd)) {
|
|
@@ -10097,7 +10380,7 @@ function runSlashCommandGeneration() {
|
|
|
10097
10380
|
}
|
|
10098
10381
|
}
|
|
10099
10382
|
function detectClient(dirName) {
|
|
10100
|
-
return
|
|
10383
|
+
return fs35.existsSync(path53.join(os8.homedir(), dirName));
|
|
10101
10384
|
}
|
|
10102
10385
|
async function runMcpSetup(cwd) {
|
|
10103
10386
|
const results = [];
|
|
@@ -10113,7 +10396,7 @@ async function runMcpSetup(cwd) {
|
|
|
10113
10396
|
{ name: "Cursor", dir: ".cursor", client: "cursor", configTarget: ".cursor/mcp.json" },
|
|
10114
10397
|
{
|
|
10115
10398
|
name: "OpenCode",
|
|
10116
|
-
dir:
|
|
10399
|
+
dir: path53.join(".config", "opencode"),
|
|
10117
10400
|
client: "opencode",
|
|
10118
10401
|
configTarget: "opencode.json"
|
|
10119
10402
|
}
|
|
@@ -10140,12 +10423,12 @@ async function runMcpSetup(cwd) {
|
|
|
10140
10423
|
return results;
|
|
10141
10424
|
}
|
|
10142
10425
|
function formatStep(result) {
|
|
10143
|
-
const icon = result.status === "pass" ?
|
|
10426
|
+
const icon = result.status === "pass" ? chalk15.green("\u2713") : result.status === "warn" ? chalk15.yellow("\u26A0") : chalk15.red("\u2717");
|
|
10144
10427
|
return ` ${icon} ${result.message}`;
|
|
10145
10428
|
}
|
|
10146
10429
|
function configureTier0Integrations(cwd) {
|
|
10147
10430
|
try {
|
|
10148
|
-
const mcpPath =
|
|
10431
|
+
const mcpPath = path53.join(cwd, ".mcp.json");
|
|
10149
10432
|
const config = readMcpConfig(mcpPath);
|
|
10150
10433
|
const tier0 = INTEGRATION_REGISTRY.filter((i) => i.tier === 0);
|
|
10151
10434
|
const added = [];
|
|
@@ -10154,9 +10437,9 @@ function configureTier0Integrations(cwd) {
|
|
|
10154
10437
|
writeMcpEntry(mcpPath, integration.name, integration.mcpConfig);
|
|
10155
10438
|
added.push(integration.displayName);
|
|
10156
10439
|
}
|
|
10157
|
-
const geminiDir =
|
|
10158
|
-
if (
|
|
10159
|
-
const geminiPath =
|
|
10440
|
+
const geminiDir = path53.join(cwd, ".gemini");
|
|
10441
|
+
if (fs35.existsSync(geminiDir)) {
|
|
10442
|
+
const geminiPath = path53.join(geminiDir, "settings.json");
|
|
10160
10443
|
const geminiConfig = readMcpConfig(geminiPath);
|
|
10161
10444
|
for (const integration of tier0) {
|
|
10162
10445
|
if (!geminiConfig.mcpServers[integration.name]) {
|
|
@@ -10164,9 +10447,9 @@ function configureTier0Integrations(cwd) {
|
|
|
10164
10447
|
}
|
|
10165
10448
|
}
|
|
10166
10449
|
}
|
|
10167
|
-
const opencodePath =
|
|
10168
|
-
const opencodeGlobalDir =
|
|
10169
|
-
if (
|
|
10450
|
+
const opencodePath = path53.join(cwd, "opencode.json");
|
|
10451
|
+
const opencodeGlobalDir = path53.join(os8.homedir(), ".config", "opencode");
|
|
10452
|
+
if (fs35.existsSync(opencodePath) || fs35.existsSync(opencodeGlobalDir)) {
|
|
10170
10453
|
for (const integration of tier0) {
|
|
10171
10454
|
writeOpencodeMcpEntry(opencodePath, integration.name, integration.mcpConfig);
|
|
10172
10455
|
}
|
|
@@ -10184,14 +10467,14 @@ function configureTier0Integrations(cwd) {
|
|
|
10184
10467
|
}
|
|
10185
10468
|
}
|
|
10186
10469
|
function ensureHooks(cwd) {
|
|
10187
|
-
const configPath =
|
|
10188
|
-
if (!
|
|
10470
|
+
const configPath = path53.join(cwd, "harness.config.json");
|
|
10471
|
+
if (!fs35.existsSync(configPath)) {
|
|
10189
10472
|
return { status: "warn", message: "Not a harness project \u2014 skipped hook installation" };
|
|
10190
10473
|
}
|
|
10191
10474
|
let profile = "standard";
|
|
10192
|
-
const profilePath =
|
|
10475
|
+
const profilePath = path53.join(cwd, ".harness", "hooks", "profile.json");
|
|
10193
10476
|
try {
|
|
10194
|
-
const data = JSON.parse(
|
|
10477
|
+
const data = JSON.parse(fs35.readFileSync(profilePath, "utf-8"));
|
|
10195
10478
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
10196
10479
|
profile = data.profile;
|
|
10197
10480
|
}
|
|
@@ -10262,10 +10545,10 @@ async function runSetup(cwd) {
|
|
|
10262
10545
|
return { steps, success };
|
|
10263
10546
|
}
|
|
10264
10547
|
function createSetupCommand() {
|
|
10265
|
-
return new
|
|
10548
|
+
return new Command88("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
|
|
10266
10549
|
const cwd = process.cwd();
|
|
10267
10550
|
console.log("");
|
|
10268
|
-
console.log(` ${
|
|
10551
|
+
console.log(` ${chalk15.bold("harness setup")}`);
|
|
10269
10552
|
console.log("");
|
|
10270
10553
|
const { steps, success } = await runSetup(cwd);
|
|
10271
10554
|
for (const step of steps) {
|
|
@@ -10285,15 +10568,15 @@ function createSetupCommand() {
|
|
|
10285
10568
|
|
|
10286
10569
|
// src/commands/share.ts
|
|
10287
10570
|
init_dist3();
|
|
10288
|
-
import { Command as
|
|
10289
|
-
import * as
|
|
10290
|
-
import * as
|
|
10291
|
-
import { parse as
|
|
10571
|
+
import { Command as Command89 } from "commander";
|
|
10572
|
+
import * as fs36 from "fs";
|
|
10573
|
+
import * as path54 from "path";
|
|
10574
|
+
import { parse as parseYaml3 } from "yaml";
|
|
10292
10575
|
var MANIFEST_FILENAME = "constraints.yaml";
|
|
10293
10576
|
async function runShareAction(projectPath, options) {
|
|
10294
|
-
const rootDir =
|
|
10295
|
-
const manifestPath =
|
|
10296
|
-
if (!
|
|
10577
|
+
const rootDir = path54.resolve(projectPath);
|
|
10578
|
+
const manifestPath = path54.join(rootDir, MANIFEST_FILENAME);
|
|
10579
|
+
if (!fs36.existsSync(manifestPath)) {
|
|
10297
10580
|
logger.error(
|
|
10298
10581
|
`No ${MANIFEST_FILENAME} found at ${manifestPath}.
|
|
10299
10582
|
Create a constraints.yaml in your project root to define what to share.`
|
|
@@ -10302,8 +10585,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10302
10585
|
}
|
|
10303
10586
|
let parsed;
|
|
10304
10587
|
try {
|
|
10305
|
-
const raw =
|
|
10306
|
-
parsed =
|
|
10588
|
+
const raw = fs36.readFileSync(manifestPath, "utf-8");
|
|
10589
|
+
parsed = parseYaml3(raw);
|
|
10307
10590
|
} catch (err) {
|
|
10308
10591
|
logger.error(
|
|
10309
10592
|
`Failed to read ${MANIFEST_FILENAME}: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -10316,7 +10599,7 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10316
10599
|
process.exit(1);
|
|
10317
10600
|
}
|
|
10318
10601
|
const manifest = manifestResult.value;
|
|
10319
|
-
const configResult = resolveConfig(
|
|
10602
|
+
const configResult = resolveConfig(path54.join(rootDir, "harness.config.json"));
|
|
10320
10603
|
if (!configResult.ok) {
|
|
10321
10604
|
logger.error(configResult.error.message);
|
|
10322
10605
|
process.exit(1);
|
|
@@ -10334,8 +10617,8 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10334
10617
|
);
|
|
10335
10618
|
process.exit(1);
|
|
10336
10619
|
}
|
|
10337
|
-
const outputDir =
|
|
10338
|
-
const outputPath =
|
|
10620
|
+
const outputDir = path54.resolve(options.output);
|
|
10621
|
+
const outputPath = path54.join(outputDir, `${manifest.name}.harness-constraints.json`);
|
|
10339
10622
|
const writeResult = await writeConfig(outputPath, bundle);
|
|
10340
10623
|
if (!writeResult.ok) {
|
|
10341
10624
|
logger.error(`Failed to write bundle: ${writeResult.error.message}`);
|
|
@@ -10344,28 +10627,28 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
10344
10627
|
logger.success(`Bundle written to ${outputPath}`);
|
|
10345
10628
|
}
|
|
10346
10629
|
function createShareCommand() {
|
|
10347
|
-
return new
|
|
10630
|
+
return new Command89("share").description("Extract and publish a constraints bundle from constraints.yaml").argument("[path]", "Path to the project root", ".").option("-o, --output <dir>", "Output directory for the bundle", ".").action(async (projectPath, options) => {
|
|
10348
10631
|
await runShareAction(projectPath, options);
|
|
10349
10632
|
});
|
|
10350
10633
|
}
|
|
10351
10634
|
|
|
10352
10635
|
// src/commands/skill/index.ts
|
|
10353
|
-
import { Command as
|
|
10636
|
+
import { Command as Command97 } from "commander";
|
|
10354
10637
|
|
|
10355
10638
|
// src/commands/skill/list.ts
|
|
10356
|
-
import { Command as
|
|
10357
|
-
import * as
|
|
10358
|
-
import * as
|
|
10639
|
+
import { Command as Command90 } from "commander";
|
|
10640
|
+
import * as fs37 from "fs";
|
|
10641
|
+
import * as path55 from "path";
|
|
10359
10642
|
import { parse } from "yaml";
|
|
10360
10643
|
function scanDirectory(dirPath, source) {
|
|
10361
|
-
if (!
|
|
10362
|
-
const entries =
|
|
10644
|
+
if (!fs37.existsSync(dirPath)) return [];
|
|
10645
|
+
const entries = fs37.readdirSync(dirPath, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
10363
10646
|
const skills = [];
|
|
10364
10647
|
for (const name of entries) {
|
|
10365
|
-
const yamlPath =
|
|
10366
|
-
if (!
|
|
10648
|
+
const yamlPath = path55.join(dirPath, name, "skill.yaml");
|
|
10649
|
+
if (!fs37.existsSync(yamlPath)) continue;
|
|
10367
10650
|
try {
|
|
10368
|
-
const raw =
|
|
10651
|
+
const raw = fs37.readFileSync(yamlPath, "utf-8");
|
|
10369
10652
|
const parsed = parse(raw);
|
|
10370
10653
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
10371
10654
|
if (result.success) {
|
|
@@ -10383,10 +10666,10 @@ function scanDirectory(dirPath, source) {
|
|
|
10383
10666
|
}
|
|
10384
10667
|
function collectCommunitySkills(seen, allSkills) {
|
|
10385
10668
|
const globalDir = resolveGlobalSkillsDir();
|
|
10386
|
-
const skillsDir =
|
|
10387
|
-
const communityBase =
|
|
10388
|
-
const communityPlatformDir =
|
|
10389
|
-
const lockfilePath =
|
|
10669
|
+
const skillsDir = path55.dirname(globalDir);
|
|
10670
|
+
const communityBase = path55.join(skillsDir, "community");
|
|
10671
|
+
const communityPlatformDir = path55.join(communityBase, "claude-code");
|
|
10672
|
+
const lockfilePath = path55.join(communityBase, "skills-lock.json");
|
|
10390
10673
|
const lockfile = readLockfile2(lockfilePath);
|
|
10391
10674
|
const communitySkills = scanDirectory(communityPlatformDir, "community");
|
|
10392
10675
|
for (const skill of communitySkills) {
|
|
@@ -10456,7 +10739,7 @@ function printSkillsVerbose(skills) {
|
|
|
10456
10739
|
for (const s of skills) printSkillEntry(s);
|
|
10457
10740
|
}
|
|
10458
10741
|
function createListCommand3() {
|
|
10459
|
-
return new
|
|
10742
|
+
return new Command90("list").description("List available skills").option("--installed", "Show only community-installed skills").option("--local", "Show only project-local skills").option("--all", "Show all skills (default)").action(async (opts, cmd) => {
|
|
10460
10743
|
const globalOpts = cmd.optsWithGlobals();
|
|
10461
10744
|
const skills = collectSkills({ filter: resolveFilter(opts) });
|
|
10462
10745
|
if (globalOpts.json) {
|
|
@@ -10471,9 +10754,9 @@ function createListCommand3() {
|
|
|
10471
10754
|
}
|
|
10472
10755
|
|
|
10473
10756
|
// src/commands/skill/run.ts
|
|
10474
|
-
import { Command as
|
|
10475
|
-
import * as
|
|
10476
|
-
import * as
|
|
10757
|
+
import { Command as Command91 } from "commander";
|
|
10758
|
+
import * as fs38 from "fs";
|
|
10759
|
+
import * as path56 from "path";
|
|
10477
10760
|
import { parse as parse2 } from "yaml";
|
|
10478
10761
|
|
|
10479
10762
|
// src/skill/complexity.ts
|
|
@@ -10569,10 +10852,10 @@ ${options.principles}`);
|
|
|
10569
10852
|
|
|
10570
10853
|
// src/commands/skill/run.ts
|
|
10571
10854
|
function loadSkillMetadata(skillDir) {
|
|
10572
|
-
const yamlPath =
|
|
10573
|
-
if (!
|
|
10855
|
+
const yamlPath = path56.join(skillDir, "skill.yaml");
|
|
10856
|
+
if (!fs38.existsSync(yamlPath)) return null;
|
|
10574
10857
|
try {
|
|
10575
|
-
const result = SkillMetadataSchema.safeParse(parse2(
|
|
10858
|
+
const result = SkillMetadataSchema.safeParse(parse2(fs38.readFileSync(yamlPath, "utf-8")));
|
|
10576
10859
|
return result.success ? result.data : null;
|
|
10577
10860
|
} catch {
|
|
10578
10861
|
return null;
|
|
@@ -10584,22 +10867,22 @@ function resolveComplexity(metadata, requested, projectPath) {
|
|
|
10584
10867
|
return requested;
|
|
10585
10868
|
}
|
|
10586
10869
|
function loadPrinciples(projectPath) {
|
|
10587
|
-
const principlesPath =
|
|
10588
|
-
return
|
|
10870
|
+
const principlesPath = path56.join(projectPath, "docs", "principles.md");
|
|
10871
|
+
return fs38.existsSync(principlesPath) ? fs38.readFileSync(principlesPath, "utf-8") : void 0;
|
|
10589
10872
|
}
|
|
10590
10873
|
function readMostRecentFileInDir(dirPath) {
|
|
10591
|
-
const files =
|
|
10592
|
-
if (files.length > 0) return
|
|
10874
|
+
const files = fs38.readdirSync(dirPath).map((f) => ({ name: f, mtime: fs38.statSync(path56.join(dirPath, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime);
|
|
10875
|
+
if (files.length > 0) return fs38.readFileSync(path56.join(dirPath, files[0].name), "utf-8");
|
|
10593
10876
|
return void 0;
|
|
10594
10877
|
}
|
|
10595
10878
|
function loadPriorState(metadata, projectPath) {
|
|
10596
10879
|
if (!metadata?.state.persistent || metadata.state.files.length === 0) return void 0;
|
|
10597
10880
|
for (const stateFilePath of metadata.state.files) {
|
|
10598
|
-
const fullPath =
|
|
10599
|
-
if (!
|
|
10600
|
-
const stat =
|
|
10881
|
+
const fullPath = path56.join(projectPath, stateFilePath);
|
|
10882
|
+
if (!fs38.existsSync(fullPath)) continue;
|
|
10883
|
+
const stat = fs38.statSync(fullPath);
|
|
10601
10884
|
if (stat.isDirectory()) return readMostRecentFileInDir(fullPath);
|
|
10602
|
-
return
|
|
10885
|
+
return fs38.readFileSync(fullPath, "utf-8");
|
|
10603
10886
|
}
|
|
10604
10887
|
return void 0;
|
|
10605
10888
|
}
|
|
@@ -10619,9 +10902,9 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
10619
10902
|
}
|
|
10620
10903
|
function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
10621
10904
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
10622
|
-
const stateFile =
|
|
10623
|
-
if (!
|
|
10624
|
-
const stateContent =
|
|
10905
|
+
const stateFile = path56.join(projectPath, ".harness", "state.json");
|
|
10906
|
+
if (!fs38.existsSync(stateFile)) return content;
|
|
10907
|
+
const stateContent = fs38.readFileSync(stateFile, "utf-8");
|
|
10625
10908
|
return content + `
|
|
10626
10909
|
|
|
10627
10910
|
---
|
|
@@ -10633,14 +10916,14 @@ ${stateContent}
|
|
|
10633
10916
|
}
|
|
10634
10917
|
async function runSkill(name, opts) {
|
|
10635
10918
|
const skillsDir = resolveSkillsDir();
|
|
10636
|
-
const skillDir =
|
|
10637
|
-
if (!
|
|
10919
|
+
const skillDir = path56.join(skillsDir, name);
|
|
10920
|
+
if (!fs38.existsSync(skillDir)) {
|
|
10638
10921
|
logger.error(`Skill not found: ${name}`);
|
|
10639
10922
|
process.exit(ExitCode.ERROR);
|
|
10640
10923
|
return;
|
|
10641
10924
|
}
|
|
10642
10925
|
const metadata = loadSkillMetadata(skillDir);
|
|
10643
|
-
const projectPath = opts.path ?
|
|
10926
|
+
const projectPath = opts.path ? path56.resolve(opts.path) : process.cwd();
|
|
10644
10927
|
const complexity = resolveComplexity(
|
|
10645
10928
|
metadata,
|
|
10646
10929
|
opts.complexity ?? "standard",
|
|
@@ -10667,14 +10950,14 @@ async function runSkill(name, opts) {
|
|
|
10667
10950
|
...stateWarning !== void 0 && { stateWarning },
|
|
10668
10951
|
...opts.party !== void 0 && { party: opts.party }
|
|
10669
10952
|
});
|
|
10670
|
-
const skillMdPath =
|
|
10671
|
-
if (!
|
|
10953
|
+
const skillMdPath = path56.join(skillDir, "SKILL.md");
|
|
10954
|
+
if (!fs38.existsSync(skillMdPath)) {
|
|
10672
10955
|
logger.error(`SKILL.md not found for skill: ${name}`);
|
|
10673
10956
|
process.exit(ExitCode.ERROR);
|
|
10674
10957
|
return;
|
|
10675
10958
|
}
|
|
10676
10959
|
const content = appendProjectState(
|
|
10677
|
-
|
|
10960
|
+
fs38.readFileSync(skillMdPath, "utf-8"),
|
|
10678
10961
|
metadata,
|
|
10679
10962
|
projectPath,
|
|
10680
10963
|
!!opts.path
|
|
@@ -10685,16 +10968,16 @@ async function runSkill(name, opts) {
|
|
|
10685
10968
|
process.exit(ExitCode.SUCCESS);
|
|
10686
10969
|
}
|
|
10687
10970
|
function createRunCommand3() {
|
|
10688
|
-
return new
|
|
10971
|
+
return new Command91("run").description("Run a skill (outputs SKILL.md content with context preamble)").argument("<name>", "Skill name (e.g., harness-tdd)").option("--path <path>", "Project root path for context injection").option("--complexity <level>", "Rigor level: fast, standard, thorough", "standard").option("--phase <name>", "Start at a specific phase (for re-entry)").option("--party", "Enable multi-perspective evaluation").option(
|
|
10689
10972
|
"--backend <name>",
|
|
10690
10973
|
"Spec B: one-shot routing override forwarded to the orchestrator as HARNESS_BACKEND_OVERRIDE"
|
|
10691
10974
|
).action(async (name, opts) => runSkill(name, opts));
|
|
10692
10975
|
}
|
|
10693
10976
|
|
|
10694
10977
|
// src/commands/skill/validate.ts
|
|
10695
|
-
import { Command as
|
|
10696
|
-
import * as
|
|
10697
|
-
import * as
|
|
10978
|
+
import { Command as Command92 } from "commander";
|
|
10979
|
+
import * as fs39 from "fs";
|
|
10980
|
+
import * as path57 from "path";
|
|
10698
10981
|
import { parse as parse3 } from "yaml";
|
|
10699
10982
|
var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
10700
10983
|
"## When to Use",
|
|
@@ -10706,11 +10989,11 @@ var BEHAVIORAL_REQUIRED_SECTIONS = [
|
|
|
10706
10989
|
];
|
|
10707
10990
|
var KNOWLEDGE_REQUIRED_SECTIONS = ["## Instructions"];
|
|
10708
10991
|
function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
10709
|
-
if (!
|
|
10992
|
+
if (!fs39.existsSync(skillMdPath)) {
|
|
10710
10993
|
errors.push(`${name}: missing SKILL.md`);
|
|
10711
10994
|
return;
|
|
10712
10995
|
}
|
|
10713
|
-
const mdContent =
|
|
10996
|
+
const mdContent = fs39.readFileSync(skillMdPath, "utf-8");
|
|
10714
10997
|
if (!mdContent.trim().startsWith("# ")) {
|
|
10715
10998
|
errors.push(`${name}/SKILL.md: must start with an h1 heading`);
|
|
10716
10999
|
}
|
|
@@ -10735,20 +11018,20 @@ function validateSkillMd(name, skillMdPath, skillType, errors) {
|
|
|
10735
11018
|
}
|
|
10736
11019
|
}
|
|
10737
11020
|
function validateSkillEntry(name, skillsDir, errors) {
|
|
10738
|
-
const skillDir =
|
|
10739
|
-
const yamlPath =
|
|
10740
|
-
if (!
|
|
11021
|
+
const skillDir = path57.join(skillsDir, name);
|
|
11022
|
+
const yamlPath = path57.join(skillDir, "skill.yaml");
|
|
11023
|
+
if (!fs39.existsSync(yamlPath)) {
|
|
10741
11024
|
errors.push(`${name}: missing skill.yaml`);
|
|
10742
11025
|
return false;
|
|
10743
11026
|
}
|
|
10744
11027
|
try {
|
|
10745
|
-
const raw =
|
|
11028
|
+
const raw = fs39.readFileSync(yamlPath, "utf-8");
|
|
10746
11029
|
const result = SkillMetadataSchema.safeParse(parse3(raw));
|
|
10747
11030
|
if (!result.success) {
|
|
10748
11031
|
errors.push(`${name}/skill.yaml: ${result.error.message}`);
|
|
10749
11032
|
return false;
|
|
10750
11033
|
}
|
|
10751
|
-
validateSkillMd(name,
|
|
11034
|
+
validateSkillMd(name, path57.join(skillDir, "SKILL.md"), result.data.type, errors);
|
|
10752
11035
|
return true;
|
|
10753
11036
|
} catch (e) {
|
|
10754
11037
|
errors.push(`${name}: parse error \u2014 ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -10756,15 +11039,15 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
10756
11039
|
}
|
|
10757
11040
|
}
|
|
10758
11041
|
function createValidateCommand2() {
|
|
10759
|
-
return new
|
|
11042
|
+
return new Command92("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
10760
11043
|
const globalOpts = cmd.optsWithGlobals();
|
|
10761
11044
|
const skillsDir = resolveSkillsDir();
|
|
10762
|
-
if (!
|
|
11045
|
+
if (!fs39.existsSync(skillsDir)) {
|
|
10763
11046
|
logger.info("No skills directory found.");
|
|
10764
11047
|
process.exit(ExitCode.SUCCESS);
|
|
10765
11048
|
return;
|
|
10766
11049
|
}
|
|
10767
|
-
const entries =
|
|
11050
|
+
const entries = fs39.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
10768
11051
|
const errors = [];
|
|
10769
11052
|
let validated = 0;
|
|
10770
11053
|
for (const name of entries) {
|
|
@@ -10786,24 +11069,24 @@ function createValidateCommand2() {
|
|
|
10786
11069
|
}
|
|
10787
11070
|
|
|
10788
11071
|
// src/commands/skill/info.ts
|
|
10789
|
-
import { Command as
|
|
10790
|
-
import * as
|
|
10791
|
-
import * as
|
|
11072
|
+
import { Command as Command93 } from "commander";
|
|
11073
|
+
import * as fs40 from "fs";
|
|
11074
|
+
import * as path58 from "path";
|
|
10792
11075
|
import { parse as parse4 } from "yaml";
|
|
10793
11076
|
function loadSkillMetadata2(name) {
|
|
10794
11077
|
const skillsDir = resolveSkillsDir();
|
|
10795
|
-
const skillDir =
|
|
10796
|
-
if (!
|
|
11078
|
+
const skillDir = path58.join(skillsDir, name);
|
|
11079
|
+
if (!fs40.existsSync(skillDir)) {
|
|
10797
11080
|
logger.error(`Skill not found: ${name}`);
|
|
10798
11081
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
10799
11082
|
}
|
|
10800
|
-
const yamlPath =
|
|
10801
|
-
if (!
|
|
11083
|
+
const yamlPath = path58.join(skillDir, "skill.yaml");
|
|
11084
|
+
if (!fs40.existsSync(yamlPath)) {
|
|
10802
11085
|
logger.error(`skill.yaml not found for skill: ${name}`);
|
|
10803
11086
|
return { ok: false, exitCode: ExitCode.ERROR };
|
|
10804
11087
|
}
|
|
10805
11088
|
try {
|
|
10806
|
-
const raw =
|
|
11089
|
+
const raw = fs40.readFileSync(yamlPath, "utf-8");
|
|
10807
11090
|
const parsed = parse4(raw);
|
|
10808
11091
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
10809
11092
|
if (!result.success) {
|
|
@@ -10832,7 +11115,7 @@ function printSkillInfo(skill) {
|
|
|
10832
11115
|
console.log(`Persistent: ${skill.state.persistent}`);
|
|
10833
11116
|
}
|
|
10834
11117
|
function createInfoCommand() {
|
|
10835
|
-
return new
|
|
11118
|
+
return new Command93("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
|
|
10836
11119
|
const globalOpts = cmd.optsWithGlobals();
|
|
10837
11120
|
const loaded = loadSkillMetadata2(name);
|
|
10838
11121
|
if (!loaded.ok) {
|
|
@@ -10849,7 +11132,7 @@ function createInfoCommand() {
|
|
|
10849
11132
|
}
|
|
10850
11133
|
|
|
10851
11134
|
// src/commands/skill/search.ts
|
|
10852
|
-
import { Command as
|
|
11135
|
+
import { Command as Command94 } from "commander";
|
|
10853
11136
|
async function runSearch(query, opts) {
|
|
10854
11137
|
const results = await searchNpmRegistry(query, opts.registry);
|
|
10855
11138
|
return results.filter((r) => {
|
|
@@ -10894,15 +11177,15 @@ async function runSearchAction(query, opts, globalOpts) {
|
|
|
10894
11177
|
}
|
|
10895
11178
|
}
|
|
10896
11179
|
function createSearchCommand2() {
|
|
10897
|
-
return new
|
|
11180
|
+
return new Command94("search").description("Search for community skills on the @harness-skills registry").argument("<query>", "Search query").option("--platform <platform>", "Filter by platform (e.g., claude-code)").option("--trigger <trigger>", "Filter by trigger type (e.g., manual, automatic)").option("--registry <url>", "Use a custom npm registry URL").action(async (query, opts, cmd) => {
|
|
10898
11181
|
await runSearchAction(query, opts, cmd.optsWithGlobals());
|
|
10899
11182
|
});
|
|
10900
11183
|
}
|
|
10901
11184
|
|
|
10902
11185
|
// src/commands/skill/create.ts
|
|
10903
|
-
import { Command as
|
|
10904
|
-
import * as
|
|
10905
|
-
import * as
|
|
11186
|
+
import { Command as Command95 } from "commander";
|
|
11187
|
+
import * as path59 from "path";
|
|
11188
|
+
import * as fs41 from "fs";
|
|
10906
11189
|
import YAML from "yaml";
|
|
10907
11190
|
var KEBAB_CASE_RE = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
10908
11191
|
function buildReadme(name, description) {
|
|
@@ -10986,22 +11269,22 @@ function runCreate(name, opts) {
|
|
|
10986
11269
|
if (!KEBAB_CASE_RE.test(name)) {
|
|
10987
11270
|
throw new Error(`Invalid skill name "${name}". Must be kebab-case (e.g., my-skill).`);
|
|
10988
11271
|
}
|
|
10989
|
-
const baseDir = opts.outputDir ??
|
|
10990
|
-
const skillDir =
|
|
10991
|
-
if (
|
|
11272
|
+
const baseDir = opts.outputDir ?? path59.join(process.cwd(), "agents", "skills", "claude-code");
|
|
11273
|
+
const skillDir = path59.join(baseDir, name);
|
|
11274
|
+
if (fs41.existsSync(skillDir)) {
|
|
10992
11275
|
throw new Error(`Skill directory already exists: ${skillDir}`);
|
|
10993
11276
|
}
|
|
10994
|
-
|
|
11277
|
+
fs41.mkdirSync(skillDir, { recursive: true });
|
|
10995
11278
|
const description = opts.description || `A community skill: ${name}`;
|
|
10996
11279
|
const skillYaml = buildSkillYaml(name, opts);
|
|
10997
|
-
const skillYamlPath =
|
|
10998
|
-
|
|
11280
|
+
const skillYamlPath = path59.join(skillDir, "skill.yaml");
|
|
11281
|
+
fs41.writeFileSync(skillYamlPath, YAML.stringify(skillYaml));
|
|
10999
11282
|
const skillMd = buildSkillMd(name, description);
|
|
11000
|
-
const skillMdPath =
|
|
11001
|
-
|
|
11283
|
+
const skillMdPath = path59.join(skillDir, "SKILL.md");
|
|
11284
|
+
fs41.writeFileSync(skillMdPath, skillMd);
|
|
11002
11285
|
const readme = buildReadme(name, description);
|
|
11003
|
-
const readmePath =
|
|
11004
|
-
|
|
11286
|
+
const readmePath = path59.join(skillDir, "README.md");
|
|
11287
|
+
fs41.writeFileSync(readmePath, readme);
|
|
11005
11288
|
return {
|
|
11006
11289
|
name,
|
|
11007
11290
|
directory: skillDir,
|
|
@@ -11015,12 +11298,12 @@ function printCreateResult(name, result) {
|
|
|
11015
11298
|
}
|
|
11016
11299
|
logger.info(`
|
|
11017
11300
|
Next steps:`);
|
|
11018
|
-
logger.info(` 1. Edit ${
|
|
11301
|
+
logger.info(` 1. Edit ${path59.join(result.directory, "SKILL.md")} with your skill content`);
|
|
11019
11302
|
logger.info(` 2. Run: harness skill validate ${name}`);
|
|
11020
11303
|
logger.info(` 3. Run: harness skills publish`);
|
|
11021
11304
|
}
|
|
11022
11305
|
function createCreateCommand() {
|
|
11023
|
-
return new
|
|
11306
|
+
return new Command95("create").description("Scaffold a new community skill").argument("<name>", "Skill name (kebab-case)").option("--description <desc>", "Skill description").option("--type <type>", "Skill type: rigid or flexible", "flexible").option("--platforms <platforms>", "Comma-separated platforms (default: claude-code)").option("--triggers <triggers>", "Comma-separated triggers (default: manual)").option("--output-dir <dir>", "Output directory (default: agents/skills/claude-code/)").action(async (name, opts, cmd) => {
|
|
11024
11307
|
const globalOpts = cmd.optsWithGlobals();
|
|
11025
11308
|
try {
|
|
11026
11309
|
const result = runCreate(name, {
|
|
@@ -11043,26 +11326,26 @@ function createCreateCommand() {
|
|
|
11043
11326
|
}
|
|
11044
11327
|
|
|
11045
11328
|
// src/commands/skill/publish.ts
|
|
11046
|
-
import { Command as
|
|
11047
|
-
import * as
|
|
11048
|
-
import * as
|
|
11329
|
+
import { Command as Command96 } from "commander";
|
|
11330
|
+
import * as fs43 from "fs";
|
|
11331
|
+
import * as path61 from "path";
|
|
11049
11332
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
11050
11333
|
|
|
11051
11334
|
// src/registry/validator.ts
|
|
11052
|
-
import * as
|
|
11053
|
-
import * as
|
|
11335
|
+
import * as fs42 from "fs";
|
|
11336
|
+
import * as path60 from "path";
|
|
11054
11337
|
import { parse as parse5 } from "yaml";
|
|
11055
11338
|
import semver4 from "semver";
|
|
11056
11339
|
async function validateForPublish(skillDir, registryUrl) {
|
|
11057
11340
|
const errors = [];
|
|
11058
|
-
const skillYamlPath =
|
|
11059
|
-
if (!
|
|
11341
|
+
const skillYamlPath = path60.join(skillDir, "skill.yaml");
|
|
11342
|
+
if (!fs42.existsSync(skillYamlPath)) {
|
|
11060
11343
|
errors.push("skill.yaml not found. Create one with: harness skill create <name>");
|
|
11061
11344
|
return { valid: false, errors };
|
|
11062
11345
|
}
|
|
11063
11346
|
let skillMeta;
|
|
11064
11347
|
try {
|
|
11065
|
-
const raw =
|
|
11348
|
+
const raw = fs42.readFileSync(skillYamlPath, "utf-8");
|
|
11066
11349
|
const parsed = parse5(raw);
|
|
11067
11350
|
const result = SkillMetadataSchema.safeParse(parsed);
|
|
11068
11351
|
if (!result.success) {
|
|
@@ -11084,11 +11367,11 @@ async function validateForPublish(skillDir, registryUrl) {
|
|
|
11084
11367
|
if (!skillMeta.triggers || skillMeta.triggers.length === 0) {
|
|
11085
11368
|
errors.push("At least one trigger is required. Add triggers to skill.yaml.");
|
|
11086
11369
|
}
|
|
11087
|
-
const skillMdPath =
|
|
11088
|
-
if (!
|
|
11370
|
+
const skillMdPath = path60.join(skillDir, "SKILL.md");
|
|
11371
|
+
if (!fs42.existsSync(skillMdPath)) {
|
|
11089
11372
|
errors.push("SKILL.md not found. Create it with content describing your skill.");
|
|
11090
11373
|
} else {
|
|
11091
|
-
const content =
|
|
11374
|
+
const content = fs42.readFileSync(skillMdPath, "utf-8");
|
|
11092
11375
|
if (!content.includes("## When to Use")) {
|
|
11093
11376
|
errors.push('SKILL.md must contain a "## When to Use" section.');
|
|
11094
11377
|
}
|
|
@@ -11168,11 +11451,11 @@ ${errorList}`);
|
|
|
11168
11451
|
}
|
|
11169
11452
|
const meta = validation.skillMeta;
|
|
11170
11453
|
const pkg = derivePackageJson(meta);
|
|
11171
|
-
const pkgPath =
|
|
11172
|
-
|
|
11173
|
-
const readmePath =
|
|
11174
|
-
if (!
|
|
11175
|
-
const skillMdContent =
|
|
11454
|
+
const pkgPath = path61.join(skillDir, "package.json");
|
|
11455
|
+
fs43.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
11456
|
+
const readmePath = path61.join(skillDir, "README.md");
|
|
11457
|
+
if (!fs43.existsSync(readmePath)) {
|
|
11458
|
+
const skillMdContent = fs43.readFileSync(path61.join(skillDir, "SKILL.md"), "utf-8");
|
|
11176
11459
|
const readme = `# ${pkg.name}
|
|
11177
11460
|
|
|
11178
11461
|
${meta.description}
|
|
@@ -11186,7 +11469,7 @@ harness install ${meta.name}
|
|
|
11186
11469
|
---
|
|
11187
11470
|
|
|
11188
11471
|
${skillMdContent}`;
|
|
11189
|
-
|
|
11472
|
+
fs43.writeFileSync(readmePath, readme);
|
|
11190
11473
|
}
|
|
11191
11474
|
if (opts.dryRun) {
|
|
11192
11475
|
return {
|
|
@@ -11212,7 +11495,7 @@ ${skillMdContent}`;
|
|
|
11212
11495
|
};
|
|
11213
11496
|
}
|
|
11214
11497
|
function createPublishCommand() {
|
|
11215
|
-
return new
|
|
11498
|
+
return new Command96("publish").description("Validate and publish a skill to @harness-skills on npm").option("--dry-run", "Run validation and generate package.json without publishing").option("--dir <dir>", "Skill directory (default: current directory)").option("--registry <url>", "Use a custom npm registry URL").action(async (opts, cmd) => {
|
|
11216
11499
|
const globalOpts = cmd.optsWithGlobals();
|
|
11217
11500
|
const skillDir = opts.dir || process.cwd();
|
|
11218
11501
|
try {
|
|
@@ -11237,7 +11520,7 @@ function createPublishCommand() {
|
|
|
11237
11520
|
|
|
11238
11521
|
// src/commands/skill/index.ts
|
|
11239
11522
|
function createSkillCommand() {
|
|
11240
|
-
const command = new
|
|
11523
|
+
const command = new Command97("skill").description("Skill management commands");
|
|
11241
11524
|
command.addCommand(createListCommand3());
|
|
11242
11525
|
command.addCommand(createRunCommand3());
|
|
11243
11526
|
command.addCommand(createValidateCommand2());
|
|
@@ -11250,9 +11533,9 @@ function createSkillCommand() {
|
|
|
11250
11533
|
|
|
11251
11534
|
// src/commands/snapshot.ts
|
|
11252
11535
|
init_dist3();
|
|
11253
|
-
import { Command as
|
|
11536
|
+
import { Command as Command98 } from "commander";
|
|
11254
11537
|
import { execSync as execSync5 } from "child_process";
|
|
11255
|
-
import
|
|
11538
|
+
import chalk16 from "chalk";
|
|
11256
11539
|
function getCommitHash3(cwd) {
|
|
11257
11540
|
try {
|
|
11258
11541
|
return execSync5("git rev-parse --short HEAD", { cwd, encoding: "utf-8" }).toString().trim();
|
|
@@ -11277,9 +11560,9 @@ function formatDelta(delta) {
|
|
|
11277
11560
|
function directionSymbol(direction) {
|
|
11278
11561
|
switch (direction) {
|
|
11279
11562
|
case "improving":
|
|
11280
|
-
return
|
|
11563
|
+
return chalk16.green("improving");
|
|
11281
11564
|
case "declining":
|
|
11282
|
-
return
|
|
11565
|
+
return chalk16.red("declining");
|
|
11283
11566
|
case "stable":
|
|
11284
11567
|
return "=";
|
|
11285
11568
|
}
|
|
@@ -11432,7 +11715,7 @@ function registerListCommand(parent) {
|
|
|
11432
11715
|
});
|
|
11433
11716
|
}
|
|
11434
11717
|
function createSnapshotCommand() {
|
|
11435
|
-
const command = new
|
|
11718
|
+
const command = new Command98("snapshot").description("Architecture timeline snapshot commands");
|
|
11436
11719
|
registerCaptureCommand(command);
|
|
11437
11720
|
registerTrendsCommand(command);
|
|
11438
11721
|
registerListCommand(command);
|
|
@@ -11440,9 +11723,9 @@ function createSnapshotCommand() {
|
|
|
11440
11723
|
}
|
|
11441
11724
|
|
|
11442
11725
|
// src/commands/spec-craft.ts
|
|
11443
|
-
import { Command as
|
|
11726
|
+
import { Command as Command99 } from "commander";
|
|
11444
11727
|
function createSpecCraftCommand() {
|
|
11445
|
-
return new
|
|
11728
|
+
return new Command99("spec-craft").description(
|
|
11446
11729
|
"LLM-judgment critique of spec quality (proposals + ADRs). Second craft-pipeline ceiling skill; 7 seed rubrics from the spec-quality canon. Per-section critique."
|
|
11447
11730
|
).option("-f, --files <files...>", "Optional spec file/glob scope").option("-k, --kinds <kinds...>", "Restrict to proposal / adr (default: both)").option("-s, --sections <names...>", "Restrict to specific canonical section names").option("--max-files <n>", "Cap doc count (default: 50)").option("--max-sections-per-file <n>", "Cap per-doc section critique (default: 10)").action(async (opts, cmd) => {
|
|
11448
11731
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -11490,10 +11773,10 @@ function printResult6(result, mode, _formatter) {
|
|
|
11490
11773
|
list.push(f);
|
|
11491
11774
|
byFile.set(f.target.file, list);
|
|
11492
11775
|
}
|
|
11493
|
-
for (const [file,
|
|
11776
|
+
for (const [file, fs48] of byFile) {
|
|
11494
11777
|
console.log(`
|
|
11495
11778
|
${file}`);
|
|
11496
|
-
for (const f of
|
|
11779
|
+
for (const f of fs48) {
|
|
11497
11780
|
console.log(
|
|
11498
11781
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ## ${f.target.section}:${f.target.line}`
|
|
11499
11782
|
);
|
|
@@ -11509,12 +11792,12 @@ ${file}`);
|
|
|
11509
11792
|
}
|
|
11510
11793
|
|
|
11511
11794
|
// src/commands/state/index.ts
|
|
11512
|
-
import { Command as
|
|
11795
|
+
import { Command as Command104 } from "commander";
|
|
11513
11796
|
|
|
11514
11797
|
// src/commands/state/show.ts
|
|
11515
11798
|
init_dist3();
|
|
11516
|
-
import { Command as
|
|
11517
|
-
import * as
|
|
11799
|
+
import { Command as Command100 } from "commander";
|
|
11800
|
+
import * as path62 from "path";
|
|
11518
11801
|
function printStateText(state, stream) {
|
|
11519
11802
|
if (stream) console.log(`Stream: ${stream}`);
|
|
11520
11803
|
console.log(`Schema Version: ${state.schemaVersion}`);
|
|
@@ -11538,9 +11821,9 @@ function printStateProgress(progress) {
|
|
|
11538
11821
|
}
|
|
11539
11822
|
}
|
|
11540
11823
|
function createShowCommand() {
|
|
11541
|
-
return new
|
|
11824
|
+
return new Command100("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
|
|
11542
11825
|
const globalOpts = cmd.optsWithGlobals();
|
|
11543
|
-
const result = await loadState(
|
|
11826
|
+
const result = await loadState(path62.resolve(opts.path), opts.stream);
|
|
11544
11827
|
if (!result.ok) {
|
|
11545
11828
|
logger.error(result.error.message);
|
|
11546
11829
|
process.exit(ExitCode.ERROR);
|
|
@@ -11556,9 +11839,9 @@ function createShowCommand() {
|
|
|
11556
11839
|
|
|
11557
11840
|
// src/commands/state/reset.ts
|
|
11558
11841
|
init_dist3();
|
|
11559
|
-
import { Command as
|
|
11560
|
-
import * as
|
|
11561
|
-
import * as
|
|
11842
|
+
import { Command as Command101 } from "commander";
|
|
11843
|
+
import * as fs44 from "fs";
|
|
11844
|
+
import * as path63 from "path";
|
|
11562
11845
|
import * as readline3 from "readline";
|
|
11563
11846
|
async function resolveStatePath(projectPath, stream) {
|
|
11564
11847
|
if (stream) {
|
|
@@ -11567,9 +11850,9 @@ async function resolveStatePath(projectPath, stream) {
|
|
|
11567
11850
|
logger.error(streamResult.error.message);
|
|
11568
11851
|
process.exit(ExitCode.ERROR);
|
|
11569
11852
|
}
|
|
11570
|
-
return
|
|
11853
|
+
return path63.join(streamResult.value, "state.json");
|
|
11571
11854
|
}
|
|
11572
|
-
return
|
|
11855
|
+
return path63.join(projectPath, ".harness", "state.json");
|
|
11573
11856
|
}
|
|
11574
11857
|
async function confirmReset() {
|
|
11575
11858
|
const rl = readline3.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -11580,9 +11863,9 @@ async function confirmReset() {
|
|
|
11580
11863
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
11581
11864
|
}
|
|
11582
11865
|
async function runReset(opts) {
|
|
11583
|
-
const projectPath =
|
|
11866
|
+
const projectPath = path63.resolve(opts.path);
|
|
11584
11867
|
const statePath = await resolveStatePath(projectPath, opts.stream);
|
|
11585
|
-
if (!
|
|
11868
|
+
if (!fs44.existsSync(statePath)) {
|
|
11586
11869
|
logger.info("No state file found. Nothing to reset.");
|
|
11587
11870
|
process.exit(ExitCode.SUCCESS);
|
|
11588
11871
|
}
|
|
@@ -11591,7 +11874,7 @@ async function runReset(opts) {
|
|
|
11591
11874
|
process.exit(ExitCode.SUCCESS);
|
|
11592
11875
|
}
|
|
11593
11876
|
try {
|
|
11594
|
-
|
|
11877
|
+
fs44.unlinkSync(statePath);
|
|
11595
11878
|
logger.success("Project state reset.");
|
|
11596
11879
|
} catch (e) {
|
|
11597
11880
|
logger.error(`Failed to reset state: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -11600,16 +11883,16 @@ async function runReset(opts) {
|
|
|
11600
11883
|
process.exit(ExitCode.SUCCESS);
|
|
11601
11884
|
}
|
|
11602
11885
|
function createResetCommand() {
|
|
11603
|
-
return new
|
|
11886
|
+
return new Command101("reset").description("Reset project state (deletes .harness/state.json)").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").option("--yes", "Skip confirmation prompt").action(async (opts) => runReset(opts));
|
|
11604
11887
|
}
|
|
11605
11888
|
|
|
11606
11889
|
// src/commands/state/learn.ts
|
|
11607
11890
|
init_dist3();
|
|
11608
|
-
import { Command as
|
|
11609
|
-
import * as
|
|
11891
|
+
import { Command as Command102 } from "commander";
|
|
11892
|
+
import * as path64 from "path";
|
|
11610
11893
|
function createLearnCommand() {
|
|
11611
|
-
return new
|
|
11612
|
-
const projectPath =
|
|
11894
|
+
return new Command102("learn").description("Append a learning to .harness/learnings.md").argument("<message>", "The learning to record").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (message, opts, _cmd) => {
|
|
11895
|
+
const projectPath = path64.resolve(opts.path);
|
|
11613
11896
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
11614
11897
|
if (!result.ok) {
|
|
11615
11898
|
logger.error(result.error.message);
|
|
@@ -11623,10 +11906,10 @@ function createLearnCommand() {
|
|
|
11623
11906
|
|
|
11624
11907
|
// src/commands/state/streams.ts
|
|
11625
11908
|
init_dist3();
|
|
11626
|
-
import { Command as
|
|
11627
|
-
import * as
|
|
11909
|
+
import { Command as Command103 } from "commander";
|
|
11910
|
+
import * as path65 from "path";
|
|
11628
11911
|
async function runListStreams(opts, globalOpts) {
|
|
11629
|
-
const projectPath =
|
|
11912
|
+
const projectPath = path65.resolve(opts.path);
|
|
11630
11913
|
const indexResult = await loadStreamIndex(projectPath);
|
|
11631
11914
|
const result = await listStreams(projectPath);
|
|
11632
11915
|
if (!result.ok) {
|
|
@@ -11647,7 +11930,7 @@ async function runListStreams(opts, globalOpts) {
|
|
|
11647
11930
|
process.exit(ExitCode.SUCCESS);
|
|
11648
11931
|
}
|
|
11649
11932
|
async function runCreateStream(name, opts) {
|
|
11650
|
-
const result = await createStream(
|
|
11933
|
+
const result = await createStream(path65.resolve(opts.path), name, opts.branch);
|
|
11651
11934
|
if (!result.ok) {
|
|
11652
11935
|
logger.error(result.error.message);
|
|
11653
11936
|
process.exit(ExitCode.ERROR);
|
|
@@ -11656,7 +11939,7 @@ async function runCreateStream(name, opts) {
|
|
|
11656
11939
|
process.exit(ExitCode.SUCCESS);
|
|
11657
11940
|
}
|
|
11658
11941
|
async function runArchiveStream(name, opts) {
|
|
11659
|
-
const result = await archiveStream(
|
|
11942
|
+
const result = await archiveStream(path65.resolve(opts.path), name);
|
|
11660
11943
|
if (!result.ok) {
|
|
11661
11944
|
logger.error(result.error.message);
|
|
11662
11945
|
process.exit(ExitCode.ERROR);
|
|
@@ -11665,7 +11948,7 @@ async function runArchiveStream(name, opts) {
|
|
|
11665
11948
|
process.exit(ExitCode.SUCCESS);
|
|
11666
11949
|
}
|
|
11667
11950
|
async function runActivateStream(name, opts) {
|
|
11668
|
-
const result = await setActiveStream(
|
|
11951
|
+
const result = await setActiveStream(path65.resolve(opts.path), name);
|
|
11669
11952
|
if (!result.ok) {
|
|
11670
11953
|
logger.error(result.error.message);
|
|
11671
11954
|
process.exit(ExitCode.ERROR);
|
|
@@ -11674,7 +11957,7 @@ async function runActivateStream(name, opts) {
|
|
|
11674
11957
|
process.exit(ExitCode.SUCCESS);
|
|
11675
11958
|
}
|
|
11676
11959
|
function createStreamsCommand() {
|
|
11677
|
-
const command = new
|
|
11960
|
+
const command = new Command103("streams").description("Manage state streams");
|
|
11678
11961
|
command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
|
|
11679
11962
|
command.command("create <name>").description("Create a new stream").option("--path <path>", "Project root path", ".").option("--branch <branch>", "Associate with a git branch").action(async (name, opts) => runCreateStream(name, opts));
|
|
11680
11963
|
command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
|
|
@@ -11684,7 +11967,7 @@ function createStreamsCommand() {
|
|
|
11684
11967
|
|
|
11685
11968
|
// src/commands/state/index.ts
|
|
11686
11969
|
function createStateCommand() {
|
|
11687
|
-
const command = new
|
|
11970
|
+
const command = new Command104("state").description("Project state management commands");
|
|
11688
11971
|
command.addCommand(createShowCommand());
|
|
11689
11972
|
command.addCommand(createResetCommand());
|
|
11690
11973
|
command.addCommand(createLearnCommand());
|
|
@@ -11693,9 +11976,9 @@ function createStateCommand() {
|
|
|
11693
11976
|
}
|
|
11694
11977
|
|
|
11695
11978
|
// src/commands/sync-analyses.ts
|
|
11696
|
-
import * as
|
|
11697
|
-
import * as
|
|
11698
|
-
import { Command as
|
|
11979
|
+
import * as fs45 from "fs";
|
|
11980
|
+
import * as path66 from "path";
|
|
11981
|
+
import { Command as Command105 } from "commander";
|
|
11699
11982
|
init_dist3();
|
|
11700
11983
|
function isValidRecord(record) {
|
|
11701
11984
|
return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
|
|
@@ -11721,7 +12004,7 @@ function extractAnalysisFromComments(comments) {
|
|
|
11721
12004
|
return null;
|
|
11722
12005
|
}
|
|
11723
12006
|
function bootstrapTrackerCommand2(opts, verb) {
|
|
11724
|
-
const projectPath =
|
|
12007
|
+
const projectPath = path66.resolve(opts.dir);
|
|
11725
12008
|
const trackerConfig = loadTrackerSyncConfig(projectPath);
|
|
11726
12009
|
if (!trackerConfig) {
|
|
11727
12010
|
logger.error(`No tracker config found in harness.config.json. Cannot ${verb}.`);
|
|
@@ -11733,8 +12016,8 @@ function bootstrapTrackerCommand2(opts, verb) {
|
|
|
11733
12016
|
);
|
|
11734
12017
|
return null;
|
|
11735
12018
|
}
|
|
11736
|
-
const projectEnvPath =
|
|
11737
|
-
if (
|
|
12019
|
+
const projectEnvPath = path66.join(projectPath, ".env");
|
|
12020
|
+
if (fs45.existsSync(projectEnvPath) && !process.env.GITHUB_TOKEN) {
|
|
11738
12021
|
__require("dotenv").config({ path: projectEnvPath });
|
|
11739
12022
|
}
|
|
11740
12023
|
const token = process.env.GITHUB_TOKEN;
|
|
@@ -11764,13 +12047,13 @@ async function loadRoadmapFeatures(projectPath) {
|
|
|
11764
12047
|
}
|
|
11765
12048
|
return r.value.features.filter((f) => !!f.externalId).map((f) => ({ name: f.name, externalId: f.externalId }));
|
|
11766
12049
|
}
|
|
11767
|
-
const roadmapFile =
|
|
11768
|
-
if (!
|
|
12050
|
+
const roadmapFile = path66.join(projectPath, "docs", "roadmap.md");
|
|
12051
|
+
if (!fs45.existsSync(roadmapFile)) {
|
|
11769
12052
|
logger.error("No docs/roadmap.md found. Cannot discover features with externalIds.");
|
|
11770
12053
|
return null;
|
|
11771
12054
|
}
|
|
11772
12055
|
const { parseRoadmap: parseRoadmap2 } = (init_dist3(), __toCommonJS(dist_exports));
|
|
11773
|
-
const roadmapParsed = parseRoadmap2(
|
|
12056
|
+
const roadmapParsed = parseRoadmap2(fs45.readFileSync(roadmapFile, "utf-8"));
|
|
11774
12057
|
if (!roadmapParsed.ok) {
|
|
11775
12058
|
logger.error("Failed to parse docs/roadmap.md");
|
|
11776
12059
|
return null;
|
|
@@ -11821,9 +12104,9 @@ async function runSyncAnalyses(opts) {
|
|
|
11821
12104
|
return;
|
|
11822
12105
|
}
|
|
11823
12106
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
11824
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
12107
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-72ID44UE.js");
|
|
11825
12108
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
11826
|
-
const archive = new AnalysisArchive(
|
|
12109
|
+
const archive = new AnalysisArchive(path66.join(projectPath, ".harness", "analyses"));
|
|
11827
12110
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
11828
12111
|
const { syncedCount, skippedCount, warnCount } = await syncFeatureAnalyses(
|
|
11829
12112
|
features,
|
|
@@ -11843,7 +12126,7 @@ async function runSyncAnalyses(opts) {
|
|
|
11843
12126
|
}
|
|
11844
12127
|
}
|
|
11845
12128
|
function createSyncAnalysesCommand() {
|
|
11846
|
-
const command = new
|
|
12129
|
+
const command = new Command105("sync-analyses").description(
|
|
11847
12130
|
"Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
|
|
11848
12131
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
11849
12132
|
try {
|
|
@@ -11857,8 +12140,8 @@ function createSyncAnalysesCommand() {
|
|
|
11857
12140
|
}
|
|
11858
12141
|
|
|
11859
12142
|
// src/commands/sync-main.ts
|
|
11860
|
-
import { Command as
|
|
11861
|
-
import * as
|
|
12143
|
+
import { Command as Command106 } from "commander";
|
|
12144
|
+
import * as path67 from "path";
|
|
11862
12145
|
import {
|
|
11863
12146
|
syncMain as defaultSyncMain
|
|
11864
12147
|
} from "@harness-engineering/orchestrator";
|
|
@@ -11888,8 +12171,8 @@ function formatHuman2(r) {
|
|
|
11888
12171
|
}
|
|
11889
12172
|
}
|
|
11890
12173
|
function createSyncMainCommand() {
|
|
11891
|
-
return new
|
|
11892
|
-
const cwd =
|
|
12174
|
+
return new Command106("sync-main").description("Fast-forward the local default branch from origin (no-op on conflict)").option("--json", "Emit a SyncMainResult JSON object", false).option("--path <path>", "Project root path", ".").action(async (opts) => {
|
|
12175
|
+
const cwd = path67.resolve(opts.path ?? ".");
|
|
11893
12176
|
const exitCode = await runSyncMain({ cwd, json: Boolean(opts.json) });
|
|
11894
12177
|
process.exit(exitCode);
|
|
11895
12178
|
});
|
|
@@ -11897,7 +12180,7 @@ function createSyncMainCommand() {
|
|
|
11897
12180
|
|
|
11898
12181
|
// src/commands/taint.ts
|
|
11899
12182
|
init_dist3();
|
|
11900
|
-
import { Command as
|
|
12183
|
+
import { Command as Command107 } from "commander";
|
|
11901
12184
|
function getProjectRoot() {
|
|
11902
12185
|
return process.cwd();
|
|
11903
12186
|
}
|
|
@@ -11969,28 +12252,28 @@ function registerStatusCommand(taint) {
|
|
|
11969
12252
|
});
|
|
11970
12253
|
}
|
|
11971
12254
|
function createTaintCommand() {
|
|
11972
|
-
const taint = new
|
|
12255
|
+
const taint = new Command107("taint").description("Manage sentinel session taint state");
|
|
11973
12256
|
registerClearCommand(taint);
|
|
11974
12257
|
registerStatusCommand(taint);
|
|
11975
12258
|
return taint;
|
|
11976
12259
|
}
|
|
11977
12260
|
|
|
11978
12261
|
// src/commands/telemetry/index.ts
|
|
11979
|
-
import { Command as
|
|
12262
|
+
import { Command as Command111 } from "commander";
|
|
11980
12263
|
|
|
11981
12264
|
// src/commands/telemetry/identify.ts
|
|
11982
12265
|
init_dist3();
|
|
11983
|
-
import { Command as
|
|
11984
|
-
import * as
|
|
11985
|
-
import * as
|
|
12266
|
+
import { Command as Command108 } from "commander";
|
|
12267
|
+
import * as fs46 from "fs";
|
|
12268
|
+
import * as path68 from "path";
|
|
11986
12269
|
function telemetryFilePath(cwd) {
|
|
11987
|
-
return
|
|
12270
|
+
return path68.join(cwd, ".harness", "telemetry.json");
|
|
11988
12271
|
}
|
|
11989
12272
|
function writeTelemetryFile(cwd, data) {
|
|
11990
12273
|
const filePath = telemetryFilePath(cwd);
|
|
11991
|
-
const dir =
|
|
11992
|
-
|
|
11993
|
-
|
|
12274
|
+
const dir = path68.dirname(filePath);
|
|
12275
|
+
fs46.mkdirSync(dir, { recursive: true });
|
|
12276
|
+
fs46.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
11994
12277
|
}
|
|
11995
12278
|
var IDENTITY_FIELDS = ["project", "team", "alias"];
|
|
11996
12279
|
function applyIdentityFields(opts, identity) {
|
|
@@ -12004,7 +12287,7 @@ function printIdentity(identity) {
|
|
|
12004
12287
|
}
|
|
12005
12288
|
}
|
|
12006
12289
|
function createIdentifyCommand() {
|
|
12007
|
-
const cmd = new
|
|
12290
|
+
const cmd = new Command108("identify").description("Set or clear telemetry identity fields in .harness/telemetry.json").option("--project <name>", "Project name").option("--team <name>", "Team name").option("--alias <name>", "User alias").option("--clear", "Remove all identity fields").action((opts) => {
|
|
12008
12291
|
const cwd = process.cwd();
|
|
12009
12292
|
if (opts.clear) {
|
|
12010
12293
|
writeTelemetryFile(cwd, { identity: {} });
|
|
@@ -12029,7 +12312,7 @@ function createIdentifyCommand() {
|
|
|
12029
12312
|
|
|
12030
12313
|
// src/commands/telemetry/status.ts
|
|
12031
12314
|
init_dist3();
|
|
12032
|
-
import { Command as
|
|
12315
|
+
import { Command as Command109 } from "commander";
|
|
12033
12316
|
function gatherEnvOverrides() {
|
|
12034
12317
|
const overrides = {};
|
|
12035
12318
|
if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
|
|
@@ -12071,7 +12354,7 @@ function printHumanStatus(result) {
|
|
|
12071
12354
|
printEnvOverrides(result.envOverrides);
|
|
12072
12355
|
}
|
|
12073
12356
|
function createStatusCommand() {
|
|
12074
|
-
const cmd = new
|
|
12357
|
+
const cmd = new Command109("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
|
|
12075
12358
|
const cwd = process.cwd();
|
|
12076
12359
|
const envOverrides = gatherEnvOverrides();
|
|
12077
12360
|
const consent = resolveConsent(cwd, void 0);
|
|
@@ -12104,11 +12387,11 @@ function createStatusCommand() {
|
|
|
12104
12387
|
|
|
12105
12388
|
// src/commands/telemetry/test.ts
|
|
12106
12389
|
init_dist3();
|
|
12107
|
-
import { Command as
|
|
12390
|
+
import { Command as Command110 } from "commander";
|
|
12108
12391
|
|
|
12109
12392
|
// src/bin/command-telemetry.ts
|
|
12110
|
-
import { existsSync as
|
|
12111
|
-
import { join as
|
|
12393
|
+
import { existsSync as existsSync43, readFileSync as readFileSync33, mkdirSync as mkdirSync19, appendFileSync, writeFileSync as writeFileSync25 } from "fs";
|
|
12394
|
+
import { join as join57, parse as parsePath, resolve as resolve43 } from "path";
|
|
12112
12395
|
import { spawn as spawn2 } from "child_process";
|
|
12113
12396
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
12114
12397
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
@@ -12116,7 +12399,7 @@ function findProjectRoot(cwd) {
|
|
|
12116
12399
|
let dir = resolve43(cwd);
|
|
12117
12400
|
const { root } = parsePath(dir);
|
|
12118
12401
|
while (dir !== root) {
|
|
12119
|
-
if (
|
|
12402
|
+
if (existsSync43(join57(dir, "harness.config.json"))) return dir;
|
|
12120
12403
|
dir = resolve43(dir, "..");
|
|
12121
12404
|
}
|
|
12122
12405
|
return cwd;
|
|
@@ -12154,7 +12437,7 @@ function resolveCommandName(cmd) {
|
|
|
12154
12437
|
}
|
|
12155
12438
|
function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
12156
12439
|
try {
|
|
12157
|
-
const metricsDir =
|
|
12440
|
+
const metricsDir = join57(cwd, ".harness", "metrics");
|
|
12158
12441
|
mkdirSync19(metricsDir, { recursive: true });
|
|
12159
12442
|
const record = {
|
|
12160
12443
|
skill: command,
|
|
@@ -12164,16 +12447,16 @@ function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
|
12164
12447
|
outcome,
|
|
12165
12448
|
phasesReached: []
|
|
12166
12449
|
};
|
|
12167
|
-
const adoptionFile =
|
|
12450
|
+
const adoptionFile = join57(metricsDir, "adoption.jsonl");
|
|
12168
12451
|
appendFileSync(adoptionFile, JSON.stringify(record) + "\n");
|
|
12169
12452
|
} catch {
|
|
12170
12453
|
}
|
|
12171
12454
|
}
|
|
12172
12455
|
function isTelemetryDisabledByConfig(cwd) {
|
|
12173
|
-
const configPath =
|
|
12174
|
-
if (!
|
|
12456
|
+
const configPath = join57(cwd, "harness.config.json");
|
|
12457
|
+
if (!existsSync43(configPath)) return false;
|
|
12175
12458
|
try {
|
|
12176
|
-
const config = JSON.parse(
|
|
12459
|
+
const config = JSON.parse(readFileSync33(configPath, "utf-8"));
|
|
12177
12460
|
return config?.telemetry?.enabled === false;
|
|
12178
12461
|
} catch {
|
|
12179
12462
|
return false;
|
|
@@ -12185,11 +12468,11 @@ function isTelemetryOptedOut(cwd) {
|
|
|
12185
12468
|
}
|
|
12186
12469
|
function flushTelemetryBackground(cwd) {
|
|
12187
12470
|
try {
|
|
12188
|
-
const adoptionFile =
|
|
12189
|
-
if (!
|
|
12471
|
+
const adoptionFile = join57(cwd, ".harness", "metrics", "adoption.jsonl");
|
|
12472
|
+
if (!existsSync43(adoptionFile)) return;
|
|
12190
12473
|
if (isTelemetryOptedOut(cwd)) return;
|
|
12191
|
-
const reporterPath =
|
|
12192
|
-
if (!
|
|
12474
|
+
const reporterPath = join57(cwd, ".harness", "hooks", "telemetry-reporter.js");
|
|
12475
|
+
if (!existsSync43(reporterPath)) return;
|
|
12193
12476
|
const child = spawn2(process.execPath, [reporterPath], {
|
|
12194
12477
|
cwd,
|
|
12195
12478
|
stdio: ["pipe", "ignore", "ignore"],
|
|
@@ -12226,7 +12509,7 @@ function reportTestSuccess(status, distinctId, identity) {
|
|
|
12226
12509
|
if (identity.team) logger.info(` team: ${identity.team}`);
|
|
12227
12510
|
}
|
|
12228
12511
|
function createTestCommand() {
|
|
12229
|
-
return new
|
|
12512
|
+
return new Command110("test").description("Send a test event to PostHog and verify connectivity").action(async () => {
|
|
12230
12513
|
const cwd = process.cwd();
|
|
12231
12514
|
const consent = resolveConsent(cwd, void 0);
|
|
12232
12515
|
if (!consent.allowed) {
|
|
@@ -12263,7 +12546,7 @@ function createTestCommand() {
|
|
|
12263
12546
|
|
|
12264
12547
|
// src/commands/telemetry/index.ts
|
|
12265
12548
|
function createTelemetryCommand() {
|
|
12266
|
-
const command = new
|
|
12549
|
+
const command = new Command111("telemetry").description("Telemetry identity and status management");
|
|
12267
12550
|
command.addCommand(createIdentifyCommand());
|
|
12268
12551
|
command.addCommand(createStatusCommand());
|
|
12269
12552
|
command.addCommand(createTestCommand());
|
|
@@ -12271,9 +12554,9 @@ function createTelemetryCommand() {
|
|
|
12271
12554
|
}
|
|
12272
12555
|
|
|
12273
12556
|
// src/commands/test-craft.ts
|
|
12274
|
-
import { Command as
|
|
12557
|
+
import { Command as Command112 } from "commander";
|
|
12275
12558
|
function createTestCraftCommand() {
|
|
12276
|
-
return new
|
|
12559
|
+
return new Command112("test-craft").description(
|
|
12277
12560
|
"LLM-judgment critique of test quality across vitest/jest/mocha/playwright. Fourth craft-pipeline ceiling skill. Per-test critique with best-effort source pairing."
|
|
12278
12561
|
).option("-f, --files <files...>", "Optional test file/glob scope").option("--frameworks <names...>", "Restrict to: vitest / jest / mocha / playwright").option("--max-files <n>", "Cap test file count (default: 100)").option("--max-tests-per-file <n>", "Cap per-file test critique (default: 20)").option("--no-source-pair", "Skip source-pairing resolution").action(async (opts, cmd) => {
|
|
12279
12562
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -12321,10 +12604,10 @@ function printResult7(result, mode, _formatter) {
|
|
|
12321
12604
|
list.push(f);
|
|
12322
12605
|
byFile.set(f.target.file, list);
|
|
12323
12606
|
}
|
|
12324
|
-
for (const [file,
|
|
12607
|
+
for (const [file, fs48] of byFile) {
|
|
12325
12608
|
console.log(`
|
|
12326
12609
|
${file}`);
|
|
12327
|
-
for (const f of
|
|
12610
|
+
for (const f of fs48) {
|
|
12328
12611
|
const nestingStr = f.target.nesting.length > 0 ? f.target.nesting.join(" > ") + " > " : "";
|
|
12329
12612
|
console.log(
|
|
12330
12613
|
` ${f.code} [${f.tier}/${f.impact}/${f.confidence}] ${f.target.framework}:${f.target.line}`
|
|
@@ -12343,8 +12626,8 @@ ${file}`);
|
|
|
12343
12626
|
}
|
|
12344
12627
|
|
|
12345
12628
|
// src/commands/traceability.ts
|
|
12346
|
-
import { Command as
|
|
12347
|
-
import
|
|
12629
|
+
import { Command as Command113 } from "commander";
|
|
12630
|
+
import chalk17 from "chalk";
|
|
12348
12631
|
function confidenceLabel(maxConfidence) {
|
|
12349
12632
|
if (maxConfidence >= 0.8) return "explicit";
|
|
12350
12633
|
if (maxConfidence > 0) return "inferred";
|
|
@@ -12353,13 +12636,13 @@ function confidenceLabel(maxConfidence) {
|
|
|
12353
12636
|
function statusIcon(status) {
|
|
12354
12637
|
switch (status) {
|
|
12355
12638
|
case "full":
|
|
12356
|
-
return
|
|
12639
|
+
return chalk17.green("\u2713 full");
|
|
12357
12640
|
case "code-only":
|
|
12358
|
-
return
|
|
12641
|
+
return chalk17.yellow("\u25D0 code-only");
|
|
12359
12642
|
case "test-only":
|
|
12360
|
-
return
|
|
12643
|
+
return chalk17.yellow("\u25D0 test-only");
|
|
12361
12644
|
case "none":
|
|
12362
|
-
return
|
|
12645
|
+
return chalk17.red("\u2717 none");
|
|
12363
12646
|
default:
|
|
12364
12647
|
return status;
|
|
12365
12648
|
}
|
|
@@ -12406,14 +12689,14 @@ function handleEmptyResults(mode) {
|
|
|
12406
12689
|
}
|
|
12407
12690
|
function printResultTable(result, mode) {
|
|
12408
12691
|
const specLabel = result.specPath || result.featureName || "Unknown";
|
|
12409
|
-
console.log(` ${
|
|
12692
|
+
console.log(` ${chalk17.cyan(specLabel)} ${chalk17.dim(`(${result.summary.total} requirements)`)}`);
|
|
12410
12693
|
console.log("");
|
|
12411
12694
|
const numWidth = 4;
|
|
12412
12695
|
const nameWidth = mode === OutputMode.VERBOSE ? 44 : 36;
|
|
12413
12696
|
const codeWidth = 6;
|
|
12414
12697
|
const testWidth = 7;
|
|
12415
12698
|
const confWidth = 12;
|
|
12416
|
-
const header =
|
|
12699
|
+
const header = chalk17.dim(
|
|
12417
12700
|
` ${pad("#", numWidth)}${pad("Requirement", nameWidth)}${pad("Code", codeWidth)}${pad("Tests", testWidth)}${pad("Confidence", confWidth)}Status`
|
|
12418
12701
|
);
|
|
12419
12702
|
console.log(header);
|
|
@@ -12442,10 +12725,10 @@ function printRequirementRows(requirements, mode, cols) {
|
|
|
12442
12725
|
);
|
|
12443
12726
|
if (mode === OutputMode.VERBOSE) {
|
|
12444
12727
|
for (const f of req.codeFiles) {
|
|
12445
|
-
console.log(
|
|
12728
|
+
console.log(chalk17.dim(` code: ${f.path} (${f.method})`));
|
|
12446
12729
|
}
|
|
12447
12730
|
for (const f of req.testFiles) {
|
|
12448
|
-
console.log(
|
|
12731
|
+
console.log(chalk17.dim(` test: ${f.path} (${f.method})`));
|
|
12449
12732
|
}
|
|
12450
12733
|
}
|
|
12451
12734
|
}
|
|
@@ -12456,7 +12739,7 @@ function printCoverageSummary(summary) {
|
|
|
12456
12739
|
const codePct = s.total > 0 ? Math.round(s.withCode / s.total * 100) : 0;
|
|
12457
12740
|
const testPct = s.total > 0 ? Math.round(s.withTests / s.total * 100) : 0;
|
|
12458
12741
|
console.log(
|
|
12459
|
-
` ${
|
|
12742
|
+
` ${chalk17.bold("Coverage:")} ${fullyPct}% fully traced (${s.fullyTraced}/${s.total}), ${codePct}% with code (${s.withCode}/${s.total}), ${testPct}% with tests (${s.withTests}/${s.total})`
|
|
12460
12743
|
);
|
|
12461
12744
|
}
|
|
12462
12745
|
async function runTraceability(options) {
|
|
@@ -12472,14 +12755,14 @@ async function runTraceability(options) {
|
|
|
12472
12755
|
console.log(JSON.stringify(results, null, 2));
|
|
12473
12756
|
} else {
|
|
12474
12757
|
console.log("");
|
|
12475
|
-
console.log(
|
|
12758
|
+
console.log(chalk17.bold("Spec-to-Implementation Traceability"));
|
|
12476
12759
|
console.log("");
|
|
12477
12760
|
for (const result of results) printResultTable(result, mode);
|
|
12478
12761
|
}
|
|
12479
12762
|
process.exit(ExitCode.SUCCESS);
|
|
12480
12763
|
}
|
|
12481
12764
|
function createTraceabilityCommand() {
|
|
12482
|
-
return new
|
|
12765
|
+
return new Command113("traceability").description("Show spec-to-implementation traceability from the knowledge graph").option("--spec <path>", "Filter by spec file path").option("--feature <name>", "Filter by feature name").action(async (opts, cmd) => {
|
|
12483
12766
|
const globalOpts = cmd.optsWithGlobals();
|
|
12484
12767
|
await runTraceability({
|
|
12485
12768
|
spec: opts.spec,
|
|
@@ -12492,15 +12775,15 @@ function createTraceabilityCommand() {
|
|
|
12492
12775
|
}
|
|
12493
12776
|
|
|
12494
12777
|
// src/commands/uninstall.ts
|
|
12495
|
-
import * as
|
|
12496
|
-
import { Command as
|
|
12778
|
+
import * as path69 from "path";
|
|
12779
|
+
import { Command as Command114 } from "commander";
|
|
12497
12780
|
async function runUninstall(skillName, options) {
|
|
12498
12781
|
const packageName = resolvePackageName(skillName);
|
|
12499
12782
|
const shortName = extractSkillName(packageName);
|
|
12500
12783
|
const globalDir = resolveGlobalSkillsDir();
|
|
12501
|
-
const skillsDir =
|
|
12502
|
-
const communityBase =
|
|
12503
|
-
const lockfilePath =
|
|
12784
|
+
const skillsDir = path69.dirname(globalDir);
|
|
12785
|
+
const communityBase = path69.join(skillsDir, "community");
|
|
12786
|
+
const lockfilePath = path69.join(communityBase, "skills-lock.json");
|
|
12504
12787
|
const lockfile = readLockfile2(lockfilePath);
|
|
12505
12788
|
const entry = lockfile.skills[packageName];
|
|
12506
12789
|
if (!entry) {
|
|
@@ -12530,7 +12813,7 @@ async function runUninstall(skillName, options) {
|
|
|
12530
12813
|
return result;
|
|
12531
12814
|
}
|
|
12532
12815
|
function createUninstallCommand() {
|
|
12533
|
-
const cmd = new
|
|
12816
|
+
const cmd = new Command114("uninstall");
|
|
12534
12817
|
cmd.description("Uninstall a community skill").argument("<skill>", "Skill name or @harness-skills/scoped package name").option("--force", "Remove even if other skills depend on this one").action(async (skill, opts) => {
|
|
12535
12818
|
try {
|
|
12536
12819
|
const result = await runUninstall(skill, opts);
|
|
@@ -12550,9 +12833,9 @@ function createUninstallCommand() {
|
|
|
12550
12833
|
|
|
12551
12834
|
// src/commands/uninstall-constraints.ts
|
|
12552
12835
|
init_dist3();
|
|
12553
|
-
import * as
|
|
12554
|
-
import * as
|
|
12555
|
-
import { Command as
|
|
12836
|
+
import * as fs47 from "fs/promises";
|
|
12837
|
+
import * as path70 from "path";
|
|
12838
|
+
import { Command as Command115 } from "commander";
|
|
12556
12839
|
async function runUninstallConstraints(options) {
|
|
12557
12840
|
const { packageName, configPath, lockfilePath } = options;
|
|
12558
12841
|
const lockfileResult = await readLockfile(lockfilePath);
|
|
@@ -12572,7 +12855,7 @@ async function runUninstallConstraints(options) {
|
|
|
12572
12855
|
}
|
|
12573
12856
|
let localConfig;
|
|
12574
12857
|
try {
|
|
12575
|
-
const raw = await
|
|
12858
|
+
const raw = await fs47.readFile(configPath, "utf-8");
|
|
12576
12859
|
localConfig = JSON.parse(raw);
|
|
12577
12860
|
} catch (err) {
|
|
12578
12861
|
return {
|
|
@@ -12609,11 +12892,11 @@ async function runUninstallConstraints(options) {
|
|
|
12609
12892
|
};
|
|
12610
12893
|
}
|
|
12611
12894
|
function createUninstallConstraintsCommand() {
|
|
12612
|
-
const cmd = new
|
|
12895
|
+
const cmd = new Command115("uninstall-constraints");
|
|
12613
12896
|
cmd.description("Remove a previously installed constraints package").argument("<name>", "Name of the constraint package to uninstall").option("-c, --config <path>", "Path to harness.config.json").action(async (name, opts) => {
|
|
12614
12897
|
let configPath;
|
|
12615
12898
|
if (opts.config) {
|
|
12616
|
-
configPath =
|
|
12899
|
+
configPath = path70.resolve(opts.config);
|
|
12617
12900
|
} else {
|
|
12618
12901
|
const found = findConfigFile();
|
|
12619
12902
|
if (!found.ok) {
|
|
@@ -12622,8 +12905,8 @@ function createUninstallConstraintsCommand() {
|
|
|
12622
12905
|
}
|
|
12623
12906
|
configPath = found.value;
|
|
12624
12907
|
}
|
|
12625
|
-
const projectRoot2 =
|
|
12626
|
-
const lockfilePath =
|
|
12908
|
+
const projectRoot2 = path70.dirname(configPath);
|
|
12909
|
+
const lockfilePath = path70.join(projectRoot2, ".harness", "constraints.lock.json");
|
|
12627
12910
|
const result = await runUninstallConstraints({
|
|
12628
12911
|
packageName: name,
|
|
12629
12912
|
configPath,
|
|
@@ -12649,13 +12932,13 @@ function createUninstallConstraintsCommand() {
|
|
|
12649
12932
|
|
|
12650
12933
|
// src/commands/update.ts
|
|
12651
12934
|
init_dist3();
|
|
12652
|
-
import { Command as
|
|
12935
|
+
import { Command as Command116 } from "commander";
|
|
12653
12936
|
import { execFile, execFileSync as execFileSync7 } from "child_process";
|
|
12654
|
-
import { realpathSync, existsSync as
|
|
12655
|
-
import { dirname as
|
|
12937
|
+
import { realpathSync, existsSync as existsSync44, readFileSync as readFileSync34 } from "fs";
|
|
12938
|
+
import { dirname as dirname23, join as join60 } from "path";
|
|
12656
12939
|
import { promisify } from "util";
|
|
12657
12940
|
import readline4 from "readline";
|
|
12658
|
-
import
|
|
12941
|
+
import chalk18 from "chalk";
|
|
12659
12942
|
var CLI_PACKAGE = "@harness-engineering/cli";
|
|
12660
12943
|
function detectPackageManagerFromPath(resolvedPath) {
|
|
12661
12944
|
const normalizedBin = resolvedPath.replace(/\\/g, "/");
|
|
@@ -12741,16 +13024,16 @@ function prompt3(question) {
|
|
|
12741
13024
|
});
|
|
12742
13025
|
}
|
|
12743
13026
|
function findPackageDir(resolvedBin) {
|
|
12744
|
-
let dir =
|
|
13027
|
+
let dir = dirname23(resolvedBin);
|
|
12745
13028
|
for (let i = 0; i < 6; i++) {
|
|
12746
|
-
if (
|
|
13029
|
+
if (existsSync44(join60(dir, "package.json"))) {
|
|
12747
13030
|
try {
|
|
12748
|
-
const pkg = JSON.parse(
|
|
13031
|
+
const pkg = JSON.parse(readFileSync34(join60(dir, "package.json"), "utf-8"));
|
|
12749
13032
|
if (pkg?.name === CLI_PACKAGE) return dir;
|
|
12750
13033
|
} catch {
|
|
12751
13034
|
}
|
|
12752
13035
|
}
|
|
12753
|
-
const parent =
|
|
13036
|
+
const parent = dirname23(dir);
|
|
12754
13037
|
if (parent === dir) return null;
|
|
12755
13038
|
dir = parent;
|
|
12756
13039
|
}
|
|
@@ -12790,7 +13073,7 @@ function findAllInstalls() {
|
|
|
12790
13073
|
if (!packageDir) continue;
|
|
12791
13074
|
let version = null;
|
|
12792
13075
|
try {
|
|
12793
|
-
const pkg = JSON.parse(
|
|
13076
|
+
const pkg = JSON.parse(readFileSync34(join60(packageDir, "package.json"), "utf-8"));
|
|
12794
13077
|
if (typeof pkg?.version === "string") version = pkg.version;
|
|
12795
13078
|
} catch {
|
|
12796
13079
|
}
|
|
@@ -12824,7 +13107,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
12824
13107
|
`Found ${installs.length} harness installs on PATH. Only the active one was updated.`
|
|
12825
13108
|
);
|
|
12826
13109
|
for (const install of installs) {
|
|
12827
|
-
const tag = install.packageDir === activeDir ?
|
|
13110
|
+
const tag = install.packageDir === activeDir ? chalk18.green(" (active, just updated)") : chalk18.yellow(" (stale)");
|
|
12828
13111
|
const versionStr = install.version ? `v${install.version}` : "unknown";
|
|
12829
13112
|
console.log(` ${install.binPath} -> ${versionStr}${tag}`);
|
|
12830
13113
|
}
|
|
@@ -12832,7 +13115,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
12832
13115
|
const commands = others.map((install) => buildUninstallCommand(install));
|
|
12833
13116
|
console.log("Cleanup commands:");
|
|
12834
13117
|
for (const { display } of commands) {
|
|
12835
|
-
console.log(` ${
|
|
13118
|
+
console.log(` ${chalk18.cyan(display)}`);
|
|
12836
13119
|
}
|
|
12837
13120
|
console.log("");
|
|
12838
13121
|
const answer = await prompt3(`Run these now to remove stale installs? (y/N) `);
|
|
@@ -12840,7 +13123,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
12840
13123
|
for (const cmd of commands) {
|
|
12841
13124
|
if (!cmd.runnable) {
|
|
12842
13125
|
logger.warn(`Skipped ${cmd.install.binPath} (no automatic uninstall for this layout).`);
|
|
12843
|
-
console.log(` Run manually: ${
|
|
13126
|
+
console.log(` Run manually: ${chalk18.cyan(cmd.display)}`);
|
|
12844
13127
|
continue;
|
|
12845
13128
|
}
|
|
12846
13129
|
try {
|
|
@@ -12848,7 +13131,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
12848
13131
|
execFileSync7(cmd.bin, cmd.args, { stdio: "inherit", timeout: 12e4 });
|
|
12849
13132
|
} catch {
|
|
12850
13133
|
logger.warn(`Uninstall failed for ${cmd.install.binPath}.`);
|
|
12851
|
-
console.log(` Run manually: ${
|
|
13134
|
+
console.log(` Run manually: ${chalk18.cyan(cmd.display)}`);
|
|
12852
13135
|
}
|
|
12853
13136
|
}
|
|
12854
13137
|
}
|
|
@@ -12883,12 +13166,12 @@ async function ensureTelemetryIfNeeded() {
|
|
|
12883
13166
|
}
|
|
12884
13167
|
function refreshHooks() {
|
|
12885
13168
|
const cwd = process.cwd();
|
|
12886
|
-
const configPath =
|
|
12887
|
-
if (!
|
|
13169
|
+
const configPath = join60(cwd, "harness.config.json");
|
|
13170
|
+
if (!existsSync44(configPath)) return;
|
|
12888
13171
|
let profile = "standard";
|
|
12889
|
-
const profilePath =
|
|
13172
|
+
const profilePath = join60(cwd, ".harness", "hooks", "profile.json");
|
|
12890
13173
|
try {
|
|
12891
|
-
const data = JSON.parse(
|
|
13174
|
+
const data = JSON.parse(readFileSync34(profilePath, "utf-8"));
|
|
12892
13175
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
12893
13176
|
profile = data.profile;
|
|
12894
13177
|
}
|
|
@@ -12908,7 +13191,7 @@ function runLocalGraphScan() {
|
|
|
12908
13191
|
execFileSync7("harness", ["graph", "scan", "."], { stdio: "inherit" });
|
|
12909
13192
|
} catch {
|
|
12910
13193
|
logger.warn("Graph scan failed. Run manually:");
|
|
12911
|
-
console.log(` ${
|
|
13194
|
+
console.log(` ${chalk18.cyan("harness graph scan .")}`);
|
|
12912
13195
|
}
|
|
12913
13196
|
}
|
|
12914
13197
|
async function offerRegeneration() {
|
|
@@ -12925,7 +13208,7 @@ async function offerRegeneration() {
|
|
|
12925
13208
|
});
|
|
12926
13209
|
} catch {
|
|
12927
13210
|
logger.warn("Generation failed. Run manually:");
|
|
12928
|
-
console.log(` ${
|
|
13211
|
+
console.log(` ${chalk18.cyan(`harness generate${isGlobal ? " --global" : ""}`)}`);
|
|
12929
13212
|
}
|
|
12930
13213
|
if (!isGlobal) {
|
|
12931
13214
|
runLocalGraphScan();
|
|
@@ -12989,8 +13272,8 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
12989
13272
|
console.log("");
|
|
12990
13273
|
for (const { pkg, current, latest } of outdated) {
|
|
12991
13274
|
const shortName = pkg.replace("@harness-engineering/", "");
|
|
12992
|
-
const currentStr = current ?
|
|
12993
|
-
logger.info(`${shortName}: ${currentStr} \u2192 ${
|
|
13275
|
+
const currentStr = current ? chalk18.dim(`v${current}`) : chalk18.dim("not installed");
|
|
13276
|
+
logger.info(`${shortName}: ${currentStr} \u2192 ${chalk18.green(`v${latest}`)}`);
|
|
12994
13277
|
}
|
|
12995
13278
|
console.log("");
|
|
12996
13279
|
}
|
|
@@ -13007,7 +13290,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
13007
13290
|
} catch {
|
|
13008
13291
|
console.log("");
|
|
13009
13292
|
logger.error("Update failed. You can try manually:");
|
|
13010
|
-
console.log(` ${
|
|
13293
|
+
console.log(` ${chalk18.cyan(installCmd)}`);
|
|
13011
13294
|
process.exit(ExitCode.ERROR);
|
|
13012
13295
|
}
|
|
13013
13296
|
refreshHooks();
|
|
@@ -13017,7 +13300,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
13017
13300
|
process.exit(ExitCode.SUCCESS);
|
|
13018
13301
|
}
|
|
13019
13302
|
function createUpdateCommand() {
|
|
13020
|
-
return new
|
|
13303
|
+
return new Command116("update").description("Update all @harness-engineering packages to the latest version").option("--version <semver>", "Pin @harness-engineering/cli to a specific version").option("--force", "Force update even if versions match").option(
|
|
13021
13304
|
"--regenerate",
|
|
13022
13305
|
"Only regenerate slash commands and agent definitions (skip package updates)"
|
|
13023
13306
|
).action(async (opts, cmd) => {
|
|
@@ -13027,9 +13310,9 @@ function createUpdateCommand() {
|
|
|
13027
13310
|
}
|
|
13028
13311
|
|
|
13029
13312
|
// src/commands/usage.ts
|
|
13030
|
-
import { Command as
|
|
13313
|
+
import { Command as Command117 } from "commander";
|
|
13031
13314
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
13032
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
13315
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-72ID44UE.js");
|
|
13033
13316
|
const records = readCostRecords(cwd);
|
|
13034
13317
|
if (includeClaudeSessions) {
|
|
13035
13318
|
const ccRecords = parseCCRecords();
|
|
@@ -13122,7 +13405,7 @@ function registerDailyCommand(usage) {
|
|
|
13122
13405
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
13123
13406
|
return;
|
|
13124
13407
|
}
|
|
13125
|
-
const { aggregateByDay } = await import("./dist-
|
|
13408
|
+
const { aggregateByDay } = await import("./dist-72ID44UE.js");
|
|
13126
13409
|
const limited = aggregateByDay(records).slice(0, days);
|
|
13127
13410
|
if (globalOpts.json) {
|
|
13128
13411
|
console.log(
|
|
@@ -13151,7 +13434,7 @@ function registerSessionsCommand(usage) {
|
|
|
13151
13434
|
}
|
|
13152
13435
|
return;
|
|
13153
13436
|
}
|
|
13154
|
-
const { aggregateBySession } = await import("./dist-
|
|
13437
|
+
const { aggregateBySession } = await import("./dist-72ID44UE.js");
|
|
13155
13438
|
const sessionData = aggregateBySession(records);
|
|
13156
13439
|
const limited = sessionData.slice(0, limit);
|
|
13157
13440
|
if (globalOpts.json) {
|
|
@@ -13231,7 +13514,7 @@ function registerSessionCommand(usage) {
|
|
|
13231
13514
|
cwd,
|
|
13232
13515
|
globalOpts.includeClaudeSessions
|
|
13233
13516
|
);
|
|
13234
|
-
const { aggregateBySession } = await import("./dist-
|
|
13517
|
+
const { aggregateBySession } = await import("./dist-72ID44UE.js");
|
|
13235
13518
|
const sessionData = aggregateBySession(records);
|
|
13236
13519
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
13237
13520
|
if (!match) {
|
|
@@ -13266,7 +13549,7 @@ function registerLatestCommand(usage) {
|
|
|
13266
13549
|
}
|
|
13267
13550
|
return;
|
|
13268
13551
|
}
|
|
13269
|
-
const { aggregateBySession } = await import("./dist-
|
|
13552
|
+
const { aggregateBySession } = await import("./dist-72ID44UE.js");
|
|
13270
13553
|
const sessionData = aggregateBySession(records);
|
|
13271
13554
|
const latest = sessionData[0];
|
|
13272
13555
|
if (!latest) {
|
|
@@ -13292,7 +13575,7 @@ function registerLatestCommand(usage) {
|
|
|
13292
13575
|
});
|
|
13293
13576
|
}
|
|
13294
13577
|
function createUsageCommand() {
|
|
13295
|
-
const usage = new
|
|
13578
|
+
const usage = new Command117("usage").description("Token usage and cost tracking");
|
|
13296
13579
|
usage.option(
|
|
13297
13580
|
"--include-claude-sessions",
|
|
13298
13581
|
"Include Claude Code session data from ~/.claude/projects/"
|
|
@@ -13307,15 +13590,15 @@ function createUsageCommand() {
|
|
|
13307
13590
|
// src/commands/validate.ts
|
|
13308
13591
|
init_dist3();
|
|
13309
13592
|
init_dist3();
|
|
13310
|
-
import { Command as
|
|
13311
|
-
import * as
|
|
13593
|
+
import { Command as Command118 } from "commander";
|
|
13594
|
+
import * as path71 from "path";
|
|
13312
13595
|
async function runValidate(options) {
|
|
13313
13596
|
const configResult = resolveConfig(options.configPath);
|
|
13314
13597
|
if (!configResult.ok) {
|
|
13315
13598
|
return configResult;
|
|
13316
13599
|
}
|
|
13317
13600
|
const config = configResult.value;
|
|
13318
|
-
const cwd = options.cwd ?? (options.configPath ?
|
|
13601
|
+
const cwd = options.cwd ?? (options.configPath ? path71.dirname(path71.resolve(options.configPath)) : process.cwd());
|
|
13319
13602
|
const result = {
|
|
13320
13603
|
valid: true,
|
|
13321
13604
|
checks: {
|
|
@@ -13325,7 +13608,7 @@ async function runValidate(options) {
|
|
|
13325
13608
|
},
|
|
13326
13609
|
issues: []
|
|
13327
13610
|
};
|
|
13328
|
-
const agentsMapPath =
|
|
13611
|
+
const agentsMapPath = path71.resolve(cwd, config.agentsMapPath);
|
|
13329
13612
|
const agentsResult = await validateAgentsMap(agentsMapPath);
|
|
13330
13613
|
if (agentsResult.ok) {
|
|
13331
13614
|
result.checks.agentsMap = true;
|
|
@@ -13531,11 +13814,11 @@ function resolveValidateMode(globalOpts) {
|
|
|
13531
13814
|
return OutputMode.TEXT;
|
|
13532
13815
|
}
|
|
13533
13816
|
async function printCrossCheckWarnings(mode) {
|
|
13534
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
13817
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-AUUZFNVL.js");
|
|
13535
13818
|
const cwd = process.cwd();
|
|
13536
13819
|
const crossResult = await runCrossCheck2({
|
|
13537
|
-
specsDir:
|
|
13538
|
-
plansDir:
|
|
13820
|
+
specsDir: path71.join(cwd, "docs", "specs"),
|
|
13821
|
+
plansDir: path71.join(cwd, "docs", "plans"),
|
|
13539
13822
|
projectPath: cwd
|
|
13540
13823
|
});
|
|
13541
13824
|
if (!crossResult.ok || crossResult.value.warnings === 0) return;
|
|
@@ -13547,7 +13830,7 @@ async function printCrossCheckWarnings(mode) {
|
|
|
13547
13830
|
${crossResult.value.warnings} warnings`);
|
|
13548
13831
|
}
|
|
13549
13832
|
function createValidateCommand3() {
|
|
13550
|
-
const command = new
|
|
13833
|
+
const command = new Command118("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
|
|
13551
13834
|
"--agent-configs",
|
|
13552
13835
|
"Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
|
|
13553
13836
|
).option("--strict", "Treat warnings as errors (applies to --agent-configs)").option("--agnix-bin <path>", "Override the agnix binary path discovered on PATH").action(async (opts, cmd) => runValidateAction(opts, cmd.optsWithGlobals()));
|
|
@@ -13596,7 +13879,7 @@ Agent configs checked via ${engineLabel}${note}`);
|
|
|
13596
13879
|
|
|
13597
13880
|
// src/commands/verify.ts
|
|
13598
13881
|
init_dist3();
|
|
13599
|
-
import { Command as
|
|
13882
|
+
import { Command as Command119 } from "commander";
|
|
13600
13883
|
import { execSync as execSync6 } from "child_process";
|
|
13601
13884
|
function loadBranchingConfig(configPath) {
|
|
13602
13885
|
const shouldLoad = configPath !== void 0 || findConfigFile().ok;
|
|
@@ -13659,7 +13942,7 @@ async function runVerify(options) {
|
|
|
13659
13942
|
process.exit(ExitCode.VALIDATION_FAILED);
|
|
13660
13943
|
}
|
|
13661
13944
|
function createVerifyCommand() {
|
|
13662
|
-
return new
|
|
13945
|
+
return new Command119("verify").description(
|
|
13663
13946
|
"Verify project conventions (currently: branch naming). Works with or without a harness.config.json."
|
|
13664
13947
|
).option(
|
|
13665
13948
|
"--branch <name>",
|
|
@@ -13722,6 +14005,7 @@ var commandCreators = [
|
|
|
13722
14005
|
createMcpCommand,
|
|
13723
14006
|
createMcpGuardCommand,
|
|
13724
14007
|
createMigrateCommand,
|
|
14008
|
+
createModelsCommand,
|
|
13725
14009
|
createNamingCraftCommand,
|
|
13726
14010
|
createNotificationsCommand,
|
|
13727
14011
|
createOrchestratorCommand,
|
|
@@ -13762,7 +14046,7 @@ var commandCreators = [
|
|
|
13762
14046
|
|
|
13763
14047
|
// src/index.ts
|
|
13764
14048
|
function createProgram() {
|
|
13765
|
-
const program = new
|
|
14049
|
+
const program = new Command120();
|
|
13766
14050
|
program.name("harness").description("CLI for Harness Engineering toolkit").version(CLI_VERSION).option("-c, --config <path>", "Path to config file").option("--json", "Output as JSON").option("--verbose", "Verbose output").option("--quiet", "Minimal output");
|
|
13767
14051
|
for (const creator of commandCreators) {
|
|
13768
14052
|
program.addCommand(creator());
|