@harness-engineering/cli 4.0.0 → 4.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/claude-code/product-advisor/SKILL.md +202 -0
- package/dist/agents/skills/claude-code/product-advisor/skill.yaml +74 -0
- package/dist/agents/skills/codex/product-advisor/SKILL.md +202 -0
- package/dist/agents/skills/codex/product-advisor/skill.yaml +74 -0
- package/dist/agents/skills/cursor/product-advisor/SKILL.md +202 -0
- package/dist/agents/skills/cursor/product-advisor/skill.yaml +74 -0
- package/dist/agents/skills/gemini-cli/product-advisor/SKILL.md +202 -0
- package/dist/agents/skills/gemini-cli/product-advisor/skill.yaml +74 -0
- package/dist/{agents-md-7OSNWU52.js → agents-md-VJLYEH2S.js} +4 -4
- package/dist/{analyzer-V633GUHY-MBCQWKDU.js → analyzer-V633GUHY-OV335HGM.js} +2 -2
- package/dist/{architecture-2XYGJGT3.js → architecture-YQH2NYXY.js} +5 -5
- package/dist/{assess-project-3KPP2D6M.js → assess-project-KOTAPAHD.js} +1 -1
- package/dist/bin/harness-mcp.js +16 -16
- package/dist/bin/harness.js +27 -27
- package/dist/{check-phase-gate-46U5AAXE.js → check-phase-gate-CHU2E3OU.js} +5 -5
- package/dist/{chunk-AK67S3O4.js → chunk-36GQREOE.js} +3 -3
- package/dist/{chunk-CLI4K2CH.js → chunk-4U5LDFCY.js} +1 -1
- package/dist/{chunk-VTUINXWR.js → chunk-4VXFZSYF.js} +1 -1
- package/dist/{chunk-HXMAIVV2.js → chunk-5RSXUAMN.js} +1 -1
- package/dist/{chunk-TTWNGOBT.js → chunk-6SCYTIF4.js} +6 -6
- package/dist/{chunk-N55WOGN3.js → chunk-6YFHUIGT.js} +1 -1
- package/dist/{chunk-WL3O2PKJ.js → chunk-7PJCK7UE.js} +8 -8
- package/dist/{chunk-PHX45JYN.js → chunk-7TIJXWG3.js} +2 -2
- package/dist/{chunk-SPE7P4GL.js → chunk-AAUY53CC.js} +1 -1
- package/dist/{chunk-VTU3QOJT.js → chunk-AX5KGRM6.js} +1 -1
- package/dist/{chunk-MYVTYFQD.js → chunk-E5EW5HVJ.js} +1 -1
- package/dist/{chunk-HMW3C5DP.js → chunk-EYMOPFSD.js} +2 -2
- package/dist/{chunk-6YXCZV33.js → chunk-GHBFNLV2.js} +278 -211
- package/dist/{chunk-363V6GPR.js → chunk-I3CGCZJF.js} +9 -9
- package/dist/{chunk-MSBJ445U.js → chunk-JPVOI7GP.js} +5 -5
- package/dist/{chunk-6NCREWWM.js → chunk-M7F2PPN3.js} +3 -3
- package/dist/{chunk-KLPX55TI.js → chunk-MMLQPHZ5.js} +210 -135
- package/dist/{chunk-BV45354Q.js → chunk-OQ6KOQEV.js} +9 -9
- package/dist/{chunk-ERILXQBP.js → chunk-RG3TNIUB.js} +610 -428
- package/dist/{chunk-GXCPDAS5.js → chunk-SDMV4QHM.js} +5 -5
- package/dist/{chunk-ZEYAOYJL.js → chunk-SEA2PFVI.js} +1 -1
- package/dist/{chunk-IYWT3JJW.js → chunk-T746REAE.js} +1 -1
- package/dist/{chunk-FZ4Q73HA.js → chunk-UWVMJEUJ.js} +1 -1
- package/dist/{chunk-PP6ZRL5T.js → chunk-WZJR6SOI.js} +536 -422
- package/dist/{chunk-XFHZ7GCI.js → chunk-XAVMTAUT.js} +2 -2
- package/dist/{chunk-S2RQPPL6.js → chunk-YANR2RL7.js} +1 -1
- package/dist/{chunk-2N4OENGE.js → chunk-YYFSFSB3.js} +1 -1
- package/dist/{ci-workflow-XJN4UO3Q.js → ci-workflow-HO4WKGNG.js} +4 -4
- package/dist/{dist-IPVFYGY2.js → dist-6D2F2J57.js} +3 -3
- package/dist/{dist-OWHMNL4W.js → dist-DWBTZXR5.js} +1 -1
- package/dist/{docs-RSFZTNPG.js → docs-FP47JONV.js} +5 -5
- package/dist/{engine-DJIHY4JT.js → engine-YUA6IYOG.js} +4 -4
- package/dist/{entropy-EAEKJKRM.js → entropy-DBW2INZU.js} +4 -4
- package/dist/{feedback-JT5X3DBC.js → feedback-BPHADTRQ.js} +1 -1
- package/dist/{generate-agent-definitions-CC6KED3E.js → generate-agent-definitions-KPVGBGDY.js} +5 -5
- package/dist/{glob-helper-GLZAURJC.js → glob-helper-VR6UCZZB.js} +1 -1
- package/dist/{graph-loader-HHXN5FLP.js → graph-loader-STFGYKZH.js} +1 -1
- package/dist/index.js +27 -27
- package/dist/{loader-UWNVTYBY.js → loader-QOJQ6NZM.js} +4 -4
- package/dist/{mcp-TCLNK7HG.js → mcp-ZPX6O767.js} +16 -16
- package/dist/{performance-R6O3N3UB.js → performance-FXYTGJGQ.js} +5 -5
- package/dist/{review-pipeline-MOOWXWSQ.js → review-pipeline-2TFQ5OAE.js} +4 -4
- package/dist/{runtime-RIWQUKBQ.js → runtime-XK6ZCHKE.js} +4 -4
- package/dist/{scan-P7YFKB77.js → scan-IYSVRPD2.js} +1 -1
- package/dist/{security-U76C54W6.js → security-4W2P5ZGI.js} +1 -1
- package/dist/{state-events-F44CUUZG.js → state-events-ECQAIZLU.js} +4 -4
- package/dist/{validate-IUAV7MOU.js → validate-64KTEGIE.js} +5 -5
- package/dist/{validate-cross-check-M2ZM6RFY.js → validate-cross-check-J5PVGICB.js} +4 -4
- package/package.json +1 -1
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
findConfigFile,
|
|
9
9
|
loadConfig
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-XAVMTAUT.js";
|
|
11
11
|
import {
|
|
12
12
|
init_dist
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-MMLQPHZ5.js";
|
|
14
14
|
import {
|
|
15
15
|
Ok
|
|
16
16
|
} from "./chunk-BDGTZRZ6.js";
|
|
@@ -50,18 +50,18 @@ async function handleCheckDocs(input) {
|
|
|
50
50
|
const projectPath = sanitizePath(input.path);
|
|
51
51
|
const scope = input.scope ?? "coverage";
|
|
52
52
|
if (scope === "integrity") {
|
|
53
|
-
const { validateKnowledgeMap } = await import("./dist-
|
|
53
|
+
const { validateKnowledgeMap } = await import("./dist-6D2F2J57.js");
|
|
54
54
|
const result2 = await validateKnowledgeMap(projectPath);
|
|
55
55
|
return resultToMcpResponse(result2);
|
|
56
56
|
}
|
|
57
57
|
if (scope === "all") {
|
|
58
|
-
const { checkDocCoverage: checkDocCoverage2, validateKnowledgeMap } = await import("./dist-
|
|
58
|
+
const { checkDocCoverage: checkDocCoverage2, validateKnowledgeMap } = await import("./dist-6D2F2J57.js");
|
|
59
59
|
const domain2 = input.domain ?? "src";
|
|
60
|
-
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-
|
|
60
|
+
const { loadGraphStore: loadGraphStore2 } = await import("./graph-loader-STFGYKZH.js");
|
|
61
61
|
const store2 = await loadGraphStore2(projectPath);
|
|
62
62
|
let graphCoverage2;
|
|
63
63
|
if (store2) {
|
|
64
|
-
const { Assembler } = await import("./dist-
|
|
64
|
+
const { Assembler } = await import("./dist-DWBTZXR5.js");
|
|
65
65
|
const assembler = new Assembler(store2);
|
|
66
66
|
const report = assembler.checkCoverage();
|
|
67
67
|
graphCoverage2 = {
|
|
@@ -94,13 +94,13 @@ async function handleCheckDocs(input) {
|
|
|
94
94
|
}
|
|
95
95
|
return resultToMcpResponse(Ok({ coverage, integrity }));
|
|
96
96
|
}
|
|
97
|
-
const { checkDocCoverage } = await import("./dist-
|
|
97
|
+
const { checkDocCoverage } = await import("./dist-6D2F2J57.js");
|
|
98
98
|
const domain = input.domain ?? "src";
|
|
99
|
-
const { loadGraphStore } = await import("./graph-loader-
|
|
99
|
+
const { loadGraphStore } = await import("./graph-loader-STFGYKZH.js");
|
|
100
100
|
const store = await loadGraphStore(projectPath);
|
|
101
101
|
let graphCoverage;
|
|
102
102
|
if (store) {
|
|
103
|
-
const { Assembler } = await import("./dist-
|
|
103
|
+
const { Assembler } = await import("./dist-DWBTZXR5.js");
|
|
104
104
|
const assembler = new Assembler(store);
|
|
105
105
|
const report = assembler.checkCoverage();
|
|
106
106
|
graphCoverage = {
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-RC3OI5NK.js";
|
|
4
4
|
import {
|
|
5
5
|
resolveProjectConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-SEA2PFVI.js";
|
|
7
7
|
import {
|
|
8
8
|
sanitizePath
|
|
9
9
|
} from "./chunk-W6Y7ZW3Y.js";
|
|
@@ -38,7 +38,7 @@ async function handleCheckDependencies(input) {
|
|
|
38
38
|
const configResult = resolveProjectConfig(projectPath);
|
|
39
39
|
if (!configResult.ok) return resultToMcpResponse(configResult);
|
|
40
40
|
try {
|
|
41
|
-
const { validateDependencies, TypeScriptParser } = await import("./dist-
|
|
41
|
+
const { validateDependencies, TypeScriptParser } = await import("./dist-6D2F2J57.js");
|
|
42
42
|
const config = configResult.value;
|
|
43
43
|
const rawLayers = Array.isArray(config.layers) ? config.layers : [];
|
|
44
44
|
const layers = rawLayers.map((l) => ({
|
|
@@ -47,11 +47,11 @@ async function handleCheckDependencies(input) {
|
|
|
47
47
|
allowedDependencies: l.allowedDependencies
|
|
48
48
|
}));
|
|
49
49
|
const parser = new TypeScriptParser();
|
|
50
|
-
const { loadGraphStore } = await import("./graph-loader-
|
|
50
|
+
const { loadGraphStore } = await import("./graph-loader-STFGYKZH.js");
|
|
51
51
|
const store = await loadGraphStore(projectPath);
|
|
52
52
|
let graphDependencyData;
|
|
53
53
|
if (store) {
|
|
54
|
-
const { GraphConstraintAdapter } = await import("./dist-
|
|
54
|
+
const { GraphConstraintAdapter } = await import("./dist-DWBTZXR5.js");
|
|
55
55
|
const adapter = new GraphConstraintAdapter(store);
|
|
56
56
|
const graphData = adapter.computeDependencyGraph();
|
|
57
57
|
graphDependencyData = {
|
|
@@ -69,7 +69,7 @@ async function handleCheckDependencies(input) {
|
|
|
69
69
|
if (store) {
|
|
70
70
|
const designTokenNodes = store.findNodes({ type: "design_token" });
|
|
71
71
|
if (designTokenNodes.length > 0) {
|
|
72
|
-
const { DesignConstraintAdapter } = await import("./dist-
|
|
72
|
+
const { DesignConstraintAdapter } = await import("./dist-DWBTZXR5.js");
|
|
73
73
|
const designAdapter = new DesignConstraintAdapter(store);
|
|
74
74
|
const fileNodes = store.findNodes({ type: "file" });
|
|
75
75
|
for (const fileNode of fileNodes) {
|
|
@@ -27,7 +27,7 @@ var runSecurityScanDefinition = {
|
|
|
27
27
|
};
|
|
28
28
|
async function handleRunSecurityScan(input) {
|
|
29
29
|
try {
|
|
30
|
-
const core = await import("./dist-
|
|
30
|
+
const core = await import("./dist-6D2F2J57.js");
|
|
31
31
|
const projectRoot = sanitizePath(input.path);
|
|
32
32
|
let configData = {};
|
|
33
33
|
try {
|
|
@@ -50,7 +50,7 @@ async function handleRunSecurityScan(input) {
|
|
|
50
50
|
if (input.files && input.files.length > 0) {
|
|
51
51
|
filesToScan = input.files.map((f) => path.resolve(projectRoot, f));
|
|
52
52
|
} else {
|
|
53
|
-
const { globFiles } = await import("./glob-helper-
|
|
53
|
+
const { globFiles } = await import("./glob-helper-VR6UCZZB.js");
|
|
54
54
|
filesToScan = await globFiles(projectRoot, securityConfig.exclude);
|
|
55
55
|
}
|
|
56
56
|
const result = await scanner.scanFiles(filesToScan);
|
|
@@ -114,7 +114,7 @@ var getSecurityTrendsDefinition = {
|
|
|
114
114
|
};
|
|
115
115
|
async function handleGetSecurityTrends(input) {
|
|
116
116
|
try {
|
|
117
|
-
const core = await import("./dist-
|
|
117
|
+
const core = await import("./dist-6D2F2J57.js");
|
|
118
118
|
const projectRoot = sanitizePath(input.path);
|
|
119
119
|
const manager = new core.SecurityTimelineManager(projectRoot);
|
|
120
120
|
const trendOptions = {};
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
resetParserCache,
|
|
32
32
|
resolveFileToLayer,
|
|
33
33
|
validateDependencies
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-6YFHUIGT.js";
|
|
35
35
|
import {
|
|
36
36
|
AuthAuditEntrySchema,
|
|
37
37
|
AuthTokenPublicSchema,
|
|
@@ -87,7 +87,7 @@ import {
|
|
|
87
87
|
init_dist as init_dist2,
|
|
88
88
|
queryTraceability,
|
|
89
89
|
skipDirGlobs
|
|
90
|
-
} from "./chunk-
|
|
90
|
+
} from "./chunk-WZJR6SOI.js";
|
|
91
91
|
import {
|
|
92
92
|
ArchBaselineSchema,
|
|
93
93
|
ArchConfigSchema,
|
|
@@ -5965,16 +5965,7 @@ function quoteYamlScalar(value) {
|
|
|
5965
5965
|
const escaped = value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
5966
5966
|
return `"${escaped}"`;
|
|
5967
5967
|
}
|
|
5968
|
-
function
|
|
5969
|
-
let data;
|
|
5970
|
-
let content;
|
|
5971
|
-
try {
|
|
5972
|
-
const parsed = matter3(md);
|
|
5973
|
-
data = parsed.data;
|
|
5974
|
-
content = parsed.content;
|
|
5975
|
-
} catch (err) {
|
|
5976
|
-
return Err(new Error(`Shard frontmatter is not valid YAML: ${err.message}`));
|
|
5977
|
-
}
|
|
5968
|
+
function parseShardFrontmatter(data) {
|
|
5978
5969
|
const slug = data.slug;
|
|
5979
5970
|
if (typeof slug !== "string" || slug.length === 0) {
|
|
5980
5971
|
return Err(new Error("Shard frontmatter missing required field: slug"));
|
|
@@ -5990,6 +5981,21 @@ function parseShard(md) {
|
|
|
5990
5981
|
new Error(`Shard "${slug}" has invalid order: "${String(rawOrder)}" (must be a number)`)
|
|
5991
5982
|
);
|
|
5992
5983
|
}
|
|
5984
|
+
return Ok({ slug, milestone, order });
|
|
5985
|
+
}
|
|
5986
|
+
function parseShard(md) {
|
|
5987
|
+
let data;
|
|
5988
|
+
let content;
|
|
5989
|
+
try {
|
|
5990
|
+
const parsed = matter3(md);
|
|
5991
|
+
data = parsed.data;
|
|
5992
|
+
content = parsed.content;
|
|
5993
|
+
} catch (err) {
|
|
5994
|
+
return Err(new Error(`Shard frontmatter is not valid YAML: ${err.message}`));
|
|
5995
|
+
}
|
|
5996
|
+
const frontmatterResult = parseShardFrontmatter(data);
|
|
5997
|
+
if (!frontmatterResult.ok) return frontmatterResult;
|
|
5998
|
+
const { slug, milestone, order } = frontmatterResult.value;
|
|
5993
5999
|
const nameMatch = content.match(H3_NAME);
|
|
5994
6000
|
if (!nameMatch) {
|
|
5995
6001
|
return Err(new Error(`Shard "${slug}" is missing its "### <name>" heading`));
|
|
@@ -6011,6 +6017,17 @@ function serializeShard(shard) {
|
|
|
6011
6017
|
return [...frontmatter, ...serializeFeature(shard.feature)].join("\n") + "\n";
|
|
6012
6018
|
}
|
|
6013
6019
|
function parseMeta(md) {
|
|
6020
|
+
const block = parseFrontmatterBlock(md);
|
|
6021
|
+
if (!block.ok) return Err(block.error);
|
|
6022
|
+
const { data, body } = block.value;
|
|
6023
|
+
const frontmatter = buildFrontmatter(data);
|
|
6024
|
+
if (!frontmatter.ok) return Err(frontmatter.error);
|
|
6025
|
+
const milestones = parseMilestones2(data);
|
|
6026
|
+
if (!milestones.ok) return Err(milestones.error);
|
|
6027
|
+
const meta = { frontmatter: frontmatter.value, milestones: milestones.value };
|
|
6028
|
+
return attachAssignmentHistory(meta, body);
|
|
6029
|
+
}
|
|
6030
|
+
function parseFrontmatterBlock(md) {
|
|
6014
6031
|
const match = md.match(FRONTMATTER);
|
|
6015
6032
|
if (!match) {
|
|
6016
6033
|
return Err(new Error("_meta.md is missing or has malformed YAML frontmatter"));
|
|
@@ -6022,6 +6039,9 @@ function parseMeta(md) {
|
|
|
6022
6039
|
} catch (err) {
|
|
6023
6040
|
return Err(new Error(`_meta.md frontmatter is not valid YAML: ${err.message}`));
|
|
6024
6041
|
}
|
|
6042
|
+
return Ok({ data, body: match[2] ?? "" });
|
|
6043
|
+
}
|
|
6044
|
+
function buildFrontmatter(data) {
|
|
6025
6045
|
const project = data.project;
|
|
6026
6046
|
const rawVersion = data.version;
|
|
6027
6047
|
const lastSynced = data.last_synced;
|
|
@@ -6040,12 +6060,16 @@ function parseMeta(md) {
|
|
|
6040
6060
|
const frontmatter = { project, version, lastSynced, lastManualEdit };
|
|
6041
6061
|
if (typeof data.created === "string") frontmatter.created = data.created;
|
|
6042
6062
|
if (typeof data.updated === "string") frontmatter.updated = data.updated;
|
|
6063
|
+
return Ok(frontmatter);
|
|
6064
|
+
}
|
|
6065
|
+
function parseMilestones2(data) {
|
|
6043
6066
|
const rawMilestones = data.milestones;
|
|
6044
6067
|
if (!Array.isArray(rawMilestones) || !rawMilestones.every((m) => typeof m === "string")) {
|
|
6045
6068
|
return Err(new Error("_meta.md frontmatter `milestones` must be a list of strings"));
|
|
6046
6069
|
}
|
|
6047
|
-
|
|
6048
|
-
|
|
6070
|
+
return Ok(rawMilestones);
|
|
6071
|
+
}
|
|
6072
|
+
function attachAssignmentHistory(meta, body) {
|
|
6049
6073
|
if (body.includes("## Assignment History")) {
|
|
6050
6074
|
const history = parseAssignmentHistory(body);
|
|
6051
6075
|
if (!history.ok) return Err(history.error);
|
|
@@ -6078,43 +6102,49 @@ function isRegression(from, to) {
|
|
|
6078
6102
|
return STATUS_RANK[to] < STATUS_RANK[from];
|
|
6079
6103
|
}
|
|
6080
6104
|
function assembleRoadmap(shards, meta) {
|
|
6105
|
+
const byMilestone = groupShardsByMilestone(shards);
|
|
6106
|
+
const ordered = orderMilestoneNames(meta, byMilestone);
|
|
6107
|
+
const milestones = ordered.map((name) => buildMilestone(name, byMilestone));
|
|
6108
|
+
return {
|
|
6109
|
+
frontmatter: meta.frontmatter,
|
|
6110
|
+
milestones,
|
|
6111
|
+
assignmentHistory: meta.assignmentHistory ?? []
|
|
6112
|
+
};
|
|
6113
|
+
}
|
|
6114
|
+
function groupShardsByMilestone(shards) {
|
|
6081
6115
|
const byMilestone = /* @__PURE__ */ new Map();
|
|
6082
6116
|
for (const shard of shards) {
|
|
6083
6117
|
const bucket = byMilestone.get(shard.milestone);
|
|
6084
6118
|
if (bucket) bucket.push(shard);
|
|
6085
6119
|
else byMilestone.set(shard.milestone, [shard]);
|
|
6086
6120
|
}
|
|
6121
|
+
return byMilestone;
|
|
6122
|
+
}
|
|
6123
|
+
function orderMilestoneNames(meta, byMilestone) {
|
|
6087
6124
|
const ordered = [];
|
|
6088
6125
|
const seen = /* @__PURE__ */ new Set();
|
|
6089
|
-
|
|
6126
|
+
const append = (name) => {
|
|
6090
6127
|
if (!seen.has(name)) {
|
|
6091
6128
|
ordered.push(name);
|
|
6092
6129
|
seen.add(name);
|
|
6093
6130
|
}
|
|
6094
|
-
}
|
|
6095
|
-
for (const name of
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6106
|
-
|
|
6107
|
-
);
|
|
6108
|
-
return {
|
|
6109
|
-
name,
|
|
6110
|
-
isBacklog: name === "Backlog",
|
|
6111
|
-
features: bucket.map((s) => s.feature)
|
|
6112
|
-
};
|
|
6113
|
-
});
|
|
6131
|
+
};
|
|
6132
|
+
for (const name of meta.milestones) append(name);
|
|
6133
|
+
for (const name of byMilestone.keys()) append(name);
|
|
6134
|
+
return ordered;
|
|
6135
|
+
}
|
|
6136
|
+
function compareShards(a, b) {
|
|
6137
|
+
return a.order - b.order || STATUS_RANK[b.feature.status] - STATUS_RANK[a.feature.status] || // Deterministic code-unit comparison (NOT localeCompare, whose ICU/locale
|
|
6138
|
+
// collation is environment-dependent and threatens byte-stable regen).
|
|
6139
|
+
(a.slug < b.slug ? -1 : a.slug > b.slug ? 1 : 0);
|
|
6140
|
+
}
|
|
6141
|
+
function buildMilestone(name, byMilestone) {
|
|
6142
|
+
const bucket = [...byMilestone.get(name) ?? []];
|
|
6143
|
+
bucket.sort(compareShards);
|
|
6114
6144
|
return {
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6145
|
+
name,
|
|
6146
|
+
isBacklog: name === "Backlog",
|
|
6147
|
+
features: bucket.map((s) => s.feature)
|
|
6118
6148
|
};
|
|
6119
6149
|
}
|
|
6120
6150
|
function joinPath(dir, name) {
|
|
@@ -13270,6 +13300,21 @@ function linearFromPriority(p) {
|
|
|
13270
13300
|
return 0;
|
|
13271
13301
|
}
|
|
13272
13302
|
}
|
|
13303
|
+
function metaDerivedFields(meta, issue) {
|
|
13304
|
+
return {
|
|
13305
|
+
spec: meta.spec ?? null,
|
|
13306
|
+
plans: meta.plan ? [meta.plan] : [],
|
|
13307
|
+
blockedBy: meta.blocked_by ?? [],
|
|
13308
|
+
assignee: assigneeDisplayName(issue.assignee),
|
|
13309
|
+
priority: meta.priority ?? priorityFromLinear(issue.priority),
|
|
13310
|
+
milestone: meta.milestone ?? null,
|
|
13311
|
+
createdAt: issue.createdAt ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
13312
|
+
updatedAt: issue.updatedAt ?? null
|
|
13313
|
+
};
|
|
13314
|
+
}
|
|
13315
|
+
function assigneeDisplayName(assignee) {
|
|
13316
|
+
return assignee?.displayName ?? assignee?.name ?? null;
|
|
13317
|
+
}
|
|
13273
13318
|
function metaFrom(feature) {
|
|
13274
13319
|
const meta = {};
|
|
13275
13320
|
if (feature.spec != null) meta.spec = feature.spec;
|
|
@@ -13279,6 +13324,25 @@ function metaFrom(feature) {
|
|
|
13279
13324
|
if (feature.milestone != null) meta.milestone = feature.milestone;
|
|
13280
13325
|
return meta;
|
|
13281
13326
|
}
|
|
13327
|
+
function collectHistoryEvents(nodes) {
|
|
13328
|
+
const events = [];
|
|
13329
|
+
for (const c of nodes ?? []) {
|
|
13330
|
+
const parsed = parseHistoryComment(c.body);
|
|
13331
|
+
if (parsed) events.push(parsed);
|
|
13332
|
+
}
|
|
13333
|
+
return events;
|
|
13334
|
+
}
|
|
13335
|
+
function parseHistoryComment(body) {
|
|
13336
|
+
if (!body || !body.includes(HISTORY_MARKER)) return null;
|
|
13337
|
+
const m = /```json\s*([\s\S]*?)\s*```/.exec(body);
|
|
13338
|
+
if (!m?.[1]) return null;
|
|
13339
|
+
try {
|
|
13340
|
+
const parsed = JSON.parse(m[1]);
|
|
13341
|
+
if (parsed && typeof parsed.type === "string") return parsed;
|
|
13342
|
+
} catch {
|
|
13343
|
+
}
|
|
13344
|
+
return null;
|
|
13345
|
+
}
|
|
13282
13346
|
function createTrackerClient(config) {
|
|
13283
13347
|
if (config.kind === "github-issues") {
|
|
13284
13348
|
const token = config.token ?? process.env.GITHUB_TOKEN;
|
|
@@ -13725,6 +13789,50 @@ function checkRoadmapHealth(roadmap, options) {
|
|
|
13725
13789
|
}
|
|
13726
13790
|
return findings;
|
|
13727
13791
|
}
|
|
13792
|
+
function demoteUnactionablePlanned(feature, milestoneName, changes) {
|
|
13793
|
+
if (!isUnactionablePlanned(feature)) return;
|
|
13794
|
+
feature.status = "backlog";
|
|
13795
|
+
changes.push({
|
|
13796
|
+
kind: "demoted",
|
|
13797
|
+
feature: feature.name,
|
|
13798
|
+
from: milestoneName,
|
|
13799
|
+
to: "backlog"
|
|
13800
|
+
});
|
|
13801
|
+
}
|
|
13802
|
+
function restoreAssigneeInvariant(feature, milestoneName, changes) {
|
|
13803
|
+
if (feature.status !== "in-progress" && feature.assignee !== null) {
|
|
13804
|
+
changes.push({
|
|
13805
|
+
kind: "unassigned",
|
|
13806
|
+
feature: feature.name,
|
|
13807
|
+
from: milestoneName,
|
|
13808
|
+
to: feature.assignee
|
|
13809
|
+
});
|
|
13810
|
+
feature.assignee = null;
|
|
13811
|
+
} else if (feature.status === "in-progress" && feature.assignee === null) {
|
|
13812
|
+
feature.status = "planned";
|
|
13813
|
+
changes.push({
|
|
13814
|
+
kind: "demoted",
|
|
13815
|
+
feature: feature.name,
|
|
13816
|
+
from: milestoneName,
|
|
13817
|
+
to: "planned"
|
|
13818
|
+
});
|
|
13819
|
+
}
|
|
13820
|
+
}
|
|
13821
|
+
function groomFeature(feature, milestoneName, context, archived, changes) {
|
|
13822
|
+
demoteUnactionablePlanned(feature, milestoneName, changes);
|
|
13823
|
+
restoreAssigneeInvariant(feature, milestoneName, changes);
|
|
13824
|
+
if (context.archiveDone && feature.status === "done" && !context.archive && !context.isIntake) {
|
|
13825
|
+
archived.push(feature);
|
|
13826
|
+
changes.push({
|
|
13827
|
+
kind: "archived",
|
|
13828
|
+
feature: feature.name,
|
|
13829
|
+
from: milestoneName,
|
|
13830
|
+
to: "Shipped"
|
|
13831
|
+
});
|
|
13832
|
+
return false;
|
|
13833
|
+
}
|
|
13834
|
+
return true;
|
|
13835
|
+
}
|
|
13728
13836
|
function groomRoadmap(roadmap, options) {
|
|
13729
13837
|
const opts = resolveOptions(options);
|
|
13730
13838
|
const archiveDone = options?.archiveDone ?? true;
|
|
@@ -13732,49 +13840,14 @@ function groomRoadmap(roadmap, options) {
|
|
|
13732
13840
|
const archived = [];
|
|
13733
13841
|
const changes = [];
|
|
13734
13842
|
for (const milestone of next.milestones) {
|
|
13735
|
-
const
|
|
13736
|
-
|
|
13737
|
-
|
|
13738
|
-
|
|
13739
|
-
|
|
13740
|
-
|
|
13741
|
-
|
|
13742
|
-
|
|
13743
|
-
feature: feature.name,
|
|
13744
|
-
from: milestone.name,
|
|
13745
|
-
to: "backlog"
|
|
13746
|
-
});
|
|
13747
|
-
}
|
|
13748
|
-
if (feature.status !== "in-progress" && feature.assignee !== null) {
|
|
13749
|
-
changes.push({
|
|
13750
|
-
kind: "unassigned",
|
|
13751
|
-
feature: feature.name,
|
|
13752
|
-
from: milestone.name,
|
|
13753
|
-
to: feature.assignee
|
|
13754
|
-
});
|
|
13755
|
-
feature.assignee = null;
|
|
13756
|
-
} else if (feature.status === "in-progress" && feature.assignee === null) {
|
|
13757
|
-
feature.status = "planned";
|
|
13758
|
-
changes.push({
|
|
13759
|
-
kind: "demoted",
|
|
13760
|
-
feature: feature.name,
|
|
13761
|
-
from: milestone.name,
|
|
13762
|
-
to: "planned"
|
|
13763
|
-
});
|
|
13764
|
-
}
|
|
13765
|
-
if (archiveDone && feature.status === "done" && !archive && !isIntake) {
|
|
13766
|
-
archived.push(feature);
|
|
13767
|
-
changes.push({
|
|
13768
|
-
kind: "archived",
|
|
13769
|
-
feature: feature.name,
|
|
13770
|
-
from: milestone.name,
|
|
13771
|
-
to: "Shipped"
|
|
13772
|
-
});
|
|
13773
|
-
continue;
|
|
13774
|
-
}
|
|
13775
|
-
kept.push(feature);
|
|
13776
|
-
}
|
|
13777
|
-
milestone.features = kept;
|
|
13843
|
+
const context = {
|
|
13844
|
+
archiveDone,
|
|
13845
|
+
archive: opts.isArchive(milestone.name),
|
|
13846
|
+
isIntake: milestone.name === opts.intakeMilestone
|
|
13847
|
+
};
|
|
13848
|
+
milestone.features = milestone.features.filter(
|
|
13849
|
+
(feature) => groomFeature(feature, milestone.name, context, archived, changes)
|
|
13850
|
+
);
|
|
13778
13851
|
}
|
|
13779
13852
|
next.milestones = next.milestones.filter(
|
|
13780
13853
|
(m) => m.features.length > 0 || m.name === opts.intakeMilestone
|
|
@@ -15060,7 +15133,7 @@ function arrayLen(arr) {
|
|
|
15060
15133
|
return Array.isArray(arr) ? arr.length : 0;
|
|
15061
15134
|
}
|
|
15062
15135
|
async function gatherEntropyBlock(projectPath) {
|
|
15063
|
-
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./analyzer-V633GUHY-
|
|
15136
|
+
const { EntropyAnalyzer: EntropyAnalyzer2 } = await import("./analyzer-V633GUHY-OV335HGM.js");
|
|
15064
15137
|
const analyzer = new EntropyAnalyzer2({
|
|
15065
15138
|
rootDir: projectPath,
|
|
15066
15139
|
include: ["src/**/*.ts", "src/**/*.tsx", "packages/*/src/**/*.ts"],
|
|
@@ -21823,14 +21896,7 @@ ${JSON.stringify(event)}`;
|
|
|
21823
21896
|
name: issue.title,
|
|
21824
21897
|
status: statusFromStateType(issue.state?.type),
|
|
21825
21898
|
summary,
|
|
21826
|
-
|
|
21827
|
-
plans: meta.plan ? [meta.plan] : [],
|
|
21828
|
-
blockedBy: meta.blocked_by ?? [],
|
|
21829
|
-
assignee: issue.assignee?.displayName ?? issue.assignee?.name ?? null,
|
|
21830
|
-
priority: meta.priority ?? priorityFromLinear(issue.priority),
|
|
21831
|
-
milestone: meta.milestone ?? null,
|
|
21832
|
-
createdAt: issue.createdAt ?? (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
21833
|
-
updatedAt: issue.updatedAt ?? null
|
|
21899
|
+
...metaDerivedFields(meta, issue)
|
|
21834
21900
|
};
|
|
21835
21901
|
}
|
|
21836
21902
|
/** Resolve (and cache) the team's first state id of each workflow type. */
|
|
@@ -21858,6 +21924,12 @@ ${JSON.stringify(event)}`;
|
|
|
21858
21924
|
if (!r.ok) return r;
|
|
21859
21925
|
return Ok(r.value.users?.nodes?.[0]?.id ?? null);
|
|
21860
21926
|
}
|
|
21927
|
+
/** Resolve an assignee to a Linear user id, or null when absent/unresolved. */
|
|
21928
|
+
async assigneeIdFor(assignee) {
|
|
21929
|
+
if (!assignee) return null;
|
|
21930
|
+
const userR = await this.resolveUserId(assignee);
|
|
21931
|
+
return userR.ok && userR.value ? userR.value : null;
|
|
21932
|
+
}
|
|
21861
21933
|
// ── Reads ──────────────────────────────────────────────────────────────
|
|
21862
21934
|
async fetchAll() {
|
|
21863
21935
|
const r = await this.gql(
|
|
@@ -21888,18 +21960,9 @@ ${JSON.stringify(event)}`;
|
|
|
21888
21960
|
async create(feature) {
|
|
21889
21961
|
const stateR = await this.resolveStateId(feature.status ?? "backlog");
|
|
21890
21962
|
if (!stateR.ok) return stateR;
|
|
21891
|
-
const
|
|
21892
|
-
const
|
|
21893
|
-
|
|
21894
|
-
title: feature.name,
|
|
21895
|
-
description: body,
|
|
21896
|
-
stateId: stateR.value,
|
|
21897
|
-
priority: linearFromPriority(feature.priority)
|
|
21898
|
-
};
|
|
21899
|
-
if (feature.assignee) {
|
|
21900
|
-
const userR = await this.resolveUserId(feature.assignee);
|
|
21901
|
-
if (userR.ok && userR.value) input.assigneeId = userR.value;
|
|
21902
|
-
}
|
|
21963
|
+
const input = this.buildCreateInput(feature, stateR.value);
|
|
21964
|
+
const assigneeId = await this.assigneeIdFor(feature.assignee);
|
|
21965
|
+
if (assigneeId) input.assigneeId = assigneeId;
|
|
21903
21966
|
const r = await this.gql(
|
|
21904
21967
|
`mutation($input:IssueCreateInput!){ issueCreate(input:$input){ issue { ${ISSUE_FIELDS} } } }`,
|
|
21905
21968
|
{ input }
|
|
@@ -21909,6 +21972,16 @@ ${JSON.stringify(event)}`;
|
|
|
21909
21972
|
if (!issue) return Err(new Error("Linear issueCreate returned no issue"));
|
|
21910
21973
|
return Ok(this.featureFromIssue(issue));
|
|
21911
21974
|
}
|
|
21975
|
+
/** Assemble the IssueCreateInput payload for a new feature (sans assignee, which is resolved async). */
|
|
21976
|
+
buildCreateInput(feature, stateId) {
|
|
21977
|
+
return {
|
|
21978
|
+
teamId: this.teamId,
|
|
21979
|
+
title: feature.name,
|
|
21980
|
+
description: serializeBodyBlock(feature.summary ?? "", metaFrom(feature)),
|
|
21981
|
+
stateId,
|
|
21982
|
+
priority: linearFromPriority(feature.priority)
|
|
21983
|
+
};
|
|
21984
|
+
}
|
|
21912
21985
|
/** Shared issueUpdate with optional optimistic-concurrency check on updatedAt. */
|
|
21913
21986
|
async issueUpdate(externalId, input, ifMatch) {
|
|
21914
21987
|
const id = this.toIssueId(externalId);
|
|
@@ -21935,6 +22008,26 @@ ${JSON.stringify(event)}`;
|
|
|
21935
22008
|
if (!issue) return Err(new Error("Linear issueUpdate returned no issue"));
|
|
21936
22009
|
return Ok(this.featureFromIssue(issue));
|
|
21937
22010
|
}
|
|
22011
|
+
/**
|
|
22012
|
+
* Merge body-backed patch fields into the current issue's harness-meta block,
|
|
22013
|
+
* returning the serialized description (so the meta block isn't clobbered).
|
|
22014
|
+
*/
|
|
22015
|
+
async mergedDescription(externalId, patch) {
|
|
22016
|
+
const cur = await this.fetchById(externalId);
|
|
22017
|
+
if (!cur.ok) return cur;
|
|
22018
|
+
if (!cur.value) return Err(new Error(`Linear issue not found: ${externalId}`));
|
|
22019
|
+
const f = cur.value.feature;
|
|
22020
|
+
const merged = {
|
|
22021
|
+
name: patch.name ?? f.name,
|
|
22022
|
+
summary: patch.summary ?? f.summary,
|
|
22023
|
+
spec: patch.spec !== void 0 ? patch.spec : f.spec,
|
|
22024
|
+
plans: patch.plans !== void 0 ? patch.plans : f.plans,
|
|
22025
|
+
blockedBy: patch.blockedBy !== void 0 ? patch.blockedBy : f.blockedBy,
|
|
22026
|
+
priority: patch.priority !== void 0 ? patch.priority : f.priority,
|
|
22027
|
+
milestone: patch.milestone !== void 0 ? patch.milestone : f.milestone
|
|
22028
|
+
};
|
|
22029
|
+
return Ok(serializeBodyBlock(merged.summary ?? "", metaFrom(merged)));
|
|
22030
|
+
}
|
|
21938
22031
|
async update(externalId, patch, ifMatch) {
|
|
21939
22032
|
const input = {};
|
|
21940
22033
|
if (patch.name !== void 0) input.title = patch.name;
|
|
@@ -21953,31 +22046,23 @@ ${JSON.stringify(event)}`;
|
|
|
21953
22046
|
"milestone"
|
|
21954
22047
|
];
|
|
21955
22048
|
if (bodyFields.some((k) => patch[k] !== void 0)) {
|
|
21956
|
-
const
|
|
21957
|
-
if (!
|
|
21958
|
-
|
|
21959
|
-
const f = cur.value.feature;
|
|
21960
|
-
const merged = {
|
|
21961
|
-
name: patch.name ?? f.name,
|
|
21962
|
-
summary: patch.summary ?? f.summary,
|
|
21963
|
-
spec: patch.spec !== void 0 ? patch.spec : f.spec,
|
|
21964
|
-
plans: patch.plans !== void 0 ? patch.plans : f.plans,
|
|
21965
|
-
blockedBy: patch.blockedBy !== void 0 ? patch.blockedBy : f.blockedBy,
|
|
21966
|
-
priority: patch.priority !== void 0 ? patch.priority : f.priority,
|
|
21967
|
-
milestone: patch.milestone !== void 0 ? patch.milestone : f.milestone
|
|
21968
|
-
};
|
|
21969
|
-
input.description = serializeBodyBlock(merged.summary ?? "", metaFrom(merged));
|
|
21970
|
-
}
|
|
21971
|
-
if (patch.assignee !== void 0) {
|
|
21972
|
-
if (patch.assignee === null) {
|
|
21973
|
-
input.assigneeId = null;
|
|
21974
|
-
} else {
|
|
21975
|
-
const userR = await this.resolveUserId(patch.assignee);
|
|
21976
|
-
if (userR.ok && userR.value) input.assigneeId = userR.value;
|
|
21977
|
-
}
|
|
22049
|
+
const descR = await this.mergedDescription(externalId, patch);
|
|
22050
|
+
if (!descR.ok) return descR;
|
|
22051
|
+
input.description = descR.value;
|
|
21978
22052
|
}
|
|
22053
|
+
await this.applyAssigneeUpdate(patch, input);
|
|
21979
22054
|
return this.issueUpdate(externalId, input, ifMatch);
|
|
21980
22055
|
}
|
|
22056
|
+
/** Resolve `patch.assignee` (name → user id, or explicit null to unassign) into the update input. */
|
|
22057
|
+
async applyAssigneeUpdate(patch, input) {
|
|
22058
|
+
if (patch.assignee === void 0) return;
|
|
22059
|
+
if (patch.assignee === null) {
|
|
22060
|
+
input.assigneeId = null;
|
|
22061
|
+
return;
|
|
22062
|
+
}
|
|
22063
|
+
const userR = await this.resolveUserId(patch.assignee);
|
|
22064
|
+
if (userR.ok && userR.value) input.assigneeId = userR.value;
|
|
22065
|
+
}
|
|
21981
22066
|
async claim(externalId, assignee, ifMatch) {
|
|
21982
22067
|
const userR = await this.resolveUserId(assignee);
|
|
21983
22068
|
if (!userR.ok) return userR;
|
|
@@ -22017,17 +22102,7 @@ ${JSON.stringify(event)}
|
|
|
22017
22102
|
{ id: this.toIssueId(externalId) }
|
|
22018
22103
|
);
|
|
22019
22104
|
if (!r.ok) return r;
|
|
22020
|
-
const events =
|
|
22021
|
-
for (const c of r.value.issue?.comments?.nodes ?? []) {
|
|
22022
|
-
if (!c.body || !c.body.includes(HISTORY_MARKER)) continue;
|
|
22023
|
-
const m = /```json\s*([\s\S]*?)\s*```/.exec(c.body);
|
|
22024
|
-
if (!m?.[1]) continue;
|
|
22025
|
-
try {
|
|
22026
|
-
const parsed = JSON.parse(m[1]);
|
|
22027
|
-
if (parsed && typeof parsed.type === "string") events.push(parsed);
|
|
22028
|
-
} catch {
|
|
22029
|
-
}
|
|
22030
|
-
}
|
|
22105
|
+
const events = collectHistoryEvents(r.value.issue?.comments?.nodes);
|
|
22031
22106
|
return Ok(typeof limit === "number" ? events.slice(0, limit) : events);
|
|
22032
22107
|
}
|
|
22033
22108
|
};
|