@harness-engineering/cli 2.0.0 → 2.1.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/harness-compound/SKILL.md +153 -0
- package/dist/agents/skills/claude-code/harness-compound/skill.yaml +60 -0
- package/dist/agents/skills/claude-code/harness-knowledge-pipeline/SKILL.md +12 -1
- package/dist/agents/skills/claude-code/harness-observability/SKILL.md +2 -0
- package/dist/agents/skills/claude-code/harness-pulse/SKILL.md +138 -0
- package/dist/agents/skills/claude-code/harness-pulse/references/interview.md +77 -0
- package/dist/agents/skills/claude-code/harness-pulse/skill.yaml +61 -0
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +25 -1
- package/dist/agents/skills/codex/harness-compound/SKILL.md +153 -0
- package/dist/agents/skills/codex/harness-compound/skill.yaml +60 -0
- package/dist/agents/skills/codex/harness-knowledge-pipeline/SKILL.md +12 -1
- package/dist/agents/skills/codex/harness-observability/SKILL.md +2 -0
- package/dist/agents/skills/codex/harness-pulse/SKILL.md +138 -0
- package/dist/agents/skills/codex/harness-pulse/references/interview.md +77 -0
- package/dist/agents/skills/codex/harness-pulse/skill.yaml +61 -0
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +25 -1
- package/dist/agents/skills/cursor/harness-compound/SKILL.md +153 -0
- package/dist/agents/skills/cursor/harness-compound/skill.yaml +60 -0
- package/dist/agents/skills/cursor/harness-knowledge-pipeline/SKILL.md +12 -1
- package/dist/agents/skills/cursor/harness-observability/SKILL.md +2 -0
- package/dist/agents/skills/cursor/harness-pulse/SKILL.md +138 -0
- package/dist/agents/skills/cursor/harness-pulse/references/interview.md +77 -0
- package/dist/agents/skills/cursor/harness-pulse/skill.yaml +61 -0
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +25 -1
- package/dist/agents/skills/gemini-cli/harness-compound/SKILL.md +153 -0
- package/dist/agents/skills/gemini-cli/harness-compound/skill.yaml +60 -0
- package/dist/agents/skills/gemini-cli/harness-knowledge-pipeline/SKILL.md +12 -1
- package/dist/agents/skills/gemini-cli/harness-observability/SKILL.md +2 -0
- package/dist/agents/skills/gemini-cli/harness-pulse/SKILL.md +138 -0
- package/dist/agents/skills/gemini-cli/harness-pulse/references/interview.md +77 -0
- package/dist/agents/skills/gemini-cli/harness-pulse/skill.yaml +61 -0
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +25 -1
- package/dist/agents/skills/package.json +1 -0
- package/dist/agents/skills/tests/fixtures/harness-compound/bug-track-fixture/input.json +10 -0
- package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/docs/solutions/bug-track/integration-issues/stalled-lease-cleanup.md +28 -0
- package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/expected.json +4 -0
- package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/input.json +10 -0
- package/dist/agents/skills/tests/fixtures/harness-compound/knowledge-track-fixture/input.json +10 -0
- package/dist/agents/skills/tests/harness-compound.test.ts +86 -0
- package/dist/{agents-md-6SAMHE7H.js → agents-md-L4X4MOWT.js} +3 -3
- package/dist/{architecture-5YR52NSV.js → architecture-WJRIYQ45.js} +4 -4
- package/dist/{assess-project-5X7TYQFM.js → assess-project-PHC2HO3V.js} +1 -1
- package/dist/bin/harness-mcp.js +16 -15
- package/dist/bin/harness.js +26 -25
- package/dist/{check-phase-gate-PLV2VDOL.js → check-phase-gate-646KY67H.js} +5 -4
- package/dist/{chunk-ZYLBIDLE.js → chunk-224XKVLP.js} +1 -1
- package/dist/{chunk-ELJ2MXMO.js → chunk-2QPHCR22.js} +9 -9
- package/dist/{chunk-SITVRO7K.js → chunk-2ZURBWEV.js} +9 -9
- package/dist/{chunk-2IBXUG6J.js → chunk-36X7TRUI.js} +2 -2
- package/dist/{chunk-6L2BU4BP.js → chunk-4HKVZO7R.js} +1 -1
- package/dist/{chunk-74YADB7T.js → chunk-7XQYOXTB.js} +96 -94
- package/dist/{chunk-S2WB4LE7.js → chunk-BMM3GXV6.js} +384 -28
- package/dist/{chunk-BYS3TQOO.js → chunk-CPYL6LJR.js} +1 -1
- package/dist/{chunk-QDRBWVVY.js → chunk-E7W3YOXL.js} +2 -2
- package/dist/{chunk-MTPHNOJW.js → chunk-FNCJ3UCU.js} +507 -244
- package/dist/{chunk-RYDPFIJY.js → chunk-JPWICLXQ.js} +1 -1
- package/dist/{chunk-RFYF7TJS.js → chunk-K4WRQTEF.js} +1835 -805
- package/dist/{chunk-DXGI7DL3.js → chunk-KTWKW6KJ.js} +1 -1
- package/dist/{chunk-JNKALGRM.js → chunk-KV7A5OZT.js} +5 -5
- package/dist/{chunk-KJD4J54I.js → chunk-LO7ZLV7S.js} +6 -1
- package/dist/{chunk-XHOGGZS7.js → chunk-QTUPHTTK.js} +1 -1
- package/dist/{chunk-R2CBBZA6.js → chunk-SVC7THFK.js} +1 -1
- package/dist/{chunk-PFM3BVVU.js → chunk-TNZYQTEE.js} +1 -1
- package/dist/{chunk-HT7HYYGT.js → chunk-VJQLUS5I.js} +1 -1
- package/dist/{chunk-6GXSJGSW.js → chunk-VKDBM3RX.js} +3 -3
- package/dist/{chunk-JZST2WZS.js → chunk-W4AFGFFB.js} +3 -3
- package/dist/{chunk-YJE23GRZ.js → chunk-YG4ZBGEH.js} +39 -5
- package/dist/{chunk-5ME3WG3G.js → chunk-YLOAQZLA.js} +5 -5
- package/dist/{chunk-7JQ7HLM4.js → chunk-YLTRFZAT.js} +8 -8
- package/dist/{chunk-BFBZK6BC.js → chunk-YWD6WXXM.js} +6 -6
- package/dist/chunk-ZCZD7FHU.js +16 -0
- package/dist/{ci-workflow-7T2XKB3P.js → ci-workflow-TOS5X527.js} +3 -3
- package/dist/{dist-77VNTHWV.js → dist-6RT2AVYU.js} +78 -2
- package/dist/{dist-42AYXCAK.js → dist-D27LJR2T.js} +7 -1
- package/dist/{docs-MSIJ6SDO.js → docs-HOJMQYDZ.js} +5 -4
- package/dist/{engine-F4P5RBE4.js → engine-D6UYH6DB.js} +3 -3
- package/dist/{entropy-GSUTIORR.js → entropy-LZ4IDGZR.js} +3 -3
- package/dist/{feedback-NST6T4HI.js → feedback-WV7EQVK7.js} +1 -1
- package/dist/{generate-agent-definitions-BOUGQIPA.js → generate-agent-definitions-ZHFFS6YG.js} +4 -4
- package/dist/glob-helper-FZPPTGJN.js +62 -0
- package/dist/{graph-loader-IAUPGLRV.js → graph-loader-5MKHAJJF.js} +1 -1
- package/dist/index.d.ts +44 -6
- package/dist/index.js +26 -25
- package/dist/{loader-6QTJC7IN.js → loader-Z23JTXIZ.js} +3 -3
- package/dist/{mcp-QFBGTUX6.js → mcp-QFZDPFNP.js} +16 -15
- package/dist/{performance-B2NAAIE5.js → performance-7ATLHTQU.js} +5 -4
- package/dist/{review-pipeline-6EYMBS6B.js → review-pipeline-COBGGXHJ.js} +3 -3
- package/dist/{runtime-VFZK6BCZ.js → runtime-KCBNQUXK.js} +3 -3
- package/dist/{scan-DB6FNYGG.js → scan-BIJYHOE7.js} +2 -1
- package/dist/{security-HRNTYHPM.js → security-MAPRIFQT.js} +1 -1
- package/dist/templates/orchestrator/harness.orchestrator.md +9 -1
- package/dist/{validate-YEMAFWJE.js → validate-74XTQR7Q.js} +4 -4
- package/dist/{validate-cross-check-DGHTJFVH.js → validate-cross-check-MI5MZ7MB.js} +3 -3
- package/package.json +7 -6
- package/dist/glob-helper-VCQXK5XY.js +0 -54
|
@@ -13,13 +13,13 @@ import {
|
|
|
13
13
|
NODE_STABILITY,
|
|
14
14
|
init_dist as init_dist2,
|
|
15
15
|
queryTraceability
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-BMM3GXV6.js";
|
|
17
17
|
import {
|
|
18
18
|
__esm,
|
|
19
19
|
__export
|
|
20
20
|
} from "./chunk-KFQGP6VL.js";
|
|
21
21
|
|
|
22
|
-
// ../core/dist/chunk-
|
|
22
|
+
// ../core/dist/chunk-CE225WRK.mjs
|
|
23
23
|
import { z } from "zod";
|
|
24
24
|
import { createHash } from "crypto";
|
|
25
25
|
import { minimatch } from "minimatch";
|
|
@@ -69,17 +69,17 @@ function resolveFileToLayer(file, layers) {
|
|
|
69
69
|
}
|
|
70
70
|
return void 0;
|
|
71
71
|
}
|
|
72
|
-
async function fileExists(
|
|
72
|
+
async function fileExists(path42) {
|
|
73
73
|
try {
|
|
74
|
-
await accessAsync(
|
|
74
|
+
await accessAsync(path42, constants.F_OK);
|
|
75
75
|
return true;
|
|
76
76
|
} catch {
|
|
77
77
|
return false;
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
async function readFileContent(
|
|
80
|
+
async function readFileContent(path42) {
|
|
81
81
|
try {
|
|
82
|
-
const content = await readFileAsync(
|
|
82
|
+
const content = await readFileAsync(path42, "utf-8");
|
|
83
83
|
return Ok(content);
|
|
84
84
|
} catch (error) {
|
|
85
85
|
return Err(error);
|
|
@@ -115,20 +115,32 @@ function getExtensionsForLang(lang) {
|
|
|
115
115
|
return [".ts", ".tsx", ".js", ".jsx"];
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
|
-
function resolveImportPath(importSource, fromFile, _rootDir) {
|
|
118
|
+
async function resolveImportPath(importSource, fromFile, _rootDir) {
|
|
119
119
|
if (!importSource.startsWith(".") && !importSource.startsWith("/")) {
|
|
120
120
|
return null;
|
|
121
121
|
}
|
|
122
122
|
const fromDir = dirname(fromFile);
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
const
|
|
123
|
+
const resolved = resolve(fromDir, importSource);
|
|
124
|
+
const sourceExt = extname(resolved);
|
|
125
|
+
const fromLang = detectLangFromExt(extname(fromFile));
|
|
126
|
+
const fallbacks = JS_EXT_FALLBACKS[sourceExt];
|
|
127
|
+
if (fallbacks) {
|
|
128
|
+
const base = resolved.slice(0, -sourceExt.length);
|
|
129
|
+
for (const ext of fallbacks) {
|
|
130
|
+
const candidate = base + ext;
|
|
131
|
+
if (await fileExists(candidate)) return candidate.replace(/\\/g, "/");
|
|
132
|
+
}
|
|
133
|
+
for (const indexExt of [".ts", ".tsx", ".jsx"]) {
|
|
134
|
+
const indexPath2 = resolve(base, "index" + indexExt);
|
|
135
|
+
if (await fileExists(indexPath2)) return indexPath2.replace(/\\/g, "/");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
127
138
|
const hasKnownExt = Object.values(EXTENSION_BY_LANG).flat().some((e) => resolved.endsWith(e));
|
|
128
|
-
if (
|
|
129
|
-
|
|
139
|
+
if (hasKnownExt) {
|
|
140
|
+
return resolved.replace(/\\/g, "/");
|
|
130
141
|
}
|
|
131
|
-
|
|
142
|
+
const extensions = getExtensionsForLang(fromLang);
|
|
143
|
+
return (resolved + extensions[0]).replace(/\\/g, "/");
|
|
132
144
|
}
|
|
133
145
|
function getImportType(imp) {
|
|
134
146
|
if (imp.kind === "type") return "type-only";
|
|
@@ -157,7 +169,7 @@ async function buildDependencyGraph(files, parser, graphDependencyData) {
|
|
|
157
169
|
continue;
|
|
158
170
|
}
|
|
159
171
|
for (const imp of importsResult.value) {
|
|
160
|
-
const resolvedPath = resolveImportPath(imp.source, file, "");
|
|
172
|
+
const resolvedPath = await resolveImportPath(imp.source, file, "");
|
|
161
173
|
if (resolvedPath) {
|
|
162
174
|
edges.push({
|
|
163
175
|
from: normalizedFile,
|
|
@@ -473,9 +485,9 @@ async function resolveWasmPath(grammarName) {
|
|
|
473
485
|
const { createRequire } = await import("module");
|
|
474
486
|
const require2 = createRequire(import.meta.url ?? __filename);
|
|
475
487
|
const pkgPath = require2.resolve("tree-sitter-wasms/package.json");
|
|
476
|
-
const
|
|
477
|
-
const pkgDir =
|
|
478
|
-
return
|
|
488
|
+
const path42 = await import("path");
|
|
489
|
+
const pkgDir = path42.dirname(pkgPath);
|
|
490
|
+
return path42.join(pkgDir, "out", `${grammarName}.wasm`);
|
|
479
491
|
}
|
|
480
492
|
async function loadLanguage(lang) {
|
|
481
493
|
const grammarName = GRAMMAR_MAP[lang];
|
|
@@ -1733,9 +1745,9 @@ async function toHaveMaxDepDepth(received, maxDepth) {
|
|
|
1733
1745
|
message: () => pass ? `Expected dependency depth in '${received.scope}' to exceed ${maxDepth} but it was ${maxActual}` : `Module '${received.scope}' has dependency depth ${maxActual} (limit: ${maxDepth})`
|
|
1734
1746
|
};
|
|
1735
1747
|
}
|
|
1736
|
-
var ArchMetricCategorySchema, ViolationSchema, MetricResultSchema, CategoryBaselineSchema, ArchBaselineSchema, CategoryRegressionSchema, ArchDiffResultSchema, ThresholdConfigSchema, ArchConfigSchema, ConstraintRuleSchema, ViolationSnapshotSchema, ViolationHistorySchema, EmergenceConfidenceSchema, EmergentConstraintSuggestionSchema, EmergenceResultSchema, accessAsync, readFileAsync, EXTENSION_BY_LANG, CircularDepsCollector, TypeScriptParser, EXTENSION_MAP, parserCache, initialized, GRAMMAR_MAP, TOP_LEVEL_TYPES, METHOD_TYPES, IDENTIFIER_TYPES, NAME_EXTRACTORS, pythonStrategy, goStrategy, RUST_USE_ARG_TYPES, RUST_PUB_ITEM_TYPES, rustStrategy, JAVA_IMPORT_TYPES, JAVA_EXPORT_TYPES, javaStrategy, STRATEGIES, TreeSitterParser, ParserRegistry, defaultRegistry, LayerViolationCollector, ArchBaselineManager, DEFAULT_THRESHOLDS, FUNCTION_PATTERNS, DECISION_PATTERNS, ComplexityCollector, DEFAULT_THRESHOLDS2, CouplingCollector, ForbiddenImportCollector, ModuleSizeCollector, DepDepthCollector, defaultCollectors, archMatchers;
|
|
1737
|
-
var
|
|
1738
|
-
"../core/dist/chunk-
|
|
1748
|
+
var ArchMetricCategorySchema, ViolationSchema, MetricResultSchema, CategoryBaselineSchema, ArchBaselineSchema, CategoryRegressionSchema, ArchDiffResultSchema, ThresholdConfigSchema, ArchConfigSchema, ConstraintRuleSchema, ViolationSnapshotSchema, ViolationHistorySchema, EmergenceConfidenceSchema, EmergentConstraintSuggestionSchema, EmergenceResultSchema, accessAsync, readFileAsync, EXTENSION_BY_LANG, JS_EXT_FALLBACKS, CircularDepsCollector, TypeScriptParser, EXTENSION_MAP, parserCache, initialized, GRAMMAR_MAP, TOP_LEVEL_TYPES, METHOD_TYPES, IDENTIFIER_TYPES, NAME_EXTRACTORS, pythonStrategy, goStrategy, RUST_USE_ARG_TYPES, RUST_PUB_ITEM_TYPES, rustStrategy, JAVA_IMPORT_TYPES, JAVA_EXPORT_TYPES, javaStrategy, STRATEGIES, TreeSitterParser, ParserRegistry, defaultRegistry, LayerViolationCollector, ArchBaselineManager, DEFAULT_THRESHOLDS, FUNCTION_PATTERNS, DECISION_PATTERNS, ComplexityCollector, DEFAULT_THRESHOLDS2, CouplingCollector, ForbiddenImportCollector, ModuleSizeCollector, DepDepthCollector, defaultCollectors, archMatchers;
|
|
1749
|
+
var init_chunk_CE225WRK = __esm({
|
|
1750
|
+
"../core/dist/chunk-CE225WRK.mjs"() {
|
|
1739
1751
|
"use strict";
|
|
1740
1752
|
init_dist();
|
|
1741
1753
|
ArchMetricCategorySchema = z.enum([
|
|
@@ -1853,6 +1865,12 @@ var init_chunk_H73442AZ = __esm({
|
|
|
1853
1865
|
rust: [".rs"],
|
|
1854
1866
|
java: [".java"]
|
|
1855
1867
|
};
|
|
1868
|
+
JS_EXT_FALLBACKS = {
|
|
1869
|
+
".js": [".ts", ".tsx", ".jsx"],
|
|
1870
|
+
".jsx": [".tsx"],
|
|
1871
|
+
".mjs": [".mts"],
|
|
1872
|
+
".cjs": [".cts"]
|
|
1873
|
+
};
|
|
1856
1874
|
CircularDepsCollector = class {
|
|
1857
1875
|
category = "circular-deps";
|
|
1858
1876
|
getRules(_config, _rootDir) {
|
|
@@ -1908,11 +1926,11 @@ var init_chunk_H73442AZ = __esm({
|
|
|
1908
1926
|
TypeScriptParser = class {
|
|
1909
1927
|
name = "typescript";
|
|
1910
1928
|
extensions = [".ts", ".tsx", ".mts", ".cts"];
|
|
1911
|
-
async parseFile(
|
|
1912
|
-
const contentResult = await readFileContent(
|
|
1929
|
+
async parseFile(path42) {
|
|
1930
|
+
const contentResult = await readFileContent(path42);
|
|
1913
1931
|
if (!contentResult.ok) {
|
|
1914
1932
|
return Err(
|
|
1915
|
-
createParseError("NOT_FOUND", `File not found: ${
|
|
1933
|
+
createParseError("NOT_FOUND", `File not found: ${path42}`, { path: path42 }, [
|
|
1916
1934
|
"Check that the file exists",
|
|
1917
1935
|
"Verify the path is correct"
|
|
1918
1936
|
])
|
|
@@ -1922,7 +1940,7 @@ var init_chunk_H73442AZ = __esm({
|
|
|
1922
1940
|
const ast = parse(contentResult.value, {
|
|
1923
1941
|
loc: true,
|
|
1924
1942
|
range: true,
|
|
1925
|
-
jsx:
|
|
1943
|
+
jsx: path42.endsWith(".tsx"),
|
|
1926
1944
|
errorOnUnknownASTType: false
|
|
1927
1945
|
});
|
|
1928
1946
|
return Ok({
|
|
@@ -1933,7 +1951,7 @@ var init_chunk_H73442AZ = __esm({
|
|
|
1933
1951
|
} catch (e) {
|
|
1934
1952
|
const error = e;
|
|
1935
1953
|
return Err(
|
|
1936
|
-
createParseError("SYNTAX_ERROR", `Failed to parse ${
|
|
1954
|
+
createParseError("SYNTAX_ERROR", `Failed to parse ${path42}: ${error.message}`, { path: path42 }, [
|
|
1937
1955
|
"Check for syntax errors in the file",
|
|
1938
1956
|
"Ensure valid TypeScript syntax"
|
|
1939
1957
|
])
|
|
@@ -2248,11 +2266,11 @@ var init_chunk_H73442AZ = __esm({
|
|
|
2248
2266
|
this.extensions = extensions;
|
|
2249
2267
|
this.strategy = strategy;
|
|
2250
2268
|
}
|
|
2251
|
-
async parseFile(
|
|
2252
|
-
const contentResult = await readFileContent(
|
|
2269
|
+
async parseFile(path42) {
|
|
2270
|
+
const contentResult = await readFileContent(path42);
|
|
2253
2271
|
if (!contentResult.ok) {
|
|
2254
2272
|
return Err(
|
|
2255
|
-
createParseError("NOT_FOUND", `File not found: ${
|
|
2273
|
+
createParseError("NOT_FOUND", `File not found: ${path42}`, { path: path42 }, [
|
|
2256
2274
|
"Check that the file exists"
|
|
2257
2275
|
])
|
|
2258
2276
|
);
|
|
@@ -2268,7 +2286,7 @@ var init_chunk_H73442AZ = __esm({
|
|
|
2268
2286
|
} catch (e) {
|
|
2269
2287
|
const error = e;
|
|
2270
2288
|
return Err(
|
|
2271
|
-
createParseError("SYNTAX_ERROR", `Failed to parse ${
|
|
2289
|
+
createParseError("SYNTAX_ERROR", `Failed to parse ${path42}: ${error.message}`, { path: path42 }, [
|
|
2272
2290
|
"Check for syntax errors in the file"
|
|
2273
2291
|
])
|
|
2274
2292
|
);
|
|
@@ -2439,6 +2457,25 @@ var init_chunk_H73442AZ = __esm({
|
|
|
2439
2457
|
return null;
|
|
2440
2458
|
}
|
|
2441
2459
|
}
|
|
2460
|
+
/**
|
|
2461
|
+
* Refresh the on-disk baseline with new results.
|
|
2462
|
+
*
|
|
2463
|
+
* Categories present in `results` overwrite their on-disk entry; categories
|
|
2464
|
+
* absent from `results` are preserved as-is. This prevents silent data loss
|
|
2465
|
+
* when a collector returns no results (e.g. transient failure or a filtered
|
|
2466
|
+
* run) and the regenerated file is committed (issue #268).
|
|
2467
|
+
*
|
|
2468
|
+
* Use this from the `--update-baseline` flow instead of `capture()` + `save()`.
|
|
2469
|
+
*/
|
|
2470
|
+
update(results, commitHash) {
|
|
2471
|
+
const fresh = this.capture(results, commitHash);
|
|
2472
|
+
const existing = this.load();
|
|
2473
|
+
if (existing) {
|
|
2474
|
+
fresh.metrics = { ...existing.metrics, ...fresh.metrics };
|
|
2475
|
+
}
|
|
2476
|
+
this.save(fresh);
|
|
2477
|
+
return fresh;
|
|
2478
|
+
}
|
|
2442
2479
|
/**
|
|
2443
2480
|
* Save an ArchBaseline to disk.
|
|
2444
2481
|
* Creates parent directories if they do not exist.
|
|
@@ -2784,6 +2821,8 @@ var dist_exports = {};
|
|
|
2784
2821
|
__export(dist_exports, {
|
|
2785
2822
|
AGENT_DESCRIPTORS: () => AGENT_DESCRIPTORS,
|
|
2786
2823
|
AGREEMENT_LINE_GAP: () => AGREEMENT_LINE_GAP,
|
|
2824
|
+
ALLOWED_FIELD_KEYS: () => ALLOWED_FIELD_KEYS,
|
|
2825
|
+
ALL_SOLUTION_CATEGORIES: () => ALL_SOLUTION_CATEGORIES,
|
|
2787
2826
|
ARCHITECTURE_DESCRIPTOR: () => ARCHITECTURE_DESCRIPTOR,
|
|
2788
2827
|
AdjustedForecastSchema: () => AdjustedForecastSchema,
|
|
2789
2828
|
AgentActionEmitter: () => AgentActionEmitter,
|
|
@@ -2794,6 +2833,7 @@ __export(dist_exports, {
|
|
|
2794
2833
|
ArchDiffResultSchema: () => ArchDiffResultSchema,
|
|
2795
2834
|
ArchMetricCategorySchema: () => ArchMetricCategorySchema,
|
|
2796
2835
|
BUG_DETECTION_DESCRIPTOR: () => BUG_DETECTION_DESCRIPTOR,
|
|
2836
|
+
BUG_TRACK_CATEGORIES: () => BUG_TRACK_CATEGORIES,
|
|
2797
2837
|
BaselineManager: () => BaselineManager,
|
|
2798
2838
|
BenchmarkRunner: () => BenchmarkRunner,
|
|
2799
2839
|
BlueprintGenerator: () => BlueprintGenerator,
|
|
@@ -2811,6 +2851,7 @@ __export(dist_exports, {
|
|
|
2811
2851
|
CircularDepsCollector: () => CircularDepsCollector,
|
|
2812
2852
|
CompactionPipeline: () => CompactionPipeline,
|
|
2813
2853
|
ComplexityCollector: () => ComplexityCollector,
|
|
2854
|
+
CompoundLockHeldError: () => CompoundLockHeldError,
|
|
2814
2855
|
ConfidenceTierSchema: () => ConfidenceTierSchema,
|
|
2815
2856
|
ConfirmationSchema: () => ConfirmationSchema,
|
|
2816
2857
|
ConsoleSink: () => ConsoleSink,
|
|
@@ -2854,10 +2895,12 @@ __export(dist_exports, {
|
|
|
2854
2895
|
HandoffSchema: () => HandoffSchema,
|
|
2855
2896
|
HarnessStateSchema: () => HarnessStateSchema,
|
|
2856
2897
|
InteractionTypeSchema: () => InteractionTypeSchema,
|
|
2898
|
+
KNOWLEDGE_TRACK_CATEGORIES: () => KNOWLEDGE_TRACK_CATEGORIES,
|
|
2857
2899
|
LITELLM_PRICING_URL: () => LITELLM_PRICING_URL,
|
|
2858
2900
|
LayerViolationCollector: () => LayerViolationCollector,
|
|
2859
2901
|
LockfilePackageSchema: () => LockfilePackageSchema,
|
|
2860
2902
|
LockfileSchema: () => LockfileSchema,
|
|
2903
|
+
MOCK_ADAPTER_NAME: () => MOCK_ADAPTER_NAME,
|
|
2861
2904
|
ManifestSchema: () => ManifestSchema,
|
|
2862
2905
|
MetricResultSchema: () => MetricResultSchema,
|
|
2863
2906
|
ModuleSizeCollector: () => ModuleSizeCollector,
|
|
@@ -2866,6 +2909,9 @@ __export(dist_exports, {
|
|
|
2866
2909
|
NoOpTelemetryAdapter: () => NoOpTelemetryAdapter,
|
|
2867
2910
|
Ok: () => Ok,
|
|
2868
2911
|
OpenAICacheAdapter: () => OpenAICacheAdapter,
|
|
2912
|
+
PII_FIELD_DENYLIST: () => PII_FIELD_DENYLIST,
|
|
2913
|
+
PII_LINE_RE: () => PII_LINE_RE,
|
|
2914
|
+
PII_TOKENS: () => PII_TOKENS,
|
|
2869
2915
|
PatternConfigSchema: () => PatternConfigSchema,
|
|
2870
2916
|
PredictionEngine: () => PredictionEngine,
|
|
2871
2917
|
PredictionOptionsSchema: () => PredictionOptionsSchema,
|
|
@@ -2873,6 +2919,10 @@ __export(dist_exports, {
|
|
|
2873
2919
|
PredictionResultSchema: () => PredictionResultSchema,
|
|
2874
2920
|
PredictionWarningSchema: () => PredictionWarningSchema,
|
|
2875
2921
|
ProjectScanner: () => ProjectScanner,
|
|
2922
|
+
PulseAdapterAlreadyRegisteredError: () => PulseAdapterAlreadyRegisteredError,
|
|
2923
|
+
PulseConfigSchema: () => PulseConfigSchema,
|
|
2924
|
+
PulseDbSourceSchema: () => PulseDbSourceSchema,
|
|
2925
|
+
PulseSourcesSchema: () => PulseSourcesSchema,
|
|
2876
2926
|
QuestionSchema: () => QuestionSchema,
|
|
2877
2927
|
REQUIRED_SECTIONS: () => REQUIRED_SECTIONS,
|
|
2878
2928
|
RegressionDetector: () => RegressionDetector,
|
|
@@ -2897,6 +2947,7 @@ __export(dist_exports, {
|
|
|
2897
2947
|
SharableLayerSchema: () => SharableLayerSchema,
|
|
2898
2948
|
SharableSecurityRulesSchema: () => SharableSecurityRulesSchema,
|
|
2899
2949
|
SkillEventSchema: () => SkillEventSchema,
|
|
2950
|
+
SolutionDocFrontmatterSchema: () => SolutionDocFrontmatterSchema,
|
|
2900
2951
|
SpecImpactEstimateSchema: () => SpecImpactEstimateSchema,
|
|
2901
2952
|
SpecImpactEstimator: () => SpecImpactEstimator,
|
|
2902
2953
|
SpecImpactSignalsSchema: () => SpecImpactSignalsSchema,
|
|
@@ -2924,6 +2975,7 @@ __export(dist_exports, {
|
|
|
2924
2975
|
ViolationHistorySchema: () => ViolationHistorySchema,
|
|
2925
2976
|
ViolationSchema: () => ViolationSchema,
|
|
2926
2977
|
ViolationSnapshotSchema: () => ViolationSnapshotSchema,
|
|
2978
|
+
acquireCompoundLock: () => acquireCompoundLock,
|
|
2927
2979
|
addProvenance: () => addProvenance,
|
|
2928
2980
|
agentConfigRules: () => agentConfigRules,
|
|
2929
2981
|
aggregateAdoptionByDay: () => aggregateByDay2,
|
|
@@ -2946,6 +2998,9 @@ __export(dist_exports, {
|
|
|
2946
2998
|
archiveLearnings: () => archiveLearnings,
|
|
2947
2999
|
archiveSession: () => archiveSession,
|
|
2948
3000
|
archiveStream: () => archiveStream,
|
|
3001
|
+
assembleCandidateReport: () => assembleCandidateReport,
|
|
3002
|
+
assembleReport: () => assembleReport,
|
|
3003
|
+
assertSanitized: () => assertSanitized,
|
|
2949
3004
|
assignFeature: () => assignFeature,
|
|
2950
3005
|
buildDependencyGraph: () => buildDependencyGraph,
|
|
2951
3006
|
buildExclusionSet: () => buildExclusionSet,
|
|
@@ -2962,15 +3017,18 @@ __export(dist_exports, {
|
|
|
2962
3017
|
clearEventHashCache: () => clearEventHashCache,
|
|
2963
3018
|
clearFailuresCache: () => clearFailuresCache,
|
|
2964
3019
|
clearLearningsCache: () => clearLearningsCache,
|
|
3020
|
+
clearPulseAdapters: () => clearPulseAdapters,
|
|
2965
3021
|
clearTaint: () => clearTaint,
|
|
2966
3022
|
clusterViolations: () => clusterViolations,
|
|
2967
3023
|
collectEvents: () => collectEvents,
|
|
2968
3024
|
computeContentHash: () => computeContentHash,
|
|
3025
|
+
computeHotspots: () => computeHotspots,
|
|
2969
3026
|
computeLexicalSimilarity: () => computeLexicalSimilarity,
|
|
2970
3027
|
computeLoadPlan: () => computeLoadPlan,
|
|
2971
3028
|
computeOverallSeverity: () => computeOverallSeverity,
|
|
2972
3029
|
computeScanExitCode: () => computeScanExitCode,
|
|
2973
3030
|
computeTrustScores: () => computeTrustScores,
|
|
3031
|
+
computeWindow: () => computeWindow,
|
|
2974
3032
|
configureFeedback: () => configureFeedback,
|
|
2975
3033
|
constraintRuleId: () => constraintRuleId,
|
|
2976
3034
|
contextBudget: () => contextBudget,
|
|
@@ -2986,6 +3044,7 @@ __export(dist_exports, {
|
|
|
2986
3044
|
createRegionMap: () => createRegionMap,
|
|
2987
3045
|
createSelfReview: () => createSelfReview,
|
|
2988
3046
|
createStream: () => createStream,
|
|
3047
|
+
crossReferenceUndocumentedFixes: () => crossReferenceUndocumentedFixes,
|
|
2989
3048
|
cryptoRules: () => cryptoRules,
|
|
2990
3049
|
deduplicateCleanupFindings: () => deduplicateCleanupFindings,
|
|
2991
3050
|
deduplicateFindings: () => deduplicateFindings,
|
|
@@ -3016,6 +3075,7 @@ __export(dist_exports, {
|
|
|
3016
3075
|
extractBundle: () => extractBundle,
|
|
3017
3076
|
extractDirectoryScope: () => extractDirectoryScope,
|
|
3018
3077
|
extractFileReferences: () => extractFileReferences,
|
|
3078
|
+
extractHeadlines: () => extractHeadlines,
|
|
3019
3079
|
extractIndexEntry: () => extractIndexEntry,
|
|
3020
3080
|
extractLevel: () => extractLevel,
|
|
3021
3081
|
extractMarkdownLinks: () => extractMarkdownLinks,
|
|
@@ -3027,6 +3087,7 @@ __export(dist_exports, {
|
|
|
3027
3087
|
formatFindingBlock: () => formatFindingBlock,
|
|
3028
3088
|
formatGitHubComment: () => formatGitHubComment,
|
|
3029
3089
|
formatGitHubSummary: () => formatGitHubSummary,
|
|
3090
|
+
formatIsoWeek: () => formatIsoWeek,
|
|
3030
3091
|
formatOutline: () => formatOutline,
|
|
3031
3092
|
formatTerminalOutput: () => formatTerminalOutput,
|
|
3032
3093
|
fullSync: () => fullSync,
|
|
@@ -3042,10 +3103,12 @@ __export(dist_exports, {
|
|
|
3042
3103
|
getOutline: () => getOutline,
|
|
3043
3104
|
getParser: () => getParser,
|
|
3044
3105
|
getPhaseCategories: () => getPhaseCategories,
|
|
3106
|
+
getPulseAdapter: () => getPulseAdapter,
|
|
3045
3107
|
getStreamForBranch: () => getStreamForBranch,
|
|
3046
3108
|
getTaintFilePath: () => getTaintFilePath,
|
|
3047
3109
|
getTrustLevel: () => getTrustLevel,
|
|
3048
3110
|
getUpdateNotification: () => getUpdateNotification,
|
|
3111
|
+
gitScan: () => gitScan,
|
|
3049
3112
|
goRules: () => goRules,
|
|
3050
3113
|
injectionRules: () => injectionRules,
|
|
3051
3114
|
insecureDefaultsRules: () => insecureDefaultsRules,
|
|
@@ -3053,9 +3116,12 @@ __export(dist_exports, {
|
|
|
3053
3116
|
isErr: () => isErr,
|
|
3054
3117
|
isOk: () => isOk,
|
|
3055
3118
|
isRegression: () => isRegression,
|
|
3119
|
+
isSanitizedResult: () => isSanitizedResult,
|
|
3056
3120
|
isSmallSuggestion: () => isSmallSuggestion,
|
|
3057
3121
|
isUpdateCheckEnabled: () => isUpdateCheckEnabled,
|
|
3122
|
+
isoWeek: () => isoWeek,
|
|
3058
3123
|
listActiveSessions: () => listActiveSessions,
|
|
3124
|
+
listPulseAdapters: () => listPulseAdapters,
|
|
3059
3125
|
listStreams: () => listStreams,
|
|
3060
3126
|
listTaintedSessions: () => listTaintedSessions,
|
|
3061
3127
|
loadBudgetedLearnings: () => loadBudgetedLearnings,
|
|
@@ -3089,6 +3155,7 @@ __export(dist_exports, {
|
|
|
3089
3155
|
parseFrontmatter: () => parseFrontmatter2,
|
|
3090
3156
|
parseHarnessIgnore: () => parseHarnessIgnore,
|
|
3091
3157
|
parseLiteLLMData: () => parseLiteLLMData,
|
|
3158
|
+
parseLookback: () => parseLookback,
|
|
3092
3159
|
parseManifest: () => parseManifest,
|
|
3093
3160
|
parseProtectedRegions: () => parseProtectedRegions,
|
|
3094
3161
|
parseRoadmap: () => parseRoadmap,
|
|
@@ -3109,6 +3176,8 @@ __export(dist_exports, {
|
|
|
3109
3176
|
readSessionSection: () => readSessionSection,
|
|
3110
3177
|
readSessionSections: () => readSessionSections,
|
|
3111
3178
|
readTaint: () => readTaint,
|
|
3179
|
+
registerMockAdapter: () => registerMockAdapter,
|
|
3180
|
+
registerPulseAdapter: () => registerPulseAdapter,
|
|
3112
3181
|
removeContributions: () => removeContributions,
|
|
3113
3182
|
removeProvenance: () => removeProvenance,
|
|
3114
3183
|
requestMultiplePeerReviews: () => requestMultiplePeerReviews,
|
|
@@ -3134,6 +3203,7 @@ __export(dist_exports, {
|
|
|
3134
3203
|
runMechanicalGate: () => runMechanicalGate,
|
|
3135
3204
|
runMultiTurnPipeline: () => runMultiTurnPipeline,
|
|
3136
3205
|
runPipeline: () => runPipeline,
|
|
3206
|
+
runPulse: () => runPulse,
|
|
3137
3207
|
runReviewPipeline: () => runReviewPipeline,
|
|
3138
3208
|
runSecurityAgent: () => runSecurityAgent,
|
|
3139
3209
|
saveHandoff: () => saveHandoff,
|
|
@@ -3145,6 +3215,7 @@ __export(dist_exports, {
|
|
|
3145
3215
|
searchSymbols: () => searchSymbols,
|
|
3146
3216
|
secretRules: () => secretRules,
|
|
3147
3217
|
securityFindingId: () => securityFindingId,
|
|
3218
|
+
seedFromStrategy: () => seedFromStrategy,
|
|
3148
3219
|
send: () => send,
|
|
3149
3220
|
serializeEnvelope: () => serializeEnvelope,
|
|
3150
3221
|
serializeRoadmap: () => serializeRoadmap,
|
|
@@ -3154,6 +3225,7 @@ __export(dist_exports, {
|
|
|
3154
3225
|
spawnBackgroundCheck: () => spawnBackgroundCheck,
|
|
3155
3226
|
splitBundlesByStage: () => splitBundlesByStage,
|
|
3156
3227
|
stageDomains: () => stageDomains,
|
|
3228
|
+
suggestCategory: () => suggestCategory,
|
|
3157
3229
|
syncConstraintNodes: () => syncConstraintNodes,
|
|
3158
3230
|
syncFromExternal: () => syncFromExternal,
|
|
3159
3231
|
syncRoadmap: () => syncRoadmap,
|
|
@@ -3176,164 +3248,188 @@ __export(dist_exports, {
|
|
|
3176
3248
|
validateFindings: () => validateFindings,
|
|
3177
3249
|
validateKnowledgeMap: () => validateKnowledgeMap,
|
|
3178
3250
|
validatePatternConfig: () => validatePatternConfig,
|
|
3251
|
+
validatePulseConfig: () => validatePulseConfig,
|
|
3252
|
+
validateSolutionsDir: () => validateSolutionsDir,
|
|
3179
3253
|
violationId: () => violationId,
|
|
3180
3254
|
weeksUntilThreshold: () => weeksUntilThreshold,
|
|
3181
3255
|
weightedLinearRegression: () => weightedLinearRegression,
|
|
3182
3256
|
writeConfig: () => writeConfig,
|
|
3183
3257
|
writeLockfile: () => writeLockfile,
|
|
3258
|
+
writePulseConfig: () => writePulseConfig,
|
|
3184
3259
|
writeSessionSummary: () => writeSessionSummary,
|
|
3185
3260
|
writeTaint: () => writeTaint,
|
|
3186
3261
|
xssRules: () => xssRules
|
|
3187
3262
|
});
|
|
3263
|
+
import * as fs from "fs/promises";
|
|
3264
|
+
import * as path from "path";
|
|
3265
|
+
import { z as z2 } from "zod";
|
|
3266
|
+
import * as fs2 from "fs/promises";
|
|
3267
|
+
import * as path2 from "path";
|
|
3268
|
+
import matter from "gray-matter";
|
|
3269
|
+
import { z as z22 } from "zod";
|
|
3188
3270
|
import { spawn } from "child_process";
|
|
3189
3271
|
import { existsSync as existsSync2 } from "fs";
|
|
3190
|
-
import { join as
|
|
3272
|
+
import { join as join32, isAbsolute } from "path";
|
|
3191
3273
|
import { existsSync as existsSync22 } from "fs";
|
|
3192
|
-
import { join as
|
|
3274
|
+
import { join as join42 } from "path";
|
|
3193
3275
|
import { readFileSync as readFileSync2, statSync } from "fs";
|
|
3194
3276
|
import { glob as glob2 } from "glob";
|
|
3195
3277
|
import { existsSync as existsSync3 } from "fs";
|
|
3196
|
-
import { isAbsolute as isAbsolute2, resolve as resolve3, join as
|
|
3278
|
+
import { isAbsolute as isAbsolute2, resolve as resolve3, join as join52 } from "path";
|
|
3197
3279
|
import { glob as glob22 } from "glob";
|
|
3198
3280
|
import { existsSync as existsSync4 } from "fs";
|
|
3199
|
-
import { basename, dirname as dirname4, join as
|
|
3281
|
+
import { basename, dirname as dirname4, join as join6 } from "path";
|
|
3200
3282
|
import { existsSync as existsSync5 } from "fs";
|
|
3201
|
-
import { join as join5 } from "path";
|
|
3202
|
-
import { existsSync as existsSync6 } from "fs";
|
|
3203
3283
|
import { join as join7 } from "path";
|
|
3204
|
-
import {
|
|
3284
|
+
import { existsSync as existsSync6 } from "fs";
|
|
3285
|
+
import { join as join9 } from "path";
|
|
3286
|
+
import { join as join8, dirname as dirname22 } from "path";
|
|
3205
3287
|
import { glob as glob3 } from "glob";
|
|
3206
3288
|
import { existsSync as existsSync7 } from "fs";
|
|
3207
3289
|
import { dirname as dirname32, resolve as resolve22, isAbsolute as isAbsolute3 } from "path";
|
|
3208
3290
|
import { existsSync as existsSync8 } from "fs";
|
|
3209
|
-
import { join as
|
|
3291
|
+
import { join as join10 } from "path";
|
|
3210
3292
|
import { glob as glob4 } from "glob";
|
|
3211
3293
|
import { existsSync as existsSync9 } from "fs";
|
|
3212
|
-
import { join as
|
|
3294
|
+
import { join as join11 } from "path";
|
|
3213
3295
|
import { existsSync as existsSync10 } from "fs";
|
|
3214
|
-
import { join as
|
|
3296
|
+
import { join as join12 } from "path";
|
|
3215
3297
|
import { minimatch as minimatch2 } from "minimatch";
|
|
3216
3298
|
import { basename as basename2 } from "path";
|
|
3217
|
-
import { join as
|
|
3299
|
+
import { join as join13, basename as basename3 } from "path";
|
|
3218
3300
|
import { basename as basename4, dirname as dirname42 } from "path";
|
|
3219
|
-
import { z as
|
|
3220
|
-
import * as
|
|
3221
|
-
import * as
|
|
3222
|
-
import { join as
|
|
3301
|
+
import { z as z3 } from "zod";
|
|
3302
|
+
import * as fs3 from "fs/promises";
|
|
3303
|
+
import * as fs4 from "fs/promises";
|
|
3304
|
+
import { join as join14, resolve as resolve32 } from "path";
|
|
3223
3305
|
import { minimatch as minimatch22 } from "minimatch";
|
|
3224
3306
|
import { dirname as dirname5, resolve as resolve4 } from "path";
|
|
3225
|
-
import { dirname as dirname6, resolve as resolve5 } from "path";
|
|
3307
|
+
import { dirname as dirname6, extname as extname2, resolve as resolve5 } from "path";
|
|
3226
3308
|
import { minimatch as minimatch3 } from "minimatch";
|
|
3227
3309
|
import { readdirSync, statSync as statSync2 } from "fs";
|
|
3228
|
-
import { join as
|
|
3229
|
-
import * as
|
|
3310
|
+
import { join as join15 } from "path";
|
|
3311
|
+
import * as fs5 from "fs";
|
|
3230
3312
|
import { promisify as promisify2 } from "util";
|
|
3231
|
-
import { dirname as dirname7, basename as basename5, join as
|
|
3232
|
-
import { z as
|
|
3313
|
+
import { dirname as dirname7, basename as basename5, join as join16 } from "path";
|
|
3314
|
+
import { z as z4 } from "zod";
|
|
3233
3315
|
import { readFileSync as readFileSync22, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, existsSync as existsSync11 } from "fs";
|
|
3234
|
-
import { join as
|
|
3316
|
+
import { join as join17, dirname as dirname8 } from "path";
|
|
3235
3317
|
import { execFileSync } from "child_process";
|
|
3236
|
-
import * as
|
|
3237
|
-
import * as
|
|
3318
|
+
import * as fs6 from "fs";
|
|
3319
|
+
import * as path3 from "path";
|
|
3238
3320
|
import { appendFileSync, writeFileSync as writeFileSync22, existsSync as existsSync12, mkdirSync as mkdirSync22 } from "fs";
|
|
3239
3321
|
import { dirname as dirname9 } from "path";
|
|
3240
|
-
import { z as
|
|
3322
|
+
import { z as z5 } from "zod";
|
|
3241
3323
|
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, renameSync as renameSync2, mkdirSync as mkdirSync3, existsSync as existsSync13 } from "fs";
|
|
3242
3324
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
3243
|
-
import { join as
|
|
3244
|
-
import { z as
|
|
3245
|
-
import * as fs5 from "fs";
|
|
3246
|
-
import * as path2 from "path";
|
|
3247
|
-
import * as fs6 from "fs";
|
|
3248
|
-
import * as path3 from "path";
|
|
3325
|
+
import { join as join19, dirname as dirname10 } from "path";
|
|
3326
|
+
import { z as z6 } from "zod";
|
|
3249
3327
|
import * as fs7 from "fs";
|
|
3250
3328
|
import * as path4 from "path";
|
|
3251
|
-
import { z as z5 } from "zod";
|
|
3252
|
-
import * as fs11 from "fs";
|
|
3253
|
-
import * as path8 from "path";
|
|
3254
|
-
import * as fs10 from "fs";
|
|
3255
|
-
import * as path7 from "path";
|
|
3256
3329
|
import * as fs8 from "fs";
|
|
3257
3330
|
import * as path5 from "path";
|
|
3258
|
-
import { execSync } from "child_process";
|
|
3259
|
-
import { z as z6 } from "zod";
|
|
3260
3331
|
import * as fs9 from "fs";
|
|
3261
3332
|
import * as path6 from "path";
|
|
3262
|
-
import
|
|
3263
|
-
import * as path9 from "path";
|
|
3264
|
-
import * as crypto2 from "crypto";
|
|
3333
|
+
import { z as z7 } from "zod";
|
|
3265
3334
|
import * as fs13 from "fs";
|
|
3266
3335
|
import * as path10 from "path";
|
|
3336
|
+
import * as fs12 from "fs";
|
|
3337
|
+
import * as path9 from "path";
|
|
3338
|
+
import * as fs10 from "fs";
|
|
3339
|
+
import * as path7 from "path";
|
|
3340
|
+
import { execSync } from "child_process";
|
|
3341
|
+
import { z as z8 } from "zod";
|
|
3342
|
+
import * as fs11 from "fs";
|
|
3343
|
+
import * as path8 from "path";
|
|
3267
3344
|
import * as fs14 from "fs";
|
|
3268
3345
|
import * as path11 from "path";
|
|
3269
|
-
import * as
|
|
3346
|
+
import * as crypto2 from "crypto";
|
|
3270
3347
|
import * as fs15 from "fs";
|
|
3271
3348
|
import * as path12 from "path";
|
|
3272
3349
|
import * as fs16 from "fs";
|
|
3273
3350
|
import * as path13 from "path";
|
|
3351
|
+
import * as crypto3 from "crypto";
|
|
3274
3352
|
import * as fs17 from "fs";
|
|
3275
3353
|
import * as path14 from "path";
|
|
3276
3354
|
import * as fs18 from "fs";
|
|
3277
3355
|
import * as path15 from "path";
|
|
3278
3356
|
import * as fs19 from "fs";
|
|
3279
3357
|
import * as path16 from "path";
|
|
3280
|
-
import { execSync as execSync2 } from "child_process";
|
|
3281
3358
|
import * as fs20 from "fs";
|
|
3282
3359
|
import * as path17 from "path";
|
|
3283
3360
|
import * as fs21 from "fs";
|
|
3284
3361
|
import * as path18 from "path";
|
|
3362
|
+
import { execSync as execSync2 } from "child_process";
|
|
3285
3363
|
import * as fs22 from "fs";
|
|
3286
3364
|
import * as path19 from "path";
|
|
3287
3365
|
import * as fs23 from "fs";
|
|
3288
3366
|
import * as path20 from "path";
|
|
3289
|
-
import { z as z7 } from "zod";
|
|
3290
|
-
import * as fs25 from "fs/promises";
|
|
3291
|
-
import { minimatch as minimatch4 } from "minimatch";
|
|
3292
|
-
import { z as z8 } from "zod";
|
|
3293
3367
|
import * as fs24 from "fs";
|
|
3294
3368
|
import * as path21 from "path";
|
|
3369
|
+
import * as fs25 from "fs";
|
|
3370
|
+
import * as path22 from "path";
|
|
3371
|
+
import { z as z9 } from "zod";
|
|
3372
|
+
import * as fs27 from "fs/promises";
|
|
3373
|
+
import { minimatch as minimatch4 } from "minimatch";
|
|
3374
|
+
import { z as z10 } from "zod";
|
|
3375
|
+
import * as fs26 from "fs";
|
|
3376
|
+
import * as path23 from "path";
|
|
3295
3377
|
import { readFileSync as readFileSync222, writeFileSync as writeFileSync15, unlinkSync as unlinkSync2, mkdirSync as mkdirSync14, readdirSync as readdirSync3 } from "fs";
|
|
3296
|
-
import { join as
|
|
3378
|
+
import { join as join382, dirname as dirname12 } from "path";
|
|
3297
3379
|
import { createHash as createHash32 } from "crypto";
|
|
3298
|
-
import { z as
|
|
3380
|
+
import { z as z11 } from "zod";
|
|
3299
3381
|
import { readFileSync as readFileSync23, writeFileSync as writeFileSync16, renameSync as renameSync82, mkdirSync as mkdirSync15, existsSync as existsSync332 } from "fs";
|
|
3300
3382
|
import { execSync as execSync3 } from "child_process";
|
|
3301
3383
|
import { randomBytes as randomBytes22 } from "crypto";
|
|
3302
|
-
import { join as
|
|
3303
|
-
import * as path22 from "path";
|
|
3304
|
-
import * as path23 from "path";
|
|
3384
|
+
import { isAbsolute as isAbsolute4, join as join39, relative as relative32, dirname as dirname13 } from "path";
|
|
3305
3385
|
import * as path24 from "path";
|
|
3306
3386
|
import * as path25 from "path";
|
|
3307
|
-
import * as fs26 from "fs";
|
|
3308
3387
|
import * as path26 from "path";
|
|
3309
|
-
import * as fs27 from "fs";
|
|
3310
3388
|
import * as path27 from "path";
|
|
3311
3389
|
import * as fs28 from "fs";
|
|
3312
|
-
import { z as z10 } from "zod";
|
|
3313
|
-
import * as fs29 from "fs/promises";
|
|
3314
3390
|
import * as path28 from "path";
|
|
3315
|
-
import * as
|
|
3391
|
+
import * as fs29 from "fs";
|
|
3316
3392
|
import * as path29 from "path";
|
|
3317
|
-
import * as
|
|
3318
|
-
import
|
|
3393
|
+
import * as fs30 from "fs";
|
|
3394
|
+
import { z as z12 } from "zod";
|
|
3395
|
+
import * as fs31 from "fs/promises";
|
|
3319
3396
|
import * as path30 from "path";
|
|
3320
|
-
import * as os from "os";
|
|
3321
|
-
import { spawn as spawn2 } from "child_process";
|
|
3322
3397
|
import * as fs32 from "fs/promises";
|
|
3323
3398
|
import * as path31 from "path";
|
|
3399
|
+
import * as ejs from "ejs";
|
|
3324
3400
|
import * as fs33 from "fs";
|
|
3325
3401
|
import * as path32 from "path";
|
|
3326
|
-
import * as
|
|
3402
|
+
import * as os from "os";
|
|
3403
|
+
import { spawn as spawn2 } from "child_process";
|
|
3404
|
+
import * as fs34 from "fs/promises";
|
|
3327
3405
|
import * as path33 from "path";
|
|
3328
|
-
import * as os2 from "os";
|
|
3329
3406
|
import * as fs35 from "fs";
|
|
3330
3407
|
import * as path34 from "path";
|
|
3408
|
+
import * as fs36 from "fs";
|
|
3409
|
+
import * as path35 from "path";
|
|
3410
|
+
import * as os2 from "os";
|
|
3331
3411
|
import * as fs37 from "fs";
|
|
3332
3412
|
import * as path36 from "path";
|
|
3413
|
+
import * as fs39 from "fs";
|
|
3414
|
+
import * as path38 from "path";
|
|
3333
3415
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
3334
|
-
import * as
|
|
3335
|
-
import * as
|
|
3416
|
+
import * as fs38 from "fs";
|
|
3417
|
+
import * as path37 from "path";
|
|
3336
3418
|
import * as crypto4 from "crypto";
|
|
3419
|
+
import * as fs40 from "fs";
|
|
3420
|
+
import * as path39 from "path";
|
|
3421
|
+
import * as fs41 from "fs";
|
|
3422
|
+
import * as fs42 from "fs";
|
|
3423
|
+
import * as path40 from "path";
|
|
3424
|
+
import { readFileSync as readFileSync36 } from "fs";
|
|
3425
|
+
import { fileURLToPath } from "url";
|
|
3426
|
+
import { dirname as dirname18, join as join572 } from "path";
|
|
3427
|
+
import { execFile } from "child_process";
|
|
3428
|
+
import { promisify as promisify22 } from "util";
|
|
3429
|
+
import { execFile as execFile2 } from "child_process";
|
|
3430
|
+
import { promisify as promisify3 } from "util";
|
|
3431
|
+
import * as fs43 from "fs/promises";
|
|
3432
|
+
import * as path41 from "path";
|
|
3337
3433
|
async function validateFileStructure(projectPath, conventions) {
|
|
3338
3434
|
const missing = [];
|
|
3339
3435
|
const unexpected = [];
|
|
@@ -3369,15 +3465,15 @@ function validateConfig(data, schema) {
|
|
|
3369
3465
|
let message = "Configuration validation failed";
|
|
3370
3466
|
const suggestions = [];
|
|
3371
3467
|
if (firstError) {
|
|
3372
|
-
const
|
|
3373
|
-
const pathDisplay =
|
|
3468
|
+
const path42 = firstError.path.join(".");
|
|
3469
|
+
const pathDisplay = path42 ? ` at "${path42}"` : "";
|
|
3374
3470
|
if (firstError.code === "invalid_type") {
|
|
3375
3471
|
const received = firstError.received;
|
|
3376
3472
|
const expected = firstError.expected;
|
|
3377
3473
|
if (received === "undefined") {
|
|
3378
3474
|
code = "MISSING_FIELD";
|
|
3379
3475
|
message = `Missing required field${pathDisplay}: ${firstError.message}`;
|
|
3380
|
-
suggestions.push(`Field "${
|
|
3476
|
+
suggestions.push(`Field "${path42}" is required and must be of type "${expected}"`);
|
|
3381
3477
|
} else {
|
|
3382
3478
|
code = "INVALID_TYPE";
|
|
3383
3479
|
message = `Invalid type${pathDisplay}: ${firstError.message}`;
|
|
@@ -3487,6 +3583,82 @@ function validateConventionalCommit(message) {
|
|
|
3487
3583
|
issues: []
|
|
3488
3584
|
});
|
|
3489
3585
|
}
|
|
3586
|
+
async function validatePulseConfig(cwd) {
|
|
3587
|
+
const configPath = path.join(cwd, "harness.config.json");
|
|
3588
|
+
let raw;
|
|
3589
|
+
try {
|
|
3590
|
+
raw = await fs.readFile(configPath, "utf-8");
|
|
3591
|
+
} catch {
|
|
3592
|
+
return Ok({ present: false, valid: true });
|
|
3593
|
+
}
|
|
3594
|
+
let parsed;
|
|
3595
|
+
try {
|
|
3596
|
+
parsed = JSON.parse(raw);
|
|
3597
|
+
} catch (e) {
|
|
3598
|
+
return Err(
|
|
3599
|
+
createError(
|
|
3600
|
+
"VALIDATION_FAILED",
|
|
3601
|
+
`harness.config.json is not valid JSON: ${e.message}`,
|
|
3602
|
+
{},
|
|
3603
|
+
[]
|
|
3604
|
+
)
|
|
3605
|
+
);
|
|
3606
|
+
}
|
|
3607
|
+
if (!("pulse" in parsed)) return Ok({ present: false, valid: true });
|
|
3608
|
+
const result = validateConfig(parsed.pulse, PulseConfigSchema);
|
|
3609
|
+
if (!result.ok) return Err(result.error);
|
|
3610
|
+
return Ok({ present: true, valid: true });
|
|
3611
|
+
}
|
|
3612
|
+
async function* walk2(dir) {
|
|
3613
|
+
let entries;
|
|
3614
|
+
try {
|
|
3615
|
+
entries = await fs2.readdir(dir, { withFileTypes: true });
|
|
3616
|
+
} catch {
|
|
3617
|
+
return;
|
|
3618
|
+
}
|
|
3619
|
+
for (const e of entries) {
|
|
3620
|
+
const p = path2.join(dir, e.name);
|
|
3621
|
+
if (e.isDirectory()) yield* walk2(p);
|
|
3622
|
+
else if (e.isFile() && e.name.endsWith(".md")) yield p;
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
async function validateSolutionsDir(cwd) {
|
|
3626
|
+
const root = path2.join(cwd, "docs", "solutions");
|
|
3627
|
+
try {
|
|
3628
|
+
await fs2.stat(root);
|
|
3629
|
+
} catch {
|
|
3630
|
+
return Ok({ filesChecked: 0, issues: [] });
|
|
3631
|
+
}
|
|
3632
|
+
const issues = [];
|
|
3633
|
+
let count = 0;
|
|
3634
|
+
for (const [track, categories] of TRACK_CATEGORIES) {
|
|
3635
|
+
for (const category of categories) {
|
|
3636
|
+
const categoryDir = path2.join(root, track, category);
|
|
3637
|
+
for await (const file of walk2(categoryDir)) {
|
|
3638
|
+
count++;
|
|
3639
|
+
const raw = await fs2.readFile(file, "utf-8");
|
|
3640
|
+
const parsed = matter(raw);
|
|
3641
|
+
const result = validateConfig(parsed.data, SolutionDocFrontmatterSchema);
|
|
3642
|
+
if (!result.ok)
|
|
3643
|
+
issues.push({
|
|
3644
|
+
file: path2.relative(cwd, file).replaceAll("\\", "/"),
|
|
3645
|
+
message: result.error.message
|
|
3646
|
+
});
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
if (issues.length > 0) {
|
|
3651
|
+
return Err(
|
|
3652
|
+
createError(
|
|
3653
|
+
"VALIDATION_FAILED",
|
|
3654
|
+
`${issues.length} solution doc(s) failed frontmatter validation`,
|
|
3655
|
+
{ issues },
|
|
3656
|
+
[]
|
|
3657
|
+
)
|
|
3658
|
+
);
|
|
3659
|
+
}
|
|
3660
|
+
return Ok({ filesChecked: count, issues: [] });
|
|
3661
|
+
}
|
|
3490
3662
|
function resolveAgnixBinary(explicit) {
|
|
3491
3663
|
if (explicit) return existsSync2(explicit) ? explicit : null;
|
|
3492
3664
|
const fromEnv = process.env[HARNESS_AGNIX_BIN];
|
|
@@ -3507,7 +3679,7 @@ function lookupOnPath() {
|
|
|
3507
3679
|
function firstExistingIn(dir, candidates) {
|
|
3508
3680
|
if (!dir) return null;
|
|
3509
3681
|
for (const name of candidates) {
|
|
3510
|
-
const candidate = isAbsolute(dir) ?
|
|
3682
|
+
const candidate = isAbsolute(dir) ? join32(dir, name) : join32(process.cwd(), dir, name);
|
|
3511
3683
|
if (existsSync2(candidate)) return candidate;
|
|
3512
3684
|
}
|
|
3513
3685
|
return null;
|
|
@@ -3584,7 +3756,7 @@ function extractDiagnostics(parsed) {
|
|
|
3584
3756
|
}
|
|
3585
3757
|
function normalizeDiagnostic(diag, cwd) {
|
|
3586
3758
|
const finding = {
|
|
3587
|
-
file: normalizeFilePath(
|
|
3759
|
+
file: normalizeFilePath(readFile32(diag), cwd),
|
|
3588
3760
|
ruleId: readRuleId(diag),
|
|
3589
3761
|
severity: normalizeSeverity(readSeverity(diag)),
|
|
3590
3762
|
message: diag.message ?? "agnix diagnostic"
|
|
@@ -3592,7 +3764,7 @@ function normalizeDiagnostic(diag, cwd) {
|
|
|
3592
3764
|
attachOptionalFields(finding, diag);
|
|
3593
3765
|
return finding;
|
|
3594
3766
|
}
|
|
3595
|
-
function
|
|
3767
|
+
function readFile32(diag) {
|
|
3596
3768
|
return diag.file ?? diag.path ?? "";
|
|
3597
3769
|
}
|
|
3598
3770
|
function readRuleId(diag) {
|
|
@@ -3635,16 +3807,16 @@ function makeFinding(input) {
|
|
|
3635
3807
|
if (input.suggestion) finding.suggestion = input.suggestion;
|
|
3636
3808
|
return finding;
|
|
3637
3809
|
}
|
|
3638
|
-
function readTextSafe(
|
|
3810
|
+
function readTextSafe(path42) {
|
|
3639
3811
|
try {
|
|
3640
|
-
return readFileSync2(
|
|
3812
|
+
return readFileSync2(path42, "utf-8");
|
|
3641
3813
|
} catch {
|
|
3642
3814
|
return null;
|
|
3643
3815
|
}
|
|
3644
3816
|
}
|
|
3645
|
-
function safeFileSize(
|
|
3817
|
+
function safeFileSize(path42) {
|
|
3646
3818
|
try {
|
|
3647
|
-
return statSync(
|
|
3819
|
+
return statSync(path42).size;
|
|
3648
3820
|
} catch {
|
|
3649
3821
|
return null;
|
|
3650
3822
|
}
|
|
@@ -3681,7 +3853,7 @@ function stripQuotes(value) {
|
|
|
3681
3853
|
function runClaudeMdRules(cwd) {
|
|
3682
3854
|
const findings = [];
|
|
3683
3855
|
for (const name of CONTEXT_FILES) {
|
|
3684
|
-
const abs =
|
|
3856
|
+
const abs = join42(cwd, name);
|
|
3685
3857
|
if (!existsSync22(abs)) continue;
|
|
3686
3858
|
findings.push(...inspectContextFile(abs, name));
|
|
3687
3859
|
}
|
|
@@ -3828,7 +4000,7 @@ function appendDescriptionFindings(out, rel, description) {
|
|
|
3828
4000
|
function runHookRules(cwd) {
|
|
3829
4001
|
const findings = [];
|
|
3830
4002
|
for (const rel of SETTINGS_PATHS) {
|
|
3831
|
-
const abs =
|
|
4003
|
+
const abs = join52(cwd, rel);
|
|
3832
4004
|
if (!existsSync3(abs)) continue;
|
|
3833
4005
|
const content = readTextSafe(abs);
|
|
3834
4006
|
if (content === null) continue;
|
|
@@ -3944,7 +4116,7 @@ async function runSkillRules(cwd) {
|
|
|
3944
4116
|
for (const abs of skillFiles) {
|
|
3945
4117
|
const rel = relPath(cwd, abs);
|
|
3946
4118
|
const dirName = basename(dirname4(abs));
|
|
3947
|
-
const siblingYaml =
|
|
4119
|
+
const siblingYaml = join6(dirname4(abs), "skill.yaml");
|
|
3948
4120
|
if (existsSync4(siblingYaml)) {
|
|
3949
4121
|
const yamlFields = parseFrontmatterFields(readTextSafe(siblingYaml) ?? "");
|
|
3950
4122
|
findings.push(...checkSkillMetadata(relPath(cwd, siblingYaml), dirName, yamlFields));
|
|
@@ -4050,7 +4222,7 @@ function runMcpRules(cwd) {
|
|
|
4050
4222
|
return findings;
|
|
4051
4223
|
}
|
|
4052
4224
|
function loadMcpServers(cwd, rel) {
|
|
4053
|
-
const abs =
|
|
4225
|
+
const abs = join7(cwd, rel);
|
|
4054
4226
|
if (!existsSync5(abs)) return null;
|
|
4055
4227
|
const content = readTextSafe(abs);
|
|
4056
4228
|
if (content === null) return null;
|
|
@@ -4189,27 +4361,27 @@ function extractSections(content) {
|
|
|
4189
4361
|
}
|
|
4190
4362
|
return sections.map((section) => buildAgentMapSection(section, lines));
|
|
4191
4363
|
}
|
|
4192
|
-
function isExternalLink(
|
|
4193
|
-
return
|
|
4364
|
+
function isExternalLink(path42) {
|
|
4365
|
+
return path42.startsWith("http://") || path42.startsWith("https://") || path42.startsWith("#") || path42.startsWith("mailto:");
|
|
4194
4366
|
}
|
|
4195
4367
|
function resolveLinkPath(linkPath, baseDir) {
|
|
4196
|
-
return linkPath.startsWith(".") ?
|
|
4368
|
+
return linkPath.startsWith(".") ? join8(baseDir, linkPath) : linkPath;
|
|
4197
4369
|
}
|
|
4198
|
-
async function validateAgentsMap(
|
|
4199
|
-
const contentResult = await readFileContent(
|
|
4370
|
+
async function validateAgentsMap(path42 = "./AGENTS.md") {
|
|
4371
|
+
const contentResult = await readFileContent(path42);
|
|
4200
4372
|
if (!contentResult.ok) {
|
|
4201
4373
|
return Err(
|
|
4202
4374
|
createError(
|
|
4203
4375
|
"PARSE_ERROR",
|
|
4204
4376
|
`Failed to read AGENTS.md: ${contentResult.error.message}`,
|
|
4205
|
-
{ path:
|
|
4377
|
+
{ path: path42 },
|
|
4206
4378
|
["Ensure the file exists", "Check file permissions"]
|
|
4207
4379
|
)
|
|
4208
4380
|
);
|
|
4209
4381
|
}
|
|
4210
4382
|
const content = contentResult.value;
|
|
4211
4383
|
const sections = extractSections(content);
|
|
4212
|
-
const baseDir = dirname22(
|
|
4384
|
+
const baseDir = dirname22(path42);
|
|
4213
4385
|
const sectionTitles = sections.map((s) => s.title);
|
|
4214
4386
|
const missingSections = REQUIRED_SECTIONS.filter(
|
|
4215
4387
|
(required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
|
|
@@ -4248,7 +4420,7 @@ async function validateAgentsMap(path37 = "./AGENTS.md") {
|
|
|
4248
4420
|
});
|
|
4249
4421
|
}
|
|
4250
4422
|
async function runAgentsMdRules(cwd) {
|
|
4251
|
-
const abs =
|
|
4423
|
+
const abs = join9(cwd, "AGENTS.md");
|
|
4252
4424
|
if (!existsSync6(abs)) return [];
|
|
4253
4425
|
const result = await validateAgentsMap(abs);
|
|
4254
4426
|
if (result.ok) return [];
|
|
@@ -4305,7 +4477,7 @@ function extractLinkTargets(content) {
|
|
|
4305
4477
|
function runSettingsJsonRules(cwd) {
|
|
4306
4478
|
const findings = [];
|
|
4307
4479
|
for (const rel of SETTINGS_FILES) {
|
|
4308
|
-
const abs =
|
|
4480
|
+
const abs = join10(cwd, rel);
|
|
4309
4481
|
if (!existsSync8(abs)) continue;
|
|
4310
4482
|
const content = readTextSafe(abs);
|
|
4311
4483
|
if (content === null) continue;
|
|
@@ -4381,15 +4553,15 @@ function stripQuotes2(value) {
|
|
|
4381
4553
|
return value;
|
|
4382
4554
|
}
|
|
4383
4555
|
function skillExists(cwd, slug) {
|
|
4384
|
-
if (existsSync9(
|
|
4556
|
+
if (existsSync9(join11(cwd, "agents", "skills", slug, "SKILL.md"))) return true;
|
|
4385
4557
|
for (const platform of ["claude-code", "gemini-cli", "codex", "cursor", "shared"]) {
|
|
4386
|
-
if (existsSync9(
|
|
4558
|
+
if (existsSync9(join11(cwd, "agents", "skills", platform, slug, "SKILL.md"))) return true;
|
|
4387
4559
|
}
|
|
4388
4560
|
return false;
|
|
4389
4561
|
}
|
|
4390
4562
|
function runAgnixTomlRules(cwd) {
|
|
4391
4563
|
const rel = ".agnix.toml";
|
|
4392
|
-
const abs =
|
|
4564
|
+
const abs = join12(cwd, rel);
|
|
4393
4565
|
if (!existsSync10(abs)) return [];
|
|
4394
4566
|
const content = readTextSafe(abs);
|
|
4395
4567
|
if (content === null) return [];
|
|
@@ -4649,8 +4821,8 @@ async function checkDocCoverage(domain, options = {}) {
|
|
|
4649
4821
|
);
|
|
4650
4822
|
}
|
|
4651
4823
|
}
|
|
4652
|
-
function suggestFix(
|
|
4653
|
-
const targetName = basename3(
|
|
4824
|
+
function suggestFix(path42, existingFiles) {
|
|
4825
|
+
const targetName = basename3(path42).toLowerCase();
|
|
4654
4826
|
const similar = existingFiles.find((file) => {
|
|
4655
4827
|
const fileName = basename3(file).toLowerCase();
|
|
4656
4828
|
return fileName.includes(targetName) || targetName.includes(fileName);
|
|
@@ -4658,10 +4830,10 @@ function suggestFix(path37, existingFiles) {
|
|
|
4658
4830
|
if (similar) {
|
|
4659
4831
|
return `Did you mean "${similar}"?`;
|
|
4660
4832
|
}
|
|
4661
|
-
return `Create the file "${
|
|
4833
|
+
return `Create the file "${path42}" or remove the link`;
|
|
4662
4834
|
}
|
|
4663
4835
|
async function validateKnowledgeMap(rootDir = process.cwd()) {
|
|
4664
|
-
const agentsPath =
|
|
4836
|
+
const agentsPath = join13(rootDir, "AGENTS.md");
|
|
4665
4837
|
const agentsResult = await validateAgentsMap(agentsPath);
|
|
4666
4838
|
if (!agentsResult.ok) {
|
|
4667
4839
|
return Err(agentsResult.error);
|
|
@@ -5016,8 +5188,8 @@ function createBoundaryValidator(schema, name) {
|
|
|
5016
5188
|
return Ok(result.data);
|
|
5017
5189
|
}
|
|
5018
5190
|
const suggestions = result.error.issues.map((issue) => {
|
|
5019
|
-
const
|
|
5020
|
-
return
|
|
5191
|
+
const path42 = issue.path.join(".");
|
|
5192
|
+
return path42 ? `${path42}: ${issue.message}` : issue.message;
|
|
5021
5193
|
});
|
|
5022
5194
|
return Err(
|
|
5023
5195
|
createError(
|
|
@@ -5063,7 +5235,7 @@ function validateBoundaries(boundaries, data) {
|
|
|
5063
5235
|
async function writeConfig(filePath, content) {
|
|
5064
5236
|
try {
|
|
5065
5237
|
const json = JSON.stringify(content, null, 2) + "\n";
|
|
5066
|
-
await
|
|
5238
|
+
await fs3.writeFile(filePath, json, "utf-8");
|
|
5067
5239
|
return Ok(void 0);
|
|
5068
5240
|
} catch (error) {
|
|
5069
5241
|
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
@@ -5337,7 +5509,7 @@ function deepMergeConstraints(localConfig, bundleConstraints, _existingContribut
|
|
|
5337
5509
|
async function readLockfile(lockfilePath) {
|
|
5338
5510
|
let raw;
|
|
5339
5511
|
try {
|
|
5340
|
-
raw = await
|
|
5512
|
+
raw = await fs4.readFile(lockfilePath, "utf-8");
|
|
5341
5513
|
} catch (err) {
|
|
5342
5514
|
if (isNodeError(err) && err.code === "ENOENT") {
|
|
5343
5515
|
return { ok: true, value: null };
|
|
@@ -5640,7 +5812,7 @@ async function resolveEntryPoints(rootDir, explicitEntries) {
|
|
|
5640
5812
|
if (explicitEntries && explicitEntries.length > 0) {
|
|
5641
5813
|
return Ok(explicitEntries.map((e) => resolve32(rootDir, e)));
|
|
5642
5814
|
}
|
|
5643
|
-
const pkgPath =
|
|
5815
|
+
const pkgPath = join14(rootDir, "package.json");
|
|
5644
5816
|
if (await fileExists(pkgPath)) {
|
|
5645
5817
|
const pkgContent = await readFileContent(pkgPath);
|
|
5646
5818
|
if (pkgContent.ok) {
|
|
@@ -5654,7 +5826,7 @@ async function resolveEntryPoints(rootDir, explicitEntries) {
|
|
|
5654
5826
|
}
|
|
5655
5827
|
const conventions = ["src/index.ts", "src/main.ts", "index.ts", "main.ts"];
|
|
5656
5828
|
for (const conv of conventions) {
|
|
5657
|
-
const convPath =
|
|
5829
|
+
const convPath = join14(rootDir, conv);
|
|
5658
5830
|
if (await fileExists(convPath)) {
|
|
5659
5831
|
return Ok([convPath]);
|
|
5660
5832
|
}
|
|
@@ -5721,22 +5893,22 @@ function extractInlineRefs(content) {
|
|
|
5721
5893
|
}
|
|
5722
5894
|
return refs;
|
|
5723
5895
|
}
|
|
5724
|
-
async function parseDocumentationFile(
|
|
5725
|
-
const contentResult = await readFileContent(
|
|
5896
|
+
async function parseDocumentationFile(path42) {
|
|
5897
|
+
const contentResult = await readFileContent(path42);
|
|
5726
5898
|
if (!contentResult.ok) {
|
|
5727
5899
|
return Err(
|
|
5728
5900
|
createEntropyError(
|
|
5729
5901
|
"PARSE_ERROR",
|
|
5730
|
-
`Failed to read documentation file: ${
|
|
5731
|
-
{ file:
|
|
5902
|
+
`Failed to read documentation file: ${path42}`,
|
|
5903
|
+
{ file: path42 },
|
|
5732
5904
|
["Check that the file exists"]
|
|
5733
5905
|
)
|
|
5734
5906
|
);
|
|
5735
5907
|
}
|
|
5736
5908
|
const content = contentResult.value;
|
|
5737
|
-
const type =
|
|
5909
|
+
const type = path42.endsWith(".md") ? "markdown" : "text";
|
|
5738
5910
|
return Ok({
|
|
5739
|
-
path:
|
|
5911
|
+
path: path42,
|
|
5740
5912
|
type,
|
|
5741
5913
|
content,
|
|
5742
5914
|
codeBlocks: extractCodeBlocks(content),
|
|
@@ -6108,19 +6280,30 @@ function resolveImportToFile(importSource, fromFile, snapshot, fileIndex) {
|
|
|
6108
6280
|
}
|
|
6109
6281
|
const hasFile = fileIndex ? (p) => fileIndex.has(p) : (p) => snapshot.files.some((f) => f.path === p);
|
|
6110
6282
|
const fromDir = dirname6(fromFile);
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6283
|
+
const resolved = resolve5(fromDir, importSource);
|
|
6284
|
+
const sourceExt = extname2(resolved);
|
|
6285
|
+
const fallbacks = JS_EXT_FALLBACKS2[sourceExt];
|
|
6286
|
+
if (fallbacks) {
|
|
6287
|
+
const base = resolved.slice(0, -sourceExt.length);
|
|
6288
|
+
for (const ext of fallbacks) {
|
|
6289
|
+
const candidate = base + ext;
|
|
6290
|
+
if (hasFile(candidate)) return candidate;
|
|
6116
6291
|
}
|
|
6117
|
-
const
|
|
6118
|
-
|
|
6119
|
-
return
|
|
6292
|
+
for (const indexExt of [".ts", ".tsx", ".jsx"]) {
|
|
6293
|
+
const indexPath2 = resolve5(base, "index" + indexExt);
|
|
6294
|
+
if (hasFile(indexPath2)) return indexPath2;
|
|
6120
6295
|
}
|
|
6121
6296
|
}
|
|
6122
|
-
if (hasFile(resolved))
|
|
6123
|
-
|
|
6297
|
+
if (hasFile(resolved)) return resolved;
|
|
6298
|
+
if (!sourceExt) {
|
|
6299
|
+
for (const ext of [".ts", ".tsx"]) {
|
|
6300
|
+
const candidate = resolved + ext;
|
|
6301
|
+
if (hasFile(candidate)) return candidate;
|
|
6302
|
+
}
|
|
6303
|
+
for (const indexExt of [".ts", ".tsx"]) {
|
|
6304
|
+
const indexPath2 = resolve5(resolved, "index" + indexExt);
|
|
6305
|
+
if (hasFile(indexPath2)) return indexPath2;
|
|
6306
|
+
}
|
|
6124
6307
|
}
|
|
6125
6308
|
return null;
|
|
6126
6309
|
}
|
|
@@ -6635,13 +6818,13 @@ function dirSize(dirPath) {
|
|
|
6635
6818
|
}
|
|
6636
6819
|
for (const entry of entries) {
|
|
6637
6820
|
if (entry === "node_modules" || entry === ".git") continue;
|
|
6638
|
-
const fullPath =
|
|
6821
|
+
const fullPath = join15(dirPath, entry);
|
|
6639
6822
|
try {
|
|
6640
|
-
const
|
|
6641
|
-
if (
|
|
6823
|
+
const stat2 = statSync2(fullPath);
|
|
6824
|
+
if (stat2.isDirectory()) {
|
|
6642
6825
|
total += dirSize(fullPath);
|
|
6643
|
-
} else if (
|
|
6644
|
-
total +=
|
|
6826
|
+
} else if (stat2.isFile()) {
|
|
6827
|
+
total += stat2.size;
|
|
6645
6828
|
}
|
|
6646
6829
|
} catch {
|
|
6647
6830
|
continue;
|
|
@@ -6655,7 +6838,7 @@ async function detectSizeBudgetViolations(rootDir, config) {
|
|
|
6655
6838
|
let packagesChecked = 0;
|
|
6656
6839
|
for (const [pkgPath, budget] of Object.entries(budgets)) {
|
|
6657
6840
|
packagesChecked++;
|
|
6658
|
-
const distPath =
|
|
6841
|
+
const distPath = join15(rootDir, pkgPath, "dist");
|
|
6659
6842
|
const currentSize = dirSize(distPath);
|
|
6660
6843
|
if (budget.warn) {
|
|
6661
6844
|
const budgetBytes = parseSize(budget.warn);
|
|
@@ -6899,7 +7082,7 @@ function previewFix(fix) {
|
|
|
6899
7082
|
}
|
|
6900
7083
|
}
|
|
6901
7084
|
async function createBackup(filePath, backupDir) {
|
|
6902
|
-
const backupPath =
|
|
7085
|
+
const backupPath = join16(backupDir, `${Date.now()}-${basename5(filePath)}`);
|
|
6903
7086
|
try {
|
|
6904
7087
|
await mkdir22(dirname7(backupPath), { recursive: true });
|
|
6905
7088
|
await copyFile2(filePath, backupPath);
|
|
@@ -6925,7 +7108,7 @@ async function applyDeleteFile(fix, config) {
|
|
|
6925
7108
|
}
|
|
6926
7109
|
async function applyDeleteLines(fix) {
|
|
6927
7110
|
if (fix.line !== void 0) {
|
|
6928
|
-
const content = await
|
|
7111
|
+
const content = await readFile62(fix.file, "utf-8");
|
|
6929
7112
|
const lines = content.split("\n");
|
|
6930
7113
|
lines.splice(fix.line - 1, 1);
|
|
6931
7114
|
await writeFile32(fix.file, lines.join("\n"));
|
|
@@ -6933,13 +7116,13 @@ async function applyDeleteLines(fix) {
|
|
|
6933
7116
|
}
|
|
6934
7117
|
async function applyReplace(fix) {
|
|
6935
7118
|
if (fix.oldContent && fix.newContent !== void 0) {
|
|
6936
|
-
const content = await
|
|
7119
|
+
const content = await readFile62(fix.file, "utf-8");
|
|
6937
7120
|
await writeFile32(fix.file, content.replace(fix.oldContent, fix.newContent));
|
|
6938
7121
|
}
|
|
6939
7122
|
}
|
|
6940
7123
|
async function applyInsert(fix) {
|
|
6941
7124
|
if (fix.line !== void 0 && fix.newContent) {
|
|
6942
|
-
const content = await
|
|
7125
|
+
const content = await readFile62(fix.file, "utf-8");
|
|
6943
7126
|
const lines = content.split("\n");
|
|
6944
7127
|
lines.splice(fix.line - 1, 0, fix.newContent);
|
|
6945
7128
|
await writeFile32(fix.file, lines.join("\n"));
|
|
@@ -7274,12 +7457,12 @@ function parseDiffHeader(part) {
|
|
|
7274
7457
|
return headerMatch[2];
|
|
7275
7458
|
}
|
|
7276
7459
|
function parseDiffPart(part) {
|
|
7277
|
-
const
|
|
7278
|
-
if (!
|
|
7460
|
+
const path42 = parseDiffHeader(part);
|
|
7461
|
+
if (!path42) return null;
|
|
7279
7462
|
const additionRegex = /^\+(?!\+\+)/gm;
|
|
7280
7463
|
const deletionRegex = /^-(?!--)/gm;
|
|
7281
7464
|
return {
|
|
7282
|
-
path:
|
|
7465
|
+
path: path42,
|
|
7283
7466
|
status: detectFileStatus(part),
|
|
7284
7467
|
additions: (part.match(additionRegex) || []).length,
|
|
7285
7468
|
deletions: (part.match(deletionRegex) || []).length
|
|
@@ -8064,10 +8247,10 @@ function detectEmergentConstraints(history, options) {
|
|
|
8064
8247
|
return { suggestions, totalViolationsAnalyzed, windowWeeks, minOccurrences };
|
|
8065
8248
|
}
|
|
8066
8249
|
function streamsDir(projectPath) {
|
|
8067
|
-
return
|
|
8250
|
+
return path7.join(projectPath, HARNESS_DIR, STREAMS_DIR);
|
|
8068
8251
|
}
|
|
8069
8252
|
function indexPath(projectPath) {
|
|
8070
|
-
return
|
|
8253
|
+
return path7.join(streamsDir(projectPath), INDEX_FILE);
|
|
8071
8254
|
}
|
|
8072
8255
|
function validateStreamName(name) {
|
|
8073
8256
|
if (!STREAM_NAME_REGEX.test(name)) {
|
|
@@ -8081,11 +8264,11 @@ function validateStreamName(name) {
|
|
|
8081
8264
|
}
|
|
8082
8265
|
async function loadStreamIndex(projectPath) {
|
|
8083
8266
|
const idxPath = indexPath(projectPath);
|
|
8084
|
-
if (!
|
|
8267
|
+
if (!fs10.existsSync(idxPath)) {
|
|
8085
8268
|
return Ok({ ...DEFAULT_STREAM_INDEX, streams: {} });
|
|
8086
8269
|
}
|
|
8087
8270
|
try {
|
|
8088
|
-
const raw =
|
|
8271
|
+
const raw = fs10.readFileSync(idxPath, "utf-8");
|
|
8089
8272
|
const parsed = JSON.parse(raw);
|
|
8090
8273
|
const result = StreamIndexSchema.safeParse(parsed);
|
|
8091
8274
|
if (!result.success) {
|
|
@@ -8103,8 +8286,8 @@ async function loadStreamIndex(projectPath) {
|
|
|
8103
8286
|
async function saveStreamIndex(projectPath, index) {
|
|
8104
8287
|
const dir = streamsDir(projectPath);
|
|
8105
8288
|
try {
|
|
8106
|
-
|
|
8107
|
-
|
|
8289
|
+
fs10.mkdirSync(dir, { recursive: true });
|
|
8290
|
+
fs10.writeFileSync(indexPath(projectPath), JSON.stringify(index, null, 2));
|
|
8108
8291
|
return Ok(void 0);
|
|
8109
8292
|
} catch (error) {
|
|
8110
8293
|
return Err(
|
|
@@ -8143,18 +8326,18 @@ async function resolveStreamPath(projectPath, options) {
|
|
|
8143
8326
|
)
|
|
8144
8327
|
);
|
|
8145
8328
|
}
|
|
8146
|
-
return Ok(
|
|
8329
|
+
return Ok(path7.join(streamsDir(projectPath), options.stream));
|
|
8147
8330
|
}
|
|
8148
8331
|
const branch = getCurrentBranch(projectPath);
|
|
8149
8332
|
if (branch && branch !== "main" && branch !== "master") {
|
|
8150
8333
|
for (const [name, info] of Object.entries(index.streams)) {
|
|
8151
8334
|
if (info.branch === branch) {
|
|
8152
|
-
return Ok(
|
|
8335
|
+
return Ok(path7.join(streamsDir(projectPath), name));
|
|
8153
8336
|
}
|
|
8154
8337
|
}
|
|
8155
8338
|
}
|
|
8156
8339
|
if (index.activeStream && index.streams[index.activeStream]) {
|
|
8157
|
-
return Ok(
|
|
8340
|
+
return Ok(path7.join(streamsDir(projectPath), index.activeStream));
|
|
8158
8341
|
}
|
|
8159
8342
|
return Err(
|
|
8160
8343
|
new Error(
|
|
@@ -8182,9 +8365,9 @@ async function createStream(projectPath, name, branch) {
|
|
|
8182
8365
|
if (index.streams[name]) {
|
|
8183
8366
|
return Err(new Error(`Stream '${name}' already exists`));
|
|
8184
8367
|
}
|
|
8185
|
-
const streamPath =
|
|
8368
|
+
const streamPath = path7.join(streamsDir(projectPath), name);
|
|
8186
8369
|
try {
|
|
8187
|
-
|
|
8370
|
+
fs10.mkdirSync(streamPath, { recursive: true });
|
|
8188
8371
|
} catch (error) {
|
|
8189
8372
|
return Err(
|
|
8190
8373
|
new Error(
|
|
@@ -8225,12 +8408,12 @@ async function archiveStream(projectPath, name) {
|
|
|
8225
8408
|
if (!index.streams[name]) {
|
|
8226
8409
|
return Err(new Error(`Stream '${name}' not found`));
|
|
8227
8410
|
}
|
|
8228
|
-
const streamPath =
|
|
8229
|
-
const archiveDir =
|
|
8411
|
+
const streamPath = path7.join(streamsDir(projectPath), name);
|
|
8412
|
+
const archiveDir = path7.join(projectPath, HARNESS_DIR, "archive", "streams");
|
|
8230
8413
|
try {
|
|
8231
|
-
|
|
8414
|
+
fs10.mkdirSync(archiveDir, { recursive: true });
|
|
8232
8415
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
8233
|
-
|
|
8416
|
+
fs10.renameSync(streamPath, path7.join(archiveDir, `${name}-${date}`));
|
|
8234
8417
|
} catch (error) {
|
|
8235
8418
|
return Err(
|
|
8236
8419
|
new Error(
|
|
@@ -8251,19 +8434,19 @@ function getStreamForBranch(index, branch) {
|
|
|
8251
8434
|
return null;
|
|
8252
8435
|
}
|
|
8253
8436
|
async function migrateToStreams(projectPath) {
|
|
8254
|
-
const harnessDir =
|
|
8255
|
-
if (
|
|
8437
|
+
const harnessDir = path7.join(projectPath, HARNESS_DIR);
|
|
8438
|
+
if (fs10.existsSync(indexPath(projectPath))) {
|
|
8256
8439
|
return Ok(void 0);
|
|
8257
8440
|
}
|
|
8258
|
-
const filesToMove = STATE_FILES.filter((f) =>
|
|
8441
|
+
const filesToMove = STATE_FILES.filter((f) => fs10.existsSync(path7.join(harnessDir, f)));
|
|
8259
8442
|
if (filesToMove.length === 0) {
|
|
8260
8443
|
return Ok(void 0);
|
|
8261
8444
|
}
|
|
8262
|
-
const defaultDir =
|
|
8445
|
+
const defaultDir = path7.join(streamsDir(projectPath), "default");
|
|
8263
8446
|
try {
|
|
8264
|
-
|
|
8447
|
+
fs10.mkdirSync(defaultDir, { recursive: true });
|
|
8265
8448
|
for (const file of filesToMove) {
|
|
8266
|
-
|
|
8449
|
+
fs10.renameSync(path7.join(harnessDir, file), path7.join(defaultDir, file));
|
|
8267
8450
|
}
|
|
8268
8451
|
} catch (error) {
|
|
8269
8452
|
return Err(
|
|
@@ -8293,26 +8476,26 @@ function resolveSessionDir(projectPath, sessionSlug, options) {
|
|
|
8293
8476
|
new Error(`Invalid session slug '${sessionSlug}': must not contain path traversal characters`)
|
|
8294
8477
|
);
|
|
8295
8478
|
}
|
|
8296
|
-
const sessionDir =
|
|
8479
|
+
const sessionDir = path8.join(projectPath, HARNESS_DIR, SESSIONS_DIR, sessionSlug);
|
|
8297
8480
|
if (options?.create) {
|
|
8298
|
-
|
|
8481
|
+
fs11.mkdirSync(sessionDir, { recursive: true });
|
|
8299
8482
|
}
|
|
8300
8483
|
return Ok(sessionDir);
|
|
8301
8484
|
}
|
|
8302
8485
|
function updateSessionIndex(projectPath, sessionSlug, description) {
|
|
8303
|
-
const sessionsDir =
|
|
8304
|
-
|
|
8305
|
-
const indexPath2 =
|
|
8486
|
+
const sessionsDir = path8.join(projectPath, HARNESS_DIR, SESSIONS_DIR);
|
|
8487
|
+
fs11.mkdirSync(sessionsDir, { recursive: true });
|
|
8488
|
+
const indexPath2 = path8.join(sessionsDir, SESSION_INDEX_FILE);
|
|
8306
8489
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
8307
8490
|
const newLine = `- [${sessionSlug}](${sessionSlug}/summary.md) \u2014 ${description} (${date})`;
|
|
8308
|
-
if (!
|
|
8309
|
-
|
|
8491
|
+
if (!fs11.existsSync(indexPath2)) {
|
|
8492
|
+
fs11.writeFileSync(indexPath2, `## Active Sessions
|
|
8310
8493
|
|
|
8311
8494
|
${newLine}
|
|
8312
8495
|
`);
|
|
8313
8496
|
return;
|
|
8314
8497
|
}
|
|
8315
|
-
const content =
|
|
8498
|
+
const content = fs11.readFileSync(indexPath2, "utf-8");
|
|
8316
8499
|
const lines = content.split("\n");
|
|
8317
8500
|
const slugPattern = `- [${sessionSlug}]`;
|
|
8318
8501
|
const existingIdx = lines.findIndex((l) => l.startsWith(slugPattern));
|
|
@@ -8322,7 +8505,7 @@ ${newLine}
|
|
|
8322
8505
|
const lastNonEmpty = lines.reduce((last, line, i) => line.trim() !== "" ? i : last, 0);
|
|
8323
8506
|
lines.splice(lastNonEmpty + 1, 0, newLine);
|
|
8324
8507
|
}
|
|
8325
|
-
|
|
8508
|
+
fs11.writeFileSync(indexPath2, lines.join("\n"));
|
|
8326
8509
|
}
|
|
8327
8510
|
function evictIfNeeded(map) {
|
|
8328
8511
|
if (map.size > MAX_CACHE_ENTRIES) {
|
|
@@ -8335,8 +8518,8 @@ async function getStateDir(projectPath, stream, session) {
|
|
|
8335
8518
|
const sessionResult = resolveSessionDir(projectPath, session, { create: true });
|
|
8336
8519
|
return sessionResult;
|
|
8337
8520
|
}
|
|
8338
|
-
const streamsIndexPath =
|
|
8339
|
-
const hasStreams =
|
|
8521
|
+
const streamsIndexPath = path9.join(projectPath, HARNESS_DIR, "streams", INDEX_FILE);
|
|
8522
|
+
const hasStreams = fs12.existsSync(streamsIndexPath);
|
|
8340
8523
|
if (stream || hasStreams) {
|
|
8341
8524
|
const result = await resolveStreamPath(projectPath, stream ? { stream } : void 0);
|
|
8342
8525
|
if (result.ok) {
|
|
@@ -8346,18 +8529,18 @@ async function getStateDir(projectPath, stream, session) {
|
|
|
8346
8529
|
return result;
|
|
8347
8530
|
}
|
|
8348
8531
|
}
|
|
8349
|
-
return Ok(
|
|
8532
|
+
return Ok(path9.join(projectPath, HARNESS_DIR));
|
|
8350
8533
|
}
|
|
8351
8534
|
async function loadState(projectPath, stream, session) {
|
|
8352
8535
|
try {
|
|
8353
8536
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
8354
8537
|
if (!dirResult.ok) return dirResult;
|
|
8355
8538
|
const stateDir = dirResult.value;
|
|
8356
|
-
const statePath =
|
|
8357
|
-
if (!
|
|
8539
|
+
const statePath = path10.join(stateDir, STATE_FILE);
|
|
8540
|
+
if (!fs13.existsSync(statePath)) {
|
|
8358
8541
|
return Ok({ ...DEFAULT_STATE });
|
|
8359
8542
|
}
|
|
8360
|
-
const raw =
|
|
8543
|
+
const raw = fs13.readFileSync(statePath, "utf-8");
|
|
8361
8544
|
const parsed = JSON.parse(raw);
|
|
8362
8545
|
const result = HarnessStateSchema.safeParse(parsed);
|
|
8363
8546
|
if (!result.success) {
|
|
@@ -8375,11 +8558,11 @@ async function saveState(projectPath, state, stream, session) {
|
|
|
8375
8558
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
8376
8559
|
if (!dirResult.ok) return dirResult;
|
|
8377
8560
|
const stateDir = dirResult.value;
|
|
8378
|
-
const statePath =
|
|
8379
|
-
|
|
8561
|
+
const statePath = path10.join(stateDir, STATE_FILE);
|
|
8562
|
+
fs13.mkdirSync(stateDir, { recursive: true });
|
|
8380
8563
|
const tmpPath = statePath + ".tmp";
|
|
8381
|
-
|
|
8382
|
-
|
|
8564
|
+
fs13.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
|
|
8565
|
+
fs13.renameSync(tmpPath, statePath);
|
|
8383
8566
|
return Ok(void 0);
|
|
8384
8567
|
} catch (error) {
|
|
8385
8568
|
return Err(
|
|
@@ -8465,10 +8648,10 @@ function computeContentHash(text) {
|
|
|
8465
8648
|
return crypto2.createHash("sha256").update(text).digest("hex").slice(0, 16);
|
|
8466
8649
|
}
|
|
8467
8650
|
function loadContentHashes(stateDir) {
|
|
8468
|
-
const hashesPath =
|
|
8469
|
-
if (!
|
|
8651
|
+
const hashesPath = path11.join(stateDir, CONTENT_HASHES_FILE);
|
|
8652
|
+
if (!fs14.existsSync(hashesPath)) return {};
|
|
8470
8653
|
try {
|
|
8471
|
-
const raw =
|
|
8654
|
+
const raw = fs14.readFileSync(hashesPath, "utf-8");
|
|
8472
8655
|
const parsed = JSON.parse(raw);
|
|
8473
8656
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return {};
|
|
8474
8657
|
return parsed;
|
|
@@ -8477,15 +8660,15 @@ function loadContentHashes(stateDir) {
|
|
|
8477
8660
|
}
|
|
8478
8661
|
}
|
|
8479
8662
|
function saveContentHashes(stateDir, index) {
|
|
8480
|
-
const hashesPath =
|
|
8663
|
+
const hashesPath = path11.join(stateDir, CONTENT_HASHES_FILE);
|
|
8481
8664
|
const tmpPath = hashesPath + ".tmp";
|
|
8482
|
-
|
|
8483
|
-
|
|
8665
|
+
fs14.writeFileSync(tmpPath, JSON.stringify(index, null, 2) + "\n");
|
|
8666
|
+
fs14.renameSync(tmpPath, hashesPath);
|
|
8484
8667
|
}
|
|
8485
8668
|
function rebuildContentHashes(stateDir, learningsFile) {
|
|
8486
|
-
const learningsPath =
|
|
8487
|
-
if (!
|
|
8488
|
-
const content =
|
|
8669
|
+
const learningsPath = path11.join(stateDir, learningsFile);
|
|
8670
|
+
if (!fs14.existsSync(learningsPath)) return {};
|
|
8671
|
+
const content = fs14.readFileSync(learningsPath, "utf-8");
|
|
8489
8672
|
const lines = content.split("\n");
|
|
8490
8673
|
const index = {};
|
|
8491
8674
|
for (let i = 0; i < lines.length; i++) {
|
|
@@ -8544,18 +8727,18 @@ async function loadRelevantLearnings(projectPath, skillName, stream, session) {
|
|
|
8544
8727
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
8545
8728
|
if (!dirResult.ok) return dirResult;
|
|
8546
8729
|
const stateDir = dirResult.value;
|
|
8547
|
-
const learningsPath =
|
|
8548
|
-
if (!
|
|
8730
|
+
const learningsPath = path12.join(stateDir, LEARNINGS_FILE);
|
|
8731
|
+
if (!fs15.existsSync(learningsPath)) {
|
|
8549
8732
|
return Ok([]);
|
|
8550
8733
|
}
|
|
8551
|
-
const stats =
|
|
8734
|
+
const stats = fs15.statSync(learningsPath);
|
|
8552
8735
|
const cacheKey = learningsPath;
|
|
8553
8736
|
const cached = learningsCacheMap.get(cacheKey);
|
|
8554
8737
|
let entries;
|
|
8555
8738
|
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
8556
8739
|
entries = cached.entries;
|
|
8557
8740
|
} else {
|
|
8558
|
-
const content =
|
|
8741
|
+
const content = fs15.readFileSync(learningsPath, "utf-8");
|
|
8559
8742
|
const lines = content.split("\n");
|
|
8560
8743
|
entries = [];
|
|
8561
8744
|
let currentBlock = [];
|
|
@@ -8696,11 +8879,11 @@ function sleep(ms) {
|
|
|
8696
8879
|
async function acquireFileLock(lockPath) {
|
|
8697
8880
|
for (let attempt = 0; attempt < LOCK_RETRIES; attempt++) {
|
|
8698
8881
|
try {
|
|
8699
|
-
const fd =
|
|
8882
|
+
const fd = fs16.openSync(
|
|
8700
8883
|
lockPath,
|
|
8701
|
-
|
|
8884
|
+
fs16.constants.O_CREAT | fs16.constants.O_EXCL | fs16.constants.O_WRONLY
|
|
8702
8885
|
);
|
|
8703
|
-
|
|
8886
|
+
fs16.closeSync(fd);
|
|
8704
8887
|
return;
|
|
8705
8888
|
} catch (err) {
|
|
8706
8889
|
const isExist = err instanceof Error && "code" in err && err.code === "EEXIST";
|
|
@@ -8714,20 +8897,20 @@ async function acquireFileLock(lockPath) {
|
|
|
8714
8897
|
}
|
|
8715
8898
|
function releaseFileLock(lockPath) {
|
|
8716
8899
|
try {
|
|
8717
|
-
|
|
8900
|
+
fs16.unlinkSync(lockPath);
|
|
8718
8901
|
} catch {
|
|
8719
8902
|
}
|
|
8720
8903
|
}
|
|
8721
8904
|
function loadOrRebuildHashes(stateDir, learningsPath) {
|
|
8722
|
-
const hashesPath =
|
|
8723
|
-
if (
|
|
8905
|
+
const hashesPath = path13.join(stateDir, CONTENT_HASHES_FILE);
|
|
8906
|
+
if (fs16.existsSync(hashesPath)) {
|
|
8724
8907
|
const hashes = loadContentHashes(stateDir);
|
|
8725
|
-
if (Object.keys(hashes).length === 0 &&
|
|
8908
|
+
if (Object.keys(hashes).length === 0 && fs16.existsSync(learningsPath)) {
|
|
8726
8909
|
return rebuildContentHashes(stateDir, LEARNINGS_FILE);
|
|
8727
8910
|
}
|
|
8728
8911
|
return hashes;
|
|
8729
8912
|
}
|
|
8730
|
-
if (
|
|
8913
|
+
if (fs16.existsSync(learningsPath)) {
|
|
8731
8914
|
return rebuildContentHashes(stateDir, LEARNINGS_FILE);
|
|
8732
8915
|
}
|
|
8733
8916
|
return {};
|
|
@@ -8751,8 +8934,8 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream,
|
|
|
8751
8934
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
8752
8935
|
if (!dirResult.ok) return dirResult;
|
|
8753
8936
|
const stateDir = dirResult.value;
|
|
8754
|
-
const learningsPath =
|
|
8755
|
-
|
|
8937
|
+
const learningsPath = path13.join(stateDir, LEARNINGS_FILE);
|
|
8938
|
+
fs16.mkdirSync(stateDir, { recursive: true });
|
|
8756
8939
|
const lockPath = learningsPath + ".lock";
|
|
8757
8940
|
await acquireFileLock(lockPath);
|
|
8758
8941
|
try {
|
|
@@ -8782,17 +8965,17 @@ ${bulletLine}
|
|
|
8782
8965
|
`;
|
|
8783
8966
|
let overlapResult;
|
|
8784
8967
|
let existingLineCount;
|
|
8785
|
-
if (!
|
|
8786
|
-
|
|
8968
|
+
if (!fs16.existsSync(learningsPath)) {
|
|
8969
|
+
fs16.writeFileSync(learningsPath, `# Learnings
|
|
8787
8970
|
${entry}`);
|
|
8788
8971
|
existingLineCount = 1;
|
|
8789
8972
|
} else {
|
|
8790
|
-
const existingContent =
|
|
8973
|
+
const existingContent = fs16.readFileSync(learningsPath, "utf-8");
|
|
8791
8974
|
const existingEntries = existingContent.split("\n").filter((line) => /^- \*\*\d{4}-\d{2}-\d{2}/.test(line));
|
|
8792
8975
|
const overlap = checkOverlap(bulletLine, existingEntries);
|
|
8793
8976
|
if (overlap.score >= 0.7) overlapResult = overlap;
|
|
8794
8977
|
existingLineCount = existingContent.split("\n").length;
|
|
8795
|
-
|
|
8978
|
+
fs16.appendFileSync(learningsPath, entry);
|
|
8796
8979
|
}
|
|
8797
8980
|
const bulletLineNum = existingLineCount + 2;
|
|
8798
8981
|
contentHashes[contentHash] = { date: timestamp ?? "", line: bulletLineNum };
|
|
@@ -8897,11 +9080,11 @@ async function loadIndexEntries(projectPath, skillName, stream, session) {
|
|
|
8897
9080
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
8898
9081
|
if (!dirResult.ok) return dirResult;
|
|
8899
9082
|
const stateDir = dirResult.value;
|
|
8900
|
-
const learningsPath =
|
|
8901
|
-
if (!
|
|
9083
|
+
const learningsPath = path13.join(stateDir, LEARNINGS_FILE);
|
|
9084
|
+
if (!fs16.existsSync(learningsPath)) {
|
|
8902
9085
|
return Ok([]);
|
|
8903
9086
|
}
|
|
8904
|
-
const content =
|
|
9087
|
+
const content = fs16.readFileSync(learningsPath, "utf-8");
|
|
8905
9088
|
const lines = content.split("\n");
|
|
8906
9089
|
const indexEntries = [];
|
|
8907
9090
|
let pendingFrontmatter = null;
|
|
@@ -8943,16 +9126,16 @@ async function archiveLearnings(projectPath, entries, stream) {
|
|
|
8943
9126
|
const dirResult = await getStateDir(projectPath, stream);
|
|
8944
9127
|
if (!dirResult.ok) return dirResult;
|
|
8945
9128
|
const stateDir = dirResult.value;
|
|
8946
|
-
const archiveDir =
|
|
8947
|
-
|
|
9129
|
+
const archiveDir = path14.join(stateDir, "learnings-archive");
|
|
9130
|
+
fs17.mkdirSync(archiveDir, { recursive: true });
|
|
8948
9131
|
const now = /* @__PURE__ */ new Date();
|
|
8949
9132
|
const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
|
|
8950
|
-
const archivePath =
|
|
9133
|
+
const archivePath = path14.join(archiveDir, `${yearMonth}.md`);
|
|
8951
9134
|
const archiveContent = entries.join("\n\n") + "\n";
|
|
8952
|
-
if (
|
|
8953
|
-
|
|
9135
|
+
if (fs17.existsSync(archivePath)) {
|
|
9136
|
+
fs17.appendFileSync(archivePath, "\n" + archiveContent);
|
|
8954
9137
|
} else {
|
|
8955
|
-
|
|
9138
|
+
fs17.writeFileSync(archivePath, `# Learnings Archive
|
|
8956
9139
|
|
|
8957
9140
|
${archiveContent}`);
|
|
8958
9141
|
}
|
|
@@ -8970,8 +9153,8 @@ async function pruneLearnings(projectPath, stream) {
|
|
|
8970
9153
|
const dirResult = await getStateDir(projectPath, stream);
|
|
8971
9154
|
if (!dirResult.ok) return dirResult;
|
|
8972
9155
|
const stateDir = dirResult.value;
|
|
8973
|
-
const learningsPath =
|
|
8974
|
-
if (!
|
|
9156
|
+
const learningsPath = path14.join(stateDir, LEARNINGS_FILE);
|
|
9157
|
+
if (!fs17.existsSync(learningsPath)) {
|
|
8975
9158
|
return Ok({ kept: 0, archived: 0, patterns: [] });
|
|
8976
9159
|
}
|
|
8977
9160
|
const loadResult = await loadRelevantLearnings(projectPath, void 0, stream);
|
|
@@ -9002,7 +9185,7 @@ async function pruneLearnings(projectPath, stream) {
|
|
|
9002
9185
|
if (!archiveResult.ok) return archiveResult;
|
|
9003
9186
|
}
|
|
9004
9187
|
const newContent = "# Learnings\n\n" + toKeep.join("\n\n") + "\n";
|
|
9005
|
-
|
|
9188
|
+
fs17.writeFileSync(learningsPath, newContent);
|
|
9006
9189
|
invalidateLearningsCacheEntry(learningsPath);
|
|
9007
9190
|
return Ok({
|
|
9008
9191
|
kept: toKeep.length,
|
|
@@ -9046,19 +9229,19 @@ async function promoteSessionLearnings(projectPath, sessionSlug, stream) {
|
|
|
9046
9229
|
const dirResult = await getStateDir(projectPath, stream);
|
|
9047
9230
|
if (!dirResult.ok) return dirResult;
|
|
9048
9231
|
const stateDir = dirResult.value;
|
|
9049
|
-
const globalPath =
|
|
9050
|
-
const existingGlobal =
|
|
9232
|
+
const globalPath = path14.join(stateDir, LEARNINGS_FILE);
|
|
9233
|
+
const existingGlobal = fs17.existsSync(globalPath) ? fs17.readFileSync(globalPath, "utf-8") : "";
|
|
9051
9234
|
const newEntries = toPromote.filter((entry) => !existingGlobal.includes(entry.trim()));
|
|
9052
9235
|
if (newEntries.length === 0) {
|
|
9053
9236
|
return Ok({ promoted: 0, skipped: skipped + toPromote.length });
|
|
9054
9237
|
}
|
|
9055
9238
|
const promotedContent = newEntries.join("\n\n") + "\n";
|
|
9056
9239
|
if (!existingGlobal) {
|
|
9057
|
-
|
|
9240
|
+
fs17.writeFileSync(globalPath, `# Learnings
|
|
9058
9241
|
|
|
9059
9242
|
${promotedContent}`);
|
|
9060
9243
|
} else {
|
|
9061
|
-
|
|
9244
|
+
fs17.appendFileSync(globalPath, "\n\n" + promotedContent);
|
|
9062
9245
|
}
|
|
9063
9246
|
invalidateLearningsCacheEntry(globalPath);
|
|
9064
9247
|
return Ok({
|
|
@@ -9094,13 +9277,13 @@ async function detectStaleLearnings(projectPath, stream, session) {
|
|
|
9094
9277
|
freshCount++;
|
|
9095
9278
|
continue;
|
|
9096
9279
|
}
|
|
9097
|
-
const resolvedRoot =
|
|
9280
|
+
const resolvedRoot = path15.resolve(projectPath);
|
|
9098
9281
|
const missing = refs.filter((ref) => {
|
|
9099
|
-
const absPath =
|
|
9100
|
-
if (!absPath.startsWith(resolvedRoot +
|
|
9282
|
+
const absPath = path15.resolve(projectPath, ref);
|
|
9283
|
+
if (!absPath.startsWith(resolvedRoot + path15.sep) && absPath !== resolvedRoot) {
|
|
9101
9284
|
return false;
|
|
9102
9285
|
}
|
|
9103
|
-
return !
|
|
9286
|
+
return !fs18.existsSync(absPath);
|
|
9104
9287
|
});
|
|
9105
9288
|
if (missing.length > 0) {
|
|
9106
9289
|
const idx = extractIndexEntry(entry);
|
|
@@ -9135,17 +9318,17 @@ async function appendFailure(projectPath, description, skillName, type, stream,
|
|
|
9135
9318
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
9136
9319
|
if (!dirResult.ok) return dirResult;
|
|
9137
9320
|
const stateDir = dirResult.value;
|
|
9138
|
-
const failuresPath =
|
|
9139
|
-
|
|
9321
|
+
const failuresPath = path16.join(stateDir, FAILURES_FILE);
|
|
9322
|
+
fs19.mkdirSync(stateDir, { recursive: true });
|
|
9140
9323
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
9141
9324
|
const entry = `
|
|
9142
9325
|
- **${timestamp} [skill:${skillName}] [type:${type}]:** ${description}
|
|
9143
9326
|
`;
|
|
9144
|
-
if (!
|
|
9145
|
-
|
|
9327
|
+
if (!fs19.existsSync(failuresPath)) {
|
|
9328
|
+
fs19.writeFileSync(failuresPath, `# Failures
|
|
9146
9329
|
${entry}`);
|
|
9147
9330
|
} else {
|
|
9148
|
-
|
|
9331
|
+
fs19.appendFileSync(failuresPath, entry);
|
|
9149
9332
|
}
|
|
9150
9333
|
failuresCacheMap.delete(failuresPath);
|
|
9151
9334
|
return Ok(void 0);
|
|
@@ -9162,17 +9345,17 @@ async function loadFailures(projectPath, stream, session) {
|
|
|
9162
9345
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
9163
9346
|
if (!dirResult.ok) return dirResult;
|
|
9164
9347
|
const stateDir = dirResult.value;
|
|
9165
|
-
const failuresPath =
|
|
9166
|
-
if (!
|
|
9348
|
+
const failuresPath = path16.join(stateDir, FAILURES_FILE);
|
|
9349
|
+
if (!fs19.existsSync(failuresPath)) {
|
|
9167
9350
|
return Ok([]);
|
|
9168
9351
|
}
|
|
9169
|
-
const stats =
|
|
9352
|
+
const stats = fs19.statSync(failuresPath);
|
|
9170
9353
|
const cacheKey = failuresPath;
|
|
9171
9354
|
const cached = failuresCacheMap.get(cacheKey);
|
|
9172
9355
|
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
9173
9356
|
return Ok(cached.entries);
|
|
9174
9357
|
}
|
|
9175
|
-
const content =
|
|
9358
|
+
const content = fs19.readFileSync(failuresPath, "utf-8");
|
|
9176
9359
|
const entries = [];
|
|
9177
9360
|
for (const line of content.split("\n")) {
|
|
9178
9361
|
const match = line.match(FAILURE_LINE_REGEX);
|
|
@@ -9201,20 +9384,20 @@ async function archiveFailures(projectPath, stream, session) {
|
|
|
9201
9384
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
9202
9385
|
if (!dirResult.ok) return dirResult;
|
|
9203
9386
|
const stateDir = dirResult.value;
|
|
9204
|
-
const failuresPath =
|
|
9205
|
-
if (!
|
|
9387
|
+
const failuresPath = path16.join(stateDir, FAILURES_FILE);
|
|
9388
|
+
if (!fs19.existsSync(failuresPath)) {
|
|
9206
9389
|
return Ok(void 0);
|
|
9207
9390
|
}
|
|
9208
|
-
const archiveDir =
|
|
9209
|
-
|
|
9391
|
+
const archiveDir = path16.join(stateDir, "archive");
|
|
9392
|
+
fs19.mkdirSync(archiveDir, { recursive: true });
|
|
9210
9393
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
9211
9394
|
let archiveName = `failures-${date}.md`;
|
|
9212
9395
|
let counter = 2;
|
|
9213
|
-
while (
|
|
9396
|
+
while (fs19.existsSync(path16.join(archiveDir, archiveName))) {
|
|
9214
9397
|
archiveName = `failures-${date}-${counter}.md`;
|
|
9215
9398
|
counter++;
|
|
9216
9399
|
}
|
|
9217
|
-
|
|
9400
|
+
fs19.renameSync(failuresPath, path16.join(archiveDir, archiveName));
|
|
9218
9401
|
failuresCacheMap.delete(failuresPath);
|
|
9219
9402
|
return Ok(void 0);
|
|
9220
9403
|
} catch (error) {
|
|
@@ -9230,11 +9413,11 @@ async function saveHandoff(projectPath, handoff, stream, session) {
|
|
|
9230
9413
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
9231
9414
|
if (!dirResult.ok) return dirResult;
|
|
9232
9415
|
const stateDir = dirResult.value;
|
|
9233
|
-
const handoffPath =
|
|
9234
|
-
|
|
9416
|
+
const handoffPath = path17.join(stateDir, HANDOFF_FILE);
|
|
9417
|
+
fs20.mkdirSync(stateDir, { recursive: true });
|
|
9235
9418
|
const tmpPath = handoffPath + ".tmp";
|
|
9236
|
-
|
|
9237
|
-
|
|
9419
|
+
fs20.writeFileSync(tmpPath, JSON.stringify(handoff, null, 2));
|
|
9420
|
+
fs20.renameSync(tmpPath, handoffPath);
|
|
9238
9421
|
return Ok(void 0);
|
|
9239
9422
|
} catch (error) {
|
|
9240
9423
|
return Err(
|
|
@@ -9247,11 +9430,11 @@ async function loadHandoff(projectPath, stream, session) {
|
|
|
9247
9430
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
9248
9431
|
if (!dirResult.ok) return dirResult;
|
|
9249
9432
|
const stateDir = dirResult.value;
|
|
9250
|
-
const handoffPath =
|
|
9251
|
-
if (!
|
|
9433
|
+
const handoffPath = path17.join(stateDir, HANDOFF_FILE);
|
|
9434
|
+
if (!fs20.existsSync(handoffPath)) {
|
|
9252
9435
|
return Ok(null);
|
|
9253
9436
|
}
|
|
9254
|
-
const raw =
|
|
9437
|
+
const raw = fs20.readFileSync(handoffPath, "utf-8");
|
|
9255
9438
|
const parsed = JSON.parse(raw);
|
|
9256
9439
|
const result = HandoffSchema.safeParse(parsed);
|
|
9257
9440
|
if (!result.success) {
|
|
@@ -9265,28 +9448,28 @@ async function loadHandoff(projectPath, stream, session) {
|
|
|
9265
9448
|
}
|
|
9266
9449
|
}
|
|
9267
9450
|
function loadChecksFromConfig(gateConfigPath) {
|
|
9268
|
-
if (!
|
|
9269
|
-
const raw = JSON.parse(
|
|
9451
|
+
if (!fs21.existsSync(gateConfigPath)) return [];
|
|
9452
|
+
const raw = JSON.parse(fs21.readFileSync(gateConfigPath, "utf-8"));
|
|
9270
9453
|
const config = GateConfigSchema.safeParse(raw);
|
|
9271
9454
|
if (config.success && config.data.checks) return config.data.checks;
|
|
9272
9455
|
return [];
|
|
9273
9456
|
}
|
|
9274
9457
|
function discoverChecksFromProject(projectPath) {
|
|
9275
9458
|
const checks = [];
|
|
9276
|
-
const packageJsonPath =
|
|
9277
|
-
if (
|
|
9278
|
-
const pkg = JSON.parse(
|
|
9459
|
+
const packageJsonPath = path18.join(projectPath, "package.json");
|
|
9460
|
+
if (fs21.existsSync(packageJsonPath)) {
|
|
9461
|
+
const pkg = JSON.parse(fs21.readFileSync(packageJsonPath, "utf-8"));
|
|
9279
9462
|
const scripts = pkg.scripts || {};
|
|
9280
9463
|
if (scripts.test) checks.push({ name: "test", command: "npm test" });
|
|
9281
9464
|
if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
|
|
9282
9465
|
if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
|
|
9283
9466
|
if (scripts.build) checks.push({ name: "build", command: "npm run build" });
|
|
9284
9467
|
}
|
|
9285
|
-
if (
|
|
9468
|
+
if (fs21.existsSync(path18.join(projectPath, "go.mod"))) {
|
|
9286
9469
|
checks.push({ name: "test", command: "go test ./..." });
|
|
9287
9470
|
checks.push({ name: "build", command: "go build ./..." });
|
|
9288
9471
|
}
|
|
9289
|
-
if (
|
|
9472
|
+
if (fs21.existsSync(path18.join(projectPath, "pyproject.toml")) || fs21.existsSync(path18.join(projectPath, "setup.py"))) {
|
|
9290
9473
|
checks.push({ name: "test", command: "python -m pytest" });
|
|
9291
9474
|
}
|
|
9292
9475
|
return checks;
|
|
@@ -9326,8 +9509,8 @@ function executeCheck(check, projectPath) {
|
|
|
9326
9509
|
}
|
|
9327
9510
|
}
|
|
9328
9511
|
async function runMechanicalGate(projectPath) {
|
|
9329
|
-
const harnessDir =
|
|
9330
|
-
const gateConfigPath =
|
|
9512
|
+
const harnessDir = path18.join(projectPath, HARNESS_DIR);
|
|
9513
|
+
const gateConfigPath = path18.join(harnessDir, GATE_CONFIG_FILE);
|
|
9331
9514
|
try {
|
|
9332
9515
|
let checks = loadChecksFromConfig(gateConfigPath);
|
|
9333
9516
|
if (checks.length === 0) {
|
|
@@ -9383,9 +9566,9 @@ function writeSessionSummary(projectPath, sessionSlug, data) {
|
|
|
9383
9566
|
const dirResult = resolveSessionDir(projectPath, sessionSlug, { create: true });
|
|
9384
9567
|
if (!dirResult.ok) return dirResult;
|
|
9385
9568
|
const sessionDir = dirResult.value;
|
|
9386
|
-
const summaryPath =
|
|
9569
|
+
const summaryPath = path19.join(sessionDir, SUMMARY_FILE);
|
|
9387
9570
|
const content = formatSummary(data);
|
|
9388
|
-
|
|
9571
|
+
fs22.writeFileSync(summaryPath, content);
|
|
9389
9572
|
const description = deriveIndexDescription(data);
|
|
9390
9573
|
updateSessionIndex(projectPath, sessionSlug, description);
|
|
9391
9574
|
return Ok(void 0);
|
|
@@ -9402,11 +9585,11 @@ function loadSessionSummary(projectPath, sessionSlug) {
|
|
|
9402
9585
|
const dirResult = resolveSessionDir(projectPath, sessionSlug);
|
|
9403
9586
|
if (!dirResult.ok) return dirResult;
|
|
9404
9587
|
const sessionDir = dirResult.value;
|
|
9405
|
-
const summaryPath =
|
|
9406
|
-
if (!
|
|
9588
|
+
const summaryPath = path19.join(sessionDir, SUMMARY_FILE);
|
|
9589
|
+
if (!fs22.existsSync(summaryPath)) {
|
|
9407
9590
|
return Ok(null);
|
|
9408
9591
|
}
|
|
9409
|
-
const content =
|
|
9592
|
+
const content = fs22.readFileSync(summaryPath, "utf-8");
|
|
9410
9593
|
return Ok(content);
|
|
9411
9594
|
} catch (error) {
|
|
9412
9595
|
return Err(
|
|
@@ -9418,11 +9601,11 @@ function loadSessionSummary(projectPath, sessionSlug) {
|
|
|
9418
9601
|
}
|
|
9419
9602
|
function listActiveSessions(projectPath) {
|
|
9420
9603
|
try {
|
|
9421
|
-
const indexPath2 =
|
|
9422
|
-
if (!
|
|
9604
|
+
const indexPath2 = path19.join(projectPath, HARNESS_DIR, SESSIONS_DIR, SESSION_INDEX_FILE);
|
|
9605
|
+
if (!fs22.existsSync(indexPath2)) {
|
|
9423
9606
|
return Ok(null);
|
|
9424
9607
|
}
|
|
9425
|
-
const content =
|
|
9608
|
+
const content = fs22.readFileSync(indexPath2, "utf-8");
|
|
9426
9609
|
return Ok(content);
|
|
9427
9610
|
} catch (error) {
|
|
9428
9611
|
return Err(
|
|
@@ -9443,12 +9626,12 @@ async function loadSessionState(projectPath, sessionSlug) {
|
|
|
9443
9626
|
const dirResult = resolveSessionDir(projectPath, sessionSlug);
|
|
9444
9627
|
if (!dirResult.ok) return dirResult;
|
|
9445
9628
|
const sessionDir = dirResult.value;
|
|
9446
|
-
const filePath =
|
|
9447
|
-
if (!
|
|
9629
|
+
const filePath = path20.join(sessionDir, SESSION_STATE_FILE);
|
|
9630
|
+
if (!fs23.existsSync(filePath)) {
|
|
9448
9631
|
return Ok(emptySections());
|
|
9449
9632
|
}
|
|
9450
9633
|
try {
|
|
9451
|
-
const raw =
|
|
9634
|
+
const raw = fs23.readFileSync(filePath, "utf-8");
|
|
9452
9635
|
const parsed = JSON.parse(raw);
|
|
9453
9636
|
const sections = emptySections();
|
|
9454
9637
|
for (const name of SESSION_SECTION_NAMES) {
|
|
@@ -9469,9 +9652,9 @@ async function saveSessionState(projectPath, sessionSlug, sections) {
|
|
|
9469
9652
|
const dirResult = resolveSessionDir(projectPath, sessionSlug, { create: true });
|
|
9470
9653
|
if (!dirResult.ok) return dirResult;
|
|
9471
9654
|
const sessionDir = dirResult.value;
|
|
9472
|
-
const filePath =
|
|
9655
|
+
const filePath = path20.join(sessionDir, SESSION_STATE_FILE);
|
|
9473
9656
|
try {
|
|
9474
|
-
|
|
9657
|
+
fs23.writeFileSync(filePath, JSON.stringify(sections, null, 2));
|
|
9475
9658
|
return Ok(void 0);
|
|
9476
9659
|
} catch (error) {
|
|
9477
9660
|
return Err(
|
|
@@ -9527,26 +9710,26 @@ async function archiveSession(projectPath, sessionSlug) {
|
|
|
9527
9710
|
const dirResult = resolveSessionDir(projectPath, sessionSlug);
|
|
9528
9711
|
if (!dirResult.ok) return dirResult;
|
|
9529
9712
|
const sessionDir = dirResult.value;
|
|
9530
|
-
if (!
|
|
9713
|
+
if (!fs24.existsSync(sessionDir)) {
|
|
9531
9714
|
return Err(new Error(`Session '${sessionSlug}' not found at ${sessionDir}`));
|
|
9532
9715
|
}
|
|
9533
|
-
const archiveBase =
|
|
9716
|
+
const archiveBase = path21.join(projectPath, HARNESS_DIR, ARCHIVE_DIR, "sessions");
|
|
9534
9717
|
try {
|
|
9535
|
-
|
|
9718
|
+
fs24.mkdirSync(archiveBase, { recursive: true });
|
|
9536
9719
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
9537
9720
|
let archiveName = `${sessionSlug}-${date}`;
|
|
9538
9721
|
let counter = 1;
|
|
9539
|
-
while (
|
|
9722
|
+
while (fs24.existsSync(path21.join(archiveBase, archiveName))) {
|
|
9540
9723
|
archiveName = `${sessionSlug}-${date}-${counter}`;
|
|
9541
9724
|
counter++;
|
|
9542
9725
|
}
|
|
9543
|
-
const dest =
|
|
9726
|
+
const dest = path21.join(archiveBase, archiveName);
|
|
9544
9727
|
try {
|
|
9545
|
-
|
|
9728
|
+
fs24.renameSync(sessionDir, dest);
|
|
9546
9729
|
} catch (renameErr) {
|
|
9547
9730
|
if (renameErr instanceof Error && "code" in renameErr && renameErr.code === "EXDEV") {
|
|
9548
|
-
|
|
9549
|
-
|
|
9731
|
+
fs24.cpSync(sessionDir, dest, { recursive: true });
|
|
9732
|
+
fs24.rmSync(sessionDir, { recursive: true });
|
|
9550
9733
|
} else {
|
|
9551
9734
|
throw renameErr;
|
|
9552
9735
|
}
|
|
@@ -9568,8 +9751,8 @@ function loadKnownHashes(eventsPath) {
|
|
|
9568
9751
|
const cached = knownHashesCache.get(eventsPath);
|
|
9569
9752
|
if (cached) return cached;
|
|
9570
9753
|
const hashes = /* @__PURE__ */ new Set();
|
|
9571
|
-
if (
|
|
9572
|
-
const content =
|
|
9754
|
+
if (fs25.existsSync(eventsPath)) {
|
|
9755
|
+
const content = fs25.readFileSync(eventsPath, "utf-8");
|
|
9573
9756
|
const lines = content.split("\n").filter((line) => line.trim() !== "");
|
|
9574
9757
|
for (const line of lines) {
|
|
9575
9758
|
try {
|
|
@@ -9592,8 +9775,8 @@ async function emitEvent(projectPath, event, options) {
|
|
|
9592
9775
|
const dirResult = await getStateDir(projectPath, options?.stream, options?.session);
|
|
9593
9776
|
if (!dirResult.ok) return dirResult;
|
|
9594
9777
|
const stateDir = dirResult.value;
|
|
9595
|
-
const eventsPath =
|
|
9596
|
-
|
|
9778
|
+
const eventsPath = path22.join(stateDir, EVENTS_FILE);
|
|
9779
|
+
fs25.mkdirSync(stateDir, { recursive: true });
|
|
9597
9780
|
const contentHash = computeEventHash(event, options?.session);
|
|
9598
9781
|
const knownHashes = loadKnownHashes(eventsPath);
|
|
9599
9782
|
if (knownHashes.has(contentHash)) {
|
|
@@ -9607,7 +9790,7 @@ async function emitEvent(projectPath, event, options) {
|
|
|
9607
9790
|
if (options?.session) {
|
|
9608
9791
|
fullEvent.session = options.session;
|
|
9609
9792
|
}
|
|
9610
|
-
|
|
9793
|
+
fs25.appendFileSync(eventsPath, JSON.stringify(fullEvent) + "\n");
|
|
9611
9794
|
knownHashes.add(contentHash);
|
|
9612
9795
|
return Ok({ written: true });
|
|
9613
9796
|
} catch (error) {
|
|
@@ -9621,11 +9804,11 @@ async function loadEvents(projectPath, options) {
|
|
|
9621
9804
|
const dirResult = await getStateDir(projectPath, options?.stream, options?.session);
|
|
9622
9805
|
if (!dirResult.ok) return dirResult;
|
|
9623
9806
|
const stateDir = dirResult.value;
|
|
9624
|
-
const eventsPath =
|
|
9625
|
-
if (!
|
|
9807
|
+
const eventsPath = path22.join(stateDir, EVENTS_FILE);
|
|
9808
|
+
if (!fs25.existsSync(eventsPath)) {
|
|
9626
9809
|
return Ok([]);
|
|
9627
9810
|
}
|
|
9628
|
-
const content =
|
|
9811
|
+
const content = fs25.readFileSync(eventsPath, "utf-8");
|
|
9629
9812
|
const lines = content.split("\n").filter((line) => line.trim() !== "");
|
|
9630
9813
|
const events = [];
|
|
9631
9814
|
for (const line of lines) {
|
|
@@ -9874,11 +10057,11 @@ function nodeSubStacks(allDeps) {
|
|
|
9874
10057
|
return found;
|
|
9875
10058
|
}
|
|
9876
10059
|
function detectNodeStacks(projectRoot) {
|
|
9877
|
-
const pkgJsonPath =
|
|
9878
|
-
if (!
|
|
10060
|
+
const pkgJsonPath = path23.join(projectRoot, "package.json");
|
|
10061
|
+
if (!fs26.existsSync(pkgJsonPath)) return [];
|
|
9879
10062
|
const stacks = ["node"];
|
|
9880
10063
|
try {
|
|
9881
|
-
const pkgJson = JSON.parse(
|
|
10064
|
+
const pkgJson = JSON.parse(fs26.readFileSync(pkgJsonPath, "utf-8"));
|
|
9882
10065
|
const allDeps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };
|
|
9883
10066
|
stacks.push(...nodeSubStacks(allDeps));
|
|
9884
10067
|
} catch {
|
|
@@ -9887,10 +10070,10 @@ function detectNodeStacks(projectRoot) {
|
|
|
9887
10070
|
}
|
|
9888
10071
|
function detectStack(projectRoot) {
|
|
9889
10072
|
const stacks = [...detectNodeStacks(projectRoot)];
|
|
9890
|
-
if (
|
|
10073
|
+
if (fs26.existsSync(path23.join(projectRoot, "go.mod"))) {
|
|
9891
10074
|
stacks.push("go");
|
|
9892
10075
|
}
|
|
9893
|
-
const hasPython =
|
|
10076
|
+
const hasPython = fs26.existsSync(path23.join(projectRoot, "requirements.txt")) || fs26.existsSync(path23.join(projectRoot, "pyproject.toml"));
|
|
9894
10077
|
if (hasPython) {
|
|
9895
10078
|
stacks.push("python");
|
|
9896
10079
|
}
|
|
@@ -9933,7 +10116,7 @@ function getInjectionPatterns() {
|
|
|
9933
10116
|
}
|
|
9934
10117
|
function getTaintFilePath(projectRoot, sessionId) {
|
|
9935
10118
|
const id = sessionId || DEFAULT_SESSION_ID;
|
|
9936
|
-
return
|
|
10119
|
+
return join382(projectRoot, ".harness", `session-taint-${id}.json`);
|
|
9937
10120
|
}
|
|
9938
10121
|
function readTaint(projectRoot, sessionId) {
|
|
9939
10122
|
const filePath = getTaintFilePath(projectRoot, sessionId);
|
|
@@ -10015,14 +10198,14 @@ function clearTaint(projectRoot, sessionId) {
|
|
|
10015
10198
|
return 0;
|
|
10016
10199
|
}
|
|
10017
10200
|
}
|
|
10018
|
-
const harnessDir =
|
|
10201
|
+
const harnessDir = join382(projectRoot, ".harness");
|
|
10019
10202
|
let count = 0;
|
|
10020
10203
|
try {
|
|
10021
10204
|
const files = readdirSync3(harnessDir);
|
|
10022
10205
|
for (const file of files) {
|
|
10023
10206
|
if (file.startsWith("session-taint-") && file.endsWith(".json")) {
|
|
10024
10207
|
try {
|
|
10025
|
-
unlinkSync2(
|
|
10208
|
+
unlinkSync2(join382(harnessDir, file));
|
|
10026
10209
|
count++;
|
|
10027
10210
|
} catch {
|
|
10028
10211
|
}
|
|
@@ -10033,7 +10216,7 @@ function clearTaint(projectRoot, sessionId) {
|
|
|
10033
10216
|
return count;
|
|
10034
10217
|
}
|
|
10035
10218
|
function listTaintedSessions(projectRoot) {
|
|
10036
|
-
const harnessDir =
|
|
10219
|
+
const harnessDir = join382(projectRoot, ".harness");
|
|
10037
10220
|
const sessions = [];
|
|
10038
10221
|
try {
|
|
10039
10222
|
const files = readdirSync3(harnessDir);
|
|
@@ -10105,7 +10288,7 @@ function securityFindingId(finding) {
|
|
|
10105
10288
|
}
|
|
10106
10289
|
async function runValidateCheck(projectRoot, config) {
|
|
10107
10290
|
const issues = [];
|
|
10108
|
-
const agentsPath =
|
|
10291
|
+
const agentsPath = path24.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
10109
10292
|
const result = await validateAgentsMap(agentsPath);
|
|
10110
10293
|
if (!result.ok) {
|
|
10111
10294
|
issues.push({ severity: "error", message: result.error.message });
|
|
@@ -10162,7 +10345,7 @@ async function runDepsCheck(projectRoot, config) {
|
|
|
10162
10345
|
}
|
|
10163
10346
|
async function runDocsCheck(projectRoot, config) {
|
|
10164
10347
|
const issues = [];
|
|
10165
|
-
const docsDir =
|
|
10348
|
+
const docsDir = path24.join(projectRoot, config.docsDir ?? "docs");
|
|
10166
10349
|
const entropyConfig = config.entropy || {};
|
|
10167
10350
|
const result = await checkDocCoverage("project", {
|
|
10168
10351
|
docsDir,
|
|
@@ -10347,7 +10530,7 @@ async function runTraceabilityCheck(projectRoot, config) {
|
|
|
10347
10530
|
const issues = [];
|
|
10348
10531
|
const traceConfig = config.traceability || {};
|
|
10349
10532
|
if (traceConfig.enabled === false) return issues;
|
|
10350
|
-
const graphDir =
|
|
10533
|
+
const graphDir = path24.join(projectRoot, ".harness", "graph");
|
|
10351
10534
|
const store = new GraphStore();
|
|
10352
10535
|
const loaded = await store.load(graphDir);
|
|
10353
10536
|
if (!loaded) {
|
|
@@ -10525,7 +10708,7 @@ async function runMechanicalChecks(options) {
|
|
|
10525
10708
|
};
|
|
10526
10709
|
if (!skip.includes("validate")) {
|
|
10527
10710
|
try {
|
|
10528
|
-
const agentsPath =
|
|
10711
|
+
const agentsPath = path25.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
10529
10712
|
const result = await validateAgentsMap(agentsPath);
|
|
10530
10713
|
if (!result.ok) {
|
|
10531
10714
|
statuses.validate = "fail";
|
|
@@ -10562,7 +10745,7 @@ async function runMechanicalChecks(options) {
|
|
|
10562
10745
|
statuses.validate = "fail";
|
|
10563
10746
|
findings.push({
|
|
10564
10747
|
tool: "validate",
|
|
10565
|
-
file:
|
|
10748
|
+
file: path25.join(projectRoot, "AGENTS.md"),
|
|
10566
10749
|
message: err instanceof Error ? err.message : String(err),
|
|
10567
10750
|
severity: "error"
|
|
10568
10751
|
});
|
|
@@ -10626,7 +10809,7 @@ async function runMechanicalChecks(options) {
|
|
|
10626
10809
|
(async () => {
|
|
10627
10810
|
const localFindings = [];
|
|
10628
10811
|
try {
|
|
10629
|
-
const docsDir =
|
|
10812
|
+
const docsDir = path25.join(projectRoot, config.docsDir ?? "docs");
|
|
10630
10813
|
const result = await checkDocCoverage("project", { docsDir });
|
|
10631
10814
|
if (!result.ok) {
|
|
10632
10815
|
statuses["check-docs"] = "warn";
|
|
@@ -10653,7 +10836,7 @@ async function runMechanicalChecks(options) {
|
|
|
10653
10836
|
statuses["check-docs"] = "warn";
|
|
10654
10837
|
localFindings.push({
|
|
10655
10838
|
tool: "check-docs",
|
|
10656
|
-
file:
|
|
10839
|
+
file: path25.join(projectRoot, "docs"),
|
|
10657
10840
|
message: err instanceof Error ? err.message : String(err),
|
|
10658
10841
|
severity: "warning"
|
|
10659
10842
|
});
|
|
@@ -10750,18 +10933,18 @@ function computeContextBudget(diffLines) {
|
|
|
10750
10933
|
return diffLines;
|
|
10751
10934
|
}
|
|
10752
10935
|
function isWithinProject(absPath, projectRoot) {
|
|
10753
|
-
const resolvedRoot =
|
|
10754
|
-
const resolvedPath =
|
|
10755
|
-
return resolvedPath.startsWith(resolvedRoot) || resolvedPath ===
|
|
10936
|
+
const resolvedRoot = path26.resolve(projectRoot) + path26.sep;
|
|
10937
|
+
const resolvedPath = path26.resolve(absPath);
|
|
10938
|
+
return resolvedPath.startsWith(resolvedRoot) || resolvedPath === path26.resolve(projectRoot);
|
|
10756
10939
|
}
|
|
10757
10940
|
async function readContextFile(projectRoot, filePath, reason) {
|
|
10758
|
-
const absPath =
|
|
10941
|
+
const absPath = path26.isAbsolute(filePath) ? filePath : path26.join(projectRoot, filePath);
|
|
10759
10942
|
if (!isWithinProject(absPath, projectRoot)) return null;
|
|
10760
10943
|
const result = await readFileContent(absPath);
|
|
10761
10944
|
if (!result.ok) return null;
|
|
10762
10945
|
const content = result.value;
|
|
10763
10946
|
const lines = content.split("\n").length;
|
|
10764
|
-
const relPath2 =
|
|
10947
|
+
const relPath2 = path26.isAbsolute(filePath) ? relativePosix(projectRoot, filePath) : filePath;
|
|
10765
10948
|
return { path: relPath2, content, reason, lines };
|
|
10766
10949
|
}
|
|
10767
10950
|
function extractImportSources2(content) {
|
|
@@ -10776,18 +10959,24 @@ function extractImportSources2(content) {
|
|
|
10776
10959
|
}
|
|
10777
10960
|
async function resolveImportPath2(projectRoot, fromFile, importSource) {
|
|
10778
10961
|
if (!importSource.startsWith(".")) return null;
|
|
10779
|
-
const fromDir =
|
|
10780
|
-
const basePath =
|
|
10962
|
+
const fromDir = path26.dirname(path26.join(projectRoot, fromFile));
|
|
10963
|
+
const basePath = path26.resolve(fromDir, importSource);
|
|
10781
10964
|
if (!isWithinProject(basePath, projectRoot)) return null;
|
|
10782
10965
|
const relBase = relativePosix(projectRoot, basePath);
|
|
10783
|
-
const
|
|
10784
|
-
|
|
10785
|
-
|
|
10786
|
-
|
|
10787
|
-
|
|
10788
|
-
|
|
10966
|
+
const sourceExt = path26.extname(relBase);
|
|
10967
|
+
const candidates = [];
|
|
10968
|
+
const fallbacks = JS_EXT_FALLBACKS22[sourceExt];
|
|
10969
|
+
if (fallbacks) {
|
|
10970
|
+
const stripped = relBase.slice(0, -sourceExt.length);
|
|
10971
|
+
for (const ext of fallbacks) candidates.push(stripped + ext);
|
|
10972
|
+
candidates.push(path26.join(stripped, "index.ts"));
|
|
10973
|
+
candidates.push(path26.join(stripped, "index.tsx"));
|
|
10974
|
+
candidates.push(path26.join(stripped, "index.jsx"));
|
|
10975
|
+
}
|
|
10976
|
+
candidates.push(relBase + ".ts", relBase + ".tsx", relBase + ".mts");
|
|
10977
|
+
candidates.push(path26.join(relBase, "index.ts"));
|
|
10789
10978
|
for (const candidate of candidates) {
|
|
10790
|
-
const absCandidate =
|
|
10979
|
+
const absCandidate = path26.join(projectRoot, candidate);
|
|
10791
10980
|
if (await fileExists(absCandidate)) {
|
|
10792
10981
|
return candidate;
|
|
10793
10982
|
}
|
|
@@ -10795,7 +10984,7 @@ async function resolveImportPath2(projectRoot, fromFile, importSource) {
|
|
|
10795
10984
|
return null;
|
|
10796
10985
|
}
|
|
10797
10986
|
async function findTestFiles(projectRoot, sourceFile) {
|
|
10798
|
-
const baseName =
|
|
10987
|
+
const baseName = path26.basename(sourceFile, path26.extname(sourceFile));
|
|
10799
10988
|
const pattern = `**/${baseName}.{test,spec}.{ts,tsx,mts}`;
|
|
10800
10989
|
const results = await findFiles(pattern, projectRoot);
|
|
10801
10990
|
return results.map((f) => relativePosix(projectRoot, f));
|
|
@@ -11563,7 +11752,7 @@ function normalizePath(filePath, projectRoot) {
|
|
|
11563
11752
|
let normalized = filePath;
|
|
11564
11753
|
normalized = normalized.replace(/\\/g, "/");
|
|
11565
11754
|
const normalizedRoot = projectRoot.replace(/\\/g, "/");
|
|
11566
|
-
if (
|
|
11755
|
+
if (path27.isAbsolute(normalized)) {
|
|
11567
11756
|
const root = normalizedRoot.endsWith("/") ? normalizedRoot : normalizedRoot + "/";
|
|
11568
11757
|
if (normalized.startsWith(root)) {
|
|
11569
11758
|
normalized = normalized.slice(root.length);
|
|
@@ -11575,12 +11764,12 @@ function normalizePath(filePath, projectRoot) {
|
|
|
11575
11764
|
return normalized;
|
|
11576
11765
|
}
|
|
11577
11766
|
function resolveImportPath22(currentFile, importPath) {
|
|
11578
|
-
const dir =
|
|
11579
|
-
let resolved =
|
|
11767
|
+
const dir = path27.dirname(currentFile);
|
|
11768
|
+
let resolved = path27.join(dir, importPath).replace(/\\/g, "/");
|
|
11580
11769
|
if (!resolved.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
11581
11770
|
resolved += ".ts";
|
|
11582
11771
|
}
|
|
11583
|
-
return
|
|
11772
|
+
return path27.normalize(resolved).replace(/\\/g, "/");
|
|
11584
11773
|
}
|
|
11585
11774
|
function enqueueImports(content, current, visited, queue, maxDepth) {
|
|
11586
11775
|
const importRegex = /import\s+.*?from\s+['"]([^'"]+)['"]/g;
|
|
@@ -11612,7 +11801,7 @@ function isMechanicallyExcluded(finding, exclusionSet, projectRoot) {
|
|
|
11612
11801
|
const normalizedFile = normalizePath(finding.file, projectRoot);
|
|
11613
11802
|
if (exclusionSet.isExcluded(normalizedFile, finding.lineRange)) return true;
|
|
11614
11803
|
if (exclusionSet.isExcluded(finding.file, finding.lineRange)) return true;
|
|
11615
|
-
const absoluteFile =
|
|
11804
|
+
const absoluteFile = path27.isAbsolute(finding.file) ? finding.file : path27.join(projectRoot, finding.file).replace(/\\/g, "/");
|
|
11616
11805
|
return exclusionSet.isExcluded(absoluteFile, finding.lineRange);
|
|
11617
11806
|
}
|
|
11618
11807
|
async function validateWithGraph(crossFileRefs, graph) {
|
|
@@ -12620,7 +12809,7 @@ function isRegression(from, to) {
|
|
|
12620
12809
|
}
|
|
12621
12810
|
function collectAutopilotStatuses(autopilotPath, featurePlans, allTaskStatuses) {
|
|
12622
12811
|
try {
|
|
12623
|
-
const raw =
|
|
12812
|
+
const raw = fs28.readFileSync(autopilotPath, "utf-8");
|
|
12624
12813
|
const autopilot = JSON.parse(raw);
|
|
12625
12814
|
if (!autopilot.phases) return;
|
|
12626
12815
|
const linkedPhases = autopilot.phases.filter(
|
|
@@ -12652,10 +12841,10 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
12652
12841
|
const featuresWithPlans = allFeatures.filter((f) => f.plans.length > 0);
|
|
12653
12842
|
const useRootState = featuresWithPlans.length <= 1;
|
|
12654
12843
|
if (useRootState) {
|
|
12655
|
-
const rootStatePath =
|
|
12656
|
-
if (
|
|
12844
|
+
const rootStatePath = path28.join(projectPath, ".harness", "state.json");
|
|
12845
|
+
if (fs28.existsSync(rootStatePath)) {
|
|
12657
12846
|
try {
|
|
12658
|
-
const raw =
|
|
12847
|
+
const raw = fs28.readFileSync(rootStatePath, "utf-8");
|
|
12659
12848
|
const state = JSON.parse(raw);
|
|
12660
12849
|
if (state.progress) {
|
|
12661
12850
|
for (const status of Object.values(state.progress)) {
|
|
@@ -12666,14 +12855,14 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
12666
12855
|
}
|
|
12667
12856
|
}
|
|
12668
12857
|
}
|
|
12669
|
-
const sessionsDir =
|
|
12670
|
-
if (
|
|
12858
|
+
const sessionsDir = path28.join(projectPath, ".harness", "sessions");
|
|
12859
|
+
if (fs28.existsSync(sessionsDir)) {
|
|
12671
12860
|
try {
|
|
12672
|
-
const sessionDirs =
|
|
12861
|
+
const sessionDirs = fs28.readdirSync(sessionsDir, { withFileTypes: true });
|
|
12673
12862
|
for (const entry of sessionDirs) {
|
|
12674
12863
|
if (!entry.isDirectory()) continue;
|
|
12675
|
-
const autopilotPath =
|
|
12676
|
-
if (!
|
|
12864
|
+
const autopilotPath = path28.join(sessionsDir, entry.name, "autopilot-state.json");
|
|
12865
|
+
if (!fs28.existsSync(autopilotPath)) continue;
|
|
12677
12866
|
collectAutopilotStatuses(autopilotPath, feature.plans, allTaskStatuses);
|
|
12678
12867
|
}
|
|
12679
12868
|
} catch {
|
|
@@ -12788,9 +12977,9 @@ function isValidTrackerShape(tracker) {
|
|
|
12788
12977
|
}
|
|
12789
12978
|
function loadTrackerSyncConfig(projectRoot) {
|
|
12790
12979
|
try {
|
|
12791
|
-
const configPath =
|
|
12792
|
-
if (!
|
|
12793
|
-
const raw =
|
|
12980
|
+
const configPath = path29.join(projectRoot, "harness.config.json");
|
|
12981
|
+
if (!fs29.existsSync(configPath)) return null;
|
|
12982
|
+
const raw = fs29.readFileSync(configPath, "utf-8");
|
|
12794
12983
|
const config = JSON.parse(raw);
|
|
12795
12984
|
const tracker = config.roadmap?.tracker;
|
|
12796
12985
|
if (!isValidTrackerShape(tracker)) return null;
|
|
@@ -12817,14 +13006,14 @@ function buildDedupIndex(tickets, config) {
|
|
|
12817
13006
|
}
|
|
12818
13007
|
return index;
|
|
12819
13008
|
}
|
|
12820
|
-
async function resolveExternalId(feature, milestone,
|
|
13009
|
+
async function resolveExternalId(feature, milestone, adapter2, dedupIndex, result) {
|
|
12821
13010
|
if (feature.externalId) return true;
|
|
12822
13011
|
const existing = dedupIndex.get(feature.name.toLowerCase());
|
|
12823
13012
|
if (existing) {
|
|
12824
13013
|
feature.externalId = existing.externalId;
|
|
12825
13014
|
return true;
|
|
12826
13015
|
}
|
|
12827
|
-
const createResult = await
|
|
13016
|
+
const createResult = await adapter2.createTicket(feature, milestone);
|
|
12828
13017
|
if (createResult.ok) {
|
|
12829
13018
|
feature.externalId = createResult.value.externalId;
|
|
12830
13019
|
result.created.push(createResult.value);
|
|
@@ -12833,7 +13022,7 @@ async function resolveExternalId(feature, milestone, adapter, dedupIndex, result
|
|
|
12833
13022
|
}
|
|
12834
13023
|
return false;
|
|
12835
13024
|
}
|
|
12836
|
-
async function syncToExternal(roadmap,
|
|
13025
|
+
async function syncToExternal(roadmap, adapter2, config, prefetchedTickets) {
|
|
12837
13026
|
const result = emptySyncResult();
|
|
12838
13027
|
const dedupIndex = buildDedupIndex(prefetchedTickets, config);
|
|
12839
13028
|
for (const milestone of roadmap.milestones) {
|
|
@@ -12841,12 +13030,12 @@ async function syncToExternal(roadmap, adapter, config, prefetchedTickets) {
|
|
|
12841
13030
|
const shouldUpdate = await resolveExternalId(
|
|
12842
13031
|
feature,
|
|
12843
13032
|
milestone.name,
|
|
12844
|
-
|
|
13033
|
+
adapter2,
|
|
12845
13034
|
dedupIndex,
|
|
12846
13035
|
result
|
|
12847
13036
|
);
|
|
12848
13037
|
if (!shouldUpdate) continue;
|
|
12849
|
-
const updateResult = await
|
|
13038
|
+
const updateResult = await adapter2.updateTicket(feature.externalId, feature, milestone.name);
|
|
12850
13039
|
if (updateResult.ok) {
|
|
12851
13040
|
result.updated.push(feature.externalId);
|
|
12852
13041
|
} else {
|
|
@@ -12872,7 +13061,7 @@ function applyTicketToFeature(ticketState, feature, config, forceSync, result) {
|
|
|
12872
13061
|
if (!forceSync && feature.status === "blocked" && newStatus === "planned") return;
|
|
12873
13062
|
feature.status = newStatus;
|
|
12874
13063
|
}
|
|
12875
|
-
async function syncFromExternal(roadmap,
|
|
13064
|
+
async function syncFromExternal(roadmap, adapter2, config, options, prefetchedTickets) {
|
|
12876
13065
|
const result = emptySyncResult();
|
|
12877
13066
|
const forceSync = options?.forceSync ?? false;
|
|
12878
13067
|
const featureByExternalId = /* @__PURE__ */ new Map();
|
|
@@ -12888,7 +13077,7 @@ async function syncFromExternal(roadmap, adapter, config, options, prefetchedTic
|
|
|
12888
13077
|
if (prefetchedTickets) {
|
|
12889
13078
|
tickets = prefetchedTickets;
|
|
12890
13079
|
} else {
|
|
12891
|
-
const fetchResult = await
|
|
13080
|
+
const fetchResult = await adapter2.fetchAllTickets();
|
|
12892
13081
|
if (!fetchResult.ok) {
|
|
12893
13082
|
result.errors.push({ featureOrId: "*", error: fetchResult.error });
|
|
12894
13083
|
return result;
|
|
@@ -12902,7 +13091,7 @@ async function syncFromExternal(roadmap, adapter, config, options, prefetchedTic
|
|
|
12902
13091
|
}
|
|
12903
13092
|
return result;
|
|
12904
13093
|
}
|
|
12905
|
-
async function fullSync(roadmapPath,
|
|
13094
|
+
async function fullSync(roadmapPath, adapter2, config, options) {
|
|
12906
13095
|
const previousSync = syncMutex;
|
|
12907
13096
|
let releaseMutex;
|
|
12908
13097
|
syncMutex = new Promise((resolve8) => {
|
|
@@ -12910,7 +13099,7 @@ async function fullSync(roadmapPath, adapter, config, options) {
|
|
|
12910
13099
|
});
|
|
12911
13100
|
await previousSync;
|
|
12912
13101
|
try {
|
|
12913
|
-
const raw =
|
|
13102
|
+
const raw = fs30.readFileSync(roadmapPath, "utf-8");
|
|
12914
13103
|
const parseResult = parseRoadmap(raw);
|
|
12915
13104
|
if (!parseResult.ok) {
|
|
12916
13105
|
return {
|
|
@@ -12919,11 +13108,11 @@ async function fullSync(roadmapPath, adapter, config, options) {
|
|
|
12919
13108
|
};
|
|
12920
13109
|
}
|
|
12921
13110
|
const roadmap = parseResult.value;
|
|
12922
|
-
const fetchResult = await
|
|
13111
|
+
const fetchResult = await adapter2.fetchAllTickets();
|
|
12923
13112
|
const tickets = fetchResult.ok ? fetchResult.value : void 0;
|
|
12924
|
-
const pushResult = await syncToExternal(roadmap,
|
|
12925
|
-
const pullResult = await syncFromExternal(roadmap,
|
|
12926
|
-
|
|
13113
|
+
const pushResult = await syncToExternal(roadmap, adapter2, config, tickets);
|
|
13114
|
+
const pullResult = await syncFromExternal(roadmap, adapter2, config, options);
|
|
13115
|
+
fs30.writeFileSync(roadmapPath, serializeRoadmap(roadmap), "utf-8");
|
|
12927
13116
|
return {
|
|
12928
13117
|
created: pushResult.created,
|
|
12929
13118
|
updated: pushResult.updated,
|
|
@@ -13045,7 +13234,7 @@ function assignFeature(roadmap, feature, assignee, date) {
|
|
|
13045
13234
|
}
|
|
13046
13235
|
function getStatePath() {
|
|
13047
13236
|
const home = process.env["HOME"] || os.homedir();
|
|
13048
|
-
return
|
|
13237
|
+
return path32.join(home, ".harness", "update-check.json");
|
|
13049
13238
|
}
|
|
13050
13239
|
function isUpdateCheckEnabled(configInterval) {
|
|
13051
13240
|
if (process.env["HARNESS_NO_UPDATE_CHECK"] === "1") return false;
|
|
@@ -13058,7 +13247,7 @@ function shouldRunCheck(state, intervalMs) {
|
|
|
13058
13247
|
}
|
|
13059
13248
|
function readCheckState() {
|
|
13060
13249
|
try {
|
|
13061
|
-
const raw =
|
|
13250
|
+
const raw = fs33.readFileSync(getStatePath(), "utf-8");
|
|
13062
13251
|
const parsed = JSON.parse(raw);
|
|
13063
13252
|
if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
|
|
13064
13253
|
const state = parsed;
|
|
@@ -13075,7 +13264,7 @@ function readCheckState() {
|
|
|
13075
13264
|
}
|
|
13076
13265
|
function spawnBackgroundCheck(currentVersion) {
|
|
13077
13266
|
const statePath = getStatePath();
|
|
13078
|
-
const stateDir =
|
|
13267
|
+
const stateDir = path32.dirname(statePath);
|
|
13079
13268
|
const script = `
|
|
13080
13269
|
const { execSync } = require('child_process');
|
|
13081
13270
|
const fs = require('fs');
|
|
@@ -13318,14 +13507,14 @@ function getModelPrice(model, dataset) {
|
|
|
13318
13507
|
return pricing;
|
|
13319
13508
|
}
|
|
13320
13509
|
function getCachePath(projectRoot) {
|
|
13321
|
-
return
|
|
13510
|
+
return path33.join(projectRoot, ".harness", "cache", "pricing.json");
|
|
13322
13511
|
}
|
|
13323
13512
|
function getStalenessMarkerPath(projectRoot) {
|
|
13324
|
-
return
|
|
13513
|
+
return path33.join(projectRoot, ".harness", "cache", "staleness-marker.json");
|
|
13325
13514
|
}
|
|
13326
13515
|
async function readDiskCache(projectRoot) {
|
|
13327
13516
|
try {
|
|
13328
|
-
const raw = await
|
|
13517
|
+
const raw = await fs34.readFile(getCachePath(projectRoot), "utf-8");
|
|
13329
13518
|
return JSON.parse(raw);
|
|
13330
13519
|
} catch {
|
|
13331
13520
|
return null;
|
|
@@ -13333,8 +13522,8 @@ async function readDiskCache(projectRoot) {
|
|
|
13333
13522
|
}
|
|
13334
13523
|
async function writeDiskCache(projectRoot, data) {
|
|
13335
13524
|
const cachePath = getCachePath(projectRoot);
|
|
13336
|
-
await
|
|
13337
|
-
await
|
|
13525
|
+
await fs34.mkdir(path33.dirname(cachePath), { recursive: true });
|
|
13526
|
+
await fs34.writeFile(cachePath, JSON.stringify(data, null, 2));
|
|
13338
13527
|
}
|
|
13339
13528
|
async function fetchFromNetwork() {
|
|
13340
13529
|
try {
|
|
@@ -13361,7 +13550,7 @@ function loadFallbackDataset() {
|
|
|
13361
13550
|
async function checkAndWarnStaleness(projectRoot) {
|
|
13362
13551
|
const markerPath = getStalenessMarkerPath(projectRoot);
|
|
13363
13552
|
try {
|
|
13364
|
-
const raw = await
|
|
13553
|
+
const raw = await fs34.readFile(markerPath, "utf-8");
|
|
13365
13554
|
const marker = JSON.parse(raw);
|
|
13366
13555
|
const firstUse = new Date(marker.firstFallbackUse).getTime();
|
|
13367
13556
|
const now = Date.now();
|
|
@@ -13373,8 +13562,8 @@ async function checkAndWarnStaleness(projectRoot) {
|
|
|
13373
13562
|
}
|
|
13374
13563
|
} catch {
|
|
13375
13564
|
try {
|
|
13376
|
-
await
|
|
13377
|
-
await
|
|
13565
|
+
await fs34.mkdir(path33.dirname(markerPath), { recursive: true });
|
|
13566
|
+
await fs34.writeFile(
|
|
13378
13567
|
markerPath,
|
|
13379
13568
|
JSON.stringify({ firstFallbackUse: (/* @__PURE__ */ new Date()).toISOString() })
|
|
13380
13569
|
);
|
|
@@ -13384,7 +13573,7 @@ async function checkAndWarnStaleness(projectRoot) {
|
|
|
13384
13573
|
}
|
|
13385
13574
|
async function clearStalenessMarker(projectRoot) {
|
|
13386
13575
|
try {
|
|
13387
|
-
await
|
|
13576
|
+
await fs34.unlink(getStalenessMarkerPath(projectRoot));
|
|
13388
13577
|
} catch {
|
|
13389
13578
|
}
|
|
13390
13579
|
}
|
|
@@ -13621,10 +13810,10 @@ function parseLine(line, lineNumber) {
|
|
|
13621
13810
|
return record;
|
|
13622
13811
|
}
|
|
13623
13812
|
function readCostRecords(projectRoot) {
|
|
13624
|
-
const costsFile =
|
|
13813
|
+
const costsFile = path34.join(projectRoot, ".harness", "metrics", "costs.jsonl");
|
|
13625
13814
|
let raw;
|
|
13626
13815
|
try {
|
|
13627
|
-
raw =
|
|
13816
|
+
raw = fs35.readFileSync(costsFile, "utf-8");
|
|
13628
13817
|
} catch {
|
|
13629
13818
|
return [];
|
|
13630
13819
|
}
|
|
@@ -13674,7 +13863,7 @@ function parseCCLine(line, filePath, lineNumber) {
|
|
|
13674
13863
|
entry = JSON.parse(line);
|
|
13675
13864
|
} catch {
|
|
13676
13865
|
console.warn(
|
|
13677
|
-
`[harness usage] Skipping malformed CC JSONL line ${lineNumber} in ${
|
|
13866
|
+
`[harness usage] Skipping malformed CC JSONL line ${lineNumber} in ${path35.basename(filePath)}`
|
|
13678
13867
|
);
|
|
13679
13868
|
return null;
|
|
13680
13869
|
}
|
|
@@ -13688,7 +13877,7 @@ function parseCCLine(line, filePath, lineNumber) {
|
|
|
13688
13877
|
function readCCFile(filePath) {
|
|
13689
13878
|
let raw;
|
|
13690
13879
|
try {
|
|
13691
|
-
raw =
|
|
13880
|
+
raw = fs36.readFileSync(filePath, "utf-8");
|
|
13692
13881
|
} catch {
|
|
13693
13882
|
return [];
|
|
13694
13883
|
}
|
|
@@ -13710,10 +13899,10 @@ function readCCFile(filePath) {
|
|
|
13710
13899
|
}
|
|
13711
13900
|
function parseCCRecords() {
|
|
13712
13901
|
const homeDir = process.env.HOME ?? os2.homedir();
|
|
13713
|
-
const projectsDir =
|
|
13902
|
+
const projectsDir = path35.join(homeDir, ".claude", "projects");
|
|
13714
13903
|
let projectDirs;
|
|
13715
13904
|
try {
|
|
13716
|
-
projectDirs =
|
|
13905
|
+
projectDirs = fs36.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path35.join(projectsDir, d.name));
|
|
13717
13906
|
} catch {
|
|
13718
13907
|
return [];
|
|
13719
13908
|
}
|
|
@@ -13721,7 +13910,7 @@ function parseCCRecords() {
|
|
|
13721
13910
|
for (const dir of projectDirs) {
|
|
13722
13911
|
let files;
|
|
13723
13912
|
try {
|
|
13724
|
-
files =
|
|
13913
|
+
files = fs36.readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => path35.join(dir, f));
|
|
13725
13914
|
} catch {
|
|
13726
13915
|
continue;
|
|
13727
13916
|
}
|
|
@@ -13749,10 +13938,10 @@ function parseLine2(line, lineNumber) {
|
|
|
13749
13938
|
}
|
|
13750
13939
|
}
|
|
13751
13940
|
function readAdoptionRecords(projectRoot) {
|
|
13752
|
-
const adoptionFile =
|
|
13941
|
+
const adoptionFile = path36.join(projectRoot, ".harness", "metrics", "adoption.jsonl");
|
|
13753
13942
|
let raw;
|
|
13754
13943
|
try {
|
|
13755
|
-
raw =
|
|
13944
|
+
raw = fs37.readFileSync(adoptionFile, "utf-8");
|
|
13756
13945
|
} catch {
|
|
13757
13946
|
return [];
|
|
13758
13947
|
}
|
|
@@ -13900,25 +14089,25 @@ function resolveStability(contentType) {
|
|
|
13900
14089
|
return STABILITY_LOOKUP[contentType] ?? "ephemeral";
|
|
13901
14090
|
}
|
|
13902
14091
|
function getOrCreateInstallId(projectRoot) {
|
|
13903
|
-
const harnessDir =
|
|
13904
|
-
const installIdFile =
|
|
14092
|
+
const harnessDir = path37.join(projectRoot, ".harness");
|
|
14093
|
+
const installIdFile = path37.join(harnessDir, ".install-id");
|
|
13905
14094
|
const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
13906
14095
|
try {
|
|
13907
|
-
const existing =
|
|
14096
|
+
const existing = fs38.readFileSync(installIdFile, "utf-8").trim();
|
|
13908
14097
|
if (UUID_V4_RE.test(existing)) {
|
|
13909
14098
|
return existing;
|
|
13910
14099
|
}
|
|
13911
14100
|
} catch {
|
|
13912
14101
|
}
|
|
13913
14102
|
const id = crypto4.randomUUID();
|
|
13914
|
-
|
|
13915
|
-
|
|
14103
|
+
fs38.mkdirSync(harnessDir, { recursive: true });
|
|
14104
|
+
fs38.writeFileSync(installIdFile, id, { encoding: "utf-8", mode: 384 });
|
|
13916
14105
|
return id;
|
|
13917
14106
|
}
|
|
13918
14107
|
function parseIdentityFromTelemetryFile(filePath) {
|
|
13919
14108
|
const identity = {};
|
|
13920
14109
|
try {
|
|
13921
|
-
const raw =
|
|
14110
|
+
const raw = fs39.readFileSync(filePath, "utf-8");
|
|
13922
14111
|
const parsed = JSON.parse(raw);
|
|
13923
14112
|
const src = parsed?.identity;
|
|
13924
14113
|
if (!src || typeof src !== "object") return identity;
|
|
@@ -13931,7 +14120,7 @@ function parseIdentityFromTelemetryFile(filePath) {
|
|
|
13931
14120
|
}
|
|
13932
14121
|
function readProjectNameFallback(configPath) {
|
|
13933
14122
|
try {
|
|
13934
|
-
const raw =
|
|
14123
|
+
const raw = fs39.readFileSync(configPath, "utf-8");
|
|
13935
14124
|
const config = JSON.parse(raw);
|
|
13936
14125
|
return typeof config?.name === "string" ? config.name : void 0;
|
|
13937
14126
|
} catch {
|
|
@@ -13952,10 +14141,10 @@ function readGitAliasFallback(cwd) {
|
|
|
13952
14141
|
}
|
|
13953
14142
|
}
|
|
13954
14143
|
function readIdentity(projectRoot) {
|
|
13955
|
-
const filePath =
|
|
14144
|
+
const filePath = path38.join(projectRoot, ".harness", "telemetry.json");
|
|
13956
14145
|
const identity = parseIdentityFromTelemetryFile(filePath);
|
|
13957
14146
|
if (!identity.project) {
|
|
13958
|
-
const fallbackProject = readProjectNameFallback(
|
|
14147
|
+
const fallbackProject = readProjectNameFallback(path38.join(projectRoot, "harness.config.json"));
|
|
13959
14148
|
if (fallbackProject) identity.project = fallbackProject;
|
|
13960
14149
|
}
|
|
13961
14150
|
if (!identity.alias) {
|
|
@@ -14025,10 +14214,560 @@ async function send(events, apiKey) {
|
|
|
14025
14214
|
}
|
|
14026
14215
|
}
|
|
14027
14216
|
}
|
|
14028
|
-
|
|
14217
|
+
function acquireCompoundLock(category, opts = {}) {
|
|
14218
|
+
if (!KNOWN_CATEGORIES.has(category)) {
|
|
14219
|
+
throw new Error(
|
|
14220
|
+
`Unknown category "${category}". Must be one of: ${[...KNOWN_CATEGORIES].join(", ")}`
|
|
14221
|
+
);
|
|
14222
|
+
}
|
|
14223
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
14224
|
+
const lockDir = path39.join(cwd, ".harness", "locks");
|
|
14225
|
+
fs40.mkdirSync(lockDir, { recursive: true });
|
|
14226
|
+
const lockPath = path39.join(lockDir, `compound-${category}.lock`);
|
|
14227
|
+
let fd;
|
|
14228
|
+
try {
|
|
14229
|
+
fd = fs40.openSync(lockPath, "wx");
|
|
14230
|
+
} catch (e) {
|
|
14231
|
+
const err = e;
|
|
14232
|
+
if (err.code === "EEXIST") {
|
|
14233
|
+
const holderPid = readHolderPid(lockPath);
|
|
14234
|
+
throw new CompoundLockHeldError(category, holderPid, lockPath);
|
|
14235
|
+
}
|
|
14236
|
+
throw err;
|
|
14237
|
+
}
|
|
14238
|
+
fs40.writeSync(fd, String(process.pid));
|
|
14239
|
+
fs40.closeSync(fd);
|
|
14240
|
+
let released = false;
|
|
14241
|
+
const release = () => {
|
|
14242
|
+
if (released) return;
|
|
14243
|
+
released = true;
|
|
14244
|
+
try {
|
|
14245
|
+
fs40.unlinkSync(lockPath);
|
|
14246
|
+
} catch {
|
|
14247
|
+
}
|
|
14248
|
+
process.removeListener("exit", onExit);
|
|
14249
|
+
process.removeListener("SIGINT", onSignal);
|
|
14250
|
+
process.removeListener("SIGTERM", onSignal);
|
|
14251
|
+
process.removeListener("uncaughtException", onUncaught);
|
|
14252
|
+
};
|
|
14253
|
+
const onExit = () => release();
|
|
14254
|
+
const onSignal = (sig) => {
|
|
14255
|
+
release();
|
|
14256
|
+
process.kill(process.pid, sig);
|
|
14257
|
+
};
|
|
14258
|
+
const onUncaught = (err) => {
|
|
14259
|
+
release();
|
|
14260
|
+
console.error(err && err.stack ? err.stack : err);
|
|
14261
|
+
process.exit(1);
|
|
14262
|
+
};
|
|
14263
|
+
process.once("exit", onExit);
|
|
14264
|
+
process.once("SIGINT", onSignal);
|
|
14265
|
+
process.once("SIGTERM", onSignal);
|
|
14266
|
+
process.once("uncaughtException", onUncaught);
|
|
14267
|
+
return { category, lockPath, release };
|
|
14268
|
+
}
|
|
14269
|
+
function readHolderPid(lockPath) {
|
|
14270
|
+
try {
|
|
14271
|
+
const raw = fs40.readFileSync(lockPath, "utf-8").trim();
|
|
14272
|
+
const n = Number.parseInt(raw, 10);
|
|
14273
|
+
return Number.isFinite(n) ? n : -1;
|
|
14274
|
+
} catch {
|
|
14275
|
+
return -1;
|
|
14276
|
+
}
|
|
14277
|
+
}
|
|
14278
|
+
function isSanitizedResult(value) {
|
|
14279
|
+
if (!value || typeof value !== "object") return false;
|
|
14280
|
+
const v = value;
|
|
14281
|
+
if (!v.fields || typeof v.fields !== "object") return false;
|
|
14282
|
+
for (const k of Object.keys(v.fields)) {
|
|
14283
|
+
if (!ALLOWED_SET.has(k)) return false;
|
|
14284
|
+
if (PII_FIELD_DENYLIST.test(k)) return false;
|
|
14285
|
+
}
|
|
14286
|
+
if (!v.distributions || typeof v.distributions !== "object") return false;
|
|
14287
|
+
return true;
|
|
14288
|
+
}
|
|
14289
|
+
function assertSanitized(value) {
|
|
14290
|
+
if (!isSanitizedResult(value)) {
|
|
14291
|
+
throw new Error("PII boundary violated: result is not a SanitizedResult");
|
|
14292
|
+
}
|
|
14293
|
+
}
|
|
14294
|
+
function writePulseConfig(config, opts) {
|
|
14295
|
+
PulseConfigSchema.parse(config);
|
|
14296
|
+
if (!fs41.existsSync(opts.configPath)) {
|
|
14297
|
+
throw new Error(`harness.config.json not found at ${opts.configPath}`);
|
|
14298
|
+
}
|
|
14299
|
+
const raw = fs41.readFileSync(opts.configPath, "utf-8");
|
|
14300
|
+
let parsed;
|
|
14301
|
+
try {
|
|
14302
|
+
parsed = JSON.parse(raw);
|
|
14303
|
+
} catch (e) {
|
|
14304
|
+
throw new Error(`Invalid JSON in ${opts.configPath}: ${e.message}`, { cause: e });
|
|
14305
|
+
}
|
|
14306
|
+
const bakPath = `${opts.configPath}.bak`;
|
|
14307
|
+
if (!opts.skipBackup && !fs41.existsSync(bakPath)) {
|
|
14308
|
+
fs41.writeFileSync(bakPath, raw, "utf-8");
|
|
14309
|
+
}
|
|
14310
|
+
parsed.pulse = config;
|
|
14311
|
+
const serialized = JSON.stringify(parsed, null, 2) + "\n";
|
|
14312
|
+
const tmpPath = `${opts.configPath}.tmp-${process.pid}`;
|
|
14313
|
+
fs41.writeFileSync(tmpPath, serialized, "utf-8");
|
|
14314
|
+
try {
|
|
14315
|
+
fs41.renameSync(tmpPath, opts.configPath);
|
|
14316
|
+
} catch (e) {
|
|
14317
|
+
try {
|
|
14318
|
+
fs41.unlinkSync(tmpPath);
|
|
14319
|
+
} catch {
|
|
14320
|
+
}
|
|
14321
|
+
throw e;
|
|
14322
|
+
}
|
|
14323
|
+
}
|
|
14324
|
+
function seedFromStrategy(opts = {}) {
|
|
14325
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
14326
|
+
const strategyPath = path40.join(cwd, "STRATEGY.md");
|
|
14327
|
+
const warnings = [];
|
|
14328
|
+
if (!fs42.existsSync(strategyPath)) {
|
|
14329
|
+
return { name: null, keyMetrics: [], warnings: ["STRATEGY.md not found"] };
|
|
14330
|
+
}
|
|
14331
|
+
const raw = fs42.readFileSync(strategyPath, "utf-8");
|
|
14332
|
+
let name = null;
|
|
14333
|
+
const fmMatch = /^---\s*\n([\s\S]*?)\n---\s*\n/.exec(raw);
|
|
14334
|
+
const fm = fmMatch?.[1];
|
|
14335
|
+
if (fm !== void 0) {
|
|
14336
|
+
const nameMatch = /^name:\s*['"]?([^'"\n]+)['"]?\s*$/m.exec(fm);
|
|
14337
|
+
const nameCapture = nameMatch?.[1];
|
|
14338
|
+
if (nameCapture !== void 0) {
|
|
14339
|
+
name = nameCapture.trim();
|
|
14340
|
+
}
|
|
14341
|
+
}
|
|
14342
|
+
if (name === null) {
|
|
14343
|
+
const h1 = /^#\s+(.+)$/m.exec(raw);
|
|
14344
|
+
const h1Capture = h1?.[1];
|
|
14345
|
+
if (h1Capture !== void 0) {
|
|
14346
|
+
name = h1Capture.trim();
|
|
14347
|
+
warnings.push("STRATEGY.md frontmatter missing name; used H1 fallback");
|
|
14348
|
+
}
|
|
14349
|
+
}
|
|
14350
|
+
const keyMetrics = [];
|
|
14351
|
+
const headerMatch = /^##\s+Key metrics\s*$/m.exec(raw);
|
|
14352
|
+
if (headerMatch && headerMatch.index !== void 0) {
|
|
14353
|
+
const startIdx = headerMatch.index + headerMatch[0].length;
|
|
14354
|
+
const after = raw.slice(startIdx);
|
|
14355
|
+
const stopMatch = /^(#{1,3})\s+/m.exec(after);
|
|
14356
|
+
const block = stopMatch ? after.slice(0, stopMatch.index) : after;
|
|
14357
|
+
for (const line of block.split("\n")) {
|
|
14358
|
+
const m = /^[-*]\s+(.+)$/.exec(line.trim());
|
|
14359
|
+
const capture = m?.[1];
|
|
14360
|
+
if (capture !== void 0) keyMetrics.push(capture.trim());
|
|
14361
|
+
}
|
|
14362
|
+
} else {
|
|
14363
|
+
warnings.push("STRATEGY.md is missing a `## Key metrics` section");
|
|
14364
|
+
}
|
|
14365
|
+
return { name, keyMetrics, warnings };
|
|
14366
|
+
}
|
|
14367
|
+
function registerPulseAdapter(name, adapter2) {
|
|
14368
|
+
if (!adapter2 || typeof adapter2 !== "object") {
|
|
14369
|
+
throw new TypeError(`Pulse adapter "${name}" must be an object with query and sanitize.`);
|
|
14370
|
+
}
|
|
14371
|
+
if (typeof adapter2.query !== "function") {
|
|
14372
|
+
throw new TypeError(`Pulse adapter "${name}" is missing required field: query`);
|
|
14373
|
+
}
|
|
14374
|
+
if (typeof adapter2.sanitize !== "function") {
|
|
14375
|
+
throw new TypeError(`Pulse adapter "${name}" is missing required field: sanitize`);
|
|
14376
|
+
}
|
|
14377
|
+
if (REGISTRY.has(name)) {
|
|
14378
|
+
throw new PulseAdapterAlreadyRegisteredError(name);
|
|
14379
|
+
}
|
|
14380
|
+
REGISTRY.set(name, adapter2);
|
|
14381
|
+
}
|
|
14382
|
+
function getPulseAdapter(name) {
|
|
14383
|
+
return REGISTRY.get(name);
|
|
14384
|
+
}
|
|
14385
|
+
function listPulseAdapters() {
|
|
14386
|
+
return [...REGISTRY.keys()].sort();
|
|
14387
|
+
}
|
|
14388
|
+
function clearPulseAdapters() {
|
|
14389
|
+
REGISTRY.clear();
|
|
14390
|
+
}
|
|
14391
|
+
function registerMockAdapter() {
|
|
14392
|
+
if (listPulseAdapters().includes(MOCK_ADAPTER_NAME)) return;
|
|
14393
|
+
registerPulseAdapter(MOCK_ADAPTER_NAME, adapter);
|
|
14394
|
+
}
|
|
14395
|
+
function parseLookback(input) {
|
|
14396
|
+
const match = LOOKBACK_RE.exec(input);
|
|
14397
|
+
if (!match) {
|
|
14398
|
+
throw new Error(`Invalid lookback "${input}": expected format like "24h" or "7d"`);
|
|
14399
|
+
}
|
|
14400
|
+
const n = Number(match[1]);
|
|
14401
|
+
const unit = match[2];
|
|
14402
|
+
return unit === "h" ? n * 60 * 60 * 1e3 : n * 24 * 60 * 60 * 1e3;
|
|
14403
|
+
}
|
|
14404
|
+
function computeWindow(now, lookback, bufferMinutes = 15) {
|
|
14405
|
+
const lookbackMs = parseLookback(lookback);
|
|
14406
|
+
const bufferMs = bufferMinutes * 60 * 1e3;
|
|
14407
|
+
const end = new Date(now.getTime() - bufferMs);
|
|
14408
|
+
const start = new Date(end.getTime() - lookbackMs);
|
|
14409
|
+
return { start, end };
|
|
14410
|
+
}
|
|
14411
|
+
async function querySource(name, window) {
|
|
14412
|
+
const adapter2 = getPulseAdapter(name);
|
|
14413
|
+
if (!adapter2) {
|
|
14414
|
+
return {
|
|
14415
|
+
ok: false,
|
|
14416
|
+
skipKind: "no-adapter",
|
|
14417
|
+
reason: `no adapter registered for "${name}"`
|
|
14418
|
+
};
|
|
14419
|
+
}
|
|
14420
|
+
let sanitized;
|
|
14421
|
+
try {
|
|
14422
|
+
const raw = await adapter2.query(window);
|
|
14423
|
+
sanitized = adapter2.sanitize(raw);
|
|
14424
|
+
} catch (err) {
|
|
14425
|
+
return {
|
|
14426
|
+
ok: false,
|
|
14427
|
+
skipKind: "query-failure",
|
|
14428
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
14429
|
+
};
|
|
14430
|
+
}
|
|
14431
|
+
try {
|
|
14432
|
+
assertSanitized(sanitized);
|
|
14433
|
+
} catch (err) {
|
|
14434
|
+
return {
|
|
14435
|
+
ok: false,
|
|
14436
|
+
skipKind: "pii-violation",
|
|
14437
|
+
reason: err instanceof Error ? err.message : String(err)
|
|
14438
|
+
};
|
|
14439
|
+
}
|
|
14440
|
+
return { ok: true, result: sanitized };
|
|
14441
|
+
}
|
|
14442
|
+
async function runPulse(config, window) {
|
|
14443
|
+
const startedAt = Date.now();
|
|
14444
|
+
const sources = [];
|
|
14445
|
+
const sourcesQueried = [];
|
|
14446
|
+
const sourcesSkipped = [];
|
|
14447
|
+
const parallelSlots = [
|
|
14448
|
+
["analytics", config.sources.analytics],
|
|
14449
|
+
["tracing", config.sources.tracing],
|
|
14450
|
+
["payments", config.sources.payments]
|
|
14451
|
+
];
|
|
14452
|
+
const parallelEntries = parallelSlots.filter(
|
|
14453
|
+
(slot) => slot[1] != null
|
|
14454
|
+
);
|
|
14455
|
+
const parallelResults = await Promise.all(
|
|
14456
|
+
parallelEntries.map(([kind, n]) => querySource(n, window).then((r) => [kind, n, r]))
|
|
14457
|
+
);
|
|
14458
|
+
for (const [kind, name, r] of parallelResults) {
|
|
14459
|
+
if (r.ok) {
|
|
14460
|
+
sources.push({ kind, name, result: r.result });
|
|
14461
|
+
sourcesQueried.push(name);
|
|
14462
|
+
} else {
|
|
14463
|
+
sourcesSkipped.push({ name, kind, skipKind: r.skipKind, reason: r.reason });
|
|
14464
|
+
}
|
|
14465
|
+
}
|
|
14466
|
+
if (config.sources.db.enabled && config.sources.db.source) {
|
|
14467
|
+
const name = config.sources.db.source;
|
|
14468
|
+
const r = await querySource(name, window);
|
|
14469
|
+
if (r.ok) {
|
|
14470
|
+
sources.push({ kind: "db", name, result: r.result });
|
|
14471
|
+
sourcesQueried.push(name);
|
|
14472
|
+
} else {
|
|
14473
|
+
sourcesSkipped.push({ name, kind: "db", skipKind: r.skipKind, reason: r.reason });
|
|
14474
|
+
}
|
|
14475
|
+
}
|
|
14476
|
+
return { sources, sourcesQueried, sourcesSkipped, durationMs: Date.now() - startedAt };
|
|
14477
|
+
}
|
|
14478
|
+
function loadTemplate() {
|
|
14479
|
+
try {
|
|
14480
|
+
const url = import.meta?.url;
|
|
14481
|
+
if (!url) return INLINE_TEMPLATE;
|
|
14482
|
+
const here = dirname18(fileURLToPath(url));
|
|
14483
|
+
return readFileSync36(join572(here, "template.md"), "utf-8");
|
|
14484
|
+
} catch {
|
|
14485
|
+
return INLINE_TEMPLATE;
|
|
14486
|
+
}
|
|
14487
|
+
}
|
|
14488
|
+
function totalCount(s) {
|
|
14489
|
+
const c = s.fields.count;
|
|
14490
|
+
return typeof c === "number" ? c : 0;
|
|
14491
|
+
}
|
|
14492
|
+
function buildHeadlines(r) {
|
|
14493
|
+
const total = r.sources.reduce((sum, s) => sum + totalCount(s.result), 0);
|
|
14494
|
+
return [
|
|
14495
|
+
`- ${r.sourcesQueried.length} source(s) queried in ${r.durationMs}ms`,
|
|
14496
|
+
`- ${total} total events recorded`,
|
|
14497
|
+
`- ${r.sourcesSkipped.length} source(s) skipped`
|
|
14498
|
+
].join("\n");
|
|
14499
|
+
}
|
|
14500
|
+
function buildUsage(r) {
|
|
14501
|
+
if (r.sources.length === 0) return "_(none)_";
|
|
14502
|
+
return r.sources.map((s) => {
|
|
14503
|
+
const name = s.result.fields.event_name ?? "unknown";
|
|
14504
|
+
const count = totalCount(s.result);
|
|
14505
|
+
return `- ${name}: count=${count}`;
|
|
14506
|
+
}).join("\n");
|
|
14507
|
+
}
|
|
14508
|
+
function buildSystemPerformance(r) {
|
|
14509
|
+
const tracing = r.sources.find((s) => s.kind === "tracing") ?? null;
|
|
14510
|
+
if (!tracing) return "_(no tracing source configured)_";
|
|
14511
|
+
const dist = tracing.result.distributions;
|
|
14512
|
+
const lines = Object.entries(dist).map(([k, v]) => `- ${k}: ${JSON.stringify(v)}`);
|
|
14513
|
+
return lines.length > 0 ? lines.join("\n") : "_(no distributions)_";
|
|
14514
|
+
}
|
|
14515
|
+
function buildFollowups(r) {
|
|
14516
|
+
if (r.sourcesSkipped.length === 0) return "_(none)_";
|
|
14517
|
+
return r.sourcesSkipped.map((s) => `- ${s.name} skipped: ${s.reason}`).join("\n");
|
|
14518
|
+
}
|
|
14519
|
+
function finalPiiSweep(text) {
|
|
14520
|
+
return text.split("\n").filter((l) => {
|
|
14521
|
+
if (l.startsWith("# ") || l.startsWith("## ")) return true;
|
|
14522
|
+
return !PII_LINE_RE.test(l);
|
|
14523
|
+
}).join("\n");
|
|
14524
|
+
}
|
|
14525
|
+
function truncateSection(lines, startIdx, endIdx) {
|
|
14526
|
+
const head = lines.slice(0, startIdx + 1);
|
|
14527
|
+
const section = lines.slice(startIdx + 1, endIdx);
|
|
14528
|
+
const tail = lines.slice(endIdx);
|
|
14529
|
+
let truncated = false;
|
|
14530
|
+
while (head.length + section.length + tail.length + (truncated ? 0 : 1) > MAX_LINES && section.length > 0) {
|
|
14531
|
+
section.pop();
|
|
14532
|
+
truncated = true;
|
|
14533
|
+
}
|
|
14534
|
+
if (truncated) section.push(TRUNCATION_MARKER2);
|
|
14535
|
+
return [...head, ...section, ...tail];
|
|
14536
|
+
}
|
|
14537
|
+
function truncateToFit(text) {
|
|
14538
|
+
let lines = text.split("\n");
|
|
14539
|
+
if (lines.length <= MAX_LINES) return text;
|
|
14540
|
+
const sections = [
|
|
14541
|
+
{ header: "## Followups" },
|
|
14542
|
+
{ header: "## System performance" },
|
|
14543
|
+
{ header: "## Usage" }
|
|
14544
|
+
];
|
|
14545
|
+
for (const { header } of sections) {
|
|
14546
|
+
if (lines.length <= MAX_LINES) break;
|
|
14547
|
+
const startIdx = lines.findIndex((l) => l === header || l.startsWith(`${header} `));
|
|
14548
|
+
if (startIdx < 0) continue;
|
|
14549
|
+
let endIdx = lines.length;
|
|
14550
|
+
for (let i = startIdx + 1; i < lines.length; i++) {
|
|
14551
|
+
const line = lines[i];
|
|
14552
|
+
if (line !== void 0 && line.startsWith("## ")) {
|
|
14553
|
+
endIdx = i;
|
|
14554
|
+
break;
|
|
14555
|
+
}
|
|
14556
|
+
}
|
|
14557
|
+
lines = truncateSection(lines, startIdx, endIdx);
|
|
14558
|
+
}
|
|
14559
|
+
if (lines.length > MAX_LINES) lines = lines.slice(0, MAX_LINES);
|
|
14560
|
+
return lines.join("\n");
|
|
14561
|
+
}
|
|
14562
|
+
function assembleReport(result, productName, windowLabel) {
|
|
14563
|
+
const template = loadTemplate();
|
|
14564
|
+
const filled = template.replace("{{productName}}", productName).replace("{{windowLabel}}", windowLabel).replace("{{headlines}}", buildHeadlines(result)).replace("{{usage}}", buildUsage(result)).replace("{{systemPerformance}}", buildSystemPerformance(result)).replace("{{followups}}", buildFollowups(result));
|
|
14565
|
+
const swept = finalPiiSweep(filled);
|
|
14566
|
+
return truncateToFit(swept);
|
|
14567
|
+
}
|
|
14568
|
+
function extractHeadlines(report) {
|
|
14569
|
+
const lines = report.split("\n");
|
|
14570
|
+
const headlinesIdx = lines.findIndex((l) => l === "## Headlines" || l.startsWith("## Headlines"));
|
|
14571
|
+
if (headlinesIdx < 0) return lines.slice(0, 5).join("\n");
|
|
14572
|
+
let endIdx = lines.length;
|
|
14573
|
+
for (let i = headlinesIdx + 1; i < lines.length; i++) {
|
|
14574
|
+
const line = lines[i];
|
|
14575
|
+
if (line !== void 0 && line.startsWith("## ")) {
|
|
14576
|
+
endIdx = i;
|
|
14577
|
+
break;
|
|
14578
|
+
}
|
|
14579
|
+
}
|
|
14580
|
+
while (endIdx > headlinesIdx + 1 && lines[endIdx - 1] === "") endIdx--;
|
|
14581
|
+
return lines.slice(0, endIdx).join("\n");
|
|
14582
|
+
}
|
|
14583
|
+
function isoWeek(date) {
|
|
14584
|
+
const d = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
|
|
14585
|
+
const dayNum = d.getUTCDay() || 7;
|
|
14586
|
+
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
|
14587
|
+
const year = d.getUTCFullYear();
|
|
14588
|
+
const yearStart = new Date(Date.UTC(year, 0, 1));
|
|
14589
|
+
const week = Math.ceil(((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
|
|
14590
|
+
return { year, week };
|
|
14591
|
+
}
|
|
14592
|
+
function formatIsoWeek(w) {
|
|
14593
|
+
return `${w.year}-W${String(w.week).padStart(2, "0")}`;
|
|
14594
|
+
}
|
|
14595
|
+
function normalizeSince(since) {
|
|
14596
|
+
const m = /^(\d+)(h|d|w|mo)$/i.exec(since.trim());
|
|
14597
|
+
if (!m) {
|
|
14598
|
+
throw new Error(
|
|
14599
|
+
`Invalid lookback "${since}": expected format like "24h", "7d", "4w", or "3mo"`
|
|
14600
|
+
);
|
|
14601
|
+
}
|
|
14602
|
+
const n = m[1];
|
|
14603
|
+
const unit = m[2]?.toLowerCase();
|
|
14604
|
+
switch (unit) {
|
|
14605
|
+
case "h":
|
|
14606
|
+
return `${n} hours ago`;
|
|
14607
|
+
case "d":
|
|
14608
|
+
return `${n} days ago`;
|
|
14609
|
+
case "w":
|
|
14610
|
+
return `${n} weeks ago`;
|
|
14611
|
+
case "mo":
|
|
14612
|
+
return `${n} months ago`;
|
|
14613
|
+
default:
|
|
14614
|
+
throw new Error(`Invalid lookback "${since}"`);
|
|
14615
|
+
}
|
|
14616
|
+
}
|
|
14617
|
+
async function readCommits(opts) {
|
|
14618
|
+
let stdout;
|
|
14619
|
+
try {
|
|
14620
|
+
const r = await execFileAsync(
|
|
14621
|
+
"git",
|
|
14622
|
+
["log", `--since=${normalizeSince(opts.since)}`, "--name-only", "--format=%x1e%H%x1f%s"],
|
|
14623
|
+
{ cwd: opts.cwd, maxBuffer: 16 * 1024 * 1024 }
|
|
14624
|
+
);
|
|
14625
|
+
stdout = r.stdout;
|
|
14626
|
+
} catch (err) {
|
|
14627
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
14628
|
+
if (EMPTY_REPO_RE.test(msg)) {
|
|
14629
|
+
process.stderr.write(`gitScan: empty repo or non-repo at ${opts.cwd}; returning []
|
|
14630
|
+
`);
|
|
14631
|
+
return [];
|
|
14632
|
+
}
|
|
14633
|
+
throw err;
|
|
14634
|
+
}
|
|
14635
|
+
const records = stdout.split("").filter((r) => r.trim().length > 0);
|
|
14636
|
+
return records.map((rec) => {
|
|
14637
|
+
const [header, ...fileLines] = rec.split("\n");
|
|
14638
|
+
const [sha, subject] = (header ?? "").split("");
|
|
14639
|
+
const files = fileLines.map((l) => l.trim()).filter((l) => l.length > 0);
|
|
14640
|
+
return { sha: sha ?? "", subject: subject ?? "", files };
|
|
14641
|
+
});
|
|
14642
|
+
}
|
|
14643
|
+
async function gitScan(opts) {
|
|
14644
|
+
const all = await readCommits(opts);
|
|
14645
|
+
const fixes = all.filter((c) => FIX_RE.test(c.subject));
|
|
14646
|
+
return fixes.map((c) => {
|
|
14647
|
+
const overlap = all.filter(
|
|
14648
|
+
(other) => other.sha !== c.sha && other.files.some((f) => c.files.includes(f))
|
|
14649
|
+
).length;
|
|
14650
|
+
return {
|
|
14651
|
+
sha: c.sha,
|
|
14652
|
+
subject: c.subject,
|
|
14653
|
+
filesChanged: c.files.length,
|
|
14654
|
+
branchIterations: overlap
|
|
14655
|
+
};
|
|
14656
|
+
});
|
|
14657
|
+
}
|
|
14658
|
+
async function computeHotspots(opts) {
|
|
14659
|
+
let stdout;
|
|
14660
|
+
try {
|
|
14661
|
+
const r = await execFileAsync2(
|
|
14662
|
+
"git",
|
|
14663
|
+
["log", `--since=${normalizeSince(opts.since)}`, "--name-only", "--format="],
|
|
14664
|
+
{ cwd: opts.cwd, maxBuffer: 16 * 1024 * 1024 }
|
|
14665
|
+
);
|
|
14666
|
+
stdout = r.stdout;
|
|
14667
|
+
} catch {
|
|
14668
|
+
return [];
|
|
14669
|
+
}
|
|
14670
|
+
const counts = /* @__PURE__ */ new Map();
|
|
14671
|
+
for (const line of stdout.split("\n")) {
|
|
14672
|
+
const path42 = line.trim();
|
|
14673
|
+
if (path42.length === 0) continue;
|
|
14674
|
+
counts.set(path42, (counts.get(path42) ?? 0) + 1);
|
|
14675
|
+
}
|
|
14676
|
+
return [...counts.entries()].filter(([, c]) => c > opts.threshold).map(([path42, churn]) => ({ path: path42, churn })).sort((a, b) => b.churn - a.churn);
|
|
14677
|
+
}
|
|
14678
|
+
function tokenize(text) {
|
|
14679
|
+
return new Set(
|
|
14680
|
+
text.toLowerCase().replace(/[^a-z0-9\s]/g, " ").split(/\s+/).filter((t) => t.length > 2 && !STOPWORDS.has(t))
|
|
14681
|
+
);
|
|
14682
|
+
}
|
|
14683
|
+
async function* walk22(dir) {
|
|
14684
|
+
let entries;
|
|
14685
|
+
try {
|
|
14686
|
+
entries = await fs43.readdir(dir, { withFileTypes: true });
|
|
14687
|
+
} catch {
|
|
14688
|
+
return;
|
|
14689
|
+
}
|
|
14690
|
+
for (const e of entries) {
|
|
14691
|
+
const p = path41.join(dir, e.name);
|
|
14692
|
+
if (e.isDirectory()) yield* walk22(p);
|
|
14693
|
+
else if (e.isFile() && e.name.endsWith(".md")) yield p;
|
|
14694
|
+
}
|
|
14695
|
+
}
|
|
14696
|
+
async function readDocumentedTokens(solutionsDir) {
|
|
14697
|
+
const docs = [];
|
|
14698
|
+
for (const [track, categories] of TRACK_CATEGORIES2) {
|
|
14699
|
+
for (const category of categories) {
|
|
14700
|
+
const dir = path41.join(solutionsDir, track, category);
|
|
14701
|
+
for await (const file of walk22(dir)) {
|
|
14702
|
+
const raw = await fs43.readFile(file, "utf-8");
|
|
14703
|
+
const m = /^#\s+(.+)$/m.exec(raw);
|
|
14704
|
+
const title = m?.[1] ?? path41.basename(file, ".md");
|
|
14705
|
+
docs.push(tokenize(title));
|
|
14706
|
+
}
|
|
14707
|
+
}
|
|
14708
|
+
}
|
|
14709
|
+
return docs;
|
|
14710
|
+
}
|
|
14711
|
+
function jaccard(a, b) {
|
|
14712
|
+
if (a.size === 0 || b.size === 0) return 0;
|
|
14713
|
+
let inter = 0;
|
|
14714
|
+
for (const t of a) if (b.has(t)) inter++;
|
|
14715
|
+
const union = a.size + b.size - inter;
|
|
14716
|
+
return union === 0 ? 0 : inter / union;
|
|
14717
|
+
}
|
|
14718
|
+
async function crossReferenceUndocumentedFixes(commits, solutionsDir) {
|
|
14719
|
+
const documented = await readDocumentedTokens(solutionsDir);
|
|
14720
|
+
const OVERLAP_THRESHOLD = 0.4;
|
|
14721
|
+
return commits.filter((c) => {
|
|
14722
|
+
const tokens = tokenize(c.subject);
|
|
14723
|
+
return !documented.some((d) => jaccard(tokens, d) >= OVERLAP_THRESHOLD);
|
|
14724
|
+
});
|
|
14725
|
+
}
|
|
14726
|
+
function suggestCategory(subject) {
|
|
14727
|
+
for (const [re, cat] of KEYWORD_TO_CATEGORY) if (re.test(subject)) return cat;
|
|
14728
|
+
return "bug-track/logic-errors";
|
|
14729
|
+
}
|
|
14730
|
+
function descriptor(subject) {
|
|
14731
|
+
return subject.replace(/^fix(\([^)]+\))?:\s*/i, "").trim();
|
|
14732
|
+
}
|
|
14733
|
+
function assembleCandidateReport(input) {
|
|
14734
|
+
const week = formatIsoWeek(input.isoWeek);
|
|
14735
|
+
const lines = [];
|
|
14736
|
+
lines.push(`# Compound candidates \u2014 week ${week}`, "");
|
|
14737
|
+
lines.push(`## Undocumented fixes (from \`git log\` past ${input.lookback})`, "");
|
|
14738
|
+
if (input.undocumentedFixes.length === 0) {
|
|
14739
|
+
lines.push("_(none this week)_", "");
|
|
14740
|
+
} else {
|
|
14741
|
+
for (const c of input.undocumentedFixes) {
|
|
14742
|
+
const d = descriptor(c.subject);
|
|
14743
|
+
const cat = suggestCategory(c.subject);
|
|
14744
|
+
lines.push(
|
|
14745
|
+
`- **${c.subject}** (commit ${c.sha.slice(0, 7)}, ${c.filesChanged} file(s), ${c.branchIterations} related commits)`
|
|
14746
|
+
);
|
|
14747
|
+
lines.push(` - Suggested category: ${cat}`);
|
|
14748
|
+
lines.push(` - Run: \`/harness:compound "${d}"\``);
|
|
14749
|
+
}
|
|
14750
|
+
lines.push("");
|
|
14751
|
+
}
|
|
14752
|
+
lines.push("## Pattern candidates (from churn + hotspot analysis)", "");
|
|
14753
|
+
if (input.hotspotCandidates.length === 0) {
|
|
14754
|
+
lines.push("_(none this week)_", "");
|
|
14755
|
+
} else {
|
|
14756
|
+
for (const h of input.hotspotCandidates) {
|
|
14757
|
+
lines.push(
|
|
14758
|
+
`- File \`${h.path}\` has ${h.churn} commits in ${input.lookback}; no docs/solutions/ entry`
|
|
14759
|
+
);
|
|
14760
|
+
lines.push(" - Suggested category: knowledge-track/architecture-patterns");
|
|
14761
|
+
lines.push(` - Run: \`/harness:compound "${h.path} pattern"\``);
|
|
14762
|
+
}
|
|
14763
|
+
lines.push("");
|
|
14764
|
+
}
|
|
14765
|
+
return lines.join("\n");
|
|
14766
|
+
}
|
|
14767
|
+
var VALID_TYPES, CONVENTIONAL_PATTERN, LOOKBACK_PATTERN, PulseDbSourceSchema, PulseSourcesSchema, PulseConfigSchema, BUG_TRACK_CATEGORIES, KNOWLEDGE_TRACK_CATEGORIES, ALL_SOLUTION_CATEGORIES, ISO_DATE, BugTrackSchema, KnowledgeTrackSchema, BaseFrontmatter, SolutionDocFrontmatterSchema, TRACK_CATEGORIES, DEFAULT_AGNIX_TIMEOUT_MS, HARNESS_AGNIX_DISABLE, HARNESS_AGNIX_BIN, CLAUDE_MD_WARN_BYTES, CLAUDE_MD_ERROR_BYTES, CONTEXT_FILES, MIN_DESCRIPTION_LENGTH, SETTINGS_PATHS, KNOWN_HOOK_EVENTS, MCP_SETTINGS_FILES, REQUIRED_SECTIONS, SETTINGS_FILES, KNOWN_AGNIX_TOOLS, DEFAULT_SECTIONS, DEFAULT_RATIOS, NODE_TYPE_TO_CATEGORY, SECTION_LEVEL_MAP, DEFAULT_LEVEL, DEFAULT_BUDGET, DEFAULT_LOADER_CONFIG, LEVEL_FRACTIONS, PHASE_PRIORITIES, ManifestSchema, BundleConstraintsSchema, BundleSchema, ContributionsSchema, LockfilePackageSchema, LockfileSchema, SharableLayerSchema, SharableForbiddenImportSchema, SharableBoundaryConfigSchema, SharableSecurityRulesSchema, VALID_SCOPES, LINE_PATTERN, START_PATTERN, END_PATTERN, CODE_BLOCK_LANGUAGES, DEFAULT_DRIFT_CONFIG, JS_EXT_FALLBACKS2, FILE_TYPES, EXPORT_TYPES, CONVENTION_DESCRIPTIONS, RULE_CHECKERS, EntropyAnalyzer, readFile62, writeFile32, unlink22, mkdir22, copyFile2, DEFAULT_FIX_CONFIG, EXPORT_TYPE_KEYWORD, ALWAYS_UNSAFE_TYPES, idCounter, DEAD_CODE_FIX_ACTIONS, MustExportRuleSchema, MustExportDefaultRuleSchema, NoExportRuleSchema, MustImportRuleSchema, NoImportRuleSchema, NamingRuleSchema, MaxExportsRuleSchema, MaxLinesRuleSchema, RequireJSDocRuleSchema, RuleSchema, ConfigPatternSchema, PatternConfigSchema, DriftConfigSchema, DeadCodeConfigSchema, EntropyConfigSchema, BaselineManager, BenchmarkRunner, RegressionDetector, SKIP_DIRS, SOURCE_EXTENSIONS, FUNCTION_DECL_RE, CONST_DECL_RE, CriticalPathResolver, NoOpTelemetryAdapter, NoOpExecutor, ConsoleSink, feedbackConfig, ChecklistBuilder, AgentActionEmitter, globalEmitter, FileSink, NoOpSink, CategorySnapshotSchema, TimelineSnapshotSchema, TimelineFileSchema, TrendLineSchema, TrendResultSchema, DEFAULT_STABILITY_THRESHOLDS, ALL_CATEGORIES, TimelineManager, ConfidenceTierSchema, RegressionResultSchema, DirectionSchema, CategoryForecastSchema, SpecImpactSignalsSchema, SpecImpactEstimateSchema, ContributingFeatureSchema, AdjustedForecastSchema, PredictionWarningSchema, StabilityForecastSchema, PredictionResultSchema, PredictionOptionsSchema, VALID_STATUSES, EM_DASH, VALID_PRIORITIES, ALL_CATEGORIES2, DIRECTION_THRESHOLD, PredictionEngine, DEFAULT_COEFFICIENTS, SpecImpactEstimator, EMPTY_HISTORY, ViolationHistoryManager, NORMALIZERS, MAX_SAMPLES, DEFAULT_WINDOW_WEEKS, DEFAULT_MIN_OCCURRENCES, FailureEntrySchema, HandoffSchema, GateCheckSchema, GateResultSchema, GateConfigSchema, HarnessStateSchema, DEFAULT_STATE, StreamInfoSchema, StreamIndexSchema, DEFAULT_STREAM_INDEX, HARNESS_DIR, STATE_FILE, LEARNINGS_FILE, FAILURES_FILE, HANDOFF_FILE, GATE_CONFIG_FILE, INDEX_FILE, SESSIONS_DIR, SESSION_INDEX_FILE, SUMMARY_FILE, SESSION_STATE_FILE, ARCHIVE_DIR, CONTENT_HASHES_FILE, EVENTS_FILE, STREAMS_DIR, STREAM_NAME_REGEX, branchCache, BRANCH_CACHE_TTL_MS, STATE_FILES, MAX_CACHE_ENTRIES, learningsCacheMap, WEIGHTS, LOCK_RETRIES, LOCK_BACKOFFS, PROMOTABLE_OUTCOMES, failuresCacheMap, FAILURE_LINE_REGEX, SAFE_GATE_COMMAND, SkillEventSchema, knownHashesCache, EVENT_FORMATTERS, RuleRegistry, DEFAULT_SECURITY_CONFIG, RuleOverrideSchema, SecurityConfigSchema, secretRules, injectionRules, xssRules, cryptoRules, pathTraversalRules, networkRules, deserializationRules, agentConfigRules, mcpRules, insecureDefaultsRules, sharpEdgesRules, nodeRules, expressRules, reactRules, goRules, SecurityScanner, hiddenUnicodePatterns, reRolingPatterns, permissionEscalationPatterns, encodedPayloadPatterns, indirectInjectionPatterns, contextManipulationPatterns, socialEngineeringPatterns, suspiciousPatterns, ALL_PATTERNS, DESTRUCTIVE_BASH, TAINT_DURATION_MS, DEFAULT_SESSION_ID, SecurityCategorySnapshotSchema, SupplyChainSnapshotSchema, SecurityTimelineSnapshotSchema, FindingLifecycleSchema, SecurityTimelineFileSchema, DirectionSchema2, SecurityTrendLineSchema, TrendAttributionSchema, SecurityTrendResultSchema, TimeToFixStatsSchema, TimeToFixResultSchema, EMPTY_SUPPLY_CHAIN, SecurityTimelineManager, ALL_CHECKS, STATUS_ICONS, CINotifier, ExclusionSet, PREFIX_PATTERNS, TEST_FILE_PATTERN, MD_FILE_PATTERN, ALL_DOMAINS, SECURITY_PATTERNS, JS_EXT_FALLBACKS22, SEVERITY_RANK, SEVERITY_ORDER, SEVERITY_LABELS, VALIDATED_BY_RANK, VALIDATION_SCORES, DOMAIN_BASELINES, FACTOR_WEIGHTS, EVIDENCE_SATURATION, CORROBORATED_AGREEMENT, STANDALONE_AGREEMENT, AGREEMENT_LINE_GAP, COMPLIANCE_DESCRIPTOR, EXPORT_RE, BUG_DETECTION_DESCRIPTOR, SECURITY_DESCRIPTOR, EVAL_PATTERN, SECRET_PATTERNS, SQL_CONCAT_PATTERN, SHELL_EXEC_PATTERN, ARCHITECTURE_DESCRIPTOR, LARGE_FILE_THRESHOLD, VIOLATION_FILE_RE, LEARNINGS_DESCRIPTOR, RELEVANCE_THRESHOLD, AGENT_DESCRIPTORS, AGENT_RUNNERS, DOWNGRADE_MAP, DEFAULT_PROVIDER_TIERS, SMALL_SUGGESTION_LINE_LIMIT, FILE_LINE_RANGE_PATTERN, FILE_LINE_PATTERN, FILE_ONLY_PATTERN, SECURITY_SENSITIVE_PATH, MIGRATION_PATH, TEST_PATH, CONFIG_PATH, STAGE_DOMAINS, EM_DASH2, STATUS_RANK, RETRY_DEFAULTS, GitHubIssuesSyncAdapter, syncMutex, PRIORITY_RANK, POSITION_WEIGHT, DEPENDENTS_WEIGHT, AFFINITY_WEIGHT, InteractionTypeSchema, QuestionSchema, ConfirmationSchema, TransitionSchema, EmitInteractionInputSchema, ProjectScanner, SHELL_TEMPLATE, STYLES, SCRIPTS, MockLLMService, llmService, ContentPipeline, BlueprintGenerator, TOKENS_PER_MILLION, fallback_default, LITELLM_PRICING_URL, CACHE_TTL_MS, STALENESS_WARNING_DAYS, MICRODOLLARS_PER_DOLLAR, TOKENS_PER_MILLION2, StructuralStrategy, DEFAULT_TOKEN_BUDGET, CHARS_PER_TOKEN, TRUNCATION_MARKER, TruncationStrategy, CompactionPipeline, STABILITY_LOOKUP, TIER_ORDER, AnthropicCacheAdapter, TIER_ORDER2, OpenAICacheAdapter, TIER_ORDER3, CACHED_CONTENT_MARKER, GeminiCacheAdapter, VERSION, POSTHOG_BATCH_URL, MAX_ATTEMPTS, TIMEOUT_MS, CompoundLockHeldError, KNOWN_CATEGORIES, ALLOWED_FIELD_KEYS, PII_TOKENS, PII_FIELD_DENYLIST, PII_LINE_RE, ALLOWED_SET, PulseAdapterAlreadyRegisteredError, REGISTRY, MOCK_ADAPTER_NAME, ALLOWED_FIELD_SET, adapter, LOOKBACK_RE, MAX_LINES, INLINE_TEMPLATE, TRUNCATION_MARKER2, execFileAsync, FIX_RE, EMPTY_REPO_RE, execFileAsync2, TRACK_CATEGORIES2, STOPWORDS, KEYWORD_TO_CATEGORY;
|
|
14029
14768
|
var init_dist3 = __esm({
|
|
14030
14769
|
"../core/dist/index.mjs"() {
|
|
14031
|
-
|
|
14770
|
+
init_chunk_CE225WRK();
|
|
14032
14771
|
init_dist();
|
|
14033
14772
|
init_dist();
|
|
14034
14773
|
init_dist();
|
|
@@ -14050,6 +14789,76 @@ var init_dist3 = __esm({
|
|
|
14050
14789
|
"revert"
|
|
14051
14790
|
];
|
|
14052
14791
|
CONVENTIONAL_PATTERN = /^(\w+)(\(([^)]+)\))?(!)?: (.+)$/;
|
|
14792
|
+
LOOKBACK_PATTERN = /^\d+(h|d|w)$/;
|
|
14793
|
+
PulseDbSourceSchema = z2.object({
|
|
14794
|
+
enabled: z2.boolean(),
|
|
14795
|
+
connectionEnv: z2.string().optional()
|
|
14796
|
+
});
|
|
14797
|
+
PulseSourcesSchema = z2.object({
|
|
14798
|
+
analytics: z2.string().nullable(),
|
|
14799
|
+
tracing: z2.string().nullable(),
|
|
14800
|
+
payments: z2.string().nullable(),
|
|
14801
|
+
db: PulseDbSourceSchema
|
|
14802
|
+
});
|
|
14803
|
+
PulseConfigSchema = z2.object({
|
|
14804
|
+
enabled: z2.boolean(),
|
|
14805
|
+
lookbackDefault: z2.string().regex(LOOKBACK_PATTERN, 'lookback must be like "24h", "7d", or "1w"'),
|
|
14806
|
+
primaryEvent: z2.string(),
|
|
14807
|
+
valueEvent: z2.string(),
|
|
14808
|
+
completionEvents: z2.array(z2.string()),
|
|
14809
|
+
qualityScoring: z2.boolean(),
|
|
14810
|
+
qualityDimension: z2.string().nullable(),
|
|
14811
|
+
sources: PulseSourcesSchema,
|
|
14812
|
+
metricSourceOverrides: z2.record(z2.string(), z2.string()),
|
|
14813
|
+
pendingMetrics: z2.array(z2.string()),
|
|
14814
|
+
excludedMetrics: z2.array(z2.string())
|
|
14815
|
+
});
|
|
14816
|
+
BUG_TRACK_CATEGORIES = [
|
|
14817
|
+
"build-errors",
|
|
14818
|
+
"test-failures",
|
|
14819
|
+
"runtime-errors",
|
|
14820
|
+
"performance-issues",
|
|
14821
|
+
"database-issues",
|
|
14822
|
+
"security-issues",
|
|
14823
|
+
"ui-bugs",
|
|
14824
|
+
"integration-issues",
|
|
14825
|
+
"logic-errors"
|
|
14826
|
+
];
|
|
14827
|
+
KNOWLEDGE_TRACK_CATEGORIES = [
|
|
14828
|
+
"architecture-patterns",
|
|
14829
|
+
"design-patterns",
|
|
14830
|
+
"tooling-decisions",
|
|
14831
|
+
"conventions",
|
|
14832
|
+
"dx",
|
|
14833
|
+
"best-practices"
|
|
14834
|
+
];
|
|
14835
|
+
ALL_SOLUTION_CATEGORIES = [
|
|
14836
|
+
...BUG_TRACK_CATEGORIES,
|
|
14837
|
+
...KNOWLEDGE_TRACK_CATEGORIES
|
|
14838
|
+
];
|
|
14839
|
+
ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
14840
|
+
BugTrackSchema = z22.object({
|
|
14841
|
+
track: z22.literal("bug-track"),
|
|
14842
|
+
category: z22.enum(BUG_TRACK_CATEGORIES)
|
|
14843
|
+
});
|
|
14844
|
+
KnowledgeTrackSchema = z22.object({
|
|
14845
|
+
track: z22.literal("knowledge-track"),
|
|
14846
|
+
category: z22.enum(KNOWLEDGE_TRACK_CATEGORIES)
|
|
14847
|
+
});
|
|
14848
|
+
BaseFrontmatter = z22.object({
|
|
14849
|
+
module: z22.string().min(1),
|
|
14850
|
+
tags: z22.array(z22.string()),
|
|
14851
|
+
problem_type: z22.string().min(1),
|
|
14852
|
+
last_updated: z22.string().regex(ISO_DATE, "last_updated must be ISO date YYYY-MM-DD")
|
|
14853
|
+
});
|
|
14854
|
+
SolutionDocFrontmatterSchema = z22.discriminatedUnion("track", [
|
|
14855
|
+
BaseFrontmatter.merge(BugTrackSchema),
|
|
14856
|
+
BaseFrontmatter.merge(KnowledgeTrackSchema)
|
|
14857
|
+
]);
|
|
14858
|
+
TRACK_CATEGORIES = [
|
|
14859
|
+
["bug-track", BUG_TRACK_CATEGORIES],
|
|
14860
|
+
["knowledge-track", KNOWLEDGE_TRACK_CATEGORIES]
|
|
14861
|
+
];
|
|
14053
14862
|
DEFAULT_AGNIX_TIMEOUT_MS = 3e4;
|
|
14054
14863
|
HARNESS_AGNIX_DISABLE = "HARNESS_AGNIX_DISABLE";
|
|
14055
14864
|
HARNESS_AGNIX_BIN = "HARNESS_AGNIX_BIN";
|
|
@@ -14196,81 +15005,81 @@ var init_dist3 = __esm({
|
|
|
14196
15005
|
{ category: "config", patterns: ["harness.config.json", "package.json"], priority: 5 }
|
|
14197
15006
|
]
|
|
14198
15007
|
};
|
|
14199
|
-
ManifestSchema =
|
|
14200
|
-
name:
|
|
14201
|
-
version:
|
|
14202
|
-
description:
|
|
14203
|
-
include:
|
|
14204
|
-
minHarnessVersion:
|
|
14205
|
-
keywords:
|
|
14206
|
-
layers:
|
|
14207
|
-
boundaries:
|
|
14208
|
-
|
|
14209
|
-
name:
|
|
14210
|
-
layer:
|
|
14211
|
-
direction:
|
|
14212
|
-
schema:
|
|
15008
|
+
ManifestSchema = z3.object({
|
|
15009
|
+
name: z3.string(),
|
|
15010
|
+
version: z3.string(),
|
|
15011
|
+
description: z3.string().optional(),
|
|
15012
|
+
include: z3.array(z3.string()).min(1),
|
|
15013
|
+
minHarnessVersion: z3.string().optional(),
|
|
15014
|
+
keywords: z3.array(z3.string()).optional().default([]),
|
|
15015
|
+
layers: z3.record(z3.array(z3.string())).optional(),
|
|
15016
|
+
boundaries: z3.array(
|
|
15017
|
+
z3.object({
|
|
15018
|
+
name: z3.string(),
|
|
15019
|
+
layer: z3.string(),
|
|
15020
|
+
direction: z3.enum(["input", "output"]),
|
|
15021
|
+
schema: z3.string()
|
|
14213
15022
|
})
|
|
14214
15023
|
).optional()
|
|
14215
15024
|
});
|
|
14216
|
-
BundleConstraintsSchema =
|
|
14217
|
-
layers:
|
|
14218
|
-
|
|
14219
|
-
name:
|
|
14220
|
-
pattern:
|
|
14221
|
-
allowedDependencies:
|
|
15025
|
+
BundleConstraintsSchema = z3.object({
|
|
15026
|
+
layers: z3.array(
|
|
15027
|
+
z3.object({
|
|
15028
|
+
name: z3.string(),
|
|
15029
|
+
pattern: z3.string(),
|
|
15030
|
+
allowedDependencies: z3.array(z3.string())
|
|
14222
15031
|
})
|
|
14223
15032
|
).optional(),
|
|
14224
|
-
forbiddenImports:
|
|
14225
|
-
|
|
14226
|
-
from:
|
|
14227
|
-
disallow:
|
|
14228
|
-
message:
|
|
15033
|
+
forbiddenImports: z3.array(
|
|
15034
|
+
z3.object({
|
|
15035
|
+
from: z3.string(),
|
|
15036
|
+
disallow: z3.array(z3.string()),
|
|
15037
|
+
message: z3.string().optional()
|
|
14229
15038
|
})
|
|
14230
15039
|
).optional(),
|
|
14231
|
-
boundaries:
|
|
14232
|
-
requireSchema:
|
|
15040
|
+
boundaries: z3.object({
|
|
15041
|
+
requireSchema: z3.array(z3.string()).optional()
|
|
14233
15042
|
}).optional(),
|
|
14234
|
-
architecture:
|
|
14235
|
-
thresholds:
|
|
14236
|
-
modules:
|
|
15043
|
+
architecture: z3.object({
|
|
15044
|
+
thresholds: z3.record(z3.unknown()).optional(),
|
|
15045
|
+
modules: z3.record(z3.record(z3.unknown())).optional()
|
|
14237
15046
|
}).optional(),
|
|
14238
|
-
security:
|
|
14239
|
-
rules:
|
|
15047
|
+
security: z3.object({
|
|
15048
|
+
rules: z3.record(z3.string()).optional()
|
|
14240
15049
|
}).optional()
|
|
14241
15050
|
});
|
|
14242
|
-
BundleSchema =
|
|
14243
|
-
name:
|
|
14244
|
-
version:
|
|
14245
|
-
description:
|
|
14246
|
-
minHarnessVersion:
|
|
15051
|
+
BundleSchema = z3.object({
|
|
15052
|
+
name: z3.string(),
|
|
15053
|
+
version: z3.string(),
|
|
15054
|
+
description: z3.string().optional(),
|
|
15055
|
+
minHarnessVersion: z3.string().optional(),
|
|
14247
15056
|
manifest: ManifestSchema,
|
|
14248
15057
|
constraints: BundleConstraintsSchema,
|
|
14249
|
-
contributions:
|
|
15058
|
+
contributions: z3.record(z3.unknown()).optional()
|
|
14250
15059
|
});
|
|
14251
|
-
ContributionsSchema =
|
|
14252
|
-
LockfilePackageSchema =
|
|
14253
|
-
version:
|
|
14254
|
-
source:
|
|
14255
|
-
installedAt:
|
|
14256
|
-
contributions:
|
|
14257
|
-
resolved:
|
|
14258
|
-
integrity:
|
|
14259
|
-
provenance:
|
|
15060
|
+
ContributionsSchema = z3.record(z3.unknown());
|
|
15061
|
+
LockfilePackageSchema = z3.object({
|
|
15062
|
+
version: z3.string(),
|
|
15063
|
+
source: z3.string(),
|
|
15064
|
+
installedAt: z3.string(),
|
|
15065
|
+
contributions: z3.record(z3.unknown()).optional().nullable(),
|
|
15066
|
+
resolved: z3.string().optional(),
|
|
15067
|
+
integrity: z3.string().optional(),
|
|
15068
|
+
provenance: z3.array(z3.string()).optional()
|
|
14260
15069
|
});
|
|
14261
|
-
LockfileSchema =
|
|
14262
|
-
version:
|
|
15070
|
+
LockfileSchema = z3.object({
|
|
15071
|
+
version: z3.literal(1).optional(),
|
|
14263
15072
|
// Used by some tests
|
|
14264
|
-
lockfileVersion:
|
|
15073
|
+
lockfileVersion: z3.literal(1).optional(),
|
|
14265
15074
|
// Standard field
|
|
14266
|
-
packages:
|
|
15075
|
+
packages: z3.record(LockfilePackageSchema)
|
|
14267
15076
|
}).refine((data) => data.version !== void 0 || data.lockfileVersion !== void 0, {
|
|
14268
15077
|
message: "Either 'version' or 'lockfileVersion' must be present and equal to 1"
|
|
14269
15078
|
});
|
|
14270
|
-
SharableLayerSchema =
|
|
14271
|
-
SharableForbiddenImportSchema =
|
|
14272
|
-
SharableBoundaryConfigSchema =
|
|
14273
|
-
SharableSecurityRulesSchema =
|
|
15079
|
+
SharableLayerSchema = z3.unknown();
|
|
15080
|
+
SharableForbiddenImportSchema = z3.unknown();
|
|
15081
|
+
SharableBoundaryConfigSchema = z3.unknown();
|
|
15082
|
+
SharableSecurityRulesSchema = z3.unknown();
|
|
14274
15083
|
VALID_SCOPES = /* @__PURE__ */ new Set([
|
|
14275
15084
|
"entropy",
|
|
14276
15085
|
"architecture",
|
|
@@ -14288,6 +15097,12 @@ var init_dist3 = __esm({
|
|
|
14288
15097
|
checkStructure: true,
|
|
14289
15098
|
ignorePatterns: []
|
|
14290
15099
|
};
|
|
15100
|
+
JS_EXT_FALLBACKS2 = {
|
|
15101
|
+
".js": [".ts", ".tsx", ".jsx"],
|
|
15102
|
+
".jsx": [".tsx"],
|
|
15103
|
+
".mjs": [".mts"],
|
|
15104
|
+
".cjs": [".cts"]
|
|
15105
|
+
};
|
|
14291
15106
|
FILE_TYPES = /* @__PURE__ */ new Set(["file", "module"]);
|
|
14292
15107
|
EXPORT_TYPES = /* @__PURE__ */ new Set(["function", "class", "method", "interface", "variable"]);
|
|
14293
15108
|
CONVENTION_DESCRIPTIONS = {
|
|
@@ -14541,11 +15356,11 @@ var init_dist3 = __esm({
|
|
|
14541
15356
|
return detectPatternViolations(snapshotResult.value, config);
|
|
14542
15357
|
}
|
|
14543
15358
|
};
|
|
14544
|
-
|
|
14545
|
-
writeFile32 = promisify2(
|
|
14546
|
-
unlink22 = promisify2(
|
|
14547
|
-
mkdir22 = promisify2(
|
|
14548
|
-
copyFile2 = promisify2(
|
|
15359
|
+
readFile62 = promisify2(fs5.readFile);
|
|
15360
|
+
writeFile32 = promisify2(fs5.writeFile);
|
|
15361
|
+
unlink22 = promisify2(fs5.unlink);
|
|
15362
|
+
mkdir22 = promisify2(fs5.mkdir);
|
|
15363
|
+
copyFile2 = promisify2(fs5.copyFile);
|
|
14549
15364
|
DEFAULT_FIX_CONFIG = {
|
|
14550
15365
|
dryRun: false,
|
|
14551
15366
|
fixTypes: ["unused-imports", "dead-files"],
|
|
@@ -14573,45 +15388,45 @@ var init_dist3 = __esm({
|
|
|
14573
15388
|
"commented-code": "Delete commented block",
|
|
14574
15389
|
"unused-import": "Remove import"
|
|
14575
15390
|
};
|
|
14576
|
-
MustExportRuleSchema =
|
|
14577
|
-
type:
|
|
14578
|
-
names:
|
|
15391
|
+
MustExportRuleSchema = z4.object({
|
|
15392
|
+
type: z4.literal("must-export"),
|
|
15393
|
+
names: z4.array(z4.string())
|
|
14579
15394
|
});
|
|
14580
|
-
MustExportDefaultRuleSchema =
|
|
14581
|
-
type:
|
|
14582
|
-
kind:
|
|
15395
|
+
MustExportDefaultRuleSchema = z4.object({
|
|
15396
|
+
type: z4.literal("must-export-default"),
|
|
15397
|
+
kind: z4.enum(["class", "function", "object"]).optional()
|
|
14583
15398
|
});
|
|
14584
|
-
NoExportRuleSchema =
|
|
14585
|
-
type:
|
|
14586
|
-
names:
|
|
15399
|
+
NoExportRuleSchema = z4.object({
|
|
15400
|
+
type: z4.literal("no-export"),
|
|
15401
|
+
names: z4.array(z4.string())
|
|
14587
15402
|
});
|
|
14588
|
-
MustImportRuleSchema =
|
|
14589
|
-
type:
|
|
14590
|
-
from:
|
|
14591
|
-
names:
|
|
15403
|
+
MustImportRuleSchema = z4.object({
|
|
15404
|
+
type: z4.literal("must-import"),
|
|
15405
|
+
from: z4.string(),
|
|
15406
|
+
names: z4.array(z4.string()).optional()
|
|
14592
15407
|
});
|
|
14593
|
-
NoImportRuleSchema =
|
|
14594
|
-
type:
|
|
14595
|
-
from:
|
|
15408
|
+
NoImportRuleSchema = z4.object({
|
|
15409
|
+
type: z4.literal("no-import"),
|
|
15410
|
+
from: z4.string()
|
|
14596
15411
|
});
|
|
14597
|
-
NamingRuleSchema =
|
|
14598
|
-
type:
|
|
14599
|
-
match:
|
|
14600
|
-
convention:
|
|
15412
|
+
NamingRuleSchema = z4.object({
|
|
15413
|
+
type: z4.literal("naming"),
|
|
15414
|
+
match: z4.string(),
|
|
15415
|
+
convention: z4.enum(["camelCase", "PascalCase", "UPPER_SNAKE", "kebab-case"])
|
|
14601
15416
|
});
|
|
14602
|
-
MaxExportsRuleSchema =
|
|
14603
|
-
type:
|
|
14604
|
-
count:
|
|
15417
|
+
MaxExportsRuleSchema = z4.object({
|
|
15418
|
+
type: z4.literal("max-exports"),
|
|
15419
|
+
count: z4.number().positive()
|
|
14605
15420
|
});
|
|
14606
|
-
MaxLinesRuleSchema =
|
|
14607
|
-
type:
|
|
14608
|
-
count:
|
|
15421
|
+
MaxLinesRuleSchema = z4.object({
|
|
15422
|
+
type: z4.literal("max-lines"),
|
|
15423
|
+
count: z4.number().positive()
|
|
14609
15424
|
});
|
|
14610
|
-
RequireJSDocRuleSchema =
|
|
14611
|
-
type:
|
|
14612
|
-
for:
|
|
15425
|
+
RequireJSDocRuleSchema = z4.object({
|
|
15426
|
+
type: z4.literal("require-jsdoc"),
|
|
15427
|
+
for: z4.array(z4.enum(["function", "class", "export"]))
|
|
14613
15428
|
});
|
|
14614
|
-
RuleSchema =
|
|
15429
|
+
RuleSchema = z4.discriminatedUnion("type", [
|
|
14615
15430
|
MustExportRuleSchema,
|
|
14616
15431
|
MustExportDefaultRuleSchema,
|
|
14617
15432
|
NoExportRuleSchema,
|
|
@@ -14622,52 +15437,52 @@ var init_dist3 = __esm({
|
|
|
14622
15437
|
MaxLinesRuleSchema,
|
|
14623
15438
|
RequireJSDocRuleSchema
|
|
14624
15439
|
]);
|
|
14625
|
-
ConfigPatternSchema =
|
|
14626
|
-
name:
|
|
14627
|
-
description:
|
|
14628
|
-
severity:
|
|
14629
|
-
files:
|
|
15440
|
+
ConfigPatternSchema = z4.object({
|
|
15441
|
+
name: z4.string().min(1),
|
|
15442
|
+
description: z4.string(),
|
|
15443
|
+
severity: z4.enum(["error", "warning"]),
|
|
15444
|
+
files: z4.array(z4.string()),
|
|
14630
15445
|
rule: RuleSchema,
|
|
14631
|
-
message:
|
|
15446
|
+
message: z4.string().optional()
|
|
14632
15447
|
});
|
|
14633
|
-
PatternConfigSchema =
|
|
14634
|
-
patterns:
|
|
14635
|
-
customPatterns:
|
|
15448
|
+
PatternConfigSchema = z4.object({
|
|
15449
|
+
patterns: z4.array(ConfigPatternSchema),
|
|
15450
|
+
customPatterns: z4.array(z4.any()).optional(),
|
|
14636
15451
|
// Code patterns are functions, can't validate
|
|
14637
|
-
ignoreFiles:
|
|
15452
|
+
ignoreFiles: z4.array(z4.string()).optional()
|
|
14638
15453
|
});
|
|
14639
|
-
DriftConfigSchema =
|
|
14640
|
-
docPaths:
|
|
14641
|
-
checkApiSignatures:
|
|
14642
|
-
checkExamples:
|
|
14643
|
-
checkStructure:
|
|
14644
|
-
ignorePatterns:
|
|
15454
|
+
DriftConfigSchema = z4.object({
|
|
15455
|
+
docPaths: z4.array(z4.string()).optional(),
|
|
15456
|
+
checkApiSignatures: z4.boolean().optional(),
|
|
15457
|
+
checkExamples: z4.boolean().optional(),
|
|
15458
|
+
checkStructure: z4.boolean().optional(),
|
|
15459
|
+
ignorePatterns: z4.array(z4.string()).optional()
|
|
14645
15460
|
});
|
|
14646
|
-
DeadCodeConfigSchema =
|
|
14647
|
-
entryPoints:
|
|
14648
|
-
includeTypes:
|
|
14649
|
-
includeInternals:
|
|
14650
|
-
ignorePatterns:
|
|
14651
|
-
treatDynamicImportsAs:
|
|
15461
|
+
DeadCodeConfigSchema = z4.object({
|
|
15462
|
+
entryPoints: z4.array(z4.string()).optional(),
|
|
15463
|
+
includeTypes: z4.boolean().optional(),
|
|
15464
|
+
includeInternals: z4.boolean().optional(),
|
|
15465
|
+
ignorePatterns: z4.array(z4.string()).optional(),
|
|
15466
|
+
treatDynamicImportsAs: z4.enum(["used", "unknown"]).optional()
|
|
14652
15467
|
});
|
|
14653
|
-
EntropyConfigSchema =
|
|
14654
|
-
rootDir:
|
|
14655
|
-
parser:
|
|
15468
|
+
EntropyConfigSchema = z4.object({
|
|
15469
|
+
rootDir: z4.string(),
|
|
15470
|
+
parser: z4.any().optional(),
|
|
14656
15471
|
// LanguageParser instance, can't validate
|
|
14657
|
-
entryPoints:
|
|
14658
|
-
analyze:
|
|
14659
|
-
drift:
|
|
14660
|
-
deadCode:
|
|
14661
|
-
patterns:
|
|
15472
|
+
entryPoints: z4.array(z4.string()).optional(),
|
|
15473
|
+
analyze: z4.object({
|
|
15474
|
+
drift: z4.union([z4.boolean(), DriftConfigSchema]).optional(),
|
|
15475
|
+
deadCode: z4.union([z4.boolean(), DeadCodeConfigSchema]).optional(),
|
|
15476
|
+
patterns: z4.union([z4.boolean(), PatternConfigSchema]).optional()
|
|
14662
15477
|
}),
|
|
14663
|
-
include:
|
|
14664
|
-
exclude:
|
|
14665
|
-
docPaths:
|
|
15478
|
+
include: z4.array(z4.string()).optional(),
|
|
15479
|
+
exclude: z4.array(z4.string()).optional(),
|
|
15480
|
+
docPaths: z4.array(z4.string()).optional()
|
|
14666
15481
|
});
|
|
14667
15482
|
BaselineManager = class {
|
|
14668
15483
|
baselinesPath;
|
|
14669
15484
|
constructor(projectRoot) {
|
|
14670
|
-
this.baselinesPath =
|
|
15485
|
+
this.baselinesPath = join17(projectRoot, ".harness", "perf", "baselines.json");
|
|
14671
15486
|
}
|
|
14672
15487
|
/**
|
|
14673
15488
|
* Load the baselines file from disk.
|
|
@@ -14942,16 +15757,16 @@ var init_dist3 = __esm({
|
|
|
14942
15757
|
walkDir(dir, entries) {
|
|
14943
15758
|
let items;
|
|
14944
15759
|
try {
|
|
14945
|
-
items =
|
|
15760
|
+
items = fs6.readdirSync(dir, { withFileTypes: true });
|
|
14946
15761
|
} catch {
|
|
14947
15762
|
return;
|
|
14948
15763
|
}
|
|
14949
15764
|
for (const item of items) {
|
|
14950
15765
|
if (item.isDirectory()) {
|
|
14951
15766
|
if (SKIP_DIRS.has(item.name)) continue;
|
|
14952
|
-
this.walkDir(
|
|
14953
|
-
} else if (item.isFile() && SOURCE_EXTENSIONS.has(
|
|
14954
|
-
this.scanFile(
|
|
15767
|
+
this.walkDir(path3.join(dir, item.name), entries);
|
|
15768
|
+
} else if (item.isFile() && SOURCE_EXTENSIONS.has(path3.extname(item.name))) {
|
|
15769
|
+
this.scanFile(path3.join(dir, item.name), entries);
|
|
14955
15770
|
}
|
|
14956
15771
|
}
|
|
14957
15772
|
}
|
|
@@ -14966,12 +15781,12 @@ var init_dist3 = __esm({
|
|
|
14966
15781
|
scanFile(filePath, entries) {
|
|
14967
15782
|
let content;
|
|
14968
15783
|
try {
|
|
14969
|
-
content =
|
|
15784
|
+
content = fs6.readFileSync(filePath, "utf-8");
|
|
14970
15785
|
} catch {
|
|
14971
15786
|
return;
|
|
14972
15787
|
}
|
|
14973
15788
|
const lines = content.split("\n");
|
|
14974
|
-
const relativePath =
|
|
15789
|
+
const relativePath = path3.relative(this.projectRoot, filePath).replace(/\\/g, "/");
|
|
14975
15790
|
for (let i = 0; i < lines.length; i++) {
|
|
14976
15791
|
if (!lines[i].includes("@perf-critical")) continue;
|
|
14977
15792
|
const fnName = this.resolveFunctionName(lines, i + 1);
|
|
@@ -15370,46 +16185,46 @@ var init_dist3 = __esm({
|
|
|
15370
16185
|
return Ok(void 0);
|
|
15371
16186
|
}
|
|
15372
16187
|
};
|
|
15373
|
-
CategorySnapshotSchema =
|
|
16188
|
+
CategorySnapshotSchema = z5.object({
|
|
15374
16189
|
/** Aggregate metric value (e.g., violation count, avg complexity) */
|
|
15375
|
-
value:
|
|
16190
|
+
value: z5.number(),
|
|
15376
16191
|
/** Count of violations in this category */
|
|
15377
|
-
violationCount:
|
|
16192
|
+
violationCount: z5.number()
|
|
15378
16193
|
});
|
|
15379
|
-
TimelineSnapshotSchema =
|
|
16194
|
+
TimelineSnapshotSchema = z5.object({
|
|
15380
16195
|
/** ISO 8601 timestamp of capture */
|
|
15381
|
-
capturedAt:
|
|
16196
|
+
capturedAt: z5.string().datetime(),
|
|
15382
16197
|
/** Git commit hash at capture time */
|
|
15383
|
-
commitHash:
|
|
16198
|
+
commitHash: z5.string(),
|
|
15384
16199
|
/** Composite stability score (0-100, higher is healthier) */
|
|
15385
|
-
stabilityScore:
|
|
16200
|
+
stabilityScore: z5.number().min(0).max(100),
|
|
15386
16201
|
/** Per-category metric aggregates */
|
|
15387
|
-
metrics:
|
|
16202
|
+
metrics: z5.record(ArchMetricCategorySchema, CategorySnapshotSchema)
|
|
15388
16203
|
});
|
|
15389
|
-
TimelineFileSchema =
|
|
15390
|
-
version:
|
|
15391
|
-
snapshots:
|
|
16204
|
+
TimelineFileSchema = z5.object({
|
|
16205
|
+
version: z5.literal(1),
|
|
16206
|
+
snapshots: z5.array(TimelineSnapshotSchema)
|
|
15392
16207
|
});
|
|
15393
|
-
TrendLineSchema =
|
|
16208
|
+
TrendLineSchema = z5.object({
|
|
15394
16209
|
/** Current value */
|
|
15395
|
-
current:
|
|
16210
|
+
current: z5.number(),
|
|
15396
16211
|
/** Previous value (from comparison snapshot) */
|
|
15397
|
-
previous:
|
|
16212
|
+
previous: z5.number(),
|
|
15398
16213
|
/** Absolute delta (current - previous) */
|
|
15399
|
-
delta:
|
|
16214
|
+
delta: z5.number(),
|
|
15400
16215
|
/** Direction indicator */
|
|
15401
|
-
direction:
|
|
16216
|
+
direction: z5.enum(["improving", "stable", "declining"])
|
|
15402
16217
|
});
|
|
15403
|
-
TrendResultSchema =
|
|
16218
|
+
TrendResultSchema = z5.object({
|
|
15404
16219
|
/** Overall stability trend */
|
|
15405
16220
|
stability: TrendLineSchema,
|
|
15406
16221
|
/** Per-category trends */
|
|
15407
|
-
categories:
|
|
16222
|
+
categories: z5.record(ArchMetricCategorySchema, TrendLineSchema),
|
|
15408
16223
|
/** Number of snapshots analyzed */
|
|
15409
|
-
snapshotCount:
|
|
16224
|
+
snapshotCount: z5.number(),
|
|
15410
16225
|
/** Time range covered */
|
|
15411
|
-
from:
|
|
15412
|
-
to:
|
|
16226
|
+
from: z5.string(),
|
|
16227
|
+
to: z5.string()
|
|
15413
16228
|
});
|
|
15414
16229
|
DEFAULT_STABILITY_THRESHOLDS = {
|
|
15415
16230
|
"circular-deps": 5,
|
|
@@ -15424,7 +16239,7 @@ var init_dist3 = __esm({
|
|
|
15424
16239
|
TimelineManager = class {
|
|
15425
16240
|
timelinePath;
|
|
15426
16241
|
constructor(rootDir) {
|
|
15427
|
-
this.timelinePath =
|
|
16242
|
+
this.timelinePath = join19(rootDir, ".harness", "arch", "timeline.json");
|
|
15428
16243
|
}
|
|
15429
16244
|
/**
|
|
15430
16245
|
* Load timeline from disk.
|
|
@@ -15608,80 +16423,80 @@ var init_dist3 = __esm({
|
|
|
15608
16423
|
};
|
|
15609
16424
|
}
|
|
15610
16425
|
};
|
|
15611
|
-
ConfidenceTierSchema =
|
|
15612
|
-
RegressionResultSchema =
|
|
15613
|
-
slope:
|
|
15614
|
-
intercept:
|
|
15615
|
-
rSquared:
|
|
15616
|
-
dataPoints:
|
|
16426
|
+
ConfidenceTierSchema = z6.enum(["high", "medium", "low"]);
|
|
16427
|
+
RegressionResultSchema = z6.object({
|
|
16428
|
+
slope: z6.number(),
|
|
16429
|
+
intercept: z6.number(),
|
|
16430
|
+
rSquared: z6.number().min(0).max(1),
|
|
16431
|
+
dataPoints: z6.number().int().min(0)
|
|
15617
16432
|
});
|
|
15618
|
-
DirectionSchema =
|
|
15619
|
-
CategoryForecastSchema =
|
|
16433
|
+
DirectionSchema = z6.enum(["improving", "stable", "declining"]);
|
|
16434
|
+
CategoryForecastSchema = z6.object({
|
|
15620
16435
|
category: ArchMetricCategorySchema,
|
|
15621
|
-
current:
|
|
15622
|
-
threshold:
|
|
15623
|
-
projectedValue4w:
|
|
15624
|
-
projectedValue8w:
|
|
15625
|
-
projectedValue12w:
|
|
15626
|
-
thresholdCrossingWeeks:
|
|
16436
|
+
current: z6.number(),
|
|
16437
|
+
threshold: z6.number(),
|
|
16438
|
+
projectedValue4w: z6.number(),
|
|
16439
|
+
projectedValue8w: z6.number(),
|
|
16440
|
+
projectedValue12w: z6.number(),
|
|
16441
|
+
thresholdCrossingWeeks: z6.number().nullable(),
|
|
15627
16442
|
confidence: ConfidenceTierSchema,
|
|
15628
16443
|
regression: RegressionResultSchema,
|
|
15629
16444
|
direction: DirectionSchema
|
|
15630
16445
|
});
|
|
15631
|
-
SpecImpactSignalsSchema =
|
|
15632
|
-
newFileCount:
|
|
15633
|
-
affectedLayers:
|
|
15634
|
-
newDependencies:
|
|
15635
|
-
phaseCount:
|
|
16446
|
+
SpecImpactSignalsSchema = z6.object({
|
|
16447
|
+
newFileCount: z6.number().int().min(0),
|
|
16448
|
+
affectedLayers: z6.array(z6.string()),
|
|
16449
|
+
newDependencies: z6.number().int().min(0),
|
|
16450
|
+
phaseCount: z6.number().int().min(0)
|
|
15636
16451
|
});
|
|
15637
|
-
SpecImpactEstimateSchema =
|
|
15638
|
-
specPath:
|
|
15639
|
-
featureName:
|
|
16452
|
+
SpecImpactEstimateSchema = z6.object({
|
|
16453
|
+
specPath: z6.string(),
|
|
16454
|
+
featureName: z6.string(),
|
|
15640
16455
|
signals: SpecImpactSignalsSchema,
|
|
15641
|
-
deltas:
|
|
16456
|
+
deltas: z6.record(ArchMetricCategorySchema, z6.number()).optional()
|
|
15642
16457
|
});
|
|
15643
|
-
ContributingFeatureSchema =
|
|
15644
|
-
name:
|
|
15645
|
-
specPath:
|
|
15646
|
-
delta:
|
|
16458
|
+
ContributingFeatureSchema = z6.object({
|
|
16459
|
+
name: z6.string(),
|
|
16460
|
+
specPath: z6.string(),
|
|
16461
|
+
delta: z6.number()
|
|
15647
16462
|
});
|
|
15648
|
-
AdjustedForecastSchema =
|
|
16463
|
+
AdjustedForecastSchema = z6.object({
|
|
15649
16464
|
baseline: CategoryForecastSchema,
|
|
15650
16465
|
adjusted: CategoryForecastSchema,
|
|
15651
|
-
contributingFeatures:
|
|
16466
|
+
contributingFeatures: z6.array(ContributingFeatureSchema)
|
|
15652
16467
|
});
|
|
15653
|
-
PredictionWarningSchema =
|
|
15654
|
-
severity:
|
|
16468
|
+
PredictionWarningSchema = z6.object({
|
|
16469
|
+
severity: z6.enum(["critical", "warning", "info"]),
|
|
15655
16470
|
category: ArchMetricCategorySchema,
|
|
15656
|
-
message:
|
|
15657
|
-
weeksUntil:
|
|
16471
|
+
message: z6.string(),
|
|
16472
|
+
weeksUntil: z6.number(),
|
|
15658
16473
|
confidence: ConfidenceTierSchema,
|
|
15659
|
-
contributingFeatures:
|
|
16474
|
+
contributingFeatures: z6.array(z6.string())
|
|
15660
16475
|
});
|
|
15661
|
-
StabilityForecastSchema =
|
|
15662
|
-
current:
|
|
15663
|
-
projected4w:
|
|
15664
|
-
projected8w:
|
|
15665
|
-
projected12w:
|
|
16476
|
+
StabilityForecastSchema = z6.object({
|
|
16477
|
+
current: z6.number(),
|
|
16478
|
+
projected4w: z6.number(),
|
|
16479
|
+
projected8w: z6.number(),
|
|
16480
|
+
projected12w: z6.number(),
|
|
15666
16481
|
confidence: ConfidenceTierSchema,
|
|
15667
16482
|
direction: DirectionSchema
|
|
15668
16483
|
});
|
|
15669
|
-
PredictionResultSchema =
|
|
15670
|
-
generatedAt:
|
|
15671
|
-
snapshotsUsed:
|
|
15672
|
-
timelineRange:
|
|
15673
|
-
from:
|
|
15674
|
-
to:
|
|
16484
|
+
PredictionResultSchema = z6.object({
|
|
16485
|
+
generatedAt: z6.string(),
|
|
16486
|
+
snapshotsUsed: z6.number().int().min(0),
|
|
16487
|
+
timelineRange: z6.object({
|
|
16488
|
+
from: z6.string(),
|
|
16489
|
+
to: z6.string()
|
|
15675
16490
|
}),
|
|
15676
16491
|
stabilityForecast: StabilityForecastSchema,
|
|
15677
|
-
categories:
|
|
15678
|
-
warnings:
|
|
16492
|
+
categories: z6.record(ArchMetricCategorySchema, AdjustedForecastSchema),
|
|
16493
|
+
warnings: z6.array(PredictionWarningSchema)
|
|
15679
16494
|
});
|
|
15680
|
-
PredictionOptionsSchema =
|
|
15681
|
-
horizon:
|
|
15682
|
-
includeRoadmap:
|
|
15683
|
-
categories:
|
|
15684
|
-
thresholds:
|
|
16495
|
+
PredictionOptionsSchema = z6.object({
|
|
16496
|
+
horizon: z6.number().int().min(1).default(12),
|
|
16497
|
+
includeRoadmap: z6.boolean().default(true),
|
|
16498
|
+
categories: z6.array(ArchMetricCategorySchema).optional(),
|
|
16499
|
+
thresholds: z6.record(ArchMetricCategorySchema, z6.number()).optional()
|
|
15685
16500
|
});
|
|
15686
16501
|
VALID_STATUSES = /* @__PURE__ */ new Set([
|
|
15687
16502
|
"backlog",
|
|
@@ -16014,8 +16829,8 @@ var init_dist3 = __esm({
|
|
|
16014
16829
|
return null;
|
|
16015
16830
|
}
|
|
16016
16831
|
try {
|
|
16017
|
-
const roadmapPath =
|
|
16018
|
-
const raw =
|
|
16832
|
+
const roadmapPath = path4.join(this.rootDir, "roadmap.md");
|
|
16833
|
+
const raw = fs7.readFileSync(roadmapPath, "utf-8");
|
|
16019
16834
|
const parseResult = parseRoadmap(raw);
|
|
16020
16835
|
if (!parseResult.ok) return null;
|
|
16021
16836
|
const features = parseResult.value.milestones.flatMap(
|
|
@@ -16050,8 +16865,8 @@ var init_dist3 = __esm({
|
|
|
16050
16865
|
* @param specPath - Relative path from rootDir to the spec file.
|
|
16051
16866
|
*/
|
|
16052
16867
|
estimate(specPath) {
|
|
16053
|
-
const absolutePath =
|
|
16054
|
-
const content =
|
|
16868
|
+
const absolutePath = path5.join(this.rootDir, specPath);
|
|
16869
|
+
const content = fs8.readFileSync(absolutePath, "utf-8");
|
|
16055
16870
|
const newFileCount = this.extractNewFileCount(content);
|
|
16056
16871
|
const affectedLayers = this.extractAffectedLayers(content);
|
|
16057
16872
|
const newDependencies = this.extractNewDependencies(content);
|
|
@@ -16063,7 +16878,7 @@ var init_dist3 = __esm({
|
|
|
16063
16878
|
phaseCount
|
|
16064
16879
|
);
|
|
16065
16880
|
const h1Match = content.match(/^#\s+(.+)$/m);
|
|
16066
|
-
const featureName = h1Match ? h1Match[1].trim() :
|
|
16881
|
+
const featureName = h1Match ? h1Match[1].trim() : path5.basename(specPath, ".md");
|
|
16067
16882
|
return {
|
|
16068
16883
|
specPath,
|
|
16069
16884
|
featureName,
|
|
@@ -16084,8 +16899,8 @@ var init_dist3 = __esm({
|
|
|
16084
16899
|
const results = [];
|
|
16085
16900
|
for (const feature of features) {
|
|
16086
16901
|
if (!feature.spec) continue;
|
|
16087
|
-
const absolutePath =
|
|
16088
|
-
if (!
|
|
16902
|
+
const absolutePath = path5.join(this.rootDir, feature.spec);
|
|
16903
|
+
if (!fs8.existsSync(absolutePath)) continue;
|
|
16089
16904
|
const estimate = this.estimate(feature.spec);
|
|
16090
16905
|
results.push({ ...estimate, featureName: feature.name });
|
|
16091
16906
|
}
|
|
@@ -16113,8 +16928,8 @@ var init_dist3 = __esm({
|
|
|
16113
16928
|
}
|
|
16114
16929
|
let count = 0;
|
|
16115
16930
|
for (const fp of filePaths) {
|
|
16116
|
-
const absolute =
|
|
16117
|
-
if (!
|
|
16931
|
+
const absolute = path5.join(this.rootDir, fp);
|
|
16932
|
+
if (!fs8.existsSync(absolute)) {
|
|
16118
16933
|
count++;
|
|
16119
16934
|
}
|
|
16120
16935
|
}
|
|
@@ -16184,8 +16999,8 @@ var init_dist3 = __esm({
|
|
|
16184
16999
|
// --- Private: Config Loading ---
|
|
16185
17000
|
loadLayerNames() {
|
|
16186
17001
|
try {
|
|
16187
|
-
const configPath =
|
|
16188
|
-
const raw =
|
|
17002
|
+
const configPath = path5.join(this.rootDir, "harness.config.json");
|
|
17003
|
+
const raw = fs8.readFileSync(configPath, "utf-8");
|
|
16189
17004
|
const config = JSON.parse(raw);
|
|
16190
17005
|
return (config.layers ?? []).map((l) => l.name);
|
|
16191
17006
|
} catch {
|
|
@@ -16204,8 +17019,8 @@ var init_dist3 = __esm({
|
|
|
16204
17019
|
historyPath;
|
|
16205
17020
|
/** Load history from disk, returning empty history if file doesn't exist. */
|
|
16206
17021
|
load() {
|
|
16207
|
-
if (!
|
|
16208
|
-
const raw =
|
|
17022
|
+
if (!fs9.existsSync(this.historyPath)) return { ...EMPTY_HISTORY, snapshots: [] };
|
|
17023
|
+
const raw = fs9.readFileSync(this.historyPath, "utf-8");
|
|
16209
17024
|
return JSON.parse(raw);
|
|
16210
17025
|
}
|
|
16211
17026
|
/** Append a timestamped snapshot of current violations. */
|
|
@@ -16219,18 +17034,18 @@ var init_dist3 = __esm({
|
|
|
16219
17034
|
}
|
|
16220
17035
|
/** Prune snapshots older than retentionDays. */
|
|
16221
17036
|
prune(retentionDays) {
|
|
16222
|
-
if (!
|
|
17037
|
+
if (!fs9.existsSync(this.historyPath)) return;
|
|
16223
17038
|
const history = this.load();
|
|
16224
17039
|
const cutoff = Date.now() - retentionDays * 24 * 60 * 60 * 1e3;
|
|
16225
17040
|
history.snapshots = history.snapshots.filter((s) => new Date(s.timestamp).getTime() >= cutoff);
|
|
16226
17041
|
this.write(history);
|
|
16227
17042
|
}
|
|
16228
17043
|
write(history) {
|
|
16229
|
-
const dir =
|
|
16230
|
-
if (!
|
|
16231
|
-
|
|
17044
|
+
const dir = path6.dirname(this.historyPath);
|
|
17045
|
+
if (!fs9.existsSync(dir)) {
|
|
17046
|
+
fs9.mkdirSync(dir, { recursive: true });
|
|
16232
17047
|
}
|
|
16233
|
-
|
|
17048
|
+
fs9.writeFileSync(this.historyPath, JSON.stringify(history, null, 2), "utf-8");
|
|
16234
17049
|
}
|
|
16235
17050
|
};
|
|
16236
17051
|
NORMALIZERS = {
|
|
@@ -16245,81 +17060,81 @@ var init_dist3 = __esm({
|
|
|
16245
17060
|
MAX_SAMPLES = 5;
|
|
16246
17061
|
DEFAULT_WINDOW_WEEKS = 4;
|
|
16247
17062
|
DEFAULT_MIN_OCCURRENCES = 3;
|
|
16248
|
-
FailureEntrySchema =
|
|
16249
|
-
date:
|
|
16250
|
-
skill:
|
|
16251
|
-
type:
|
|
16252
|
-
description:
|
|
17063
|
+
FailureEntrySchema = z7.object({
|
|
17064
|
+
date: z7.string(),
|
|
17065
|
+
skill: z7.string(),
|
|
17066
|
+
type: z7.string(),
|
|
17067
|
+
description: z7.string()
|
|
16253
17068
|
});
|
|
16254
|
-
HandoffSchema =
|
|
16255
|
-
timestamp:
|
|
16256
|
-
fromSkill:
|
|
16257
|
-
phase:
|
|
16258
|
-
summary:
|
|
16259
|
-
completed:
|
|
16260
|
-
pending:
|
|
16261
|
-
concerns:
|
|
16262
|
-
decisions:
|
|
16263
|
-
|
|
16264
|
-
what:
|
|
16265
|
-
why:
|
|
17069
|
+
HandoffSchema = z7.object({
|
|
17070
|
+
timestamp: z7.string(),
|
|
17071
|
+
fromSkill: z7.string(),
|
|
17072
|
+
phase: z7.string(),
|
|
17073
|
+
summary: z7.string(),
|
|
17074
|
+
completed: z7.array(z7.string()).default([]),
|
|
17075
|
+
pending: z7.array(z7.string()).default([]),
|
|
17076
|
+
concerns: z7.array(z7.string()).default([]),
|
|
17077
|
+
decisions: z7.array(
|
|
17078
|
+
z7.object({
|
|
17079
|
+
what: z7.string(),
|
|
17080
|
+
why: z7.string()
|
|
16266
17081
|
})
|
|
16267
17082
|
).default([]),
|
|
16268
|
-
blockers:
|
|
16269
|
-
contextKeywords:
|
|
16270
|
-
recommendedSkills:
|
|
16271
|
-
apply:
|
|
16272
|
-
reference:
|
|
16273
|
-
consider:
|
|
16274
|
-
skillsPath:
|
|
17083
|
+
blockers: z7.array(z7.string()).default([]),
|
|
17084
|
+
contextKeywords: z7.array(z7.string()).default([]),
|
|
17085
|
+
recommendedSkills: z7.object({
|
|
17086
|
+
apply: z7.array(z7.string()),
|
|
17087
|
+
reference: z7.array(z7.string()),
|
|
17088
|
+
consider: z7.array(z7.string()),
|
|
17089
|
+
skillsPath: z7.string()
|
|
16275
17090
|
}).optional()
|
|
16276
17091
|
});
|
|
16277
|
-
GateCheckSchema =
|
|
16278
|
-
name:
|
|
16279
|
-
passed:
|
|
16280
|
-
command:
|
|
16281
|
-
output:
|
|
16282
|
-
duration:
|
|
17092
|
+
GateCheckSchema = z7.object({
|
|
17093
|
+
name: z7.string(),
|
|
17094
|
+
passed: z7.boolean(),
|
|
17095
|
+
command: z7.string(),
|
|
17096
|
+
output: z7.string().optional(),
|
|
17097
|
+
duration: z7.number().optional()
|
|
16283
17098
|
});
|
|
16284
|
-
GateResultSchema =
|
|
16285
|
-
passed:
|
|
16286
|
-
checks:
|
|
17099
|
+
GateResultSchema = z7.object({
|
|
17100
|
+
passed: z7.boolean(),
|
|
17101
|
+
checks: z7.array(GateCheckSchema)
|
|
16287
17102
|
});
|
|
16288
|
-
GateConfigSchema =
|
|
16289
|
-
checks:
|
|
16290
|
-
|
|
16291
|
-
name:
|
|
16292
|
-
command:
|
|
17103
|
+
GateConfigSchema = z7.object({
|
|
17104
|
+
checks: z7.array(
|
|
17105
|
+
z7.object({
|
|
17106
|
+
name: z7.string(),
|
|
17107
|
+
command: z7.string()
|
|
16293
17108
|
})
|
|
16294
17109
|
).optional(),
|
|
16295
|
-
trace:
|
|
17110
|
+
trace: z7.boolean().optional()
|
|
16296
17111
|
});
|
|
16297
|
-
HarnessStateSchema =
|
|
16298
|
-
schemaVersion:
|
|
16299
|
-
position:
|
|
16300
|
-
phase:
|
|
16301
|
-
task:
|
|
17112
|
+
HarnessStateSchema = z7.object({
|
|
17113
|
+
schemaVersion: z7.literal(1),
|
|
17114
|
+
position: z7.object({
|
|
17115
|
+
phase: z7.string().optional(),
|
|
17116
|
+
task: z7.string().optional()
|
|
16302
17117
|
}).default({}),
|
|
16303
|
-
decisions:
|
|
16304
|
-
|
|
16305
|
-
date:
|
|
16306
|
-
decision:
|
|
16307
|
-
context:
|
|
17118
|
+
decisions: z7.array(
|
|
17119
|
+
z7.object({
|
|
17120
|
+
date: z7.string(),
|
|
17121
|
+
decision: z7.string(),
|
|
17122
|
+
context: z7.string()
|
|
16308
17123
|
})
|
|
16309
17124
|
).default([]),
|
|
16310
|
-
blockers:
|
|
16311
|
-
|
|
16312
|
-
id:
|
|
16313
|
-
description:
|
|
16314
|
-
status:
|
|
17125
|
+
blockers: z7.array(
|
|
17126
|
+
z7.object({
|
|
17127
|
+
id: z7.string(),
|
|
17128
|
+
description: z7.string(),
|
|
17129
|
+
status: z7.enum(["open", "resolved"])
|
|
16315
17130
|
})
|
|
16316
17131
|
).default([]),
|
|
16317
|
-
progress:
|
|
16318
|
-
lastSession:
|
|
16319
|
-
date:
|
|
16320
|
-
summary:
|
|
16321
|
-
lastSkill:
|
|
16322
|
-
pendingTasks:
|
|
17132
|
+
progress: z7.record(z7.enum(["pending", "in_progress", "complete"])).default({}),
|
|
17133
|
+
lastSession: z7.object({
|
|
17134
|
+
date: z7.string(),
|
|
17135
|
+
summary: z7.string(),
|
|
17136
|
+
lastSkill: z7.string().optional(),
|
|
17137
|
+
pendingTasks: z7.array(z7.string()).optional()
|
|
16323
17138
|
}).optional()
|
|
16324
17139
|
});
|
|
16325
17140
|
DEFAULT_STATE = {
|
|
@@ -16329,16 +17144,16 @@ var init_dist3 = __esm({
|
|
|
16329
17144
|
blockers: [],
|
|
16330
17145
|
progress: {}
|
|
16331
17146
|
};
|
|
16332
|
-
StreamInfoSchema =
|
|
16333
|
-
name:
|
|
16334
|
-
branch:
|
|
16335
|
-
createdAt:
|
|
16336
|
-
lastActiveAt:
|
|
17147
|
+
StreamInfoSchema = z8.object({
|
|
17148
|
+
name: z8.string(),
|
|
17149
|
+
branch: z8.string().optional(),
|
|
17150
|
+
createdAt: z8.string(),
|
|
17151
|
+
lastActiveAt: z8.string()
|
|
16337
17152
|
});
|
|
16338
|
-
StreamIndexSchema =
|
|
16339
|
-
schemaVersion:
|
|
16340
|
-
activeStream:
|
|
16341
|
-
streams:
|
|
17153
|
+
StreamIndexSchema = z8.object({
|
|
17154
|
+
schemaVersion: z8.literal(1),
|
|
17155
|
+
activeStream: z8.string().nullable(),
|
|
17156
|
+
streams: z8.record(StreamInfoSchema)
|
|
16342
17157
|
});
|
|
16343
17158
|
DEFAULT_STREAM_INDEX = {
|
|
16344
17159
|
schemaVersion: 1,
|
|
@@ -16379,15 +17194,15 @@ var init_dist3 = __esm({
|
|
|
16379
17194
|
failuresCacheMap = /* @__PURE__ */ new Map();
|
|
16380
17195
|
FAILURE_LINE_REGEX = /^- \*\*(\d{4}-\d{2}-\d{2}) \[skill:([^\]]+)\] \[type:([^\]]+)\]:\*\* (.+)$/;
|
|
16381
17196
|
SAFE_GATE_COMMAND = /^(?:npm|pnpm|yarn)\s+(?:test|run\s+[\w.-]+|run-script\s+[\w.-]+)$|^go\s+(?:test|build|vet|fmt)\s+[\w./ -]+$|^(?:python|python3)\s+-m\s+[\w.-]+$|^make\s+[\w.-]+$|^cargo\s+(?:test|build|check|clippy)(?:\s+[\w./ -]+)?$|^(?:gradle|mvn)\s+[\w:.-]+$/;
|
|
16382
|
-
SkillEventSchema =
|
|
16383
|
-
timestamp:
|
|
16384
|
-
skill:
|
|
16385
|
-
session:
|
|
16386
|
-
type:
|
|
16387
|
-
summary:
|
|
16388
|
-
data:
|
|
16389
|
-
refs:
|
|
16390
|
-
contentHash:
|
|
17197
|
+
SkillEventSchema = z9.object({
|
|
17198
|
+
timestamp: z9.string(),
|
|
17199
|
+
skill: z9.string(),
|
|
17200
|
+
session: z9.string().optional(),
|
|
17201
|
+
type: z9.enum(["phase_transition", "decision", "gate_result", "handoff", "error", "checkpoint"]),
|
|
17202
|
+
summary: z9.string(),
|
|
17203
|
+
data: z9.record(z9.unknown()).optional(),
|
|
17204
|
+
refs: z9.array(z9.string()).optional(),
|
|
17205
|
+
contentHash: z9.string().optional()
|
|
16391
17206
|
});
|
|
16392
17207
|
knownHashesCache = /* @__PURE__ */ new Map();
|
|
16393
17208
|
EVENT_FORMATTERS = {
|
|
@@ -16430,19 +17245,19 @@ var init_dist3 = __esm({
|
|
|
16430
17245
|
rules: {},
|
|
16431
17246
|
exclude: ["**/node_modules/**", "**/dist/**", "**/*.test.ts", "**/fixtures/**"]
|
|
16432
17247
|
};
|
|
16433
|
-
RuleOverrideSchema =
|
|
16434
|
-
SecurityConfigSchema =
|
|
16435
|
-
enabled:
|
|
16436
|
-
strict:
|
|
16437
|
-
rules:
|
|
16438
|
-
exclude:
|
|
16439
|
-
external:
|
|
16440
|
-
semgrep:
|
|
16441
|
-
enabled:
|
|
16442
|
-
rulesets:
|
|
17248
|
+
RuleOverrideSchema = z10.enum(["off", "error", "warning", "info"]);
|
|
17249
|
+
SecurityConfigSchema = z10.object({
|
|
17250
|
+
enabled: z10.boolean().default(true),
|
|
17251
|
+
strict: z10.boolean().default(false),
|
|
17252
|
+
rules: z10.record(z10.string(), RuleOverrideSchema).optional().default({}),
|
|
17253
|
+
exclude: z10.array(z10.string()).optional().default(["**/node_modules/**", "**/dist/**", "**/*.test.ts", "**/fixtures/**"]),
|
|
17254
|
+
external: z10.object({
|
|
17255
|
+
semgrep: z10.object({
|
|
17256
|
+
enabled: z10.union([z10.literal("auto"), z10.boolean()]).default("auto"),
|
|
17257
|
+
rulesets: z10.array(z10.string()).optional()
|
|
16443
17258
|
}).optional(),
|
|
16444
|
-
gitleaks:
|
|
16445
|
-
enabled:
|
|
17259
|
+
gitleaks: z10.object({
|
|
17260
|
+
enabled: z10.union([z10.literal("auto"), z10.boolean()]).default("auto")
|
|
16446
17261
|
}).optional()
|
|
16447
17262
|
}).optional()
|
|
16448
17263
|
});
|
|
@@ -17232,7 +18047,7 @@ var init_dist3 = __esm({
|
|
|
17232
18047
|
}
|
|
17233
18048
|
async scanFile(filePath) {
|
|
17234
18049
|
if (!this.config.enabled) return [];
|
|
17235
|
-
const content = await
|
|
18050
|
+
const content = await fs27.readFile(filePath, "utf-8");
|
|
17236
18051
|
return this.scanContentForFile(content, filePath, 1);
|
|
17237
18052
|
}
|
|
17238
18053
|
scanContentForFile(content, filePath, startLine = 1) {
|
|
@@ -17541,88 +18356,88 @@ var init_dist3 = __esm({
|
|
|
17541
18356
|
];
|
|
17542
18357
|
TAINT_DURATION_MS = 30 * 60 * 1e3;
|
|
17543
18358
|
DEFAULT_SESSION_ID = "default";
|
|
17544
|
-
SecurityCategorySnapshotSchema =
|
|
17545
|
-
findingCount:
|
|
17546
|
-
errorCount:
|
|
17547
|
-
warningCount:
|
|
17548
|
-
infoCount:
|
|
18359
|
+
SecurityCategorySnapshotSchema = z11.object({
|
|
18360
|
+
findingCount: z11.number(),
|
|
18361
|
+
errorCount: z11.number(),
|
|
18362
|
+
warningCount: z11.number(),
|
|
18363
|
+
infoCount: z11.number()
|
|
17549
18364
|
});
|
|
17550
|
-
SupplyChainSnapshotSchema =
|
|
17551
|
-
critical:
|
|
17552
|
-
high:
|
|
17553
|
-
moderate:
|
|
17554
|
-
low:
|
|
17555
|
-
info:
|
|
17556
|
-
total:
|
|
18365
|
+
SupplyChainSnapshotSchema = z11.object({
|
|
18366
|
+
critical: z11.number(),
|
|
18367
|
+
high: z11.number(),
|
|
18368
|
+
moderate: z11.number(),
|
|
18369
|
+
low: z11.number(),
|
|
18370
|
+
info: z11.number(),
|
|
18371
|
+
total: z11.number()
|
|
17557
18372
|
});
|
|
17558
|
-
SecurityTimelineSnapshotSchema =
|
|
17559
|
-
capturedAt:
|
|
17560
|
-
commitHash:
|
|
17561
|
-
securityScore:
|
|
17562
|
-
totalFindings:
|
|
17563
|
-
bySeverity:
|
|
17564
|
-
error:
|
|
17565
|
-
warning:
|
|
17566
|
-
info:
|
|
18373
|
+
SecurityTimelineSnapshotSchema = z11.object({
|
|
18374
|
+
capturedAt: z11.string().datetime(),
|
|
18375
|
+
commitHash: z11.string(),
|
|
18376
|
+
securityScore: z11.number().min(0).max(100),
|
|
18377
|
+
totalFindings: z11.number(),
|
|
18378
|
+
bySeverity: z11.object({
|
|
18379
|
+
error: z11.number(),
|
|
18380
|
+
warning: z11.number(),
|
|
18381
|
+
info: z11.number()
|
|
17567
18382
|
}),
|
|
17568
|
-
byCategory:
|
|
18383
|
+
byCategory: z11.record(z11.string(), SecurityCategorySnapshotSchema),
|
|
17569
18384
|
supplyChain: SupplyChainSnapshotSchema,
|
|
17570
|
-
suppressionCount:
|
|
17571
|
-
findingIds:
|
|
18385
|
+
suppressionCount: z11.number(),
|
|
18386
|
+
findingIds: z11.array(z11.string())
|
|
17572
18387
|
});
|
|
17573
|
-
FindingLifecycleSchema =
|
|
17574
|
-
findingId:
|
|
17575
|
-
ruleId:
|
|
17576
|
-
category:
|
|
17577
|
-
severity:
|
|
17578
|
-
file:
|
|
17579
|
-
firstSeenAt:
|
|
17580
|
-
firstSeenCommit:
|
|
17581
|
-
resolvedAt:
|
|
17582
|
-
resolvedCommit:
|
|
18388
|
+
FindingLifecycleSchema = z11.object({
|
|
18389
|
+
findingId: z11.string(),
|
|
18390
|
+
ruleId: z11.string(),
|
|
18391
|
+
category: z11.string(),
|
|
18392
|
+
severity: z11.string(),
|
|
18393
|
+
file: z11.string(),
|
|
18394
|
+
firstSeenAt: z11.string().datetime(),
|
|
18395
|
+
firstSeenCommit: z11.string(),
|
|
18396
|
+
resolvedAt: z11.string().datetime().nullable(),
|
|
18397
|
+
resolvedCommit: z11.string().nullable()
|
|
17583
18398
|
});
|
|
17584
|
-
SecurityTimelineFileSchema =
|
|
17585
|
-
version:
|
|
17586
|
-
snapshots:
|
|
17587
|
-
findingLifecycles:
|
|
18399
|
+
SecurityTimelineFileSchema = z11.object({
|
|
18400
|
+
version: z11.literal(1),
|
|
18401
|
+
snapshots: z11.array(SecurityTimelineSnapshotSchema),
|
|
18402
|
+
findingLifecycles: z11.array(FindingLifecycleSchema)
|
|
17588
18403
|
});
|
|
17589
|
-
DirectionSchema2 =
|
|
17590
|
-
SecurityTrendLineSchema =
|
|
17591
|
-
current:
|
|
17592
|
-
previous:
|
|
17593
|
-
delta:
|
|
18404
|
+
DirectionSchema2 = z11.enum(["improving", "stable", "declining"]);
|
|
18405
|
+
SecurityTrendLineSchema = z11.object({
|
|
18406
|
+
current: z11.number(),
|
|
18407
|
+
previous: z11.number(),
|
|
18408
|
+
delta: z11.number(),
|
|
17594
18409
|
direction: DirectionSchema2
|
|
17595
18410
|
});
|
|
17596
|
-
TrendAttributionSchema =
|
|
17597
|
-
category:
|
|
17598
|
-
delta:
|
|
18411
|
+
TrendAttributionSchema = z11.object({
|
|
18412
|
+
category: z11.string(),
|
|
18413
|
+
delta: z11.number(),
|
|
17599
18414
|
direction: DirectionSchema2,
|
|
17600
|
-
description:
|
|
18415
|
+
description: z11.string()
|
|
17601
18416
|
});
|
|
17602
|
-
SecurityTrendResultSchema =
|
|
18417
|
+
SecurityTrendResultSchema = z11.object({
|
|
17603
18418
|
score: SecurityTrendLineSchema,
|
|
17604
18419
|
totalFindings: SecurityTrendLineSchema,
|
|
17605
|
-
bySeverity:
|
|
18420
|
+
bySeverity: z11.object({
|
|
17606
18421
|
error: SecurityTrendLineSchema,
|
|
17607
18422
|
warning: SecurityTrendLineSchema,
|
|
17608
18423
|
info: SecurityTrendLineSchema
|
|
17609
18424
|
}),
|
|
17610
18425
|
supplyChain: SecurityTrendLineSchema,
|
|
17611
|
-
snapshotCount:
|
|
17612
|
-
from:
|
|
17613
|
-
to:
|
|
17614
|
-
attribution:
|
|
18426
|
+
snapshotCount: z11.number(),
|
|
18427
|
+
from: z11.string(),
|
|
18428
|
+
to: z11.string(),
|
|
18429
|
+
attribution: z11.array(TrendAttributionSchema)
|
|
17615
18430
|
});
|
|
17616
|
-
TimeToFixStatsSchema =
|
|
17617
|
-
mean:
|
|
17618
|
-
median:
|
|
17619
|
-
count:
|
|
18431
|
+
TimeToFixStatsSchema = z11.object({
|
|
18432
|
+
mean: z11.number(),
|
|
18433
|
+
median: z11.number(),
|
|
18434
|
+
count: z11.number()
|
|
17620
18435
|
});
|
|
17621
|
-
TimeToFixResultSchema =
|
|
18436
|
+
TimeToFixResultSchema = z11.object({
|
|
17622
18437
|
overall: TimeToFixStatsSchema,
|
|
17623
|
-
byCategory:
|
|
17624
|
-
openFindings:
|
|
17625
|
-
oldestOpenDays:
|
|
18438
|
+
byCategory: z11.record(z11.string(), TimeToFixStatsSchema),
|
|
18439
|
+
openFindings: z11.number(),
|
|
18440
|
+
oldestOpenDays: z11.number().nullable()
|
|
17626
18441
|
});
|
|
17627
18442
|
EMPTY_SUPPLY_CHAIN = {
|
|
17628
18443
|
critical: 0,
|
|
@@ -17637,11 +18452,15 @@ var init_dist3 = __esm({
|
|
|
17637
18452
|
timelinePath;
|
|
17638
18453
|
constructor(rootDir) {
|
|
17639
18454
|
this.rootDir = rootDir;
|
|
17640
|
-
this.timelinePath =
|
|
18455
|
+
this.timelinePath = join39(rootDir, ".harness", "security", "timeline.json");
|
|
17641
18456
|
}
|
|
17642
18457
|
/**
|
|
17643
18458
|
* Load timeline from disk.
|
|
17644
18459
|
* Returns empty SecurityTimelineFile if file does not exist or is invalid.
|
|
18460
|
+
* Lifecycle entries with absolute paths under rootDir are migrated to repo-relative
|
|
18461
|
+
* form on read (older versions persisted whatever the scanner emitted, which leaked
|
|
18462
|
+
* machine paths into committed timeline.json). Migrated files are re-saved so the
|
|
18463
|
+
* fixup is one-shot.
|
|
17645
18464
|
*/
|
|
17646
18465
|
load() {
|
|
17647
18466
|
if (!existsSync332(this.timelinePath)) {
|
|
@@ -17658,6 +18477,15 @@ var init_dist3 = __esm({
|
|
|
17658
18477
|
);
|
|
17659
18478
|
return { version: 1, snapshots: [], findingLifecycles: [] };
|
|
17660
18479
|
}
|
|
18480
|
+
let mutated = false;
|
|
18481
|
+
for (const lc of parsed.data.findingLifecycles) {
|
|
18482
|
+
const next = this.toRepoRelativePath(lc.file);
|
|
18483
|
+
if (next !== lc.file) {
|
|
18484
|
+
lc.file = next;
|
|
18485
|
+
mutated = true;
|
|
18486
|
+
}
|
|
18487
|
+
}
|
|
18488
|
+
if (mutated) this.save(parsed.data);
|
|
17661
18489
|
return parsed.data;
|
|
17662
18490
|
} catch (error) {
|
|
17663
18491
|
console.error(`Error loading security timeline from ${this.timelinePath}:`, error);
|
|
@@ -17682,16 +18510,17 @@ var init_dist3 = __esm({
|
|
|
17682
18510
|
* appends to timeline (or replaces if same commitHash), and saves.
|
|
17683
18511
|
*/
|
|
17684
18512
|
capture(scanResult, commitHash, supplyChain) {
|
|
17685
|
-
const
|
|
17686
|
-
const
|
|
17687
|
-
const
|
|
18513
|
+
const normalized = scanResult.findings.map((f) => this.normalizeFindingPath(f));
|
|
18514
|
+
const byCategory = this.aggregateByCategory(normalized);
|
|
18515
|
+
const bySeverity = this.aggregateBySeverity(normalized);
|
|
18516
|
+
const findingIds = normalized.map((f) => securityFindingId(f));
|
|
17688
18517
|
const supply = supplyChain ?? EMPTY_SUPPLY_CHAIN;
|
|
17689
18518
|
const snapshot = {
|
|
17690
18519
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
17691
18520
|
commitHash,
|
|
17692
18521
|
securityScore: 0,
|
|
17693
18522
|
// computed below
|
|
17694
|
-
totalFindings:
|
|
18523
|
+
totalFindings: normalized.length,
|
|
17695
18524
|
bySeverity,
|
|
17696
18525
|
byCategory,
|
|
17697
18526
|
supplyChain: supply,
|
|
@@ -17754,13 +18583,14 @@ var init_dist3 = __esm({
|
|
|
17754
18583
|
*/
|
|
17755
18584
|
updateLifecycles(currentFindings, commitHash) {
|
|
17756
18585
|
const timeline = this.load();
|
|
17757
|
-
const
|
|
18586
|
+
const normalized = currentFindings.map((f) => this.normalizeFindingPath(f));
|
|
18587
|
+
const currentIds = new Set(normalized.map((f) => securityFindingId(f)));
|
|
17758
18588
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
17759
18589
|
const lifecycleMap = /* @__PURE__ */ new Map();
|
|
17760
18590
|
for (const lc of timeline.findingLifecycles) {
|
|
17761
18591
|
lifecycleMap.set(lc.findingId, lc);
|
|
17762
18592
|
}
|
|
17763
|
-
for (const finding of
|
|
18593
|
+
for (const finding of normalized) {
|
|
17764
18594
|
const id = securityFindingId(finding);
|
|
17765
18595
|
this.upsertLifecycle(lifecycleMap, id, finding, now, commitHash);
|
|
17766
18596
|
}
|
|
@@ -17875,6 +18705,23 @@ var init_dist3 = __esm({
|
|
|
17875
18705
|
return Math.round(Math.max(0, Math.min(100, 100 - penalty)));
|
|
17876
18706
|
}
|
|
17877
18707
|
// --- Private helpers ---
|
|
18708
|
+
/**
|
|
18709
|
+
* Convert a path to repo-relative form using forward slashes. Absolute paths under
|
|
18710
|
+
* rootDir are stripped; already-relative paths are normalized to forward slashes;
|
|
18711
|
+
* paths that escape rootDir (relative starts with `..`) are returned unchanged so we
|
|
18712
|
+
* never silently misattribute a finding outside the project.
|
|
18713
|
+
*/
|
|
18714
|
+
toRepoRelativePath(filePath) {
|
|
18715
|
+
if (!filePath) return filePath;
|
|
18716
|
+
if (!isAbsolute4(filePath)) return filePath.replaceAll("\\", "/");
|
|
18717
|
+
const rel = relative32(this.rootDir, filePath).replaceAll("\\", "/");
|
|
18718
|
+
if (rel === "" || rel.startsWith("../") || rel === "..") return filePath;
|
|
18719
|
+
return rel;
|
|
18720
|
+
}
|
|
18721
|
+
normalizeFindingPath(finding) {
|
|
18722
|
+
const next = this.toRepoRelativePath(finding.file);
|
|
18723
|
+
return next === finding.file ? finding : { ...finding, file: next };
|
|
18724
|
+
}
|
|
17878
18725
|
upsertLifecycle(map, id, finding, now, commitHash) {
|
|
17879
18726
|
if (!map.has(id)) {
|
|
17880
18727
|
map.set(id, {
|
|
@@ -18015,8 +18862,8 @@ var init_dist3 = __esm({
|
|
|
18015
18862
|
skip: "\u23ED\uFE0F"
|
|
18016
18863
|
};
|
|
18017
18864
|
CINotifier = class {
|
|
18018
|
-
constructor(
|
|
18019
|
-
this.adapter =
|
|
18865
|
+
constructor(adapter2, repo) {
|
|
18866
|
+
this.adapter = adapter2;
|
|
18020
18867
|
this.repo = repo;
|
|
18021
18868
|
}
|
|
18022
18869
|
adapter;
|
|
@@ -18110,6 +18957,12 @@ var init_dist3 = __esm({
|
|
|
18110
18957
|
MD_FILE_PATTERN = /\.md$/;
|
|
18111
18958
|
ALL_DOMAINS = ["compliance", "bug", "security", "architecture", "learnings"];
|
|
18112
18959
|
SECURITY_PATTERNS = /auth|crypto|password|secret|token|session|cookie|hash|encrypt|decrypt|sql|shell|exec|eval/i;
|
|
18960
|
+
JS_EXT_FALLBACKS22 = {
|
|
18961
|
+
".js": [".ts", ".tsx", ".jsx"],
|
|
18962
|
+
".jsx": [".tsx"],
|
|
18963
|
+
".mjs": [".mts"],
|
|
18964
|
+
".cjs": [".cts"]
|
|
18965
|
+
};
|
|
18113
18966
|
SEVERITY_RANK = {
|
|
18114
18967
|
suggestion: 0,
|
|
18115
18968
|
important: 1,
|
|
@@ -18667,28 +19520,28 @@ var init_dist3 = __esm({
|
|
|
18667
19520
|
POSITION_WEIGHT = 0.5;
|
|
18668
19521
|
DEPENDENTS_WEIGHT = 0.3;
|
|
18669
19522
|
AFFINITY_WEIGHT = 0.2;
|
|
18670
|
-
InteractionTypeSchema =
|
|
18671
|
-
QuestionSchema =
|
|
18672
|
-
text:
|
|
18673
|
-
options:
|
|
18674
|
-
default:
|
|
19523
|
+
InteractionTypeSchema = z12.enum(["question", "confirmation", "transition"]);
|
|
19524
|
+
QuestionSchema = z12.object({
|
|
19525
|
+
text: z12.string(),
|
|
19526
|
+
options: z12.array(z12.string()).optional(),
|
|
19527
|
+
default: z12.string().optional()
|
|
18675
19528
|
});
|
|
18676
|
-
ConfirmationSchema =
|
|
18677
|
-
text:
|
|
18678
|
-
context:
|
|
19529
|
+
ConfirmationSchema = z12.object({
|
|
19530
|
+
text: z12.string(),
|
|
19531
|
+
context: z12.string()
|
|
18679
19532
|
});
|
|
18680
|
-
TransitionSchema =
|
|
18681
|
-
completedPhase:
|
|
18682
|
-
suggestedNext:
|
|
18683
|
-
reason:
|
|
18684
|
-
artifacts:
|
|
18685
|
-
requiresConfirmation:
|
|
18686
|
-
summary:
|
|
19533
|
+
TransitionSchema = z12.object({
|
|
19534
|
+
completedPhase: z12.string(),
|
|
19535
|
+
suggestedNext: z12.string(),
|
|
19536
|
+
reason: z12.string(),
|
|
19537
|
+
artifacts: z12.array(z12.string()),
|
|
19538
|
+
requiresConfirmation: z12.boolean(),
|
|
19539
|
+
summary: z12.string()
|
|
18687
19540
|
});
|
|
18688
|
-
EmitInteractionInputSchema =
|
|
18689
|
-
path:
|
|
19541
|
+
EmitInteractionInputSchema = z12.object({
|
|
19542
|
+
path: z12.string(),
|
|
18690
19543
|
type: InteractionTypeSchema,
|
|
18691
|
-
stream:
|
|
19544
|
+
stream: z12.string().optional(),
|
|
18692
19545
|
question: QuestionSchema.optional(),
|
|
18693
19546
|
confirmation: ConfirmationSchema.optional(),
|
|
18694
19547
|
transition: TransitionSchema.optional()
|
|
@@ -18699,10 +19552,10 @@ var init_dist3 = __esm({
|
|
|
18699
19552
|
}
|
|
18700
19553
|
rootDir;
|
|
18701
19554
|
async scan() {
|
|
18702
|
-
let projectName =
|
|
19555
|
+
let projectName = path30.basename(this.rootDir);
|
|
18703
19556
|
try {
|
|
18704
|
-
const pkgPath =
|
|
18705
|
-
const pkgRaw = await
|
|
19557
|
+
const pkgPath = path30.join(this.rootDir, "package.json");
|
|
19558
|
+
const pkgRaw = await fs31.readFile(pkgPath, "utf-8");
|
|
18706
19559
|
const pkg = JSON.parse(pkgRaw);
|
|
18707
19560
|
if (pkg.name) projectName = pkg.name;
|
|
18708
19561
|
} catch {
|
|
@@ -18815,8 +19668,8 @@ console.log('Blueprint player initialized.');
|
|
|
18815
19668
|
styles: STYLES,
|
|
18816
19669
|
scripts: SCRIPTS
|
|
18817
19670
|
});
|
|
18818
|
-
await
|
|
18819
|
-
await
|
|
19671
|
+
await fs32.mkdir(options.outputDir, { recursive: true });
|
|
19672
|
+
await fs32.writeFile(path31.join(options.outputDir, "index.html"), html);
|
|
18820
19673
|
}
|
|
18821
19674
|
};
|
|
18822
19675
|
TOKENS_PER_MILLION = 1e6;
|
|
@@ -19039,6 +19892,145 @@ console.log('Blueprint player initialized.');
|
|
|
19039
19892
|
POSTHOG_BATCH_URL = "https://app.posthog.com/batch";
|
|
19040
19893
|
MAX_ATTEMPTS = 3;
|
|
19041
19894
|
TIMEOUT_MS = 5e3;
|
|
19895
|
+
CompoundLockHeldError = class extends Error {
|
|
19896
|
+
constructor(category, holderPid, lockPath) {
|
|
19897
|
+
super(
|
|
19898
|
+
`Compound lock for category "${category}" is held by pid ${holderPid} (lock file: ${lockPath}). If the holder is no longer running (e.g. it was SIGKILL'd), the lock is stale; recover with: rm ${lockPath}`
|
|
19899
|
+
);
|
|
19900
|
+
this.category = category;
|
|
19901
|
+
this.holderPid = holderPid;
|
|
19902
|
+
this.lockPath = lockPath;
|
|
19903
|
+
this.name = "CompoundLockHeldError";
|
|
19904
|
+
}
|
|
19905
|
+
category;
|
|
19906
|
+
holderPid;
|
|
19907
|
+
lockPath;
|
|
19908
|
+
};
|
|
19909
|
+
KNOWN_CATEGORIES = new Set(ALL_SOLUTION_CATEGORIES);
|
|
19910
|
+
ALLOWED_FIELD_KEYS = [
|
|
19911
|
+
"event_name",
|
|
19912
|
+
"count",
|
|
19913
|
+
"timestamp_bucket",
|
|
19914
|
+
"error_signature",
|
|
19915
|
+
"latency_ms",
|
|
19916
|
+
"category"
|
|
19917
|
+
];
|
|
19918
|
+
PII_TOKENS = [
|
|
19919
|
+
"email",
|
|
19920
|
+
"user_id",
|
|
19921
|
+
"session_id",
|
|
19922
|
+
"ip",
|
|
19923
|
+
"name",
|
|
19924
|
+
"phone",
|
|
19925
|
+
"address",
|
|
19926
|
+
"message",
|
|
19927
|
+
"content",
|
|
19928
|
+
"payload"
|
|
19929
|
+
];
|
|
19930
|
+
PII_FIELD_DENYLIST = new RegExp(`^(?:${PII_TOKENS.join("|")})$`, "i");
|
|
19931
|
+
PII_LINE_RE = new RegExp(`\\b(?:${PII_TOKENS.join("|")})\\b`, "i");
|
|
19932
|
+
ALLOWED_SET = new Set(ALLOWED_FIELD_KEYS);
|
|
19933
|
+
PulseAdapterAlreadyRegisteredError = class extends Error {
|
|
19934
|
+
constructor(adapterName) {
|
|
19935
|
+
super(`Pulse adapter "${adapterName}" is already registered.`);
|
|
19936
|
+
this.adapterName = adapterName;
|
|
19937
|
+
this.name = "PulseAdapterAlreadyRegisteredError";
|
|
19938
|
+
}
|
|
19939
|
+
adapterName;
|
|
19940
|
+
};
|
|
19941
|
+
REGISTRY = /* @__PURE__ */ new Map();
|
|
19942
|
+
MOCK_ADAPTER_NAME = "mock";
|
|
19943
|
+
ALLOWED_FIELD_SET = new Set(ALLOWED_FIELD_KEYS);
|
|
19944
|
+
adapter = {
|
|
19945
|
+
async query(_window, _eventNames) {
|
|
19946
|
+
return {
|
|
19947
|
+
event_name: "mock.event",
|
|
19948
|
+
count: 42,
|
|
19949
|
+
timestamp_bucket: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
19950
|
+
latency_ms: 123
|
|
19951
|
+
};
|
|
19952
|
+
},
|
|
19953
|
+
sanitize(raw) {
|
|
19954
|
+
const fields = {};
|
|
19955
|
+
if (raw && typeof raw === "object") {
|
|
19956
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
19957
|
+
if (PII_FIELD_DENYLIST.test(key)) continue;
|
|
19958
|
+
if (!ALLOWED_FIELD_SET.has(key)) continue;
|
|
19959
|
+
fields[key] = value;
|
|
19960
|
+
}
|
|
19961
|
+
}
|
|
19962
|
+
return { fields, distributions: {} };
|
|
19963
|
+
}
|
|
19964
|
+
};
|
|
19965
|
+
registerMockAdapter();
|
|
19966
|
+
LOOKBACK_RE = /^(\d+)(h|d)$/;
|
|
19967
|
+
MAX_LINES = 40;
|
|
19968
|
+
INLINE_TEMPLATE = `# {{productName}} Pulse \u2014 {{windowLabel}}
|
|
19969
|
+
|
|
19970
|
+
## Headlines
|
|
19971
|
+
|
|
19972
|
+
{{headlines}}
|
|
19973
|
+
|
|
19974
|
+
## Usage
|
|
19975
|
+
|
|
19976
|
+
{{usage}}
|
|
19977
|
+
|
|
19978
|
+
## System performance
|
|
19979
|
+
|
|
19980
|
+
{{systemPerformance}}
|
|
19981
|
+
|
|
19982
|
+
## Followups
|
|
19983
|
+
|
|
19984
|
+
{{followups}}
|
|
19985
|
+
`;
|
|
19986
|
+
TRUNCATION_MARKER2 = "_(truncated to fit single-page constraint)_";
|
|
19987
|
+
execFileAsync = promisify22(execFile);
|
|
19988
|
+
FIX_RE = /^fix(\([^)]+\))?:/i;
|
|
19989
|
+
EMPTY_REPO_RE = /(does not have any commits yet|not a git repository)/i;
|
|
19990
|
+
execFileAsync2 = promisify3(execFile2);
|
|
19991
|
+
TRACK_CATEGORIES2 = [
|
|
19992
|
+
["bug-track", BUG_TRACK_CATEGORIES],
|
|
19993
|
+
["knowledge-track", KNOWLEDGE_TRACK_CATEGORIES]
|
|
19994
|
+
];
|
|
19995
|
+
STOPWORDS = /* @__PURE__ */ new Set([
|
|
19996
|
+
"fix",
|
|
19997
|
+
"the",
|
|
19998
|
+
"a",
|
|
19999
|
+
"an",
|
|
20000
|
+
"in",
|
|
20001
|
+
"on",
|
|
20002
|
+
"of",
|
|
20003
|
+
"and",
|
|
20004
|
+
"or",
|
|
20005
|
+
"to",
|
|
20006
|
+
"for",
|
|
20007
|
+
"with",
|
|
20008
|
+
"is",
|
|
20009
|
+
"be",
|
|
20010
|
+
"when",
|
|
20011
|
+
"if",
|
|
20012
|
+
"this",
|
|
20013
|
+
"that",
|
|
20014
|
+
"edge",
|
|
20015
|
+
"case",
|
|
20016
|
+
"handle"
|
|
20017
|
+
]);
|
|
20018
|
+
KEYWORD_TO_CATEGORY = [
|
|
20019
|
+
[/\b(test|flaky|spec)\b/i, "bug-track/test-failures"],
|
|
20020
|
+
[/\b(perf|slow|latency|throughput|timeout|deadlock|n\+1|oom)\b/i, "bug-track/performance-issues"],
|
|
20021
|
+
[
|
|
20022
|
+
/\b(security|sqli|injection|xss|csrf|jwt|oauth|auth(?:n|z|entication)?|crypt(?:o|ographic|ography)?|encrypt(?:ion)?|decrypt(?:ion)?|password|token|session|tls|ssl)\b/i,
|
|
20023
|
+
"bug-track/security-issues"
|
|
20024
|
+
],
|
|
20025
|
+
[/\b(ui|css|color|contrast|layout|render)\b/i, "bug-track/ui-bugs"],
|
|
20026
|
+
[/\b(build|compile|tsc|webpack|tsup)\b/i, "bug-track/build-errors"],
|
|
20027
|
+
[
|
|
20028
|
+
/\b(db|database|sql|postgres|mysql|query|migration|transaction|deadlock)\b/i,
|
|
20029
|
+
"bug-track/database-issues"
|
|
20030
|
+
],
|
|
20031
|
+
[/\b(runtime|crash|exception|panic)\b/i, "bug-track/runtime-errors"],
|
|
20032
|
+
[/\(orchestrator\)|\bintegrat|\blease\b|\brace\b|\bconcurren/i, "bug-track/integration-issues"]
|
|
20033
|
+
];
|
|
19042
20034
|
}
|
|
19043
20035
|
});
|
|
19044
20036
|
|
|
@@ -19095,6 +20087,15 @@ export {
|
|
|
19095
20087
|
validateFileStructure,
|
|
19096
20088
|
validateConfig,
|
|
19097
20089
|
validateCommitMessage,
|
|
20090
|
+
PulseDbSourceSchema,
|
|
20091
|
+
PulseSourcesSchema,
|
|
20092
|
+
PulseConfigSchema,
|
|
20093
|
+
validatePulseConfig,
|
|
20094
|
+
BUG_TRACK_CATEGORIES,
|
|
20095
|
+
KNOWLEDGE_TRACK_CATEGORIES,
|
|
20096
|
+
ALL_SOLUTION_CATEGORIES,
|
|
20097
|
+
SolutionDocFrontmatterSchema,
|
|
20098
|
+
validateSolutionsDir,
|
|
19098
20099
|
REQUIRED_SECTIONS,
|
|
19099
20100
|
extractMarkdownLinks,
|
|
19100
20101
|
extractSections,
|
|
@@ -19438,6 +20439,35 @@ export {
|
|
|
19438
20439
|
VERSION,
|
|
19439
20440
|
collectEvents,
|
|
19440
20441
|
send,
|
|
20442
|
+
CompoundLockHeldError,
|
|
20443
|
+
acquireCompoundLock,
|
|
20444
|
+
ALLOWED_FIELD_KEYS,
|
|
20445
|
+
PII_TOKENS,
|
|
20446
|
+
PII_FIELD_DENYLIST,
|
|
20447
|
+
PII_LINE_RE,
|
|
20448
|
+
isSanitizedResult,
|
|
20449
|
+
assertSanitized,
|
|
20450
|
+
writePulseConfig,
|
|
20451
|
+
seedFromStrategy,
|
|
20452
|
+
PulseAdapterAlreadyRegisteredError,
|
|
20453
|
+
registerPulseAdapter,
|
|
20454
|
+
getPulseAdapter,
|
|
20455
|
+
listPulseAdapters,
|
|
20456
|
+
clearPulseAdapters,
|
|
20457
|
+
MOCK_ADAPTER_NAME,
|
|
20458
|
+
registerMockAdapter,
|
|
20459
|
+
parseLookback,
|
|
20460
|
+
computeWindow,
|
|
20461
|
+
runPulse,
|
|
20462
|
+
assembleReport,
|
|
20463
|
+
extractHeadlines,
|
|
20464
|
+
isoWeek,
|
|
20465
|
+
formatIsoWeek,
|
|
20466
|
+
gitScan,
|
|
20467
|
+
computeHotspots,
|
|
20468
|
+
crossReferenceUndocumentedFixes,
|
|
20469
|
+
suggestCategory,
|
|
20470
|
+
assembleCandidateReport,
|
|
19441
20471
|
dist_exports,
|
|
19442
20472
|
init_dist3 as init_dist
|
|
19443
20473
|
};
|