@harness-engineering/cli 7.0.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/harness.js +1 -1
- package/dist/{chunk-N4VEUBJ5.js → chunk-MSJJU6KH.js} +146 -58
- package/dist/index.js +1 -1
- package/package.json +6 -6
package/dist/bin/harness.js
CHANGED
|
@@ -11987,15 +11987,45 @@ async function buildShapeHistory(cwd) {
|
|
|
11987
11987
|
}
|
|
11988
11988
|
return (shapeKey) => byShape.get(shapeKey) ?? [];
|
|
11989
11989
|
}
|
|
11990
|
+
function selectActionableFeatures(roadmap, opts = {}) {
|
|
11991
|
+
let features = roadmap.milestones.flatMap((m) => m.features).filter(isActionable);
|
|
11992
|
+
if (opts.only !== void 0 && opts.only.trim().length > 0) {
|
|
11993
|
+
const needle = opts.only.trim().toLowerCase();
|
|
11994
|
+
features = features.filter((f) => f.name.toLowerCase().includes(needle));
|
|
11995
|
+
}
|
|
11996
|
+
if (opts.limit !== void 0 && opts.limit >= 0) {
|
|
11997
|
+
features = features.slice(0, opts.limit);
|
|
11998
|
+
}
|
|
11999
|
+
return features;
|
|
12000
|
+
}
|
|
12001
|
+
function isPlausibleForModel(verdict) {
|
|
12002
|
+
const scope = verdict.levers.scope.value;
|
|
12003
|
+
const scopeResolvedBounded = scope !== "unknown" && scope.resolved.length > 0;
|
|
12004
|
+
const level = verdict.verdict.level;
|
|
12005
|
+
const inBand = level === "trivial" || level === "simple";
|
|
12006
|
+
return scopeResolvedBounded && inBand;
|
|
12007
|
+
}
|
|
11990
12008
|
async function runTriageReport(roadmap, deps = {}) {
|
|
11991
|
-
const features = roadmap
|
|
12009
|
+
const features = selectActionableFeatures(roadmap, {
|
|
12010
|
+
...deps.only !== void 0 ? { only: deps.only } : {},
|
|
12011
|
+
...deps.limit !== void 0 ? { limit: deps.limit } : {}
|
|
12012
|
+
});
|
|
12013
|
+
const cheapDeps = {
|
|
12014
|
+
...deps.graphStore ? { graphStore: deps.graphStore } : {},
|
|
12015
|
+
...deps.precedent ? { precedent: deps.precedent } : {},
|
|
12016
|
+
...deps.config ? { config: deps.config } : {}
|
|
12017
|
+
};
|
|
12018
|
+
const useModel = deps.offline !== true && deps.provider != null;
|
|
11992
12019
|
const rows = [];
|
|
11993
12020
|
for (const feature of features) {
|
|
11994
|
-
|
|
11995
|
-
|
|
11996
|
-
|
|
11997
|
-
|
|
11998
|
-
|
|
12021
|
+
let verdict = await triageIssue(featureToIssue(feature), cheapDeps);
|
|
12022
|
+
if (useModel && isPlausibleForModel(verdict)) {
|
|
12023
|
+
deps.onProgress?.(`Refining "${feature.name}" on the local model\u2026`);
|
|
12024
|
+
verdict = await triageIssue(featureToIssue(feature), {
|
|
12025
|
+
...cheapDeps,
|
|
12026
|
+
...deps.provider ? { provider: deps.provider } : {}
|
|
12027
|
+
});
|
|
12028
|
+
}
|
|
11999
12029
|
rows.push({
|
|
12000
12030
|
externalId: verdict.externalId,
|
|
12001
12031
|
name: feature.name,
|
|
@@ -12051,8 +12081,27 @@ function renderJson(rows) {
|
|
|
12051
12081
|
2
|
|
12052
12082
|
);
|
|
12053
12083
|
}
|
|
12084
|
+
function skippedBrainstormRow(feature, verdict, level) {
|
|
12085
|
+
return {
|
|
12086
|
+
externalId: verdict.externalId,
|
|
12087
|
+
name: feature.name,
|
|
12088
|
+
status: feature.status,
|
|
12089
|
+
level,
|
|
12090
|
+
result: {
|
|
12091
|
+
outcome: {
|
|
12092
|
+
kind: "halted",
|
|
12093
|
+
reason: "error",
|
|
12094
|
+
fork: { id: "scope-gate", question: "", options: [] },
|
|
12095
|
+
detail: `Skipped brainstorm \u2014 not a plausible candidate (${verdict.holdReason ?? "held"}). Held to human by the cheap scope/band gate.`
|
|
12096
|
+
}
|
|
12097
|
+
}
|
|
12098
|
+
};
|
|
12099
|
+
}
|
|
12054
12100
|
async function runBrainstormReport(roadmap, deps = {}) {
|
|
12055
|
-
const features = roadmap
|
|
12101
|
+
const features = selectActionableFeatures(roadmap, {
|
|
12102
|
+
...deps.only !== void 0 ? { only: deps.only } : {},
|
|
12103
|
+
...deps.limit !== void 0 ? { limit: deps.limit } : {}
|
|
12104
|
+
});
|
|
12056
12105
|
const rows = [];
|
|
12057
12106
|
for (const feature of features) {
|
|
12058
12107
|
const issue = featureToIssue(feature);
|
|
@@ -12063,6 +12112,11 @@ async function runBrainstormReport(roadmap, deps = {}) {
|
|
|
12063
12112
|
...deps.config ? { config: deps.config } : {}
|
|
12064
12113
|
});
|
|
12065
12114
|
const level = verdict.verdict.level;
|
|
12115
|
+
if (deps.gatePlausible === true && !isPlausibleForModel(verdict)) {
|
|
12116
|
+
rows.push(skippedBrainstormRow(feature, verdict, level));
|
|
12117
|
+
continue;
|
|
12118
|
+
}
|
|
12119
|
+
deps.onProgress?.(`Brainstorming "${feature.name}" [${level}]\u2026`);
|
|
12066
12120
|
const wiringDeps = {
|
|
12067
12121
|
...deps.generator ? { generator: deps.generator } : {},
|
|
12068
12122
|
...deps.provider ? { provider: deps.provider } : {},
|
|
@@ -12262,59 +12316,93 @@ function createRoadmapTriageCommand() {
|
|
|
12262
12316
|
).option(
|
|
12263
12317
|
"--brainstorm",
|
|
12264
12318
|
"Run the autonomous brainstorm per candidate: draft a spec (docs only) or halt to a human at the first fork it can not confidently recommend. No dispatch, no execution."
|
|
12265
|
-
).
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
|
|
12271
|
-
|
|
12272
|
-
|
|
12273
|
-
|
|
12274
|
-
|
|
12275
|
-
|
|
12276
|
-
|
|
12277
|
-
|
|
12278
|
-
logger.error(
|
|
12279
|
-
`No roadmap aggregate at ${roadmapPath}. If your roadmap is sharded, run \`harness roadmap regen\` first, then re-run triage.`
|
|
12280
|
-
);
|
|
12281
|
-
process.exitCode = 1;
|
|
12282
|
-
return;
|
|
12283
|
-
}
|
|
12284
|
-
const parsed = parseRoadmap(fs36.readFileSync(roadmapPath, "utf-8"));
|
|
12285
|
-
if (!parsed.ok) {
|
|
12286
|
-
logger.error(`Failed to parse roadmap: ${parsed.error.message}`);
|
|
12287
|
-
process.exitCode = 1;
|
|
12288
|
-
return;
|
|
12289
|
-
}
|
|
12290
|
-
const graphStore = await loadGraphStore(cwd);
|
|
12291
|
-
const precedent = await buildPrecedentLookup(cwd);
|
|
12292
|
-
if (opts.brainstorm) {
|
|
12293
|
-
const provider = resolveBrainstormProvider(globalOpts.config);
|
|
12294
|
-
const rows2 = await runBrainstormReport(parsed.value, {
|
|
12295
|
-
...graphStore ? { graphStore } : {},
|
|
12296
|
-
...provider ? { provider } : {},
|
|
12297
|
-
...precedent ? { precedent } : {},
|
|
12298
|
-
// Specs are written under docs/changes/<slug>/proposal.md (docs only, no dispatch).
|
|
12299
|
-
docsRoot: path60.join(cwd, "docs")
|
|
12300
|
-
});
|
|
12301
|
-
if (globalOpts.json) {
|
|
12302
|
-
process.stdout.write(renderBrainstormJson(rows2) + "\n");
|
|
12303
|
-
} else {
|
|
12304
|
-
logger.info(renderBrainstormHuman(rows2));
|
|
12305
|
-
}
|
|
12306
|
-
return;
|
|
12319
|
+
).option(
|
|
12320
|
+
"--only <substring>",
|
|
12321
|
+
'Process ONLY roadmap items whose title contains this substring (case-insensitive). Lets you triage/brainstorm a single item, e.g. --only "prefer-execfile".'
|
|
12322
|
+
).option(
|
|
12323
|
+
"--limit <n>",
|
|
12324
|
+
"Process at most N items (applied after --only). Useful for a quick partial scan.",
|
|
12325
|
+
(v) => Number.parseInt(v, 10)
|
|
12326
|
+
).option(
|
|
12327
|
+
"--offline",
|
|
12328
|
+
"Force the pure static path \u2014 never consult the local model. A fast scan that holds every item to a human (byte-identical to running without a resolvable model)."
|
|
12329
|
+
).action(
|
|
12330
|
+
async (opts, cmd) => {
|
|
12331
|
+
await runTriageCommandAction(opts, cmd);
|
|
12307
12332
|
}
|
|
12308
|
-
|
|
12309
|
-
|
|
12310
|
-
|
|
12333
|
+
).addCommand(createTriageApproveCommand());
|
|
12334
|
+
}
|
|
12335
|
+
async function runTriageCommandAction(opts, cmd) {
|
|
12336
|
+
const cwd = process.cwd();
|
|
12337
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
12338
|
+
const parsed = gateAndParseRoadmap(cwd, globalOpts.config);
|
|
12339
|
+
if (!parsed) return;
|
|
12340
|
+
const graphStore = await loadGraphStore(cwd);
|
|
12341
|
+
const precedent = await buildPrecedentLookup(cwd);
|
|
12342
|
+
const provider = opts.offline === true ? null : resolveBrainstormProvider(globalOpts.config);
|
|
12343
|
+
const onProgress = globalOpts.json === true ? void 0 : (message) => logger.info(message);
|
|
12344
|
+
const shared = {
|
|
12345
|
+
...graphStore ? { graphStore } : {},
|
|
12346
|
+
...provider ? { provider } : {},
|
|
12347
|
+
...precedent ? { precedent } : {},
|
|
12348
|
+
...opts.only !== void 0 ? { only: opts.only } : {},
|
|
12349
|
+
...typeof opts.limit === "number" && !Number.isNaN(opts.limit) ? { limit: opts.limit } : {},
|
|
12350
|
+
...onProgress ? { onProgress } : {}
|
|
12351
|
+
};
|
|
12352
|
+
if (opts.brainstorm) {
|
|
12353
|
+
const rows2 = await runBrainstormReport(parsed, {
|
|
12354
|
+
...shared,
|
|
12355
|
+
gatePlausible: true,
|
|
12356
|
+
docsRoot: path60.join(cwd, "docs")
|
|
12311
12357
|
});
|
|
12312
|
-
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
|
|
12316
|
-
|
|
12317
|
-
|
|
12358
|
+
emitReport(
|
|
12359
|
+
globalOpts.json === true,
|
|
12360
|
+
() => renderBrainstormJson(rows2),
|
|
12361
|
+
() => renderBrainstormHuman(rows2)
|
|
12362
|
+
);
|
|
12363
|
+
return;
|
|
12364
|
+
}
|
|
12365
|
+
const rows = await runTriageReport(parsed, {
|
|
12366
|
+
...shared,
|
|
12367
|
+
...opts.offline === true ? { offline: true } : {}
|
|
12368
|
+
});
|
|
12369
|
+
emitReport(
|
|
12370
|
+
globalOpts.json === true,
|
|
12371
|
+
() => renderJson(rows),
|
|
12372
|
+
() => renderHuman(rows)
|
|
12373
|
+
);
|
|
12374
|
+
}
|
|
12375
|
+
function gateAndParseRoadmap(cwd, configPath) {
|
|
12376
|
+
const configResult = resolveConfig(configPath);
|
|
12377
|
+
const enabled = configResult.ok && configResult.value.roadmap?.autoTriage?.enabled === true;
|
|
12378
|
+
if (!enabled) {
|
|
12379
|
+
logger.info(
|
|
12380
|
+
"Roadmap auto-triage is disabled (roadmap.autoTriage.enabled is not true). Enable it in harness.config.json to run the read-only triage report. No changes made."
|
|
12381
|
+
);
|
|
12382
|
+
return null;
|
|
12383
|
+
}
|
|
12384
|
+
const roadmapPath = path60.join(cwd, "docs", "roadmap.md");
|
|
12385
|
+
if (!fs36.existsSync(roadmapPath)) {
|
|
12386
|
+
logger.error(
|
|
12387
|
+
`No roadmap aggregate at ${roadmapPath}. If your roadmap is sharded, run \`harness roadmap regen\` first, then re-run triage.`
|
|
12388
|
+
);
|
|
12389
|
+
process.exitCode = 1;
|
|
12390
|
+
return null;
|
|
12391
|
+
}
|
|
12392
|
+
const parsed = parseRoadmap(fs36.readFileSync(roadmapPath, "utf-8"));
|
|
12393
|
+
if (!parsed.ok) {
|
|
12394
|
+
logger.error(`Failed to parse roadmap: ${parsed.error.message}`);
|
|
12395
|
+
process.exitCode = 1;
|
|
12396
|
+
return null;
|
|
12397
|
+
}
|
|
12398
|
+
return parsed.value;
|
|
12399
|
+
}
|
|
12400
|
+
function emitReport(json, toJson, toHuman) {
|
|
12401
|
+
if (json) {
|
|
12402
|
+
process.stdout.write(toJson() + "\n");
|
|
12403
|
+
} else {
|
|
12404
|
+
logger.info(toHuman());
|
|
12405
|
+
}
|
|
12318
12406
|
}
|
|
12319
12407
|
|
|
12320
12408
|
// src/commands/roadmap/index.ts
|
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@harness-engineering/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "8.0.0",
|
|
4
4
|
"description": "CLI for Harness Engineering toolkit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -40,15 +40,15 @@
|
|
|
40
40
|
"yaml": "^2.8.3",
|
|
41
41
|
"zod": "^3.25.76",
|
|
42
42
|
"@harness-engineering/core": "0.37.0",
|
|
43
|
+
"@harness-engineering/dashboard": "0.14.4",
|
|
43
44
|
"@harness-engineering/graph": "0.11.8",
|
|
44
|
-
"@harness-engineering/dashboard": "0.14.3",
|
|
45
45
|
"@harness-engineering/linter-gen": "0.1.7",
|
|
46
|
-
"@harness-engineering/orchestrator": "0.15.
|
|
47
|
-
"@harness-engineering/
|
|
48
|
-
"@harness-engineering/
|
|
46
|
+
"@harness-engineering/orchestrator": "0.15.1",
|
|
47
|
+
"@harness-engineering/signals": "0.2.6",
|
|
48
|
+
"@harness-engineering/types": "0.22.0"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@harness-engineering/intelligence": "0.
|
|
51
|
+
"@harness-engineering/intelligence": "0.8.0"
|
|
52
52
|
},
|
|
53
53
|
"peerDependenciesMeta": {
|
|
54
54
|
"@harness-engineering/intelligence": {
|