@harness-engineering/cli 4.3.3 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/claude-code/harness-brainstorming/SKILL.md +3 -1
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +5 -0
- package/dist/agents/skills/claude-code/harness-rollback/SKILL.md +124 -0
- package/dist/agents/skills/claude-code/harness-rollback/skill.yaml +52 -0
- package/dist/agents/skills/claude-code/product-requirements/SKILL.md +150 -0
- package/dist/agents/skills/claude-code/product-requirements/skill.yaml +70 -0
- package/dist/agents/skills/codex/harness-brainstorming/SKILL.md +3 -1
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +5 -0
- package/dist/agents/skills/codex/harness-rollback/SKILL.md +124 -0
- package/dist/agents/skills/codex/harness-rollback/skill.yaml +52 -0
- package/dist/agents/skills/codex/product-requirements/SKILL.md +150 -0
- package/dist/agents/skills/codex/product-requirements/skill.yaml +70 -0
- package/dist/agents/skills/cursor/harness-brainstorming/SKILL.md +3 -1
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +5 -0
- package/dist/agents/skills/cursor/harness-rollback/SKILL.md +124 -0
- package/dist/agents/skills/cursor/harness-rollback/skill.yaml +52 -0
- package/dist/agents/skills/cursor/product-requirements/SKILL.md +150 -0
- package/dist/agents/skills/cursor/product-requirements/skill.yaml +70 -0
- package/dist/agents/skills/gemini-cli/harness-brainstorming/SKILL.md +3 -1
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +5 -0
- package/dist/agents/skills/gemini-cli/harness-rollback/SKILL.md +124 -0
- package/dist/agents/skills/gemini-cli/harness-rollback/skill.yaml +52 -0
- package/dist/agents/skills/gemini-cli/product-requirements/SKILL.md +150 -0
- package/dist/agents/skills/gemini-cli/product-requirements/skill.yaml +70 -0
- package/dist/{agents-md-N4ULMUAK.js → agents-md-GOFDOUEI.js} +4 -4
- package/dist/{analyzer-XRCV63KC-WGRHW2ZM.js → analyzer-XRCV63KC-CUGBENHK.js} +2 -2
- package/dist/{architecture-OIMIBDJC.js → architecture-UJZTBKJ2.js} +5 -5
- package/dist/{assess-project-ITSB6W4L.js → assess-project-S3UOQUQW.js} +1 -1
- package/dist/bin/harness-mcp.js +15 -15
- package/dist/bin/harness.js +25 -25
- package/dist/{check-phase-gate-GN4X7HT6.js → check-phase-gate-SQ2X2Q7T.js} +5 -5
- package/dist/{chunk-F3GNBRGL.js → chunk-4ITYAEOA.js} +85 -85
- package/dist/{chunk-H5GN7R43.js → chunk-6RXT67Q4.js} +2 -2
- package/dist/{chunk-GTHL3TRZ.js → chunk-7BBQ5C7I.js} +2 -2
- package/dist/{chunk-574JCBYH.js → chunk-AIFPI3UZ.js} +2 -2
- package/dist/{chunk-CRHQYBDA.js → chunk-CFK4RJFP.js} +89 -4
- package/dist/{chunk-TKPAOB2T.js → chunk-DFWM2SNS.js} +2 -2
- package/dist/{chunk-LP764MGR.js → chunk-DPG2WNSN.js} +3 -3
- package/dist/{chunk-T46G6YNI.js → chunk-DSXUS6KG.js} +3 -3
- package/dist/{chunk-CMU4DNLY.js → chunk-DU6AUYTA.js} +1 -1
- package/dist/{chunk-MEDRUIRO.js → chunk-FXO3EODY.js} +3 -3
- package/dist/{chunk-D7A5JR7M.js → chunk-JGW4CHIJ.js} +2 -2
- package/dist/{chunk-JZL2O3RC.js → chunk-KI64IQLS.js} +5 -5
- package/dist/{chunk-5BUHPOCP.js → chunk-OIMJWYWF.js} +7 -7
- package/dist/{chunk-HJYAXAYQ.js → chunk-OWJTMHVP.js} +2 -2
- package/dist/{chunk-YVI7ZV73.js → chunk-PSFJ5YKQ.js} +585 -154
- package/dist/{chunk-VJDSB7CI.js → chunk-R5C7ZFON.js} +14 -0
- package/dist/{chunk-MIZQY35D.js → chunk-RD75T4XK.js} +2 -2
- package/dist/{chunk-7RH2NTLA.js → chunk-RPOBWUKV.js} +1 -1
- package/dist/{chunk-U2NOOATF.js → chunk-SQDQMVNE.js} +6 -6
- package/dist/{chunk-6NEJPS7W.js → chunk-T2PQWRRK.js} +2 -2
- package/dist/{chunk-NEMB36N4.js → chunk-UJW4POA3.js} +2 -2
- package/dist/{chunk-LTYVAKWK.js → chunk-V6QYVM6K.js} +20 -2
- package/dist/{chunk-PT65VY4S.js → chunk-WHPZG5TX.js} +6 -6
- package/dist/{chunk-XKILFNRK.js → chunk-XC7NR63Q.js} +2 -2
- package/dist/{chunk-73QZ43DF.js → chunk-YE7NZ44F.js} +2 -2
- package/dist/{ci-workflow-PA3WL6WK.js → ci-workflow-5WHUEHOF.js} +4 -4
- package/dist/{dist-ISDDHDOR.js → dist-O7MTFDG5.js} +1 -1
- package/dist/{dist-BWFFVKEW.js → dist-SOKOW7BC.js} +5 -3
- package/dist/{docs-HOTWQJQF.js → docs-AEHPPDKX.js} +5 -5
- package/dist/{engine-MVG2SPGD.js → engine-HYM5SLCD.js} +4 -4
- package/dist/{entropy-3B4U2BTF.js → entropy-6KH5G3NR.js} +5 -5
- package/dist/{feedback-WIWWDVUC.js → feedback-AUP2JK43.js} +1 -1
- package/dist/{generate-agent-definitions-KPRHY6X3.js → generate-agent-definitions-OH4RF445.js} +5 -5
- package/dist/index.d.ts +66 -0
- package/dist/index.js +25 -25
- package/dist/{loader-SPJ2ZGYW.js → loader-KWRPA7D5.js} +4 -4
- package/dist/{mcp-VSSIWTII.js → mcp-JTPO5RAK.js} +15 -15
- package/dist/{performance-JYFS27B3.js → performance-HW53ZIAF.js} +5 -5
- package/dist/{review-pipeline-BDT6Y6QV.js → review-pipeline-2R7UBFTG.js} +4 -4
- package/dist/{runtime-ZEPHPPQJ.js → runtime-BB35PFZY.js} +4 -4
- package/dist/{security-FWSDTTEW.js → security-IDJHGBBH.js} +1 -1
- package/dist/{state-events-PBDNQPXT.js → state-events-H3GGBKTB.js} +4 -4
- package/dist/{validate-XIMNCIKR.js → validate-6SDGP7AH.js} +5 -5
- package/dist/{validate-cross-check-H2OUHTID.js → validate-cross-check-OF3FPM32.js} +4 -4
- package/package.json +8 -8
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import {
|
|
2
2
|
generateRuntime
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-AIFPI3UZ.js";
|
|
4
4
|
import {
|
|
5
5
|
generateAgentsMd
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-DFWM2SNS.js";
|
|
7
7
|
import {
|
|
8
8
|
generateCIWorkflow
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-7BBQ5C7I.js";
|
|
10
10
|
import {
|
|
11
11
|
readHarnessState
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-UJW4POA3.js";
|
|
13
13
|
import {
|
|
14
14
|
runCrossCheck
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-RD75T4XK.js";
|
|
16
16
|
import {
|
|
17
17
|
createGenerateAgentDefinitionsCommand,
|
|
18
18
|
generateAgentDefinitions
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-DU6AUYTA.js";
|
|
20
20
|
import {
|
|
21
21
|
toKebabCase
|
|
22
22
|
} from "./chunk-KET4QQZB.js";
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from "./chunk-YYFSFSB3.js";
|
|
27
27
|
import {
|
|
28
28
|
TemplateEngine
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-T2PQWRRK.js";
|
|
30
30
|
import {
|
|
31
31
|
generate,
|
|
32
32
|
validate
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
import {
|
|
38
38
|
listPersonas,
|
|
39
39
|
loadPersona
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-YE7NZ44F.js";
|
|
41
41
|
import {
|
|
42
42
|
runPersona
|
|
43
43
|
} from "./chunk-GISMXMVL.js";
|
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
OutputFormatter,
|
|
52
52
|
OutputMode,
|
|
53
53
|
createCheckPhaseGateCommand
|
|
54
|
-
} from "./chunk-
|
|
54
|
+
} from "./chunk-DPG2WNSN.js";
|
|
55
55
|
import {
|
|
56
56
|
appendFrameworkAgents,
|
|
57
57
|
captureHealthSnapshot,
|
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
runSecurityCraft,
|
|
80
80
|
runSpecCraft,
|
|
81
81
|
runTestCraft
|
|
82
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-4ITYAEOA.js";
|
|
83
83
|
import {
|
|
84
84
|
VALID_PLATFORMS
|
|
85
85
|
} from "./chunk-3ISINLYT.js";
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
BranchingConfigSchema,
|
|
109
109
|
findConfigFile,
|
|
110
110
|
resolveConfig
|
|
111
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-V6QYVM6K.js";
|
|
112
112
|
import {
|
|
113
113
|
logger
|
|
114
114
|
} from "./chunk-EBJQ6N4M.js";
|
|
@@ -146,6 +146,7 @@ import {
|
|
|
146
146
|
checkRoadmapAggregateDrift,
|
|
147
147
|
checkRoadmapHealth,
|
|
148
148
|
checkTaint,
|
|
149
|
+
classifyRevert,
|
|
149
150
|
clearTaint,
|
|
150
151
|
computeHotspots,
|
|
151
152
|
computeOverallSeverity,
|
|
@@ -225,7 +226,7 @@ import {
|
|
|
225
226
|
writeConfig,
|
|
226
227
|
writeLockfile,
|
|
227
228
|
writeRegeneratedRoadmap
|
|
228
|
-
} from "./chunk-
|
|
229
|
+
} from "./chunk-CFK4RJFP.js";
|
|
229
230
|
import {
|
|
230
231
|
CLI_VERSION
|
|
231
232
|
} from "./chunk-BM3PWGXQ.js";
|
|
@@ -238,14 +239,14 @@ import {
|
|
|
238
239
|
detectDocDrift,
|
|
239
240
|
generateSuggestions,
|
|
240
241
|
validateDependencies
|
|
241
|
-
} from "./chunk-
|
|
242
|
+
} from "./chunk-RPOBWUKV.js";
|
|
242
243
|
import {
|
|
243
244
|
Err,
|
|
244
245
|
INDEXED_FILE_KINDS,
|
|
245
246
|
INSIGHTS_KEYS,
|
|
246
247
|
Ok,
|
|
247
248
|
init_dist
|
|
248
|
-
} from "./chunk-
|
|
249
|
+
} from "./chunk-R5C7ZFON.js";
|
|
249
250
|
import {
|
|
250
251
|
DEFAULT_SKIP_DIRS,
|
|
251
252
|
DesignConstraintAdapter,
|
|
@@ -262,7 +263,7 @@ import {
|
|
|
262
263
|
} from "./chunk-KFQGP6VL.js";
|
|
263
264
|
|
|
264
265
|
// src/index.ts
|
|
265
|
-
import { Command as
|
|
266
|
+
import { Command as Command131 } from "commander";
|
|
266
267
|
|
|
267
268
|
// src/commands/add.ts
|
|
268
269
|
init_dist3();
|
|
@@ -453,7 +454,7 @@ function registerSkillsCommand(adoption) {
|
|
|
453
454
|
const globalOpts = cmd.optsWithGlobals();
|
|
454
455
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
455
456
|
const cwd = process.cwd();
|
|
456
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
457
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-SOKOW7BC.js");
|
|
457
458
|
const records = readAdoptionRecords(cwd);
|
|
458
459
|
if (records.length === 0) {
|
|
459
460
|
if (globalOpts.json) {
|
|
@@ -489,7 +490,7 @@ function registerRecentCommand(adoption) {
|
|
|
489
490
|
const globalOpts = cmd.optsWithGlobals();
|
|
490
491
|
const limit = Math.max(parseInt(opts.limit, 10) || 20, 1);
|
|
491
492
|
const cwd = process.cwd();
|
|
492
|
-
const { readAdoptionRecords } = await import("./dist-
|
|
493
|
+
const { readAdoptionRecords } = await import("./dist-SOKOW7BC.js");
|
|
493
494
|
const records = readAdoptionRecords(cwd);
|
|
494
495
|
if (records.length === 0) {
|
|
495
496
|
if (globalOpts.json) {
|
|
@@ -567,7 +568,7 @@ function registerSkillCommand(adoption) {
|
|
|
567
568
|
adoption.command("skill <name>").description("Show detail for a specific skill").action(async (name, _opts, cmd) => {
|
|
568
569
|
const globalOpts = cmd.optsWithGlobals();
|
|
569
570
|
const cwd = process.cwd();
|
|
570
|
-
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-
|
|
571
|
+
const { readAdoptionRecords, aggregateBySkill } = await import("./dist-SOKOW7BC.js");
|
|
571
572
|
const records = readAdoptionRecords(cwd);
|
|
572
573
|
const skillRecords = records.filter((r) => r.skill === name);
|
|
573
574
|
if (skillRecords.length === 0) {
|
|
@@ -601,7 +602,7 @@ function registerRetrospectiveCommand(adoption) {
|
|
|
601
602
|
adoption.command("retrospective").description("Generate a catalog retrospective from adoption telemetry").option("--inactive-days <n>", "Inactivity threshold in days (default: 90)", "90").option("--top <n>", "Rows per ranked section (default: 10)", "10").option("--out <path>", "Output file path (default: docs/retrospectives/YYYY-MM-DD.md)").option("--no-write", "Print the report to stdout instead of writing a file").action(async (opts, cmd) => {
|
|
602
603
|
const globalOpts = cmd.optsWithGlobals();
|
|
603
604
|
const cwd = process.cwd();
|
|
604
|
-
const { readAdoptionRecords, getCatalogRetrospectiveReport, renderRetrospectiveMarkdown } = await import("./dist-
|
|
605
|
+
const { readAdoptionRecords, getCatalogRetrospectiveReport, renderRetrospectiveMarkdown } = await import("./dist-SOKOW7BC.js");
|
|
605
606
|
const records = readAdoptionRecords(cwd);
|
|
606
607
|
const catalogSkills = discoverCatalogSkills(cwd);
|
|
607
608
|
const report = getCatalogRetrospectiveReport(records, {
|
|
@@ -6209,7 +6210,7 @@ function createInsightsCommand() {
|
|
|
6209
6210
|
return new Command46("insights").description("Composite project report \u2014 health, entropy, decay, attention, impact.").option("--json", "Emit JSON to stdout instead of pretty text").option("--skip <list>", `Comma-separated keys to skip (${INSIGHTS_KEYS.join(",")})`).action(async (opts) => {
|
|
6210
6211
|
const cwd = process.cwd();
|
|
6211
6212
|
const skip = parseSkip2(opts.skip);
|
|
6212
|
-
const { composeInsights } = await import("./dist-
|
|
6213
|
+
const { composeInsights } = await import("./dist-SOKOW7BC.js");
|
|
6213
6214
|
const report = await composeInsights(cwd, { skip });
|
|
6214
6215
|
if (opts.json) {
|
|
6215
6216
|
console.log(JSON.stringify(report, null, 2));
|
|
@@ -7944,10 +7945,10 @@ function reportDispatcher() {
|
|
|
7944
7945
|
function realGit(args, cwd) {
|
|
7945
7946
|
return execFileSync4("git", args, { cwd, encoding: "utf-8" }).toString().trim();
|
|
7946
7947
|
}
|
|
7947
|
-
function createFixDispatcher(resolveBackend,
|
|
7948
|
+
function createFixDispatcher(resolveBackend, git2 = realGit) {
|
|
7948
7949
|
return createAgentDispatcher({
|
|
7949
7950
|
resolveBackend,
|
|
7950
|
-
git,
|
|
7951
|
+
git: git2,
|
|
7951
7952
|
logger: {
|
|
7952
7953
|
info: (message) => logger.info(message),
|
|
7953
7954
|
warn: (message) => logger.warn(message)
|
|
@@ -8320,7 +8321,7 @@ function createMcpCommand() {
|
|
|
8320
8321
|
parseBudget
|
|
8321
8322
|
).action(async (opts) => {
|
|
8322
8323
|
const [{ startServer: startServer2, getToolDefinitions: getToolDefinitions2 }, { selectTier }] = await Promise.all([
|
|
8323
|
-
import("./mcp-
|
|
8324
|
+
import("./mcp-JTPO5RAK.js"),
|
|
8324
8325
|
import("./tool-tiers-OKK5FE57.js")
|
|
8325
8326
|
]);
|
|
8326
8327
|
if (opts.tools && opts.tools.length > 0) {
|
|
@@ -9507,7 +9508,12 @@ function createNotificationsCommand() {
|
|
|
9507
9508
|
// src/commands/orchestrator.ts
|
|
9508
9509
|
import { Command as Command70 } from "commander";
|
|
9509
9510
|
import * as path50 from "path";
|
|
9510
|
-
import {
|
|
9511
|
+
import {
|
|
9512
|
+
Orchestrator,
|
|
9513
|
+
WorkflowLoader as WorkflowLoader2,
|
|
9514
|
+
launchTUI,
|
|
9515
|
+
discoverCandidates
|
|
9516
|
+
} from "@harness-engineering/orchestrator";
|
|
9511
9517
|
function createOrchestratorCommand() {
|
|
9512
9518
|
const orchestrator = new Command70("orchestrator");
|
|
9513
9519
|
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) => {
|
|
@@ -9520,7 +9526,7 @@ function createOrchestratorCommand() {
|
|
|
9520
9526
|
}
|
|
9521
9527
|
const { config, promptTemplate, warnings } = result.value;
|
|
9522
9528
|
for (const w of warnings) logger.warn(w);
|
|
9523
|
-
const daemon = new Orchestrator(config, promptTemplate);
|
|
9529
|
+
const daemon = new Orchestrator(config, promptTemplate, { discoverCandidates });
|
|
9524
9530
|
const shutdown = () => {
|
|
9525
9531
|
daemon.stop();
|
|
9526
9532
|
process.exit(ExitCode.SUCCESS);
|
|
@@ -9551,7 +9557,7 @@ function registerBenchCommand(perf) {
|
|
|
9551
9557
|
perf.command("bench [glob]").description("Run benchmarks via vitest bench").action(async (glob2, _opts, cmd) => {
|
|
9552
9558
|
const globalOpts = cmd.optsWithGlobals();
|
|
9553
9559
|
const cwd = process.cwd();
|
|
9554
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
9560
|
+
const { BenchmarkRunner } = await import("./dist-SOKOW7BC.js");
|
|
9555
9561
|
const runner = new BenchmarkRunner();
|
|
9556
9562
|
const benchFiles = runner.discover(cwd, glob2);
|
|
9557
9563
|
if (benchFiles.length === 0) {
|
|
@@ -9615,7 +9621,7 @@ async function runBaselinesShow(globalOpts) {
|
|
|
9615
9621
|
}
|
|
9616
9622
|
async function runBaselinesUpdate(globalOpts) {
|
|
9617
9623
|
const cwd = process.cwd();
|
|
9618
|
-
const { BenchmarkRunner } = await import("./dist-
|
|
9624
|
+
const { BenchmarkRunner } = await import("./dist-SOKOW7BC.js");
|
|
9619
9625
|
const runner = new BenchmarkRunner();
|
|
9620
9626
|
const manager = new BaselineManager(cwd);
|
|
9621
9627
|
logger.info("Running benchmarks to update baselines...");
|
|
@@ -9656,7 +9662,7 @@ function registerReportCommand(perf) {
|
|
|
9656
9662
|
perf.command("report").description("Full performance report with metrics, trends, and hotspots").action(async (_opts, cmd) => {
|
|
9657
9663
|
const globalOpts = cmd.optsWithGlobals();
|
|
9658
9664
|
const cwd = process.cwd();
|
|
9659
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-
|
|
9665
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./dist-SOKOW7BC.js");
|
|
9660
9666
|
const analyzer = new EntropyAnalyzer2({
|
|
9661
9667
|
rootDir: path51.resolve(cwd),
|
|
9662
9668
|
analyze: { complexity: true, coupling: true }
|
|
@@ -10679,7 +10685,7 @@ async function runPublishAnalyses(opts) {
|
|
|
10679
10685
|
const nameToExternalId = await buildNameToExternalIdMap(projectPath);
|
|
10680
10686
|
if (!nameToExternalId) return process.exit(1);
|
|
10681
10687
|
const { AnalysisArchive } = await import("@harness-engineering/orchestrator");
|
|
10682
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
10688
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-SOKOW7BC.js");
|
|
10683
10689
|
const archive = new AnalysisArchive(path53.join(projectPath, ".harness", "analyses"));
|
|
10684
10690
|
const analyses = await archive.list();
|
|
10685
10691
|
if (analyses.length === 0) {
|
|
@@ -11782,11 +11788,435 @@ function createRoadmapCommand() {
|
|
|
11782
11788
|
return roadmap;
|
|
11783
11789
|
}
|
|
11784
11790
|
|
|
11791
|
+
// src/commands/rollback.ts
|
|
11792
|
+
init_dist3();
|
|
11793
|
+
import { execFileSync as execFileSync10 } from "child_process";
|
|
11794
|
+
import { Command as Command90, Option as Option3, InvalidArgumentError } from "commander";
|
|
11795
|
+
|
|
11796
|
+
// src/rollback/compose.ts
|
|
11797
|
+
var ROLLBACK_LABEL = "harness:rollback";
|
|
11798
|
+
function buildRevertBody(d, originalTitle, reason) {
|
|
11799
|
+
const lines = [
|
|
11800
|
+
`## Automated rollback proposal`,
|
|
11801
|
+
``,
|
|
11802
|
+
`**Trigger:** ${d.trigger}`,
|
|
11803
|
+
`**Target PR:** #${d.targetPr} \u2014 ${originalTitle}`,
|
|
11804
|
+
`**Revert-ready:** ${d.revertReady}`,
|
|
11805
|
+
d.blastRadius !== void 0 ? `**Blast radius:** ${d.blastRadius}` : void 0,
|
|
11806
|
+
// #4: surface the human-provided --reason when present (help text promises it).
|
|
11807
|
+
reason && reason.trim() !== "" ? `**Reason:** ${reason.trim()}` : void 0,
|
|
11808
|
+
``,
|
|
11809
|
+
`### Classification`,
|
|
11810
|
+
...d.reasons.map((r) => `- ${r}`)
|
|
11811
|
+
];
|
|
11812
|
+
if (d.migrationWarnings.length > 0) {
|
|
11813
|
+
lines.push(
|
|
11814
|
+
``,
|
|
11815
|
+
`### Migration / irreversibility warnings`,
|
|
11816
|
+
...d.migrationWarnings.map((w) => `- ${w}`)
|
|
11817
|
+
);
|
|
11818
|
+
}
|
|
11819
|
+
if (d.dependentMerges.length > 0) {
|
|
11820
|
+
lines.push(
|
|
11821
|
+
``,
|
|
11822
|
+
`### Dependent later merges`,
|
|
11823
|
+
`- ${d.dependentMerges.map((n) => `#${n}`).join(", ")}`
|
|
11824
|
+
);
|
|
11825
|
+
}
|
|
11826
|
+
return lines.filter((l) => l !== void 0).join("\n");
|
|
11827
|
+
}
|
|
11828
|
+
async function composeRevertPr(decision, originalTitle, opts) {
|
|
11829
|
+
if (!decision.revertReady) return { action: decision.action };
|
|
11830
|
+
const body = buildRevertBody(decision, originalTitle, opts.reason);
|
|
11831
|
+
if (opts.dryRun) {
|
|
11832
|
+
const print = opts.print ?? ((s) => process.stdout.write(`${s}
|
|
11833
|
+
`));
|
|
11834
|
+
print(body);
|
|
11835
|
+
return { action: "proposed" };
|
|
11836
|
+
}
|
|
11837
|
+
const existing = await opts.gh.findOpenRevertPr(decision.targetPr, ROLLBACK_LABEL);
|
|
11838
|
+
if (existing !== null) {
|
|
11839
|
+
return { action: "skipped", ...existing.url ? { prUrl: existing.url } : {} };
|
|
11840
|
+
}
|
|
11841
|
+
const prUrl = await opts.gh.openPr({
|
|
11842
|
+
title: `revert: ${originalTitle} (automated rollback)`,
|
|
11843
|
+
body,
|
|
11844
|
+
label: ROLLBACK_LABEL,
|
|
11845
|
+
targetPr: decision.targetPr
|
|
11846
|
+
});
|
|
11847
|
+
return { action: "proposed", prUrl };
|
|
11848
|
+
}
|
|
11849
|
+
|
|
11850
|
+
// src/rollback/breadcrumb.ts
|
|
11851
|
+
import { appendFileSync, mkdirSync as mkdirSync19 } from "fs";
|
|
11852
|
+
import { dirname as dirname21, join as join50 } from "path";
|
|
11853
|
+
var ROLLBACK_EVENTS_FILE = join50(".harness", "signals", "rollback-events.jsonl");
|
|
11854
|
+
async function appendRollbackEvent(event, opts = {}) {
|
|
11855
|
+
const root = opts.root ?? process.cwd();
|
|
11856
|
+
const ts = (opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))();
|
|
11857
|
+
const file = join50(root, ROLLBACK_EVENTS_FILE);
|
|
11858
|
+
mkdirSync19(dirname21(file), { recursive: true });
|
|
11859
|
+
appendFileSync(file, `${JSON.stringify({ ...event, ts })}
|
|
11860
|
+
`, "utf-8");
|
|
11861
|
+
}
|
|
11862
|
+
async function linkRollbackEventToGraph(event, opts = {}) {
|
|
11863
|
+
const root = opts.root ?? process.cwd();
|
|
11864
|
+
try {
|
|
11865
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-STFGYKZH.js");
|
|
11866
|
+
const store = await loadGraphStore2(root);
|
|
11867
|
+
if (!store) return;
|
|
11868
|
+
const ts = (opts.now ?? (() => (/* @__PURE__ */ new Date()).toISOString()))();
|
|
11869
|
+
const nodeId = `rollback_event:${event.targetPr}:${ts}`;
|
|
11870
|
+
store.addNode({
|
|
11871
|
+
id: nodeId,
|
|
11872
|
+
type: "learning",
|
|
11873
|
+
name: `rollback ${event.action} for PR #${event.targetPr}`,
|
|
11874
|
+
metadata: { kind: "rollback_event", ...event, ts },
|
|
11875
|
+
lastModified: ts
|
|
11876
|
+
});
|
|
11877
|
+
const outcomes = store.findNodes({ type: "execution_outcome" });
|
|
11878
|
+
const target = outcomes.find(
|
|
11879
|
+
(n) => n.metadata.pr === event.targetPr
|
|
11880
|
+
);
|
|
11881
|
+
if (target) {
|
|
11882
|
+
store.addEdge({ from: nodeId, to: target.id, type: "references" });
|
|
11883
|
+
}
|
|
11884
|
+
await store.save(join50(root, ".harness", "graph"));
|
|
11885
|
+
} catch {
|
|
11886
|
+
}
|
|
11887
|
+
}
|
|
11888
|
+
|
|
11889
|
+
// src/rollback/io.ts
|
|
11890
|
+
import { execFileSync as execFileSync8 } from "child_process";
|
|
11891
|
+
var git = (args) => execFileSync8("git", args, { encoding: "utf-8" }).toString();
|
|
11892
|
+
var nodeGitSeam = {
|
|
11893
|
+
run: (args) => git(args).trim(),
|
|
11894
|
+
tryMergeTree: (args) => {
|
|
11895
|
+
try {
|
|
11896
|
+
const stdout = execFileSync8("git", args, {
|
|
11897
|
+
encoding: "utf-8",
|
|
11898
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
11899
|
+
}).toString();
|
|
11900
|
+
return { status: 0, stdout };
|
|
11901
|
+
} catch (err) {
|
|
11902
|
+
const e = err;
|
|
11903
|
+
return { status: e?.status ?? 1, stdout: e?.stdout?.toString?.() ?? "" };
|
|
11904
|
+
}
|
|
11905
|
+
}
|
|
11906
|
+
};
|
|
11907
|
+
var gh = (args) => execFileSync8("gh", args, { encoding: "utf-8" }).toString();
|
|
11908
|
+
function parseConflictPaths(stdout) {
|
|
11909
|
+
const lines = stdout.split("\n");
|
|
11910
|
+
const paths = /* @__PURE__ */ new Set();
|
|
11911
|
+
for (let i = 1; i < lines.length; i++) {
|
|
11912
|
+
const line = lines[i];
|
|
11913
|
+
if (line === void 0 || line.trim() === "") break;
|
|
11914
|
+
const tab = line.indexOf(" ");
|
|
11915
|
+
if (tab === -1) continue;
|
|
11916
|
+
const path81 = line.slice(tab + 1).trim();
|
|
11917
|
+
if (path81) paths.add(path81);
|
|
11918
|
+
}
|
|
11919
|
+
return [...paths];
|
|
11920
|
+
}
|
|
11921
|
+
async function computeRevertDryRun(mergeSha, gitSeam = nodeGitSeam) {
|
|
11922
|
+
const revList = gitSeam.run(["rev-list", "--parents", "-n", "1", mergeSha]);
|
|
11923
|
+
const parents = revList.trim().split(/\s+/).slice(1);
|
|
11924
|
+
if (parents.length === 0) {
|
|
11925
|
+
throw new Error(`cannot revert ${mergeSha}: commit has no parent (root commit)`);
|
|
11926
|
+
}
|
|
11927
|
+
const parent = parents[0];
|
|
11928
|
+
const { status, stdout } = gitSeam.tryMergeTree([
|
|
11929
|
+
"merge-tree",
|
|
11930
|
+
"--write-tree",
|
|
11931
|
+
"--merge-base",
|
|
11932
|
+
mergeSha,
|
|
11933
|
+
"HEAD",
|
|
11934
|
+
parent
|
|
11935
|
+
]);
|
|
11936
|
+
if (status === 0) return { clean: true, conflictPaths: [] };
|
|
11937
|
+
if (status === 1) return { clean: false, conflictPaths: parseConflictPaths(stdout) };
|
|
11938
|
+
throw new Error(
|
|
11939
|
+
`git merge-tree failed for ${mergeSha} (exit ${status})${stdout ? `: ${stdout.trim()}` : ""}`
|
|
11940
|
+
);
|
|
11941
|
+
}
|
|
11942
|
+
async function revertDryRun(mergeSha) {
|
|
11943
|
+
return computeRevertDryRun(mergeSha, nodeGitSeam);
|
|
11944
|
+
}
|
|
11945
|
+
async function resolveTarget(pr) {
|
|
11946
|
+
const raw = gh(["pr", "view", String(pr), "--json", "mergeCommit,files,title"]);
|
|
11947
|
+
const parsed = JSON.parse(raw);
|
|
11948
|
+
return {
|
|
11949
|
+
mergeSha: parsed.mergeCommit?.oid ?? "",
|
|
11950
|
+
changedFiles: (parsed.files ?? []).map((f) => f.path),
|
|
11951
|
+
title: parsed.title ?? `PR #${pr}`
|
|
11952
|
+
};
|
|
11953
|
+
}
|
|
11954
|
+
async function listLaterMerges(pr) {
|
|
11955
|
+
const targetRaw = gh(["pr", "view", String(pr), "--json", "mergedAt"]);
|
|
11956
|
+
const targetMergedAt = JSON.parse(targetRaw).mergedAt;
|
|
11957
|
+
if (!targetMergedAt) return [];
|
|
11958
|
+
const raw = gh([
|
|
11959
|
+
"pr",
|
|
11960
|
+
"list",
|
|
11961
|
+
"--state",
|
|
11962
|
+
"merged",
|
|
11963
|
+
"--limit",
|
|
11964
|
+
"100",
|
|
11965
|
+
"--json",
|
|
11966
|
+
"number,files,mergedAt"
|
|
11967
|
+
]);
|
|
11968
|
+
const list = JSON.parse(raw);
|
|
11969
|
+
return list.filter((p) => p.number !== pr && p.mergedAt > targetMergedAt).map((p) => ({ pr: p.number, changedFiles: (p.files ?? []).map((f) => f.path) }));
|
|
11970
|
+
}
|
|
11971
|
+
function createNodeRollbackIO() {
|
|
11972
|
+
return { revertDryRun, resolveTarget, listLaterMerges };
|
|
11973
|
+
}
|
|
11974
|
+
|
|
11975
|
+
// src/rollback/sweep.ts
|
|
11976
|
+
import { execFileSync as execFileSync9 } from "child_process";
|
|
11977
|
+
import { SignalTimelineStore } from "@harness-engineering/signals";
|
|
11978
|
+
var WINDOW_UNIT_MS = {
|
|
11979
|
+
h: 36e5,
|
|
11980
|
+
d: 864e5,
|
|
11981
|
+
w: 6048e5
|
|
11982
|
+
};
|
|
11983
|
+
function parseWindow(window) {
|
|
11984
|
+
const match = /^(\d+)([hdw])$/.exec(window);
|
|
11985
|
+
if (!match) throw new Error(`invalid window: ${window}`);
|
|
11986
|
+
const count = Number.parseInt(match[1] ?? "", 10);
|
|
11987
|
+
const unit = match[2];
|
|
11988
|
+
return count * WINDOW_UNIT_MS[unit];
|
|
11989
|
+
}
|
|
11990
|
+
function detectCrossing(points, rule) {
|
|
11991
|
+
const prev = points.at(-2);
|
|
11992
|
+
const curr = points.at(-1);
|
|
11993
|
+
if (!prev || !curr) return false;
|
|
11994
|
+
if (rule.direction === "above") {
|
|
11995
|
+
return prev.value < rule.threshold && curr.value >= rule.threshold;
|
|
11996
|
+
}
|
|
11997
|
+
return prev.value > rule.threshold && curr.value <= rule.threshold;
|
|
11998
|
+
}
|
|
11999
|
+
function windowStart(now, window) {
|
|
12000
|
+
return new Date(now.getTime() - parseWindow(window)).toISOString();
|
|
12001
|
+
}
|
|
12002
|
+
function pointsInWindow(points, now, window) {
|
|
12003
|
+
const startDate = windowStart(now, window).slice(0, 10);
|
|
12004
|
+
const nowDate = now.toISOString().slice(0, 10);
|
|
12005
|
+
return points.filter((p) => p.date >= startDate && p.date <= nowDate);
|
|
12006
|
+
}
|
|
12007
|
+
async function runRollbackSweep(signals, deps) {
|
|
12008
|
+
const now = (deps.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
12009
|
+
for (const [name, rule] of Object.entries(signals)) {
|
|
12010
|
+
const pts = pointsInWindow(deps.readTimeline(name), now, rule.window);
|
|
12011
|
+
const crossed = detectCrossing(pts, rule);
|
|
12012
|
+
const forwarded = [];
|
|
12013
|
+
if (crossed) {
|
|
12014
|
+
const prs = await deps.resolveMergedPrs(windowStart(now, rule.window), now.toISOString());
|
|
12015
|
+
for (const pr of prs) {
|
|
12016
|
+
const decision = await deps.evaluate(pr);
|
|
12017
|
+
forwarded.push({
|
|
12018
|
+
pr,
|
|
12019
|
+
action: decision.action,
|
|
12020
|
+
...decision.prUrl ? { prUrl: decision.prUrl } : {}
|
|
12021
|
+
});
|
|
12022
|
+
}
|
|
12023
|
+
}
|
|
12024
|
+
deps.report?.({ signal: name, window: rule.window, crossed, forwarded });
|
|
12025
|
+
}
|
|
12026
|
+
}
|
|
12027
|
+
function createTimelineReader(root) {
|
|
12028
|
+
const store = new SignalTimelineStore(root);
|
|
12029
|
+
return (name) => store.read(name);
|
|
12030
|
+
}
|
|
12031
|
+
function createPrResolver() {
|
|
12032
|
+
return async (startIso, nowIso) => {
|
|
12033
|
+
try {
|
|
12034
|
+
const raw = execFileSync9(
|
|
12035
|
+
"gh",
|
|
12036
|
+
[
|
|
12037
|
+
"pr",
|
|
12038
|
+
"list",
|
|
12039
|
+
"--state",
|
|
12040
|
+
"merged",
|
|
12041
|
+
"--search",
|
|
12042
|
+
`merged:${startIso}..${nowIso}`,
|
|
12043
|
+
"--json",
|
|
12044
|
+
"number",
|
|
12045
|
+
"--limit",
|
|
12046
|
+
"100"
|
|
12047
|
+
],
|
|
12048
|
+
{ encoding: "utf-8" }
|
|
12049
|
+
);
|
|
12050
|
+
return JSON.parse(raw).map((p) => p.number);
|
|
12051
|
+
} catch {
|
|
12052
|
+
return [];
|
|
12053
|
+
}
|
|
12054
|
+
};
|
|
12055
|
+
}
|
|
12056
|
+
|
|
12057
|
+
// src/commands/rollback.ts
|
|
12058
|
+
async function runRollbackEvaluate(args, deps) {
|
|
12059
|
+
const trigger = args.trigger ?? "signal";
|
|
12060
|
+
const target = await deps.io.resolveTarget(args.pr);
|
|
12061
|
+
const laterMerges = await deps.io.listLaterMerges(args.pr);
|
|
12062
|
+
const decision = await classifyRevert(
|
|
12063
|
+
{
|
|
12064
|
+
targetPr: args.pr,
|
|
12065
|
+
trigger,
|
|
12066
|
+
mergeSha: target.mergeSha,
|
|
12067
|
+
changedFiles: target.changedFiles,
|
|
12068
|
+
laterMerges
|
|
12069
|
+
},
|
|
12070
|
+
deps.io
|
|
12071
|
+
);
|
|
12072
|
+
const composed = await composeRevertPr(decision, target.title, {
|
|
12073
|
+
gh: deps.gh,
|
|
12074
|
+
...args.dryRun ? { dryRun: true } : {},
|
|
12075
|
+
...deps.print ? { print: deps.print } : {},
|
|
12076
|
+
...args.reason !== void 0 ? { reason: args.reason } : {}
|
|
12077
|
+
});
|
|
12078
|
+
const finalDecision = {
|
|
12079
|
+
...decision,
|
|
12080
|
+
action: composed.action,
|
|
12081
|
+
...composed.prUrl ? { prUrl: composed.prUrl } : {}
|
|
12082
|
+
};
|
|
12083
|
+
await appendRollbackEvent(
|
|
12084
|
+
{
|
|
12085
|
+
targetPr: finalDecision.targetPr,
|
|
12086
|
+
trigger: finalDecision.trigger,
|
|
12087
|
+
revertReady: finalDecision.revertReady,
|
|
12088
|
+
action: finalDecision.action,
|
|
12089
|
+
...finalDecision.prUrl ? { prUrl: finalDecision.prUrl } : {},
|
|
12090
|
+
// #4: record the human-provided reason on the breadcrumb (help text promises it).
|
|
12091
|
+
...args.reason !== void 0 ? { reason: args.reason } : {}
|
|
12092
|
+
},
|
|
12093
|
+
deps.root !== void 0 ? { root: deps.root } : {}
|
|
12094
|
+
);
|
|
12095
|
+
return finalDecision;
|
|
12096
|
+
}
|
|
12097
|
+
var gh2 = (args) => execFileSync10("gh", args, { encoding: "utf-8" }).toString();
|
|
12098
|
+
function referencesTargetPr(text, targetPr) {
|
|
12099
|
+
if (!text) return false;
|
|
12100
|
+
return new RegExp(`(?<!\\d)#${targetPr}(?!\\d)`).test(text);
|
|
12101
|
+
}
|
|
12102
|
+
function findOpenRevertPrNode(targetPr) {
|
|
12103
|
+
try {
|
|
12104
|
+
const raw = gh2([
|
|
12105
|
+
"pr",
|
|
12106
|
+
"list",
|
|
12107
|
+
"--state",
|
|
12108
|
+
"open",
|
|
12109
|
+
"--label",
|
|
12110
|
+
ROLLBACK_LABEL,
|
|
12111
|
+
"--search",
|
|
12112
|
+
`#${targetPr}`,
|
|
12113
|
+
"--json",
|
|
12114
|
+
"number,url,body,title"
|
|
12115
|
+
]);
|
|
12116
|
+
const list = JSON.parse(raw);
|
|
12117
|
+
const match = list.find(
|
|
12118
|
+
(p) => referencesTargetPr(p.body, targetPr) || referencesTargetPr(p.title, targetPr)
|
|
12119
|
+
);
|
|
12120
|
+
return match ? { number: match.number, url: match.url } : null;
|
|
12121
|
+
} catch {
|
|
12122
|
+
return null;
|
|
12123
|
+
}
|
|
12124
|
+
}
|
|
12125
|
+
function createGhSeam() {
|
|
12126
|
+
return {
|
|
12127
|
+
async findOpenRevertPr(targetPr) {
|
|
12128
|
+
return findOpenRevertPrNode(targetPr);
|
|
12129
|
+
},
|
|
12130
|
+
async openPr({ title, body, label }) {
|
|
12131
|
+
return execFileSync10(
|
|
12132
|
+
"gh",
|
|
12133
|
+
["pr", "create", "--title", title, "--body-file", "-", "--label", label],
|
|
12134
|
+
{ input: body, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
12135
|
+
).toString().trim();
|
|
12136
|
+
}
|
|
12137
|
+
};
|
|
12138
|
+
}
|
|
12139
|
+
function summarizeSweepReport(r) {
|
|
12140
|
+
if (!r.crossed) {
|
|
12141
|
+
return `rollback sweep: ${r.signal} (${r.window}) \u2014 no crossing`;
|
|
12142
|
+
}
|
|
12143
|
+
if (r.forwarded.length === 0) {
|
|
12144
|
+
return `rollback sweep: ${r.signal} (${r.window}) \u2014 crossed, no PRs in window`;
|
|
12145
|
+
}
|
|
12146
|
+
const parts = r.forwarded.map((f) => `#${f.pr} \u2192 ${f.action}${f.prUrl ? ` (${f.prUrl})` : ""}`);
|
|
12147
|
+
return `rollback sweep: ${r.signal} (${r.window}) \u2014 crossed; forwarded ${parts.join(", ")}`;
|
|
12148
|
+
}
|
|
12149
|
+
async function runRollbackSweepCommand(deps) {
|
|
12150
|
+
const cfg = deps.resolveConfig();
|
|
12151
|
+
if (!cfg.ok) {
|
|
12152
|
+
deps.warn(`rollback sweep: config error, breaker inactive: ${cfg.error.message}`);
|
|
12153
|
+
}
|
|
12154
|
+
const signals = cfg.ok ? cfg.value.rollback?.signals ?? {} : {};
|
|
12155
|
+
await deps.runSweep(signals, {
|
|
12156
|
+
readTimeline: deps.makeReader(deps.root),
|
|
12157
|
+
resolveMergedPrs: deps.makeResolver(),
|
|
12158
|
+
evaluate: deps.evaluate,
|
|
12159
|
+
report: (r) => deps.info(summarizeSweepReport(r))
|
|
12160
|
+
});
|
|
12161
|
+
}
|
|
12162
|
+
function createRollbackCommand() {
|
|
12163
|
+
const rollback = new Command90("rollback").description(
|
|
12164
|
+
"Post-ship revert circuit breaker (propose-only in v1)"
|
|
12165
|
+
);
|
|
12166
|
+
rollback.command("evaluate").description("Classify a merged PR for revert-readiness and, if ready, propose a revert PR").requiredOption("--pr <n>", "target merged PR number", (v) => {
|
|
12167
|
+
const n = Number.parseInt(v, 10);
|
|
12168
|
+
if (Number.isNaN(n)) throw new InvalidArgumentError("--pr must be a number");
|
|
12169
|
+
return n;
|
|
12170
|
+
}).addOption(
|
|
12171
|
+
new Option3("--trigger <trigger>", "what fired this evaluation").choices(["signal", "eval"]).default("signal")
|
|
12172
|
+
).option("--reason <str>", "human-readable reason recorded on the proposal").option("--dry-run", "print the revert PR body without opening a PR", false).action(async (opts) => {
|
|
12173
|
+
const reason = opts.reason;
|
|
12174
|
+
const dryRun = opts.dryRun;
|
|
12175
|
+
const decision = await runRollbackEvaluate(
|
|
12176
|
+
{
|
|
12177
|
+
pr: opts.pr,
|
|
12178
|
+
trigger: opts.trigger,
|
|
12179
|
+
...reason !== void 0 ? { reason } : {},
|
|
12180
|
+
...dryRun !== void 0 ? { dryRun } : {}
|
|
12181
|
+
},
|
|
12182
|
+
{ io: createNodeRollbackIO(), gh: createGhSeam() }
|
|
12183
|
+
);
|
|
12184
|
+
await linkRollbackEventToGraph({
|
|
12185
|
+
targetPr: decision.targetPr,
|
|
12186
|
+
trigger: decision.trigger,
|
|
12187
|
+
revertReady: decision.revertReady,
|
|
12188
|
+
action: decision.action,
|
|
12189
|
+
...decision.prUrl ? { prUrl: decision.prUrl } : {}
|
|
12190
|
+
});
|
|
12191
|
+
logger.info(JSON.stringify(decision, null, 2));
|
|
12192
|
+
});
|
|
12193
|
+
rollback.command("sweep").description(
|
|
12194
|
+
"Read the signal timeline and propose reverts for threshold crossings (signal arm)"
|
|
12195
|
+
).action(async () => {
|
|
12196
|
+
await runRollbackSweepCommand({
|
|
12197
|
+
resolveConfig,
|
|
12198
|
+
runSweep: runRollbackSweep,
|
|
12199
|
+
makeReader: createTimelineReader,
|
|
12200
|
+
makeResolver: createPrResolver,
|
|
12201
|
+
evaluate: (pr) => runRollbackEvaluate(
|
|
12202
|
+
{ pr, trigger: "signal" },
|
|
12203
|
+
{ io: createNodeRollbackIO(), gh: createGhSeam() }
|
|
12204
|
+
),
|
|
12205
|
+
root: process.cwd(),
|
|
12206
|
+
// #S4: per-signal summary on stdout so the sweep's work is auditable.
|
|
12207
|
+
info: (line) => logger.info(line),
|
|
12208
|
+
// #I2: config error → disarm warning on stderr, never silent.
|
|
12209
|
+
warn: (line) => logger.error(line)
|
|
12210
|
+
});
|
|
12211
|
+
});
|
|
12212
|
+
return rollback;
|
|
12213
|
+
}
|
|
12214
|
+
|
|
11785
12215
|
// src/commands/routing/index.ts
|
|
11786
|
-
import { Command as
|
|
12216
|
+
import { Command as Command94 } from "commander";
|
|
11787
12217
|
|
|
11788
12218
|
// src/commands/routing/config.ts
|
|
11789
|
-
import { Command as
|
|
12219
|
+
import { Command as Command91 } from "commander";
|
|
11790
12220
|
|
|
11791
12221
|
// src/commands/routing/http-client.ts
|
|
11792
12222
|
function orchestratorBase() {
|
|
@@ -11851,7 +12281,7 @@ function renderHuman(data) {
|
|
|
11851
12281
|
}
|
|
11852
12282
|
}
|
|
11853
12283
|
function createConfigCommand() {
|
|
11854
|
-
return new
|
|
12284
|
+
return new Command91("config").description("Print active routing config and resolved fallback chains").option("--json", "Emit JSON to stdout instead of human-readable text").action(async (opts) => {
|
|
11855
12285
|
const r = await getJson("/api/v1/routing/config");
|
|
11856
12286
|
if (!r.ok) {
|
|
11857
12287
|
if (r.status === 0) {
|
|
@@ -11877,7 +12307,7 @@ function createConfigCommand() {
|
|
|
11877
12307
|
}
|
|
11878
12308
|
|
|
11879
12309
|
// src/commands/routing/trace.ts
|
|
11880
|
-
import { Command as
|
|
12310
|
+
import { Command as Command92 } from "commander";
|
|
11881
12311
|
function buildUseCase(opts) {
|
|
11882
12312
|
if (opts.skill) {
|
|
11883
12313
|
return opts.mode ? { kind: "skill", skillName: opts.skill, cognitiveMode: opts.mode } : { kind: "skill", skillName: opts.skill };
|
|
@@ -11898,7 +12328,7 @@ function renderHuman2(r) {
|
|
|
11898
12328
|
}
|
|
11899
12329
|
}
|
|
11900
12330
|
function createTraceCommand() {
|
|
11901
|
-
return new
|
|
12331
|
+
return new Command92("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) => {
|
|
11902
12332
|
const useCase = buildUseCase(opts);
|
|
11903
12333
|
if (!useCase) {
|
|
11904
12334
|
logger.error("Either --skill <name> or --mode <m> is required");
|
|
@@ -11930,7 +12360,7 @@ function createTraceCommand() {
|
|
|
11930
12360
|
}
|
|
11931
12361
|
|
|
11932
12362
|
// src/commands/routing/decisions.ts
|
|
11933
|
-
import { Command as
|
|
12363
|
+
import { Command as Command93 } from "commander";
|
|
11934
12364
|
function summarizeUseCase(uc) {
|
|
11935
12365
|
switch (uc.kind) {
|
|
11936
12366
|
case "skill":
|
|
@@ -11978,7 +12408,7 @@ function buildQuery(opts) {
|
|
|
11978
12408
|
return q ? `?${q}` : "";
|
|
11979
12409
|
}
|
|
11980
12410
|
function createDecisionsCommand() {
|
|
11981
|
-
return new
|
|
12411
|
+
return new Command93("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(
|
|
11982
12412
|
async (opts) => {
|
|
11983
12413
|
const query = buildQuery(opts);
|
|
11984
12414
|
const r = await getJson(`/api/v1/routing/decisions${query}`);
|
|
@@ -12008,7 +12438,7 @@ function createDecisionsCommand() {
|
|
|
12008
12438
|
|
|
12009
12439
|
// src/commands/routing/index.ts
|
|
12010
12440
|
function createRoutingCommand() {
|
|
12011
|
-
const cmd = new
|
|
12441
|
+
const cmd = new Command94("routing").description(
|
|
12012
12442
|
"Inspect routing config, trace decisions, and read recent dispatches"
|
|
12013
12443
|
);
|
|
12014
12444
|
cmd.addCommand(createConfigCommand());
|
|
@@ -12019,9 +12449,9 @@ function createRoutingCommand() {
|
|
|
12019
12449
|
|
|
12020
12450
|
// src/commands/scan-config.ts
|
|
12021
12451
|
init_dist3();
|
|
12022
|
-
import { Command as
|
|
12452
|
+
import { Command as Command95 } from "commander";
|
|
12023
12453
|
import { existsSync as existsSync31, readFileSync as readFileSync22, writeFileSync as writeFileSync23 } from "fs";
|
|
12024
|
-
import { join as
|
|
12454
|
+
import { join as join51, relative as relative4 } from "path";
|
|
12025
12455
|
var CONFIG_FILES = ["CLAUDE.md", "AGENTS.md", ".gemini/settings.json", "skill.yaml"];
|
|
12026
12456
|
function stripHighSeverityPatterns(content, injectionFindings) {
|
|
12027
12457
|
const highLines = /* @__PURE__ */ new Set();
|
|
@@ -12078,7 +12508,7 @@ async function runScanConfig(targetDir, options) {
|
|
|
12078
12508
|
const scanner = new SecurityScanner(parseSecurityConfig({}));
|
|
12079
12509
|
const results = [];
|
|
12080
12510
|
for (const configFile of CONFIG_FILES) {
|
|
12081
|
-
const result = scanSingleFile(
|
|
12511
|
+
const result = scanSingleFile(join51(targetDir, configFile), targetDir, scanner, options);
|
|
12082
12512
|
if (result) results.push(result);
|
|
12083
12513
|
}
|
|
12084
12514
|
return { exitCode: computeScanExitCode(results), results };
|
|
@@ -12108,7 +12538,7 @@ function formatTextOutput(result) {
|
|
|
12108
12538
|
}
|
|
12109
12539
|
}
|
|
12110
12540
|
function createScanConfigCommand() {
|
|
12111
|
-
const command = new
|
|
12541
|
+
const command = new Command95("scan-config").description(
|
|
12112
12542
|
"Scan CLAUDE.md, AGENTS.md, .gemini/settings.json, and skill.yaml for prompt injection patterns"
|
|
12113
12543
|
).option("--path <dir>", "Target directory to scan (default: cwd)").option("--fix", "Strip high-severity patterns from files in-place").action(async (opts, cmd) => {
|
|
12114
12544
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -12126,7 +12556,7 @@ function createScanConfigCommand() {
|
|
|
12126
12556
|
|
|
12127
12557
|
// src/commands/search.ts
|
|
12128
12558
|
init_dist();
|
|
12129
|
-
import { Command as
|
|
12559
|
+
import { Command as Command96 } from "commander";
|
|
12130
12560
|
function parseFileKinds(raw) {
|
|
12131
12561
|
if (!raw) return void 0;
|
|
12132
12562
|
const set = /* @__PURE__ */ new Set();
|
|
@@ -12147,7 +12577,7 @@ function fmtBytes(n) {
|
|
|
12147
12577
|
return n.toFixed(0);
|
|
12148
12578
|
}
|
|
12149
12579
|
function createSearchCommand() {
|
|
12150
|
-
return new
|
|
12580
|
+
return new Command96("search").description("Full-text search over archived + live session content.").argument(
|
|
12151
12581
|
"<query>",
|
|
12152
12582
|
"FTS5 query (bare words AND-joined; quotes/AND/OR/NOT/column: for advanced syntax)"
|
|
12153
12583
|
).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(
|
|
@@ -12207,9 +12637,9 @@ function createSearchCommand() {
|
|
|
12207
12637
|
}
|
|
12208
12638
|
|
|
12209
12639
|
// src/commands/security-craft.ts
|
|
12210
|
-
import { Command as
|
|
12640
|
+
import { Command as Command97 } from "commander";
|
|
12211
12641
|
function createSecurityCraftCommand() {
|
|
12212
|
-
return new
|
|
12642
|
+
return new Command97("security-craft").description(
|
|
12213
12643
|
"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."
|
|
12214
12644
|
).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) => {
|
|
12215
12645
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -12275,7 +12705,7 @@ ${file}`);
|
|
|
12275
12705
|
}
|
|
12276
12706
|
|
|
12277
12707
|
// src/commands/setup.ts
|
|
12278
|
-
import { Command as
|
|
12708
|
+
import { Command as Command98 } from "commander";
|
|
12279
12709
|
import * as fs38 from "fs";
|
|
12280
12710
|
import * as os9 from "os";
|
|
12281
12711
|
import * as path62 from "path";
|
|
@@ -12620,7 +13050,7 @@ async function runSetup(cwd) {
|
|
|
12620
13050
|
return { steps, success };
|
|
12621
13051
|
}
|
|
12622
13052
|
function createSetupCommand() {
|
|
12623
|
-
return new
|
|
13053
|
+
return new Command98("setup").description("Configure harness environment: slash commands, MCP, and more").action(async () => {
|
|
12624
13054
|
const cwd = process.cwd();
|
|
12625
13055
|
console.log("");
|
|
12626
13056
|
console.log(` ${chalk16.bold("harness setup")}`);
|
|
@@ -12643,7 +13073,7 @@ function createSetupCommand() {
|
|
|
12643
13073
|
|
|
12644
13074
|
// src/commands/share.ts
|
|
12645
13075
|
init_dist3();
|
|
12646
|
-
import { Command as
|
|
13076
|
+
import { Command as Command99 } from "commander";
|
|
12647
13077
|
import * as fs39 from "fs";
|
|
12648
13078
|
import * as path63 from "path";
|
|
12649
13079
|
import { parse as parseYaml3 } from "yaml";
|
|
@@ -12702,16 +13132,16 @@ Create a constraints.yaml in your project root to define what to share.`
|
|
|
12702
13132
|
logger.success(`Bundle written to ${outputPath}`);
|
|
12703
13133
|
}
|
|
12704
13134
|
function createShareCommand() {
|
|
12705
|
-
return new
|
|
13135
|
+
return new Command99("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) => {
|
|
12706
13136
|
await runShareAction(projectPath, options);
|
|
12707
13137
|
});
|
|
12708
13138
|
}
|
|
12709
13139
|
|
|
12710
13140
|
// src/commands/skill/index.ts
|
|
12711
|
-
import { Command as
|
|
13141
|
+
import { Command as Command107 } from "commander";
|
|
12712
13142
|
|
|
12713
13143
|
// src/commands/skill/list.ts
|
|
12714
|
-
import { Command as
|
|
13144
|
+
import { Command as Command100 } from "commander";
|
|
12715
13145
|
import * as fs40 from "fs";
|
|
12716
13146
|
import * as path64 from "path";
|
|
12717
13147
|
import { parse } from "yaml";
|
|
@@ -12814,7 +13244,7 @@ function printSkillsVerbose(skills) {
|
|
|
12814
13244
|
for (const s of skills) printSkillEntry(s);
|
|
12815
13245
|
}
|
|
12816
13246
|
function createListCommand3() {
|
|
12817
|
-
return new
|
|
13247
|
+
return new Command100("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) => {
|
|
12818
13248
|
const globalOpts = cmd.optsWithGlobals();
|
|
12819
13249
|
const skills = collectSkills({ filter: resolveFilter(opts) });
|
|
12820
13250
|
if (globalOpts.json) {
|
|
@@ -12829,13 +13259,13 @@ function createListCommand3() {
|
|
|
12829
13259
|
}
|
|
12830
13260
|
|
|
12831
13261
|
// src/commands/skill/run.ts
|
|
12832
|
-
import { Command as
|
|
13262
|
+
import { Command as Command101 } from "commander";
|
|
12833
13263
|
import * as fs41 from "fs";
|
|
12834
13264
|
import * as path65 from "path";
|
|
12835
13265
|
import { parse as parse2 } from "yaml";
|
|
12836
13266
|
|
|
12837
13267
|
// src/skill/complexity.ts
|
|
12838
|
-
import { execFileSync as
|
|
13268
|
+
import { execFileSync as execFileSync11 } from "child_process";
|
|
12839
13269
|
function evaluateSignals(signals) {
|
|
12840
13270
|
if (signals.fileCount >= 3) return "thorough";
|
|
12841
13271
|
if (signals.newDir) return "thorough";
|
|
@@ -12847,17 +13277,17 @@ function evaluateSignals(signals) {
|
|
|
12847
13277
|
}
|
|
12848
13278
|
function detectComplexity(projectPath) {
|
|
12849
13279
|
try {
|
|
12850
|
-
const base =
|
|
13280
|
+
const base = execFileSync11("git", ["merge-base", "HEAD", "main"], {
|
|
12851
13281
|
cwd: projectPath,
|
|
12852
13282
|
encoding: "utf-8",
|
|
12853
13283
|
stdio: ["pipe", "pipe", "pipe"]
|
|
12854
13284
|
}).trim();
|
|
12855
|
-
const diffFiles =
|
|
13285
|
+
const diffFiles = execFileSync11("git", ["diff", "--name-only", base], {
|
|
12856
13286
|
cwd: projectPath,
|
|
12857
13287
|
encoding: "utf-8",
|
|
12858
13288
|
stdio: ["pipe", "pipe", "pipe"]
|
|
12859
13289
|
}).trim().split("\n").filter(Boolean);
|
|
12860
|
-
const diffStat =
|
|
13290
|
+
const diffStat = execFileSync11("git", ["diff", "--stat", base], {
|
|
12861
13291
|
cwd: projectPath,
|
|
12862
13292
|
encoding: "utf-8",
|
|
12863
13293
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -12977,7 +13407,7 @@ function resolvePhaseState(metadata, projectPath, phase) {
|
|
|
12977
13407
|
}
|
|
12978
13408
|
async function appendProjectState(content, metadata, projectPath, hasPathOpt) {
|
|
12979
13409
|
if (!metadata?.state.persistent || !hasPathOpt) return content;
|
|
12980
|
-
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-
|
|
13410
|
+
const { readHarnessState: readHarnessState2, isEmptyHarnessState } = await import("./state-events-H3GGBKTB.js");
|
|
12981
13411
|
const stateResult = await readHarnessState2(projectPath);
|
|
12982
13412
|
if (!stateResult.ok || isEmptyHarnessState(stateResult.value)) return content;
|
|
12983
13413
|
const stateContent = JSON.stringify(stateResult.value, null, 2);
|
|
@@ -13043,14 +13473,14 @@ async function runSkill(name, opts) {
|
|
|
13043
13473
|
process.exit(ExitCode.SUCCESS);
|
|
13044
13474
|
}
|
|
13045
13475
|
function createRunCommand3() {
|
|
13046
|
-
return new
|
|
13476
|
+
return new Command101("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(
|
|
13047
13477
|
"--backend <name>",
|
|
13048
13478
|
"Spec B: one-shot routing override forwarded to the orchestrator as HARNESS_BACKEND_OVERRIDE"
|
|
13049
13479
|
).action(async (name, opts) => runSkill(name, opts));
|
|
13050
13480
|
}
|
|
13051
13481
|
|
|
13052
13482
|
// src/commands/skill/validate.ts
|
|
13053
|
-
import { Command as
|
|
13483
|
+
import { Command as Command102 } from "commander";
|
|
13054
13484
|
import * as fs42 from "fs";
|
|
13055
13485
|
import * as path66 from "path";
|
|
13056
13486
|
import { parse as parse3 } from "yaml";
|
|
@@ -13114,7 +13544,7 @@ function validateSkillEntry(name, skillsDir, errors) {
|
|
|
13114
13544
|
}
|
|
13115
13545
|
}
|
|
13116
13546
|
function createValidateCommand2() {
|
|
13117
|
-
return new
|
|
13547
|
+
return new Command102("validate").description("Validate all skill.yaml files and SKILL.md structure").action(async (_opts, cmd) => {
|
|
13118
13548
|
const globalOpts = cmd.optsWithGlobals();
|
|
13119
13549
|
const skillsDir = resolveSkillsDir();
|
|
13120
13550
|
if (!fs42.existsSync(skillsDir)) {
|
|
@@ -13144,7 +13574,7 @@ function createValidateCommand2() {
|
|
|
13144
13574
|
}
|
|
13145
13575
|
|
|
13146
13576
|
// src/commands/skill/info.ts
|
|
13147
|
-
import { Command as
|
|
13577
|
+
import { Command as Command103 } from "commander";
|
|
13148
13578
|
import * as fs43 from "fs";
|
|
13149
13579
|
import * as path67 from "path";
|
|
13150
13580
|
import { parse as parse4 } from "yaml";
|
|
@@ -13189,7 +13619,7 @@ function printSkillInfo(skill) {
|
|
|
13189
13619
|
console.log(`Persistent: ${skill.state.persistent}`);
|
|
13190
13620
|
}
|
|
13191
13621
|
function createInfoCommand() {
|
|
13192
|
-
return new
|
|
13622
|
+
return new Command103("info").description("Show metadata for a skill").argument("<name>", "Skill name (e.g., harness-tdd)").action(async (name, _opts, cmd) => {
|
|
13193
13623
|
const globalOpts = cmd.optsWithGlobals();
|
|
13194
13624
|
const loaded = loadSkillMetadata2(name);
|
|
13195
13625
|
if (!loaded.ok) {
|
|
@@ -13206,7 +13636,7 @@ function createInfoCommand() {
|
|
|
13206
13636
|
}
|
|
13207
13637
|
|
|
13208
13638
|
// src/commands/skill/search.ts
|
|
13209
|
-
import { Command as
|
|
13639
|
+
import { Command as Command104 } from "commander";
|
|
13210
13640
|
async function runSearch(query, opts) {
|
|
13211
13641
|
const results = await searchNpmRegistry(query, opts.registry);
|
|
13212
13642
|
return results.filter((r) => {
|
|
@@ -13251,13 +13681,13 @@ async function runSearchAction(query, opts, globalOpts) {
|
|
|
13251
13681
|
}
|
|
13252
13682
|
}
|
|
13253
13683
|
function createSearchCommand2() {
|
|
13254
|
-
return new
|
|
13684
|
+
return new Command104("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) => {
|
|
13255
13685
|
await runSearchAction(query, opts, cmd.optsWithGlobals());
|
|
13256
13686
|
});
|
|
13257
13687
|
}
|
|
13258
13688
|
|
|
13259
13689
|
// src/commands/skill/create.ts
|
|
13260
|
-
import { Command as
|
|
13690
|
+
import { Command as Command105 } from "commander";
|
|
13261
13691
|
import * as path68 from "path";
|
|
13262
13692
|
import * as fs44 from "fs";
|
|
13263
13693
|
import YAML from "yaml";
|
|
@@ -13377,7 +13807,7 @@ Next steps:`);
|
|
|
13377
13807
|
logger.info(` 3. Run: harness skills publish`);
|
|
13378
13808
|
}
|
|
13379
13809
|
function createCreateCommand() {
|
|
13380
|
-
return new
|
|
13810
|
+
return new Command105("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) => {
|
|
13381
13811
|
const globalOpts = cmd.optsWithGlobals();
|
|
13382
13812
|
try {
|
|
13383
13813
|
const result = runCreate(name, {
|
|
@@ -13400,10 +13830,10 @@ function createCreateCommand() {
|
|
|
13400
13830
|
}
|
|
13401
13831
|
|
|
13402
13832
|
// src/commands/skill/publish.ts
|
|
13403
|
-
import { Command as
|
|
13833
|
+
import { Command as Command106 } from "commander";
|
|
13404
13834
|
import * as fs46 from "fs";
|
|
13405
13835
|
import * as path70 from "path";
|
|
13406
|
-
import { execFileSync as
|
|
13836
|
+
import { execFileSync as execFileSync12 } from "child_process";
|
|
13407
13837
|
|
|
13408
13838
|
// src/registry/validator.ts
|
|
13409
13839
|
import * as fs45 from "fs";
|
|
@@ -13557,7 +13987,7 @@ ${skillMdContent}`;
|
|
|
13557
13987
|
if (opts.registry) {
|
|
13558
13988
|
publishArgs.push("--registry", opts.registry);
|
|
13559
13989
|
}
|
|
13560
|
-
|
|
13990
|
+
execFileSync12("npm", publishArgs, {
|
|
13561
13991
|
cwd: skillDir,
|
|
13562
13992
|
stdio: "pipe",
|
|
13563
13993
|
timeout: 6e4
|
|
@@ -13569,7 +13999,7 @@ ${skillMdContent}`;
|
|
|
13569
13999
|
};
|
|
13570
14000
|
}
|
|
13571
14001
|
function createPublishCommand() {
|
|
13572
|
-
return new
|
|
14002
|
+
return new Command106("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) => {
|
|
13573
14003
|
const globalOpts = cmd.optsWithGlobals();
|
|
13574
14004
|
const skillDir = opts.dir || process.cwd();
|
|
13575
14005
|
try {
|
|
@@ -13594,7 +14024,7 @@ function createPublishCommand() {
|
|
|
13594
14024
|
|
|
13595
14025
|
// src/commands/skill/index.ts
|
|
13596
14026
|
function createSkillCommand() {
|
|
13597
|
-
const command = new
|
|
14027
|
+
const command = new Command107("skill").description("Skill management commands");
|
|
13598
14028
|
command.addCommand(createListCommand3());
|
|
13599
14029
|
command.addCommand(createRunCommand3());
|
|
13600
14030
|
command.addCommand(createValidateCommand2());
|
|
@@ -13607,7 +14037,7 @@ function createSkillCommand() {
|
|
|
13607
14037
|
|
|
13608
14038
|
// src/commands/snapshot.ts
|
|
13609
14039
|
init_dist3();
|
|
13610
|
-
import { Command as
|
|
14040
|
+
import { Command as Command108 } from "commander";
|
|
13611
14041
|
import { execSync as execSync5 } from "child_process";
|
|
13612
14042
|
import chalk17 from "chalk";
|
|
13613
14043
|
function getCommitHash3(cwd) {
|
|
@@ -13789,7 +14219,7 @@ function registerListCommand2(parent) {
|
|
|
13789
14219
|
});
|
|
13790
14220
|
}
|
|
13791
14221
|
function createSnapshotCommand() {
|
|
13792
|
-
const command = new
|
|
14222
|
+
const command = new Command108("snapshot").description("Architecture timeline snapshot commands");
|
|
13793
14223
|
registerCaptureCommand(command);
|
|
13794
14224
|
registerTrendsCommand(command);
|
|
13795
14225
|
registerListCommand2(command);
|
|
@@ -13797,9 +14227,9 @@ function createSnapshotCommand() {
|
|
|
13797
14227
|
}
|
|
13798
14228
|
|
|
13799
14229
|
// src/commands/spec-craft.ts
|
|
13800
|
-
import { Command as
|
|
14230
|
+
import { Command as Command109 } from "commander";
|
|
13801
14231
|
function createSpecCraftCommand() {
|
|
13802
|
-
return new
|
|
14232
|
+
return new Command109("spec-craft").description(
|
|
13803
14233
|
"LLM-judgment critique of spec quality (proposals + ADRs). Second craft-pipeline ceiling skill; 7 seed rubrics from the spec-quality canon. Per-section critique."
|
|
13804
14234
|
).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) => {
|
|
13805
14235
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -13867,7 +14297,7 @@ ${file}`);
|
|
|
13867
14297
|
|
|
13868
14298
|
// src/commands/stale-constraints.ts
|
|
13869
14299
|
init_dist3();
|
|
13870
|
-
import { Command as
|
|
14300
|
+
import { Command as Command110 } from "commander";
|
|
13871
14301
|
import chalk18 from "chalk";
|
|
13872
14302
|
var CATEGORIES = [
|
|
13873
14303
|
"circular-deps",
|
|
@@ -13954,7 +14384,7 @@ async function runStaleConstraintsCommand(options) {
|
|
|
13954
14384
|
);
|
|
13955
14385
|
}
|
|
13956
14386
|
function createStaleConstraintsCommand() {
|
|
13957
|
-
return new
|
|
14387
|
+
return new Command110("stale-constraints").description(
|
|
13958
14388
|
"Detect architectural constraints not violated within a window (candidates for removal)"
|
|
13959
14389
|
).option(
|
|
13960
14390
|
"--window <days>",
|
|
@@ -13972,10 +14402,10 @@ function createStaleConstraintsCommand() {
|
|
|
13972
14402
|
}
|
|
13973
14403
|
|
|
13974
14404
|
// src/commands/state/index.ts
|
|
13975
|
-
import { Command as
|
|
14405
|
+
import { Command as Command115 } from "commander";
|
|
13976
14406
|
|
|
13977
14407
|
// src/commands/state/show.ts
|
|
13978
|
-
import { Command as
|
|
14408
|
+
import { Command as Command111 } from "commander";
|
|
13979
14409
|
import * as path71 from "path";
|
|
13980
14410
|
function printStateText(state, stream) {
|
|
13981
14411
|
if (stream) console.log(`Stream: ${stream}`);
|
|
@@ -14000,7 +14430,7 @@ function printStateProgress(progress) {
|
|
|
14000
14430
|
}
|
|
14001
14431
|
}
|
|
14002
14432
|
function createShowCommand() {
|
|
14003
|
-
return new
|
|
14433
|
+
return new Command111("show").description("Show current project state").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").action(async (opts, cmd) => {
|
|
14004
14434
|
const globalOpts = cmd.optsWithGlobals();
|
|
14005
14435
|
const result = await readHarnessState(path71.resolve(opts.path), { stream: opts.stream });
|
|
14006
14436
|
if (!result.ok) {
|
|
@@ -14018,7 +14448,7 @@ function createShowCommand() {
|
|
|
14018
14448
|
|
|
14019
14449
|
// src/commands/state/reset.ts
|
|
14020
14450
|
init_dist3();
|
|
14021
|
-
import { Command as
|
|
14451
|
+
import { Command as Command112 } from "commander";
|
|
14022
14452
|
import * as path72 from "path";
|
|
14023
14453
|
import * as readline3 from "readline";
|
|
14024
14454
|
async function confirmReset() {
|
|
@@ -14044,15 +14474,15 @@ async function runReset(opts) {
|
|
|
14044
14474
|
process.exit(ExitCode.SUCCESS);
|
|
14045
14475
|
}
|
|
14046
14476
|
function createResetCommand() {
|
|
14047
|
-
return new
|
|
14477
|
+
return new Command112("reset").description("Reset project state (truncates the event log to a fresh default)").option("--path <path>", "Project root path", ".").option("--stream <name>", "Target a specific stream").option("--yes", "Skip confirmation prompt").action(async (opts) => runReset(opts));
|
|
14048
14478
|
}
|
|
14049
14479
|
|
|
14050
14480
|
// src/commands/state/learn.ts
|
|
14051
14481
|
init_dist3();
|
|
14052
|
-
import { Command as
|
|
14482
|
+
import { Command as Command113 } from "commander";
|
|
14053
14483
|
import * as path73 from "path";
|
|
14054
14484
|
function createLearnCommand() {
|
|
14055
|
-
return new
|
|
14485
|
+
return new Command113("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) => {
|
|
14056
14486
|
const projectPath = path73.resolve(opts.path);
|
|
14057
14487
|
const result = await appendLearning(projectPath, message, void 0, void 0, opts.stream);
|
|
14058
14488
|
if (!result.ok) {
|
|
@@ -14067,7 +14497,7 @@ function createLearnCommand() {
|
|
|
14067
14497
|
|
|
14068
14498
|
// src/commands/state/streams.ts
|
|
14069
14499
|
init_dist3();
|
|
14070
|
-
import { Command as
|
|
14500
|
+
import { Command as Command114 } from "commander";
|
|
14071
14501
|
import * as path74 from "path";
|
|
14072
14502
|
async function runListStreams(opts, globalOpts) {
|
|
14073
14503
|
const projectPath = path74.resolve(opts.path);
|
|
@@ -14118,7 +14548,7 @@ async function runActivateStream(name, opts) {
|
|
|
14118
14548
|
process.exit(ExitCode.SUCCESS);
|
|
14119
14549
|
}
|
|
14120
14550
|
function createStreamsCommand() {
|
|
14121
|
-
const command = new
|
|
14551
|
+
const command = new Command114("streams").description("Manage state streams");
|
|
14122
14552
|
command.command("list").description("List all known streams").option("--path <path>", "Project root path", ".").action(async (opts, cmd) => runListStreams(opts, cmd.optsWithGlobals()));
|
|
14123
14553
|
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));
|
|
14124
14554
|
command.command("archive <name>").description("Archive a stream").option("--path <path>", "Project root path", ".").action(async (name, opts) => runArchiveStream(name, opts));
|
|
@@ -14128,7 +14558,7 @@ function createStreamsCommand() {
|
|
|
14128
14558
|
|
|
14129
14559
|
// src/commands/state/index.ts
|
|
14130
14560
|
function createStateCommand() {
|
|
14131
|
-
const command = new
|
|
14561
|
+
const command = new Command115("state").description("Project state management commands");
|
|
14132
14562
|
command.addCommand(createShowCommand());
|
|
14133
14563
|
command.addCommand(createResetCommand());
|
|
14134
14564
|
command.addCommand(createLearnCommand());
|
|
@@ -14139,7 +14569,7 @@ function createStateCommand() {
|
|
|
14139
14569
|
// src/commands/sync-analyses.ts
|
|
14140
14570
|
import * as fs47 from "fs";
|
|
14141
14571
|
import * as path75 from "path";
|
|
14142
|
-
import { Command as
|
|
14572
|
+
import { Command as Command116 } from "commander";
|
|
14143
14573
|
init_dist3();
|
|
14144
14574
|
function isValidRecord(record) {
|
|
14145
14575
|
return typeof record.issueId === "string" && !!record.issueId && typeof record.identifier === "string" && !!record.identifier && typeof record.analyzedAt === "string" && !/[/\\]/.test(record.issueId);
|
|
@@ -14264,7 +14694,7 @@ async function runSyncAnalyses(opts) {
|
|
|
14264
14694
|
return;
|
|
14265
14695
|
}
|
|
14266
14696
|
const { AnalysisArchive, loadPublishedIndex: loadPublishedIndex2, savePublishedIndex: savePublishedIndex2 } = await import("@harness-engineering/orchestrator");
|
|
14267
|
-
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-
|
|
14697
|
+
const { GitHubIssuesSyncAdapter: GitHubIssuesSyncAdapter2 } = await import("./dist-SOKOW7BC.js");
|
|
14268
14698
|
const adapter = new GitHubIssuesSyncAdapter2({ token, config: trackerConfig });
|
|
14269
14699
|
const archive = new AnalysisArchive(path75.join(projectPath, ".harness", "analyses"));
|
|
14270
14700
|
const publishedIndex = loadPublishedIndex2(projectPath);
|
|
@@ -14286,7 +14716,7 @@ async function runSyncAnalyses(opts) {
|
|
|
14286
14716
|
}
|
|
14287
14717
|
}
|
|
14288
14718
|
function createSyncAnalysesCommand() {
|
|
14289
|
-
const command = new
|
|
14719
|
+
const command = new Command116("sync-analyses").description(
|
|
14290
14720
|
"Pull published intelligence analyses from the external issue tracker into the local .harness/analyses/ directory"
|
|
14291
14721
|
).option("-d, --dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
14292
14722
|
try {
|
|
@@ -14300,7 +14730,7 @@ function createSyncAnalysesCommand() {
|
|
|
14300
14730
|
}
|
|
14301
14731
|
|
|
14302
14732
|
// src/commands/sync-main.ts
|
|
14303
|
-
import { Command as
|
|
14733
|
+
import { Command as Command117 } from "commander";
|
|
14304
14734
|
import * as path76 from "path";
|
|
14305
14735
|
import {
|
|
14306
14736
|
syncMain as defaultSyncMain
|
|
@@ -14331,7 +14761,7 @@ function formatHuman2(r) {
|
|
|
14331
14761
|
}
|
|
14332
14762
|
}
|
|
14333
14763
|
function createSyncMainCommand() {
|
|
14334
|
-
return new
|
|
14764
|
+
return new Command117("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) => {
|
|
14335
14765
|
const cwd = path76.resolve(opts.path ?? ".");
|
|
14336
14766
|
const exitCode = await runSyncMain({ cwd, json: Boolean(opts.json) });
|
|
14337
14767
|
process.exit(exitCode);
|
|
@@ -14340,7 +14770,7 @@ function createSyncMainCommand() {
|
|
|
14340
14770
|
|
|
14341
14771
|
// src/commands/taint.ts
|
|
14342
14772
|
init_dist3();
|
|
14343
|
-
import { Command as
|
|
14773
|
+
import { Command as Command118 } from "commander";
|
|
14344
14774
|
function getProjectRoot() {
|
|
14345
14775
|
return process.cwd();
|
|
14346
14776
|
}
|
|
@@ -14412,18 +14842,18 @@ function registerStatusCommand(taint) {
|
|
|
14412
14842
|
});
|
|
14413
14843
|
}
|
|
14414
14844
|
function createTaintCommand() {
|
|
14415
|
-
const taint = new
|
|
14845
|
+
const taint = new Command118("taint").description("Manage sentinel session taint state");
|
|
14416
14846
|
registerClearCommand(taint);
|
|
14417
14847
|
registerStatusCommand(taint);
|
|
14418
14848
|
return taint;
|
|
14419
14849
|
}
|
|
14420
14850
|
|
|
14421
14851
|
// src/commands/telemetry/index.ts
|
|
14422
|
-
import { Command as
|
|
14852
|
+
import { Command as Command122 } from "commander";
|
|
14423
14853
|
|
|
14424
14854
|
// src/commands/telemetry/identify.ts
|
|
14425
14855
|
init_dist3();
|
|
14426
|
-
import { Command as
|
|
14856
|
+
import { Command as Command119 } from "commander";
|
|
14427
14857
|
import * as fs48 from "fs";
|
|
14428
14858
|
import * as path77 from "path";
|
|
14429
14859
|
function telemetryFilePath(cwd) {
|
|
@@ -14447,7 +14877,7 @@ function printIdentity(identity) {
|
|
|
14447
14877
|
}
|
|
14448
14878
|
}
|
|
14449
14879
|
function createIdentifyCommand() {
|
|
14450
|
-
const cmd = new
|
|
14880
|
+
const cmd = new Command119("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) => {
|
|
14451
14881
|
const cwd = process.cwd();
|
|
14452
14882
|
if (opts.clear) {
|
|
14453
14883
|
writeTelemetryFile(cwd, { identity: {} });
|
|
@@ -14472,7 +14902,7 @@ function createIdentifyCommand() {
|
|
|
14472
14902
|
|
|
14473
14903
|
// src/commands/telemetry/status.ts
|
|
14474
14904
|
init_dist3();
|
|
14475
|
-
import { Command as
|
|
14905
|
+
import { Command as Command120 } from "commander";
|
|
14476
14906
|
function gatherEnvOverrides() {
|
|
14477
14907
|
const overrides = {};
|
|
14478
14908
|
if (process.env.DO_NOT_TRACK) overrides.DO_NOT_TRACK = process.env.DO_NOT_TRACK;
|
|
@@ -14514,7 +14944,7 @@ function printHumanStatus(result) {
|
|
|
14514
14944
|
printEnvOverrides(result.envOverrides);
|
|
14515
14945
|
}
|
|
14516
14946
|
function createStatusCommand() {
|
|
14517
|
-
const cmd = new
|
|
14947
|
+
const cmd = new Command120("status").description("Show current telemetry consent state, install ID, and identity").option("--json", "Output as JSON").action((opts) => {
|
|
14518
14948
|
const cwd = process.cwd();
|
|
14519
14949
|
const envOverrides = gatherEnvOverrides();
|
|
14520
14950
|
const consent = resolveConsent(cwd, void 0);
|
|
@@ -14547,11 +14977,11 @@ function createStatusCommand() {
|
|
|
14547
14977
|
|
|
14548
14978
|
// src/commands/telemetry/test.ts
|
|
14549
14979
|
init_dist3();
|
|
14550
|
-
import { Command as
|
|
14980
|
+
import { Command as Command121 } from "commander";
|
|
14551
14981
|
|
|
14552
14982
|
// src/bin/command-telemetry.ts
|
|
14553
|
-
import { existsSync as existsSync44, readFileSync as readFileSync32, mkdirSync as
|
|
14554
|
-
import { join as
|
|
14983
|
+
import { existsSync as existsSync44, readFileSync as readFileSync32, mkdirSync as mkdirSync24, appendFileSync as appendFileSync2, writeFileSync as writeFileSync29 } from "fs";
|
|
14984
|
+
import { join as join65, parse as parsePath, resolve as resolve47 } from "path";
|
|
14555
14985
|
import { spawn as spawn2 } from "child_process";
|
|
14556
14986
|
var POSTHOG_API_KEY = "phc_wNTdCMcfJXZPgdNeDociZW6vwoGGo4nb7vqEfWThFfsG";
|
|
14557
14987
|
var EXCLUDED_COMMANDS = /* @__PURE__ */ new Set(["help", "completion"]);
|
|
@@ -14559,7 +14989,7 @@ function findProjectRoot(cwd) {
|
|
|
14559
14989
|
let dir = resolve47(cwd);
|
|
14560
14990
|
const { root } = parsePath(dir);
|
|
14561
14991
|
while (dir !== root) {
|
|
14562
|
-
if (existsSync44(
|
|
14992
|
+
if (existsSync44(join65(dir, "harness.config.json"))) return dir;
|
|
14563
14993
|
dir = resolve47(dir, "..");
|
|
14564
14994
|
}
|
|
14565
14995
|
return cwd;
|
|
@@ -14597,8 +15027,8 @@ function resolveCommandName(cmd) {
|
|
|
14597
15027
|
}
|
|
14598
15028
|
function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
14599
15029
|
try {
|
|
14600
|
-
const metricsDir =
|
|
14601
|
-
|
|
15030
|
+
const metricsDir = join65(cwd, ".harness", "metrics");
|
|
15031
|
+
mkdirSync24(metricsDir, { recursive: true });
|
|
14602
15032
|
const record = {
|
|
14603
15033
|
skill: command,
|
|
14604
15034
|
session: `cli-${Date.now()}`,
|
|
@@ -14607,13 +15037,13 @@ function writeCommandRecordSync(cwd, command, duration, outcome) {
|
|
|
14607
15037
|
outcome,
|
|
14608
15038
|
phasesReached: []
|
|
14609
15039
|
};
|
|
14610
|
-
const adoptionFile =
|
|
14611
|
-
|
|
15040
|
+
const adoptionFile = join65(metricsDir, "adoption.jsonl");
|
|
15041
|
+
appendFileSync2(adoptionFile, JSON.stringify(record) + "\n");
|
|
14612
15042
|
} catch {
|
|
14613
15043
|
}
|
|
14614
15044
|
}
|
|
14615
15045
|
function isTelemetryDisabledByConfig(cwd) {
|
|
14616
|
-
const configPath =
|
|
15046
|
+
const configPath = join65(cwd, "harness.config.json");
|
|
14617
15047
|
if (!existsSync44(configPath)) return false;
|
|
14618
15048
|
try {
|
|
14619
15049
|
const config = JSON.parse(readFileSync32(configPath, "utf-8"));
|
|
@@ -14628,10 +15058,10 @@ function isTelemetryOptedOut(cwd) {
|
|
|
14628
15058
|
}
|
|
14629
15059
|
function flushTelemetryBackground(cwd) {
|
|
14630
15060
|
try {
|
|
14631
|
-
const adoptionFile =
|
|
15061
|
+
const adoptionFile = join65(cwd, ".harness", "metrics", "adoption.jsonl");
|
|
14632
15062
|
if (!existsSync44(adoptionFile)) return;
|
|
14633
15063
|
if (isTelemetryOptedOut(cwd)) return;
|
|
14634
|
-
const reporterPath =
|
|
15064
|
+
const reporterPath = join65(cwd, ".harness", "hooks", "telemetry-reporter.js");
|
|
14635
15065
|
if (!existsSync44(reporterPath)) return;
|
|
14636
15066
|
const child = spawn2(process.execPath, [reporterPath], {
|
|
14637
15067
|
cwd,
|
|
@@ -14669,7 +15099,7 @@ function reportTestSuccess(status, distinctId, identity) {
|
|
|
14669
15099
|
if (identity.team) logger.info(` team: ${identity.team}`);
|
|
14670
15100
|
}
|
|
14671
15101
|
function createTestCommand() {
|
|
14672
|
-
return new
|
|
15102
|
+
return new Command121("test").description("Send a test event to PostHog and verify connectivity").action(async () => {
|
|
14673
15103
|
const cwd = process.cwd();
|
|
14674
15104
|
const consent = resolveConsent(cwd, void 0);
|
|
14675
15105
|
if (!consent.allowed) {
|
|
@@ -14706,7 +15136,7 @@ function createTestCommand() {
|
|
|
14706
15136
|
|
|
14707
15137
|
// src/commands/telemetry/index.ts
|
|
14708
15138
|
function createTelemetryCommand() {
|
|
14709
|
-
const command = new
|
|
15139
|
+
const command = new Command122("telemetry").description("Telemetry identity and status management");
|
|
14710
15140
|
command.addCommand(createIdentifyCommand());
|
|
14711
15141
|
command.addCommand(createStatusCommand());
|
|
14712
15142
|
command.addCommand(createTestCommand());
|
|
@@ -14714,9 +15144,9 @@ function createTelemetryCommand() {
|
|
|
14714
15144
|
}
|
|
14715
15145
|
|
|
14716
15146
|
// src/commands/test-craft.ts
|
|
14717
|
-
import { Command as
|
|
15147
|
+
import { Command as Command123 } from "commander";
|
|
14718
15148
|
function createTestCraftCommand() {
|
|
14719
|
-
return new
|
|
15149
|
+
return new Command123("test-craft").description(
|
|
14720
15150
|
"LLM-judgment critique of test quality across vitest/jest/mocha/playwright. Fourth craft-pipeline ceiling skill. Per-test critique with best-effort source pairing."
|
|
14721
15151
|
).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) => {
|
|
14722
15152
|
const globalOpts = cmd.optsWithGlobals();
|
|
@@ -14786,7 +15216,7 @@ ${file}`);
|
|
|
14786
15216
|
}
|
|
14787
15217
|
|
|
14788
15218
|
// src/commands/traceability.ts
|
|
14789
|
-
import { Command as
|
|
15219
|
+
import { Command as Command124 } from "commander";
|
|
14790
15220
|
import chalk19 from "chalk";
|
|
14791
15221
|
function confidenceLabel(maxConfidence) {
|
|
14792
15222
|
if (maxConfidence >= 0.8) return "explicit";
|
|
@@ -14926,7 +15356,7 @@ async function runTraceability(options) {
|
|
|
14926
15356
|
process.exit(ExitCode.SUCCESS);
|
|
14927
15357
|
}
|
|
14928
15358
|
function createTraceabilityCommand() {
|
|
14929
|
-
return new
|
|
15359
|
+
return new Command124("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) => {
|
|
14930
15360
|
const globalOpts = cmd.optsWithGlobals();
|
|
14931
15361
|
await runTraceability({
|
|
14932
15362
|
spec: opts.spec,
|
|
@@ -14940,7 +15370,7 @@ function createTraceabilityCommand() {
|
|
|
14940
15370
|
|
|
14941
15371
|
// src/commands/uninstall.ts
|
|
14942
15372
|
import * as path78 from "path";
|
|
14943
|
-
import { Command as
|
|
15373
|
+
import { Command as Command125 } from "commander";
|
|
14944
15374
|
async function runUninstall(skillName, options) {
|
|
14945
15375
|
const packageName = resolvePackageName(skillName);
|
|
14946
15376
|
const shortName = extractSkillName(packageName);
|
|
@@ -14977,7 +15407,7 @@ async function runUninstall(skillName, options) {
|
|
|
14977
15407
|
return result;
|
|
14978
15408
|
}
|
|
14979
15409
|
function createUninstallCommand() {
|
|
14980
|
-
const cmd = new
|
|
15410
|
+
const cmd = new Command125("uninstall");
|
|
14981
15411
|
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) => {
|
|
14982
15412
|
try {
|
|
14983
15413
|
const result = await runUninstall(skill, opts);
|
|
@@ -14999,7 +15429,7 @@ function createUninstallCommand() {
|
|
|
14999
15429
|
init_dist3();
|
|
15000
15430
|
import * as fs49 from "fs/promises";
|
|
15001
15431
|
import * as path79 from "path";
|
|
15002
|
-
import { Command as
|
|
15432
|
+
import { Command as Command126 } from "commander";
|
|
15003
15433
|
async function runUninstallConstraints(options) {
|
|
15004
15434
|
const { packageName, configPath, lockfilePath } = options;
|
|
15005
15435
|
const lockfileResult = await readLockfile(lockfilePath);
|
|
@@ -15056,7 +15486,7 @@ async function runUninstallConstraints(options) {
|
|
|
15056
15486
|
};
|
|
15057
15487
|
}
|
|
15058
15488
|
function createUninstallConstraintsCommand() {
|
|
15059
|
-
const cmd = new
|
|
15489
|
+
const cmd = new Command126("uninstall-constraints");
|
|
15060
15490
|
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) => {
|
|
15061
15491
|
let configPath;
|
|
15062
15492
|
if (opts.config) {
|
|
@@ -15096,10 +15526,10 @@ function createUninstallConstraintsCommand() {
|
|
|
15096
15526
|
|
|
15097
15527
|
// src/commands/update.ts
|
|
15098
15528
|
init_dist3();
|
|
15099
|
-
import { Command as
|
|
15100
|
-
import { execFile as execFile2, execFileSync as
|
|
15529
|
+
import { Command as Command127 } from "commander";
|
|
15530
|
+
import { execFile as execFile2, execFileSync as execFileSync13 } from "child_process";
|
|
15101
15531
|
import { realpathSync, existsSync as existsSync45, readFileSync as readFileSync33 } from "fs";
|
|
15102
|
-
import { dirname as
|
|
15532
|
+
import { dirname as dirname26, join as join68 } from "path";
|
|
15103
15533
|
import { promisify as promisify2 } from "util";
|
|
15104
15534
|
import readline4 from "readline";
|
|
15105
15535
|
import chalk20 from "chalk";
|
|
@@ -15139,7 +15569,7 @@ async function getLatestVersionAsync(pkg) {
|
|
|
15139
15569
|
function getInstalledVersions(pm, packages) {
|
|
15140
15570
|
const versions = {};
|
|
15141
15571
|
try {
|
|
15142
|
-
const output =
|
|
15572
|
+
const output = execFileSync13(pm, ["list", "-g", "--json"], {
|
|
15143
15573
|
encoding: "utf-8",
|
|
15144
15574
|
timeout: 15e3
|
|
15145
15575
|
});
|
|
@@ -15160,7 +15590,7 @@ function getInstalledVersions(pm, packages) {
|
|
|
15160
15590
|
}
|
|
15161
15591
|
function getInstalledPackages(pm) {
|
|
15162
15592
|
try {
|
|
15163
|
-
const output =
|
|
15593
|
+
const output = execFileSync13(pm, ["list", "-g", "--json"], {
|
|
15164
15594
|
encoding: "utf-8",
|
|
15165
15595
|
timeout: 15e3
|
|
15166
15596
|
});
|
|
@@ -15188,16 +15618,16 @@ function prompt3(question) {
|
|
|
15188
15618
|
});
|
|
15189
15619
|
}
|
|
15190
15620
|
function findPackageDir(resolvedBin) {
|
|
15191
|
-
let dir =
|
|
15621
|
+
let dir = dirname26(resolvedBin);
|
|
15192
15622
|
for (let i = 0; i < 6; i++) {
|
|
15193
|
-
if (existsSync45(
|
|
15623
|
+
if (existsSync45(join68(dir, "package.json"))) {
|
|
15194
15624
|
try {
|
|
15195
|
-
const pkg = JSON.parse(readFileSync33(
|
|
15625
|
+
const pkg = JSON.parse(readFileSync33(join68(dir, "package.json"), "utf-8"));
|
|
15196
15626
|
if (pkg?.name === CLI_PACKAGE) return dir;
|
|
15197
15627
|
} catch {
|
|
15198
15628
|
}
|
|
15199
15629
|
}
|
|
15200
|
-
const parent =
|
|
15630
|
+
const parent = dirname26(dir);
|
|
15201
15631
|
if (parent === dir) return null;
|
|
15202
15632
|
dir = parent;
|
|
15203
15633
|
}
|
|
@@ -15215,7 +15645,7 @@ function findAllInstalls() {
|
|
|
15215
15645
|
const args = isWindows ? ["harness"] : ["-a", "harness"];
|
|
15216
15646
|
let output;
|
|
15217
15647
|
try {
|
|
15218
|
-
const raw =
|
|
15648
|
+
const raw = execFileSync13(cmd, args, { encoding: "utf-8", timeout: 5e3 });
|
|
15219
15649
|
if (typeof raw !== "string") return [];
|
|
15220
15650
|
output = raw;
|
|
15221
15651
|
} catch {
|
|
@@ -15237,7 +15667,7 @@ function findAllInstalls() {
|
|
|
15237
15667
|
if (!packageDir) continue;
|
|
15238
15668
|
let version = null;
|
|
15239
15669
|
try {
|
|
15240
|
-
const pkg = JSON.parse(readFileSync33(
|
|
15670
|
+
const pkg = JSON.parse(readFileSync33(join68(packageDir, "package.json"), "utf-8"));
|
|
15241
15671
|
if (typeof pkg?.version === "string") version = pkg.version;
|
|
15242
15672
|
} catch {
|
|
15243
15673
|
}
|
|
@@ -15292,7 +15722,7 @@ async function offerCleanupOfOtherInstalls(activeDir) {
|
|
|
15292
15722
|
}
|
|
15293
15723
|
try {
|
|
15294
15724
|
logger.info(`Uninstalling: ${cmd.display}`);
|
|
15295
|
-
|
|
15725
|
+
execFileSync13(cmd.bin, cmd.args, { stdio: "inherit", timeout: 12e4 });
|
|
15296
15726
|
} catch {
|
|
15297
15727
|
logger.warn(`Uninstall failed for ${cmd.install.binPath}.`);
|
|
15298
15728
|
console.log(` Run manually: ${chalk20.cyan(cmd.display)}`);
|
|
@@ -15330,10 +15760,10 @@ async function ensureTelemetryIfNeeded() {
|
|
|
15330
15760
|
}
|
|
15331
15761
|
function refreshHooks() {
|
|
15332
15762
|
const cwd = process.cwd();
|
|
15333
|
-
const configPath =
|
|
15763
|
+
const configPath = join68(cwd, "harness.config.json");
|
|
15334
15764
|
if (!existsSync45(configPath)) return;
|
|
15335
15765
|
let profile = "standard";
|
|
15336
|
-
const profilePath =
|
|
15766
|
+
const profilePath = join68(cwd, ".harness", "hooks", "profile.json");
|
|
15337
15767
|
try {
|
|
15338
15768
|
const data = JSON.parse(readFileSync33(profilePath, "utf-8"));
|
|
15339
15769
|
if (data.profile && ["minimal", "standard", "strict"].includes(data.profile)) {
|
|
@@ -15352,7 +15782,7 @@ function refreshHooks() {
|
|
|
15352
15782
|
function runLocalGraphScan() {
|
|
15353
15783
|
try {
|
|
15354
15784
|
logger.info("Scanning codebase to rebuild knowledge graph...");
|
|
15355
|
-
|
|
15785
|
+
execFileSync13("harness", ["graph", "scan", "."], { stdio: "inherit" });
|
|
15356
15786
|
} catch {
|
|
15357
15787
|
logger.warn("Graph scan failed. Run manually:");
|
|
15358
15788
|
console.log(` ${chalk20.cyan("harness graph scan .")}`);
|
|
@@ -15367,7 +15797,7 @@ async function offerRegeneration() {
|
|
|
15367
15797
|
const scopeAnswer = await prompt3("Generate for (G)lobal or (l)ocal project? (G/l) ");
|
|
15368
15798
|
const isGlobal = scopeAnswer !== "l" && scopeAnswer !== "local";
|
|
15369
15799
|
try {
|
|
15370
|
-
|
|
15800
|
+
execFileSync13("harness", ["generate", ...isGlobal ? ["--global"] : []], {
|
|
15371
15801
|
stdio: "inherit"
|
|
15372
15802
|
});
|
|
15373
15803
|
} catch {
|
|
@@ -15447,7 +15877,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
15447
15877
|
}
|
|
15448
15878
|
try {
|
|
15449
15879
|
logger.info("Updating packages...");
|
|
15450
|
-
|
|
15880
|
+
execFileSync13(pm, ["install", "-g", ...installPkgs], { stdio: "inherit", timeout: 12e4 });
|
|
15451
15881
|
console.log("");
|
|
15452
15882
|
logger.success("Update complete");
|
|
15453
15883
|
invalidateCheckState();
|
|
@@ -15464,7 +15894,7 @@ async function runUpdateAction(opts, globalOpts) {
|
|
|
15464
15894
|
process.exit(ExitCode.SUCCESS);
|
|
15465
15895
|
}
|
|
15466
15896
|
function createUpdateCommand() {
|
|
15467
|
-
return new
|
|
15897
|
+
return new Command127("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(
|
|
15468
15898
|
"--regenerate",
|
|
15469
15899
|
"Only regenerate slash commands and agent definitions (skip package updates)"
|
|
15470
15900
|
).action(async (opts, cmd) => {
|
|
@@ -15474,9 +15904,9 @@ function createUpdateCommand() {
|
|
|
15474
15904
|
}
|
|
15475
15905
|
|
|
15476
15906
|
// src/commands/usage.ts
|
|
15477
|
-
import { Command as
|
|
15907
|
+
import { Command as Command128 } from "commander";
|
|
15478
15908
|
async function loadAndPriceRecords(cwd, includeClaudeSessions = false) {
|
|
15479
|
-
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-
|
|
15909
|
+
const { readCostRecords, loadPricingData, calculateCost, parseCCRecords } = await import("./dist-SOKOW7BC.js");
|
|
15480
15910
|
const records = readCostRecords(cwd);
|
|
15481
15911
|
if (includeClaudeSessions) {
|
|
15482
15912
|
const ccRecords = parseCCRecords();
|
|
@@ -15569,7 +15999,7 @@ function registerDailyCommand(usage) {
|
|
|
15569
15999
|
else logger.info("No usage data found. Run some harness sessions first.");
|
|
15570
16000
|
return;
|
|
15571
16001
|
}
|
|
15572
|
-
const { aggregateByDay } = await import("./dist-
|
|
16002
|
+
const { aggregateByDay } = await import("./dist-SOKOW7BC.js");
|
|
15573
16003
|
const limited = aggregateByDay(records).slice(0, days);
|
|
15574
16004
|
if (globalOpts.json) {
|
|
15575
16005
|
console.log(
|
|
@@ -15598,7 +16028,7 @@ function registerSessionsCommand(usage) {
|
|
|
15598
16028
|
}
|
|
15599
16029
|
return;
|
|
15600
16030
|
}
|
|
15601
|
-
const { aggregateBySession } = await import("./dist-
|
|
16031
|
+
const { aggregateBySession } = await import("./dist-SOKOW7BC.js");
|
|
15602
16032
|
const sessionData = aggregateBySession(records);
|
|
15603
16033
|
const limited = sessionData.slice(0, limit);
|
|
15604
16034
|
if (globalOpts.json) {
|
|
@@ -15678,7 +16108,7 @@ function registerSessionCommand(usage) {
|
|
|
15678
16108
|
cwd,
|
|
15679
16109
|
globalOpts.includeClaudeSessions
|
|
15680
16110
|
);
|
|
15681
|
-
const { aggregateBySession } = await import("./dist-
|
|
16111
|
+
const { aggregateBySession } = await import("./dist-SOKOW7BC.js");
|
|
15682
16112
|
const sessionData = aggregateBySession(records);
|
|
15683
16113
|
const match = sessionData.find((s) => s.sessionId === id);
|
|
15684
16114
|
if (!match) {
|
|
@@ -15713,7 +16143,7 @@ function registerLatestCommand(usage) {
|
|
|
15713
16143
|
}
|
|
15714
16144
|
return;
|
|
15715
16145
|
}
|
|
15716
|
-
const { aggregateBySession } = await import("./dist-
|
|
16146
|
+
const { aggregateBySession } = await import("./dist-SOKOW7BC.js");
|
|
15717
16147
|
const sessionData = aggregateBySession(records);
|
|
15718
16148
|
const latest = sessionData[0];
|
|
15719
16149
|
if (!latest) {
|
|
@@ -15739,7 +16169,7 @@ function registerLatestCommand(usage) {
|
|
|
15739
16169
|
});
|
|
15740
16170
|
}
|
|
15741
16171
|
function createUsageCommand() {
|
|
15742
|
-
const usage = new
|
|
16172
|
+
const usage = new Command128("usage").description("Token usage and cost tracking");
|
|
15743
16173
|
usage.option(
|
|
15744
16174
|
"--include-claude-sessions",
|
|
15745
16175
|
"Include Claude Code session data from ~/.claude/projects/"
|
|
@@ -15754,10 +16184,10 @@ function createUsageCommand() {
|
|
|
15754
16184
|
// src/commands/validate.ts
|
|
15755
16185
|
init_dist3();
|
|
15756
16186
|
init_dist3();
|
|
15757
|
-
import { Command as
|
|
16187
|
+
import { Command as Command129 } from "commander";
|
|
15758
16188
|
import * as fs50 from "fs";
|
|
15759
16189
|
import * as path80 from "path";
|
|
15760
|
-
import { execFileSync as
|
|
16190
|
+
import { execFileSync as execFileSync14 } from "child_process";
|
|
15761
16191
|
async function runValidate(options) {
|
|
15762
16192
|
const configResult = resolveConfig(options.configPath);
|
|
15763
16193
|
if (!configResult.ok) {
|
|
@@ -15876,7 +16306,7 @@ async function runValidate(options) {
|
|
|
15876
16306
|
const gitattributesContent = fs50.readFileSync(gitattributesPath, "utf-8");
|
|
15877
16307
|
let driverConfigured = false;
|
|
15878
16308
|
try {
|
|
15879
|
-
const out =
|
|
16309
|
+
const out = execFileSync14("git", ["config", "--get", "merge.ours.driver"], {
|
|
15880
16310
|
cwd,
|
|
15881
16311
|
encoding: "utf-8",
|
|
15882
16312
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -16062,7 +16492,7 @@ function resolveValidateMode(globalOpts) {
|
|
|
16062
16492
|
return OutputMode.TEXT;
|
|
16063
16493
|
}
|
|
16064
16494
|
async function printCrossCheckWarnings(mode) {
|
|
16065
|
-
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-
|
|
16495
|
+
const { runCrossCheck: runCrossCheck2 } = await import("./validate-cross-check-OF3FPM32.js");
|
|
16066
16496
|
const cwd = process.cwd();
|
|
16067
16497
|
const crossResult = await runCrossCheck2({
|
|
16068
16498
|
specsDir: path80.join(cwd, "docs", "specs"),
|
|
@@ -16078,7 +16508,7 @@ async function printCrossCheckWarnings(mode) {
|
|
|
16078
16508
|
${crossResult.value.warnings} warnings`);
|
|
16079
16509
|
}
|
|
16080
16510
|
function createValidateCommand3() {
|
|
16081
|
-
const command = new
|
|
16511
|
+
const command = new Command129("validate").description("Run all validation checks").option("--cross-check", "Run cross-artifact consistency validation").option(
|
|
16082
16512
|
"--agent-configs",
|
|
16083
16513
|
"Validate agent configs (CLAUDE.md, hooks, skills) via agnix or built-in fallback rules"
|
|
16084
16514
|
).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()));
|
|
@@ -16127,7 +16557,7 @@ Agent configs checked via ${engineLabel}${note}`);
|
|
|
16127
16557
|
|
|
16128
16558
|
// src/commands/verify.ts
|
|
16129
16559
|
init_dist3();
|
|
16130
|
-
import { Command as
|
|
16560
|
+
import { Command as Command130 } from "commander";
|
|
16131
16561
|
import { execSync as execSync6 } from "child_process";
|
|
16132
16562
|
function loadBranchingConfig(configPath) {
|
|
16133
16563
|
const shouldLoad = configPath !== void 0 || findConfigFile().ok;
|
|
@@ -16190,7 +16620,7 @@ async function runVerify(options) {
|
|
|
16190
16620
|
process.exit(ExitCode.VALIDATION_FAILED);
|
|
16191
16621
|
}
|
|
16192
16622
|
function createVerifyCommand() {
|
|
16193
|
-
return new
|
|
16623
|
+
return new Command130("verify").description(
|
|
16194
16624
|
"Verify project conventions (currently: branch naming). Works with or without a harness.config.json."
|
|
16195
16625
|
).option(
|
|
16196
16626
|
"--branch <name>",
|
|
@@ -16268,6 +16698,7 @@ var commandCreators = [
|
|
|
16268
16698
|
createRecommendCommand,
|
|
16269
16699
|
createReviewCiCommand,
|
|
16270
16700
|
createRoadmapCommand,
|
|
16701
|
+
createRollbackCommand,
|
|
16271
16702
|
createRoutingCommand,
|
|
16272
16703
|
createScanConfigCommand,
|
|
16273
16704
|
createSearchCommand,
|
|
@@ -16317,7 +16748,7 @@ function registerDeprecatedGraphAliases(program) {
|
|
|
16317
16748
|
|
|
16318
16749
|
// src/index.ts
|
|
16319
16750
|
function createProgram() {
|
|
16320
|
-
const program = new
|
|
16751
|
+
const program = new Command131();
|
|
16321
16752
|
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");
|
|
16322
16753
|
for (const creator of commandCreators) {
|
|
16323
16754
|
program.addCommand(creator());
|