@harness-engineering/core 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/architecture/matchers.d.mts +1 -1
- package/dist/architecture/matchers.d.ts +1 -1
- package/dist/index.d.mts +484 -358
- package/dist/index.d.ts +484 -358
- package/dist/index.js +638 -271
- package/dist/index.mjs +609 -254
- package/dist/{matchers-D20x48U9.d.mts → matchers-Dj1t5vpg.d.mts} +46 -46
- package/dist/{matchers-D20x48U9.d.ts → matchers-Dj1t5vpg.d.ts} +46 -46
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -106,6 +106,7 @@ __export(index_exports, {
|
|
|
106
106
|
ViolationSchema: () => ViolationSchema,
|
|
107
107
|
addProvenance: () => addProvenance,
|
|
108
108
|
analyzeDiff: () => analyzeDiff,
|
|
109
|
+
analyzeLearningPatterns: () => analyzeLearningPatterns,
|
|
109
110
|
appendFailure: () => appendFailure,
|
|
110
111
|
appendLearning: () => appendLearning,
|
|
111
112
|
applyFixes: () => applyFixes,
|
|
@@ -114,6 +115,7 @@ __export(index_exports, {
|
|
|
114
115
|
archModule: () => archModule,
|
|
115
116
|
architecture: () => architecture,
|
|
116
117
|
archiveFailures: () => archiveFailures,
|
|
118
|
+
archiveLearnings: () => archiveLearnings,
|
|
117
119
|
archiveStream: () => archiveStream,
|
|
118
120
|
buildDependencyGraph: () => buildDependencyGraph,
|
|
119
121
|
buildExclusionSet: () => buildExclusionSet,
|
|
@@ -121,6 +123,8 @@ __export(index_exports, {
|
|
|
121
123
|
checkDocCoverage: () => checkDocCoverage,
|
|
122
124
|
checkEligibility: () => checkEligibility,
|
|
123
125
|
classifyFinding: () => classifyFinding,
|
|
126
|
+
clearFailuresCache: () => clearFailuresCache,
|
|
127
|
+
clearLearningsCache: () => clearLearningsCache,
|
|
124
128
|
configureFeedback: () => configureFeedback,
|
|
125
129
|
constraintRuleId: () => constraintRuleId,
|
|
126
130
|
contextBudget: () => contextBudget,
|
|
@@ -176,16 +180,20 @@ __export(index_exports, {
|
|
|
176
180
|
injectionRules: () => injectionRules,
|
|
177
181
|
isSmallSuggestion: () => isSmallSuggestion,
|
|
178
182
|
isUpdateCheckEnabled: () => isUpdateCheckEnabled,
|
|
183
|
+
listActiveSessions: () => listActiveSessions,
|
|
179
184
|
listStreams: () => listStreams,
|
|
185
|
+
loadBudgetedLearnings: () => loadBudgetedLearnings,
|
|
180
186
|
loadFailures: () => loadFailures,
|
|
181
187
|
loadHandoff: () => loadHandoff,
|
|
182
188
|
loadRelevantLearnings: () => loadRelevantLearnings,
|
|
189
|
+
loadSessionSummary: () => loadSessionSummary,
|
|
183
190
|
loadState: () => loadState,
|
|
184
191
|
loadStreamIndex: () => loadStreamIndex,
|
|
185
192
|
logAgentAction: () => logAgentAction,
|
|
186
193
|
migrateToStreams: () => migrateToStreams,
|
|
187
194
|
networkRules: () => networkRules,
|
|
188
195
|
nodeRules: () => nodeRules,
|
|
196
|
+
parseDateFromEntry: () => parseDateFromEntry,
|
|
189
197
|
parseDiff: () => parseDiff,
|
|
190
198
|
parseManifest: () => parseManifest,
|
|
191
199
|
parseRoadmap: () => parseRoadmap,
|
|
@@ -193,6 +201,7 @@ __export(index_exports, {
|
|
|
193
201
|
parseSize: () => parseSize,
|
|
194
202
|
pathTraversalRules: () => pathTraversalRules,
|
|
195
203
|
previewFix: () => previewFix,
|
|
204
|
+
pruneLearnings: () => pruneLearnings,
|
|
196
205
|
reactRules: () => reactRules,
|
|
197
206
|
readCheckState: () => readCheckState,
|
|
198
207
|
readLockfile: () => readLockfile,
|
|
@@ -204,6 +213,7 @@ __export(index_exports, {
|
|
|
204
213
|
resolveFileToLayer: () => resolveFileToLayer,
|
|
205
214
|
resolveModelTier: () => resolveModelTier,
|
|
206
215
|
resolveRuleSeverity: () => resolveRuleSeverity,
|
|
216
|
+
resolveSessionDir: () => resolveSessionDir,
|
|
207
217
|
resolveStreamPath: () => resolveStreamPath,
|
|
208
218
|
resolveThresholds: () => resolveThresholds,
|
|
209
219
|
runAll: () => runAll,
|
|
@@ -230,6 +240,7 @@ __export(index_exports, {
|
|
|
230
240
|
syncRoadmap: () => syncRoadmap,
|
|
231
241
|
touchStream: () => touchStream,
|
|
232
242
|
trackAction: () => trackAction,
|
|
243
|
+
updateSessionIndex: () => updateSessionIndex,
|
|
233
244
|
validateAgentsMap: () => validateAgentsMap,
|
|
234
245
|
validateBoundaries: () => validateBoundaries,
|
|
235
246
|
validateCommitMessage: () => validateCommitMessage,
|
|
@@ -242,6 +253,7 @@ __export(index_exports, {
|
|
|
242
253
|
violationId: () => violationId,
|
|
243
254
|
writeConfig: () => writeConfig,
|
|
244
255
|
writeLockfile: () => writeLockfile,
|
|
256
|
+
writeSessionSummary: () => writeSessionSummary,
|
|
245
257
|
xssRules: () => xssRules
|
|
246
258
|
});
|
|
247
259
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -264,17 +276,17 @@ var import_util = require("util");
|
|
|
264
276
|
var import_glob = require("glob");
|
|
265
277
|
var accessAsync = (0, import_util.promisify)(import_fs.access);
|
|
266
278
|
var readFileAsync = (0, import_util.promisify)(import_fs.readFile);
|
|
267
|
-
async function fileExists(
|
|
279
|
+
async function fileExists(path20) {
|
|
268
280
|
try {
|
|
269
|
-
await accessAsync(
|
|
281
|
+
await accessAsync(path20, import_fs.constants.F_OK);
|
|
270
282
|
return true;
|
|
271
283
|
} catch {
|
|
272
284
|
return false;
|
|
273
285
|
}
|
|
274
286
|
}
|
|
275
|
-
async function readFileContent(
|
|
287
|
+
async function readFileContent(path20) {
|
|
276
288
|
try {
|
|
277
|
-
const content = await readFileAsync(
|
|
289
|
+
const content = await readFileAsync(path20, "utf-8");
|
|
278
290
|
return (0, import_types.Ok)(content);
|
|
279
291
|
} catch (error) {
|
|
280
292
|
return (0, import_types.Err)(error);
|
|
@@ -322,15 +334,15 @@ function validateConfig(data, schema) {
|
|
|
322
334
|
let message = "Configuration validation failed";
|
|
323
335
|
const suggestions = [];
|
|
324
336
|
if (firstError) {
|
|
325
|
-
const
|
|
326
|
-
const pathDisplay =
|
|
337
|
+
const path20 = firstError.path.join(".");
|
|
338
|
+
const pathDisplay = path20 ? ` at "${path20}"` : "";
|
|
327
339
|
if (firstError.code === "invalid_type") {
|
|
328
340
|
const received = firstError.received;
|
|
329
341
|
const expected = firstError.expected;
|
|
330
342
|
if (received === "undefined") {
|
|
331
343
|
code = "MISSING_FIELD";
|
|
332
344
|
message = `Missing required field${pathDisplay}: ${firstError.message}`;
|
|
333
|
-
suggestions.push(`Field "${
|
|
345
|
+
suggestions.push(`Field "${path20}" is required and must be of type "${expected}"`);
|
|
334
346
|
} else {
|
|
335
347
|
code = "INVALID_TYPE";
|
|
336
348
|
message = `Invalid type${pathDisplay}: ${firstError.message}`;
|
|
@@ -543,30 +555,27 @@ function extractSections(content) {
|
|
|
543
555
|
return result;
|
|
544
556
|
});
|
|
545
557
|
}
|
|
546
|
-
function isExternalLink(
|
|
547
|
-
return
|
|
558
|
+
function isExternalLink(path20) {
|
|
559
|
+
return path20.startsWith("http://") || path20.startsWith("https://") || path20.startsWith("#") || path20.startsWith("mailto:");
|
|
548
560
|
}
|
|
549
561
|
function resolveLinkPath(linkPath, baseDir) {
|
|
550
562
|
return linkPath.startsWith(".") ? (0, import_path.join)(baseDir, linkPath) : linkPath;
|
|
551
563
|
}
|
|
552
|
-
async function validateAgentsMap(
|
|
553
|
-
|
|
554
|
-
"[harness] validateAgentsMap() is deprecated. Use graph-based validation via Assembler.checkCoverage() from @harness-engineering/graph"
|
|
555
|
-
);
|
|
556
|
-
const contentResult = await readFileContent(path13);
|
|
564
|
+
async function validateAgentsMap(path20 = "./AGENTS.md") {
|
|
565
|
+
const contentResult = await readFileContent(path20);
|
|
557
566
|
if (!contentResult.ok) {
|
|
558
567
|
return (0, import_types.Err)(
|
|
559
568
|
createError(
|
|
560
569
|
"PARSE_ERROR",
|
|
561
570
|
`Failed to read AGENTS.md: ${contentResult.error.message}`,
|
|
562
|
-
{ path:
|
|
571
|
+
{ path: path20 },
|
|
563
572
|
["Ensure the file exists", "Check file permissions"]
|
|
564
573
|
)
|
|
565
574
|
);
|
|
566
575
|
}
|
|
567
576
|
const content = contentResult.value;
|
|
568
577
|
const sections = extractSections(content);
|
|
569
|
-
const baseDir = (0, import_path.dirname)(
|
|
578
|
+
const baseDir = (0, import_path.dirname)(path20);
|
|
570
579
|
const sectionTitles = sections.map((s) => s.title);
|
|
571
580
|
const missingSections = REQUIRED_SECTIONS.filter(
|
|
572
581
|
(required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
|
|
@@ -707,8 +716,8 @@ async function checkDocCoverage(domain, options = {}) {
|
|
|
707
716
|
|
|
708
717
|
// src/context/knowledge-map.ts
|
|
709
718
|
var import_path3 = require("path");
|
|
710
|
-
function suggestFix(
|
|
711
|
-
const targetName = (0, import_path3.basename)(
|
|
719
|
+
function suggestFix(path20, existingFiles) {
|
|
720
|
+
const targetName = (0, import_path3.basename)(path20).toLowerCase();
|
|
712
721
|
const similar = existingFiles.find((file) => {
|
|
713
722
|
const fileName = (0, import_path3.basename)(file).toLowerCase();
|
|
714
723
|
return fileName.includes(targetName) || targetName.includes(fileName);
|
|
@@ -716,12 +725,9 @@ function suggestFix(path13, existingFiles) {
|
|
|
716
725
|
if (similar) {
|
|
717
726
|
return `Did you mean "${similar}"?`;
|
|
718
727
|
}
|
|
719
|
-
return `Create the file "${
|
|
728
|
+
return `Create the file "${path20}" or remove the link`;
|
|
720
729
|
}
|
|
721
730
|
async function validateKnowledgeMap(rootDir = process.cwd()) {
|
|
722
|
-
console.warn(
|
|
723
|
-
"[harness] validateKnowledgeMap() is deprecated. Use graph-based validation via Assembler.checkCoverage() from @harness-engineering/graph"
|
|
724
|
-
);
|
|
725
731
|
const agentsPath = (0, import_path3.join)(rootDir, "AGENTS.md");
|
|
726
732
|
const agentsResult = await validateAgentsMap(agentsPath);
|
|
727
733
|
if (!agentsResult.ok) {
|
|
@@ -1319,8 +1325,8 @@ function createBoundaryValidator(schema, name) {
|
|
|
1319
1325
|
return (0, import_types.Ok)(result.data);
|
|
1320
1326
|
}
|
|
1321
1327
|
const suggestions = result.error.issues.map((issue) => {
|
|
1322
|
-
const
|
|
1323
|
-
return
|
|
1328
|
+
const path20 = issue.path.join(".");
|
|
1329
|
+
return path20 ? `${path20}: ${issue.message}` : issue.message;
|
|
1324
1330
|
});
|
|
1325
1331
|
return (0, import_types.Err)(
|
|
1326
1332
|
createError(
|
|
@@ -1874,11 +1880,11 @@ function walk(node, visitor) {
|
|
|
1874
1880
|
var TypeScriptParser = class {
|
|
1875
1881
|
name = "typescript";
|
|
1876
1882
|
extensions = [".ts", ".tsx", ".mts", ".cts"];
|
|
1877
|
-
async parseFile(
|
|
1878
|
-
const contentResult = await readFileContent(
|
|
1883
|
+
async parseFile(path20) {
|
|
1884
|
+
const contentResult = await readFileContent(path20);
|
|
1879
1885
|
if (!contentResult.ok) {
|
|
1880
1886
|
return (0, import_types.Err)(
|
|
1881
|
-
createParseError("NOT_FOUND", `File not found: ${
|
|
1887
|
+
createParseError("NOT_FOUND", `File not found: ${path20}`, { path: path20 }, [
|
|
1882
1888
|
"Check that the file exists",
|
|
1883
1889
|
"Verify the path is correct"
|
|
1884
1890
|
])
|
|
@@ -1888,7 +1894,7 @@ var TypeScriptParser = class {
|
|
|
1888
1894
|
const ast = (0, import_typescript_estree.parse)(contentResult.value, {
|
|
1889
1895
|
loc: true,
|
|
1890
1896
|
range: true,
|
|
1891
|
-
jsx:
|
|
1897
|
+
jsx: path20.endsWith(".tsx"),
|
|
1892
1898
|
errorOnUnknownASTType: false
|
|
1893
1899
|
});
|
|
1894
1900
|
return (0, import_types.Ok)({
|
|
@@ -1899,7 +1905,7 @@ var TypeScriptParser = class {
|
|
|
1899
1905
|
} catch (e) {
|
|
1900
1906
|
const error = e;
|
|
1901
1907
|
return (0, import_types.Err)(
|
|
1902
|
-
createParseError("SYNTAX_ERROR", `Failed to parse ${
|
|
1908
|
+
createParseError("SYNTAX_ERROR", `Failed to parse ${path20}: ${error.message}`, { path: path20 }, [
|
|
1903
1909
|
"Check for syntax errors in the file",
|
|
1904
1910
|
"Ensure valid TypeScript syntax"
|
|
1905
1911
|
])
|
|
@@ -2183,22 +2189,22 @@ function extractInlineRefs(content) {
|
|
|
2183
2189
|
}
|
|
2184
2190
|
return refs;
|
|
2185
2191
|
}
|
|
2186
|
-
async function parseDocumentationFile(
|
|
2187
|
-
const contentResult = await readFileContent(
|
|
2192
|
+
async function parseDocumentationFile(path20) {
|
|
2193
|
+
const contentResult = await readFileContent(path20);
|
|
2188
2194
|
if (!contentResult.ok) {
|
|
2189
2195
|
return (0, import_types.Err)(
|
|
2190
2196
|
createEntropyError(
|
|
2191
2197
|
"PARSE_ERROR",
|
|
2192
|
-
`Failed to read documentation file: ${
|
|
2193
|
-
{ file:
|
|
2198
|
+
`Failed to read documentation file: ${path20}`,
|
|
2199
|
+
{ file: path20 },
|
|
2194
2200
|
["Check that the file exists"]
|
|
2195
2201
|
)
|
|
2196
2202
|
);
|
|
2197
2203
|
}
|
|
2198
2204
|
const content = contentResult.value;
|
|
2199
|
-
const type =
|
|
2205
|
+
const type = path20.endsWith(".md") ? "markdown" : "text";
|
|
2200
2206
|
return (0, import_types.Ok)({
|
|
2201
|
-
path:
|
|
2207
|
+
path: path20,
|
|
2202
2208
|
type,
|
|
2203
2209
|
content,
|
|
2204
2210
|
codeBlocks: extractCodeBlocks(content),
|
|
@@ -3009,15 +3015,34 @@ async function detectPatternViolations(snapshot, config) {
|
|
|
3009
3015
|
}
|
|
3010
3016
|
}
|
|
3011
3017
|
}
|
|
3018
|
+
if (config?.customPatterns) {
|
|
3019
|
+
for (const file of snapshot.files) {
|
|
3020
|
+
for (const custom of config.customPatterns) {
|
|
3021
|
+
const matches = custom.check(file, snapshot);
|
|
3022
|
+
for (const match of matches) {
|
|
3023
|
+
violations.push({
|
|
3024
|
+
pattern: custom.name,
|
|
3025
|
+
file: file.path,
|
|
3026
|
+
line: match.line,
|
|
3027
|
+
message: match.message,
|
|
3028
|
+
suggestion: match.suggestion || "Review and fix this pattern violation",
|
|
3029
|
+
severity: custom.severity
|
|
3030
|
+
});
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3012
3035
|
const errorCount = violations.filter((v) => v.severity === "error").length;
|
|
3013
3036
|
const warningCount = violations.filter((v) => v.severity === "warning").length;
|
|
3014
|
-
const
|
|
3015
|
-
const
|
|
3037
|
+
const customCount = config?.customPatterns?.length ?? 0;
|
|
3038
|
+
const allPatternsCount = patterns.length + customCount;
|
|
3039
|
+
const totalChecks = snapshot.files.length * allPatternsCount;
|
|
3040
|
+
const passRate = totalChecks > 0 ? Math.max(0, (totalChecks - violations.length) / totalChecks) : 1;
|
|
3016
3041
|
return (0, import_types.Ok)({
|
|
3017
3042
|
violations,
|
|
3018
3043
|
stats: {
|
|
3019
3044
|
filesChecked: snapshot.files.length,
|
|
3020
|
-
patternsApplied:
|
|
3045
|
+
patternsApplied: allPatternsCount,
|
|
3021
3046
|
violationCount: violations.length,
|
|
3022
3047
|
errorCount,
|
|
3023
3048
|
warningCount
|
|
@@ -5516,8 +5541,8 @@ var import_node_path3 = require("path");
|
|
|
5516
5541
|
// src/architecture/collectors/hash.ts
|
|
5517
5542
|
var import_node_crypto = require("crypto");
|
|
5518
5543
|
function violationId(relativePath, category, normalizedDetail) {
|
|
5519
|
-
const
|
|
5520
|
-
const input = `${
|
|
5544
|
+
const path20 = relativePath.replace(/\\/g, "/");
|
|
5545
|
+
const input = `${path20}:${category}:${normalizedDetail}`;
|
|
5521
5546
|
return (0, import_node_crypto.createHash)("sha256").update(input).digest("hex");
|
|
5522
5547
|
}
|
|
5523
5548
|
function constraintRuleId(category, scope, description) {
|
|
@@ -6695,10 +6720,13 @@ var DEFAULT_STATE = {
|
|
|
6695
6720
|
progress: {}
|
|
6696
6721
|
};
|
|
6697
6722
|
|
|
6698
|
-
// src/state/state-
|
|
6699
|
-
var
|
|
6700
|
-
var
|
|
6701
|
-
|
|
6723
|
+
// src/state/state-persistence.ts
|
|
6724
|
+
var fs8 = __toESM(require("fs"));
|
|
6725
|
+
var path5 = __toESM(require("path"));
|
|
6726
|
+
|
|
6727
|
+
// src/state/state-shared.ts
|
|
6728
|
+
var fs7 = __toESM(require("fs"));
|
|
6729
|
+
var path4 = __toESM(require("path"));
|
|
6702
6730
|
|
|
6703
6731
|
// src/state/stream-resolver.ts
|
|
6704
6732
|
var fs5 = __toESM(require("fs"));
|
|
@@ -6724,10 +6752,20 @@ var DEFAULT_STREAM_INDEX = {
|
|
|
6724
6752
|
streams: {}
|
|
6725
6753
|
};
|
|
6726
6754
|
|
|
6727
|
-
// src/state/
|
|
6755
|
+
// src/state/constants.ts
|
|
6728
6756
|
var HARNESS_DIR = ".harness";
|
|
6729
|
-
var
|
|
6757
|
+
var STATE_FILE = "state.json";
|
|
6758
|
+
var LEARNINGS_FILE = "learnings.md";
|
|
6759
|
+
var FAILURES_FILE = "failures.md";
|
|
6760
|
+
var HANDOFF_FILE = "handoff.json";
|
|
6761
|
+
var GATE_CONFIG_FILE = "gate.json";
|
|
6730
6762
|
var INDEX_FILE = "index.json";
|
|
6763
|
+
var SESSIONS_DIR = "sessions";
|
|
6764
|
+
var SESSION_INDEX_FILE = "index.md";
|
|
6765
|
+
var SUMMARY_FILE = "summary.md";
|
|
6766
|
+
|
|
6767
|
+
// src/state/stream-resolver.ts
|
|
6768
|
+
var STREAMS_DIR = "streams";
|
|
6731
6769
|
var STREAM_NAME_REGEX = /^[a-z0-9][a-z0-9._-]*$/;
|
|
6732
6770
|
function streamsDir(projectPath) {
|
|
6733
6771
|
return path2.join(projectPath, HARNESS_DIR, STREAMS_DIR);
|
|
@@ -6954,26 +6992,65 @@ async function migrateToStreams(projectPath) {
|
|
|
6954
6992
|
return saveStreamIndex(projectPath, index);
|
|
6955
6993
|
}
|
|
6956
6994
|
|
|
6957
|
-
// src/state/
|
|
6958
|
-
var
|
|
6959
|
-
var
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6995
|
+
// src/state/session-resolver.ts
|
|
6996
|
+
var fs6 = __toESM(require("fs"));
|
|
6997
|
+
var path3 = __toESM(require("path"));
|
|
6998
|
+
function resolveSessionDir(projectPath, sessionSlug, options) {
|
|
6999
|
+
if (!sessionSlug || sessionSlug.trim() === "") {
|
|
7000
|
+
return (0, import_types.Err)(new Error("Session slug must not be empty"));
|
|
7001
|
+
}
|
|
7002
|
+
if (sessionSlug.includes("..") || sessionSlug.includes("/") || sessionSlug.includes("\\")) {
|
|
7003
|
+
return (0, import_types.Err)(
|
|
7004
|
+
new Error(`Invalid session slug '${sessionSlug}': must not contain path traversal characters`)
|
|
7005
|
+
);
|
|
7006
|
+
}
|
|
7007
|
+
const sessionDir = path3.join(projectPath, HARNESS_DIR, SESSIONS_DIR, sessionSlug);
|
|
7008
|
+
if (options?.create) {
|
|
7009
|
+
fs6.mkdirSync(sessionDir, { recursive: true });
|
|
7010
|
+
}
|
|
7011
|
+
return (0, import_types.Ok)(sessionDir);
|
|
7012
|
+
}
|
|
7013
|
+
function updateSessionIndex(projectPath, sessionSlug, description) {
|
|
7014
|
+
const sessionsDir = path3.join(projectPath, HARNESS_DIR, SESSIONS_DIR);
|
|
7015
|
+
fs6.mkdirSync(sessionsDir, { recursive: true });
|
|
7016
|
+
const indexPath2 = path3.join(sessionsDir, SESSION_INDEX_FILE);
|
|
7017
|
+
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
7018
|
+
const newLine = `- [${sessionSlug}](${sessionSlug}/summary.md) \u2014 ${description} (${date})`;
|
|
7019
|
+
if (!fs6.existsSync(indexPath2)) {
|
|
7020
|
+
fs6.writeFileSync(indexPath2, `## Active Sessions
|
|
7021
|
+
|
|
7022
|
+
${newLine}
|
|
7023
|
+
`);
|
|
7024
|
+
return;
|
|
7025
|
+
}
|
|
7026
|
+
const content = fs6.readFileSync(indexPath2, "utf-8");
|
|
7027
|
+
const lines = content.split("\n");
|
|
7028
|
+
const slugPattern = `- [${sessionSlug}]`;
|
|
7029
|
+
const existingIdx = lines.findIndex((l) => l.startsWith(slugPattern));
|
|
7030
|
+
if (existingIdx >= 0) {
|
|
7031
|
+
lines[existingIdx] = newLine;
|
|
7032
|
+
} else {
|
|
7033
|
+
const lastNonEmpty = lines.reduce((last, line, i) => line.trim() !== "" ? i : last, 0);
|
|
7034
|
+
lines.splice(lastNonEmpty + 1, 0, newLine);
|
|
7035
|
+
}
|
|
7036
|
+
fs6.writeFileSync(indexPath2, lines.join("\n"));
|
|
7037
|
+
}
|
|
7038
|
+
|
|
7039
|
+
// src/state/state-shared.ts
|
|
6965
7040
|
var MAX_CACHE_ENTRIES = 8;
|
|
6966
|
-
var learningsCacheMap = /* @__PURE__ */ new Map();
|
|
6967
|
-
var failuresCacheMap = /* @__PURE__ */ new Map();
|
|
6968
7041
|
function evictIfNeeded(map) {
|
|
6969
7042
|
if (map.size > MAX_CACHE_ENTRIES) {
|
|
6970
7043
|
const oldest = map.keys().next().value;
|
|
6971
7044
|
if (oldest !== void 0) map.delete(oldest);
|
|
6972
7045
|
}
|
|
6973
7046
|
}
|
|
6974
|
-
async function getStateDir(projectPath, stream) {
|
|
6975
|
-
|
|
6976
|
-
|
|
7047
|
+
async function getStateDir(projectPath, stream, session) {
|
|
7048
|
+
if (session) {
|
|
7049
|
+
const sessionResult = resolveSessionDir(projectPath, session, { create: true });
|
|
7050
|
+
return sessionResult;
|
|
7051
|
+
}
|
|
7052
|
+
const streamsIndexPath = path4.join(projectPath, HARNESS_DIR, "streams", INDEX_FILE);
|
|
7053
|
+
const hasStreams = fs7.existsSync(streamsIndexPath);
|
|
6977
7054
|
if (stream || hasStreams) {
|
|
6978
7055
|
const result = await resolveStreamPath(projectPath, stream ? { stream } : void 0);
|
|
6979
7056
|
if (result.ok) {
|
|
@@ -6983,18 +7060,20 @@ async function getStateDir(projectPath, stream) {
|
|
|
6983
7060
|
return result;
|
|
6984
7061
|
}
|
|
6985
7062
|
}
|
|
6986
|
-
return (0, import_types.Ok)(
|
|
7063
|
+
return (0, import_types.Ok)(path4.join(projectPath, HARNESS_DIR));
|
|
6987
7064
|
}
|
|
6988
|
-
|
|
7065
|
+
|
|
7066
|
+
// src/state/state-persistence.ts
|
|
7067
|
+
async function loadState(projectPath, stream, session) {
|
|
6989
7068
|
try {
|
|
6990
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
7069
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
6991
7070
|
if (!dirResult.ok) return dirResult;
|
|
6992
7071
|
const stateDir = dirResult.value;
|
|
6993
|
-
const statePath =
|
|
6994
|
-
if (!
|
|
7072
|
+
const statePath = path5.join(stateDir, STATE_FILE);
|
|
7073
|
+
if (!fs8.existsSync(statePath)) {
|
|
6995
7074
|
return (0, import_types.Ok)({ ...DEFAULT_STATE });
|
|
6996
7075
|
}
|
|
6997
|
-
const raw =
|
|
7076
|
+
const raw = fs8.readFileSync(statePath, "utf-8");
|
|
6998
7077
|
const parsed = JSON.parse(raw);
|
|
6999
7078
|
const result = HarnessStateSchema.safeParse(parsed);
|
|
7000
7079
|
if (!result.success) {
|
|
@@ -7007,14 +7086,14 @@ async function loadState(projectPath, stream) {
|
|
|
7007
7086
|
);
|
|
7008
7087
|
}
|
|
7009
7088
|
}
|
|
7010
|
-
async function saveState(projectPath, state, stream) {
|
|
7089
|
+
async function saveState(projectPath, state, stream, session) {
|
|
7011
7090
|
try {
|
|
7012
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
7091
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
7013
7092
|
if (!dirResult.ok) return dirResult;
|
|
7014
7093
|
const stateDir = dirResult.value;
|
|
7015
|
-
const statePath =
|
|
7016
|
-
|
|
7017
|
-
|
|
7094
|
+
const statePath = path5.join(stateDir, STATE_FILE);
|
|
7095
|
+
fs8.mkdirSync(stateDir, { recursive: true });
|
|
7096
|
+
fs8.writeFileSync(statePath, JSON.stringify(state, null, 2));
|
|
7018
7097
|
return (0, import_types.Ok)(void 0);
|
|
7019
7098
|
} catch (error) {
|
|
7020
7099
|
return (0, import_types.Err)(
|
|
@@ -7022,13 +7101,21 @@ async function saveState(projectPath, state, stream) {
|
|
|
7022
7101
|
);
|
|
7023
7102
|
}
|
|
7024
7103
|
}
|
|
7025
|
-
|
|
7104
|
+
|
|
7105
|
+
// src/state/learnings.ts
|
|
7106
|
+
var fs9 = __toESM(require("fs"));
|
|
7107
|
+
var path6 = __toESM(require("path"));
|
|
7108
|
+
var learningsCacheMap = /* @__PURE__ */ new Map();
|
|
7109
|
+
function clearLearningsCache() {
|
|
7110
|
+
learningsCacheMap.clear();
|
|
7111
|
+
}
|
|
7112
|
+
async function appendLearning(projectPath, learning, skillName, outcome, stream, session) {
|
|
7026
7113
|
try {
|
|
7027
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
7114
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
7028
7115
|
if (!dirResult.ok) return dirResult;
|
|
7029
7116
|
const stateDir = dirResult.value;
|
|
7030
|
-
const learningsPath =
|
|
7031
|
-
|
|
7117
|
+
const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
|
|
7118
|
+
fs9.mkdirSync(stateDir, { recursive: true });
|
|
7032
7119
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
7033
7120
|
let entry;
|
|
7034
7121
|
if (skillName && outcome) {
|
|
@@ -7044,11 +7131,11 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream)
|
|
|
7044
7131
|
- **${timestamp}:** ${learning}
|
|
7045
7132
|
`;
|
|
7046
7133
|
}
|
|
7047
|
-
if (!
|
|
7048
|
-
|
|
7134
|
+
if (!fs9.existsSync(learningsPath)) {
|
|
7135
|
+
fs9.writeFileSync(learningsPath, `# Learnings
|
|
7049
7136
|
${entry}`);
|
|
7050
7137
|
} else {
|
|
7051
|
-
|
|
7138
|
+
fs9.appendFileSync(learningsPath, entry);
|
|
7052
7139
|
}
|
|
7053
7140
|
learningsCacheMap.delete(learningsPath);
|
|
7054
7141
|
return (0, import_types.Ok)(void 0);
|
|
@@ -7060,23 +7147,92 @@ ${entry}`);
|
|
|
7060
7147
|
);
|
|
7061
7148
|
}
|
|
7062
7149
|
}
|
|
7063
|
-
|
|
7150
|
+
function estimateTokens(text) {
|
|
7151
|
+
return Math.ceil(text.length / 4);
|
|
7152
|
+
}
|
|
7153
|
+
function scoreRelevance(entry, intent) {
|
|
7154
|
+
if (!intent || intent.trim() === "") return 0;
|
|
7155
|
+
const intentWords = intent.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
7156
|
+
if (intentWords.length === 0) return 0;
|
|
7157
|
+
const entryLower = entry.toLowerCase();
|
|
7158
|
+
const matches = intentWords.filter((word) => entryLower.includes(word));
|
|
7159
|
+
return matches.length / intentWords.length;
|
|
7160
|
+
}
|
|
7161
|
+
function parseDateFromEntry(entry) {
|
|
7162
|
+
const match = entry.match(/(\d{4}-\d{2}-\d{2})/);
|
|
7163
|
+
return match ? match[1] ?? null : null;
|
|
7164
|
+
}
|
|
7165
|
+
function analyzeLearningPatterns(entries) {
|
|
7166
|
+
const tagGroups = /* @__PURE__ */ new Map();
|
|
7167
|
+
for (const entry of entries) {
|
|
7168
|
+
const tagMatches = entry.matchAll(/\[(skill:[^\]]+)\]|\[(outcome:[^\]]+)\]/g);
|
|
7169
|
+
for (const match of tagMatches) {
|
|
7170
|
+
const tag = match[1] ?? match[2];
|
|
7171
|
+
if (tag) {
|
|
7172
|
+
const group = tagGroups.get(tag) ?? [];
|
|
7173
|
+
group.push(entry);
|
|
7174
|
+
tagGroups.set(tag, group);
|
|
7175
|
+
}
|
|
7176
|
+
}
|
|
7177
|
+
}
|
|
7178
|
+
const patterns = [];
|
|
7179
|
+
for (const [tag, groupEntries] of tagGroups) {
|
|
7180
|
+
if (groupEntries.length >= 3) {
|
|
7181
|
+
patterns.push({ tag, count: groupEntries.length, entries: groupEntries });
|
|
7182
|
+
}
|
|
7183
|
+
}
|
|
7184
|
+
return patterns.sort((a, b) => b.count - a.count);
|
|
7185
|
+
}
|
|
7186
|
+
async function loadBudgetedLearnings(projectPath, options) {
|
|
7187
|
+
const { intent, tokenBudget = 1e3, skill, session, stream } = options;
|
|
7188
|
+
const sortByRecencyAndRelevance = (entries) => {
|
|
7189
|
+
return [...entries].sort((a, b) => {
|
|
7190
|
+
const dateA = parseDateFromEntry(a) ?? "0000-00-00";
|
|
7191
|
+
const dateB = parseDateFromEntry(b) ?? "0000-00-00";
|
|
7192
|
+
const dateCompare = dateB.localeCompare(dateA);
|
|
7193
|
+
if (dateCompare !== 0) return dateCompare;
|
|
7194
|
+
return scoreRelevance(b, intent) - scoreRelevance(a, intent);
|
|
7195
|
+
});
|
|
7196
|
+
};
|
|
7197
|
+
const allEntries = [];
|
|
7198
|
+
if (session) {
|
|
7199
|
+
const sessionResult = await loadRelevantLearnings(projectPath, skill, stream, session);
|
|
7200
|
+
if (sessionResult.ok) {
|
|
7201
|
+
allEntries.push(...sortByRecencyAndRelevance(sessionResult.value));
|
|
7202
|
+
}
|
|
7203
|
+
}
|
|
7204
|
+
const globalResult = await loadRelevantLearnings(projectPath, skill, stream);
|
|
7205
|
+
if (globalResult.ok) {
|
|
7206
|
+
allEntries.push(...sortByRecencyAndRelevance(globalResult.value));
|
|
7207
|
+
}
|
|
7208
|
+
const budgeted = [];
|
|
7209
|
+
let totalTokens = 0;
|
|
7210
|
+
for (const entry of allEntries) {
|
|
7211
|
+
const separator = budgeted.length > 0 ? "\n" : "";
|
|
7212
|
+
const entryCost = estimateTokens(entry + separator);
|
|
7213
|
+
if (totalTokens + entryCost > tokenBudget) break;
|
|
7214
|
+
budgeted.push(entry);
|
|
7215
|
+
totalTokens += entryCost;
|
|
7216
|
+
}
|
|
7217
|
+
return (0, import_types.Ok)(budgeted);
|
|
7218
|
+
}
|
|
7219
|
+
async function loadRelevantLearnings(projectPath, skillName, stream, session) {
|
|
7064
7220
|
try {
|
|
7065
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
7221
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
7066
7222
|
if (!dirResult.ok) return dirResult;
|
|
7067
7223
|
const stateDir = dirResult.value;
|
|
7068
|
-
const learningsPath =
|
|
7069
|
-
if (!
|
|
7224
|
+
const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
|
|
7225
|
+
if (!fs9.existsSync(learningsPath)) {
|
|
7070
7226
|
return (0, import_types.Ok)([]);
|
|
7071
7227
|
}
|
|
7072
|
-
const stats =
|
|
7228
|
+
const stats = fs9.statSync(learningsPath);
|
|
7073
7229
|
const cacheKey = learningsPath;
|
|
7074
7230
|
const cached = learningsCacheMap.get(cacheKey);
|
|
7075
7231
|
let entries;
|
|
7076
7232
|
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
7077
7233
|
entries = cached.entries;
|
|
7078
7234
|
} else {
|
|
7079
|
-
const content =
|
|
7235
|
+
const content = fs9.readFileSync(learningsPath, "utf-8");
|
|
7080
7236
|
const lines = content.split("\n");
|
|
7081
7237
|
entries = [];
|
|
7082
7238
|
let currentBlock = [];
|
|
@@ -7112,23 +7268,110 @@ async function loadRelevantLearnings(projectPath, skillName, stream) {
|
|
|
7112
7268
|
);
|
|
7113
7269
|
}
|
|
7114
7270
|
}
|
|
7115
|
-
|
|
7116
|
-
async function appendFailure(projectPath, description, skillName, type, stream) {
|
|
7271
|
+
async function archiveLearnings(projectPath, entries, stream) {
|
|
7117
7272
|
try {
|
|
7118
7273
|
const dirResult = await getStateDir(projectPath, stream);
|
|
7119
7274
|
if (!dirResult.ok) return dirResult;
|
|
7120
7275
|
const stateDir = dirResult.value;
|
|
7121
|
-
const
|
|
7122
|
-
|
|
7276
|
+
const archiveDir = path6.join(stateDir, "learnings-archive");
|
|
7277
|
+
fs9.mkdirSync(archiveDir, { recursive: true });
|
|
7278
|
+
const now = /* @__PURE__ */ new Date();
|
|
7279
|
+
const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
|
|
7280
|
+
const archivePath = path6.join(archiveDir, `${yearMonth}.md`);
|
|
7281
|
+
const archiveContent = entries.join("\n\n") + "\n";
|
|
7282
|
+
if (fs9.existsSync(archivePath)) {
|
|
7283
|
+
fs9.appendFileSync(archivePath, "\n" + archiveContent);
|
|
7284
|
+
} else {
|
|
7285
|
+
fs9.writeFileSync(archivePath, `# Learnings Archive
|
|
7286
|
+
|
|
7287
|
+
${archiveContent}`);
|
|
7288
|
+
}
|
|
7289
|
+
return (0, import_types.Ok)(void 0);
|
|
7290
|
+
} catch (error) {
|
|
7291
|
+
return (0, import_types.Err)(
|
|
7292
|
+
new Error(
|
|
7293
|
+
`Failed to archive learnings: ${error instanceof Error ? error.message : String(error)}`
|
|
7294
|
+
)
|
|
7295
|
+
);
|
|
7296
|
+
}
|
|
7297
|
+
}
|
|
7298
|
+
async function pruneLearnings(projectPath, stream) {
|
|
7299
|
+
try {
|
|
7300
|
+
const dirResult = await getStateDir(projectPath, stream);
|
|
7301
|
+
if (!dirResult.ok) return dirResult;
|
|
7302
|
+
const stateDir = dirResult.value;
|
|
7303
|
+
const learningsPath = path6.join(stateDir, LEARNINGS_FILE);
|
|
7304
|
+
if (!fs9.existsSync(learningsPath)) {
|
|
7305
|
+
return (0, import_types.Ok)({ kept: 0, archived: 0, patterns: [] });
|
|
7306
|
+
}
|
|
7307
|
+
const loadResult = await loadRelevantLearnings(projectPath, void 0, stream);
|
|
7308
|
+
if (!loadResult.ok) return loadResult;
|
|
7309
|
+
const allEntries = loadResult.value;
|
|
7310
|
+
if (allEntries.length <= 20) {
|
|
7311
|
+
const cutoffDate = /* @__PURE__ */ new Date();
|
|
7312
|
+
cutoffDate.setDate(cutoffDate.getDate() - 14);
|
|
7313
|
+
const cutoffStr = cutoffDate.toISOString().split("T")[0];
|
|
7314
|
+
const hasOld = allEntries.some((entry) => {
|
|
7315
|
+
const date = parseDateFromEntry(entry);
|
|
7316
|
+
return date !== null && date < cutoffStr;
|
|
7317
|
+
});
|
|
7318
|
+
if (!hasOld) {
|
|
7319
|
+
return (0, import_types.Ok)({ kept: allEntries.length, archived: 0, patterns: [] });
|
|
7320
|
+
}
|
|
7321
|
+
}
|
|
7322
|
+
const sorted = [...allEntries].sort((a, b) => {
|
|
7323
|
+
const dateA = parseDateFromEntry(a) ?? "0000-00-00";
|
|
7324
|
+
const dateB = parseDateFromEntry(b) ?? "0000-00-00";
|
|
7325
|
+
return dateB.localeCompare(dateA);
|
|
7326
|
+
});
|
|
7327
|
+
const toKeep = sorted.slice(0, 20);
|
|
7328
|
+
const toArchive = sorted.slice(20);
|
|
7329
|
+
const patterns = analyzeLearningPatterns(allEntries);
|
|
7330
|
+
if (toArchive.length > 0) {
|
|
7331
|
+
const archiveResult = await archiveLearnings(projectPath, toArchive, stream);
|
|
7332
|
+
if (!archiveResult.ok) return archiveResult;
|
|
7333
|
+
}
|
|
7334
|
+
const newContent = "# Learnings\n\n" + toKeep.join("\n\n") + "\n";
|
|
7335
|
+
fs9.writeFileSync(learningsPath, newContent);
|
|
7336
|
+
learningsCacheMap.delete(learningsPath);
|
|
7337
|
+
return (0, import_types.Ok)({
|
|
7338
|
+
kept: toKeep.length,
|
|
7339
|
+
archived: toArchive.length,
|
|
7340
|
+
patterns
|
|
7341
|
+
});
|
|
7342
|
+
} catch (error) {
|
|
7343
|
+
return (0, import_types.Err)(
|
|
7344
|
+
new Error(
|
|
7345
|
+
`Failed to prune learnings: ${error instanceof Error ? error.message : String(error)}`
|
|
7346
|
+
)
|
|
7347
|
+
);
|
|
7348
|
+
}
|
|
7349
|
+
}
|
|
7350
|
+
|
|
7351
|
+
// src/state/failures.ts
|
|
7352
|
+
var fs10 = __toESM(require("fs"));
|
|
7353
|
+
var path7 = __toESM(require("path"));
|
|
7354
|
+
var failuresCacheMap = /* @__PURE__ */ new Map();
|
|
7355
|
+
function clearFailuresCache() {
|
|
7356
|
+
failuresCacheMap.clear();
|
|
7357
|
+
}
|
|
7358
|
+
var FAILURE_LINE_REGEX = /^- \*\*(\d{4}-\d{2}-\d{2}) \[skill:([^\]]+)\] \[type:([^\]]+)\]:\*\* (.+)$/;
|
|
7359
|
+
async function appendFailure(projectPath, description, skillName, type, stream, session) {
|
|
7360
|
+
try {
|
|
7361
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
7362
|
+
if (!dirResult.ok) return dirResult;
|
|
7363
|
+
const stateDir = dirResult.value;
|
|
7364
|
+
const failuresPath = path7.join(stateDir, FAILURES_FILE);
|
|
7365
|
+
fs10.mkdirSync(stateDir, { recursive: true });
|
|
7123
7366
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
7124
7367
|
const entry = `
|
|
7125
7368
|
- **${timestamp} [skill:${skillName}] [type:${type}]:** ${description}
|
|
7126
7369
|
`;
|
|
7127
|
-
if (!
|
|
7128
|
-
|
|
7370
|
+
if (!fs10.existsSync(failuresPath)) {
|
|
7371
|
+
fs10.writeFileSync(failuresPath, `# Failures
|
|
7129
7372
|
${entry}`);
|
|
7130
7373
|
} else {
|
|
7131
|
-
|
|
7374
|
+
fs10.appendFileSync(failuresPath, entry);
|
|
7132
7375
|
}
|
|
7133
7376
|
failuresCacheMap.delete(failuresPath);
|
|
7134
7377
|
return (0, import_types.Ok)(void 0);
|
|
@@ -7140,22 +7383,22 @@ ${entry}`);
|
|
|
7140
7383
|
);
|
|
7141
7384
|
}
|
|
7142
7385
|
}
|
|
7143
|
-
async function loadFailures(projectPath, stream) {
|
|
7386
|
+
async function loadFailures(projectPath, stream, session) {
|
|
7144
7387
|
try {
|
|
7145
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
7388
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
7146
7389
|
if (!dirResult.ok) return dirResult;
|
|
7147
7390
|
const stateDir = dirResult.value;
|
|
7148
|
-
const failuresPath =
|
|
7149
|
-
if (!
|
|
7391
|
+
const failuresPath = path7.join(stateDir, FAILURES_FILE);
|
|
7392
|
+
if (!fs10.existsSync(failuresPath)) {
|
|
7150
7393
|
return (0, import_types.Ok)([]);
|
|
7151
7394
|
}
|
|
7152
|
-
const stats =
|
|
7395
|
+
const stats = fs10.statSync(failuresPath);
|
|
7153
7396
|
const cacheKey = failuresPath;
|
|
7154
7397
|
const cached = failuresCacheMap.get(cacheKey);
|
|
7155
7398
|
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
7156
7399
|
return (0, import_types.Ok)(cached.entries);
|
|
7157
7400
|
}
|
|
7158
|
-
const content =
|
|
7401
|
+
const content = fs10.readFileSync(failuresPath, "utf-8");
|
|
7159
7402
|
const entries = [];
|
|
7160
7403
|
for (const line of content.split("\n")) {
|
|
7161
7404
|
const match = line.match(FAILURE_LINE_REGEX);
|
|
@@ -7179,25 +7422,25 @@ async function loadFailures(projectPath, stream) {
|
|
|
7179
7422
|
);
|
|
7180
7423
|
}
|
|
7181
7424
|
}
|
|
7182
|
-
async function archiveFailures(projectPath, stream) {
|
|
7425
|
+
async function archiveFailures(projectPath, stream, session) {
|
|
7183
7426
|
try {
|
|
7184
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
7427
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
7185
7428
|
if (!dirResult.ok) return dirResult;
|
|
7186
7429
|
const stateDir = dirResult.value;
|
|
7187
|
-
const failuresPath =
|
|
7188
|
-
if (!
|
|
7430
|
+
const failuresPath = path7.join(stateDir, FAILURES_FILE);
|
|
7431
|
+
if (!fs10.existsSync(failuresPath)) {
|
|
7189
7432
|
return (0, import_types.Ok)(void 0);
|
|
7190
7433
|
}
|
|
7191
|
-
const archiveDir =
|
|
7192
|
-
|
|
7434
|
+
const archiveDir = path7.join(stateDir, "archive");
|
|
7435
|
+
fs10.mkdirSync(archiveDir, { recursive: true });
|
|
7193
7436
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
7194
7437
|
let archiveName = `failures-${date}.md`;
|
|
7195
7438
|
let counter = 2;
|
|
7196
|
-
while (
|
|
7439
|
+
while (fs10.existsSync(path7.join(archiveDir, archiveName))) {
|
|
7197
7440
|
archiveName = `failures-${date}-${counter}.md`;
|
|
7198
7441
|
counter++;
|
|
7199
7442
|
}
|
|
7200
|
-
|
|
7443
|
+
fs10.renameSync(failuresPath, path7.join(archiveDir, archiveName));
|
|
7201
7444
|
failuresCacheMap.delete(failuresPath);
|
|
7202
7445
|
return (0, import_types.Ok)(void 0);
|
|
7203
7446
|
} catch (error) {
|
|
@@ -7208,14 +7451,18 @@ async function archiveFailures(projectPath, stream) {
|
|
|
7208
7451
|
);
|
|
7209
7452
|
}
|
|
7210
7453
|
}
|
|
7211
|
-
|
|
7454
|
+
|
|
7455
|
+
// src/state/handoff.ts
|
|
7456
|
+
var fs11 = __toESM(require("fs"));
|
|
7457
|
+
var path8 = __toESM(require("path"));
|
|
7458
|
+
async function saveHandoff(projectPath, handoff, stream, session) {
|
|
7212
7459
|
try {
|
|
7213
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
7460
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
7214
7461
|
if (!dirResult.ok) return dirResult;
|
|
7215
7462
|
const stateDir = dirResult.value;
|
|
7216
|
-
const handoffPath =
|
|
7217
|
-
|
|
7218
|
-
|
|
7463
|
+
const handoffPath = path8.join(stateDir, HANDOFF_FILE);
|
|
7464
|
+
fs11.mkdirSync(stateDir, { recursive: true });
|
|
7465
|
+
fs11.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
|
|
7219
7466
|
return (0, import_types.Ok)(void 0);
|
|
7220
7467
|
} catch (error) {
|
|
7221
7468
|
return (0, import_types.Err)(
|
|
@@ -7223,16 +7470,16 @@ async function saveHandoff(projectPath, handoff, stream) {
|
|
|
7223
7470
|
);
|
|
7224
7471
|
}
|
|
7225
7472
|
}
|
|
7226
|
-
async function loadHandoff(projectPath, stream) {
|
|
7473
|
+
async function loadHandoff(projectPath, stream, session) {
|
|
7227
7474
|
try {
|
|
7228
|
-
const dirResult = await getStateDir(projectPath, stream);
|
|
7475
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
7229
7476
|
if (!dirResult.ok) return dirResult;
|
|
7230
7477
|
const stateDir = dirResult.value;
|
|
7231
|
-
const handoffPath =
|
|
7232
|
-
if (!
|
|
7478
|
+
const handoffPath = path8.join(stateDir, HANDOFF_FILE);
|
|
7479
|
+
if (!fs11.existsSync(handoffPath)) {
|
|
7233
7480
|
return (0, import_types.Ok)(null);
|
|
7234
7481
|
}
|
|
7235
|
-
const raw =
|
|
7482
|
+
const raw = fs11.readFileSync(handoffPath, "utf-8");
|
|
7236
7483
|
const parsed = JSON.parse(raw);
|
|
7237
7484
|
const result = HandoffSchema.safeParse(parsed);
|
|
7238
7485
|
if (!result.success) {
|
|
@@ -7245,73 +7492,82 @@ async function loadHandoff(projectPath, stream) {
|
|
|
7245
7492
|
);
|
|
7246
7493
|
}
|
|
7247
7494
|
}
|
|
7495
|
+
|
|
7496
|
+
// src/state/mechanical-gate.ts
|
|
7497
|
+
var fs12 = __toESM(require("fs"));
|
|
7498
|
+
var path9 = __toESM(require("path"));
|
|
7499
|
+
var import_child_process2 = require("child_process");
|
|
7500
|
+
var 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:.-]+$/;
|
|
7501
|
+
function loadChecksFromConfig(gateConfigPath) {
|
|
7502
|
+
if (!fs12.existsSync(gateConfigPath)) return [];
|
|
7503
|
+
const raw = JSON.parse(fs12.readFileSync(gateConfigPath, "utf-8"));
|
|
7504
|
+
const config = GateConfigSchema.safeParse(raw);
|
|
7505
|
+
if (config.success && config.data.checks) return config.data.checks;
|
|
7506
|
+
return [];
|
|
7507
|
+
}
|
|
7508
|
+
function discoverChecksFromProject(projectPath) {
|
|
7509
|
+
const checks = [];
|
|
7510
|
+
const packageJsonPath = path9.join(projectPath, "package.json");
|
|
7511
|
+
if (fs12.existsSync(packageJsonPath)) {
|
|
7512
|
+
const pkg = JSON.parse(fs12.readFileSync(packageJsonPath, "utf-8"));
|
|
7513
|
+
const scripts = pkg.scripts || {};
|
|
7514
|
+
if (scripts.test) checks.push({ name: "test", command: "npm test" });
|
|
7515
|
+
if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
|
|
7516
|
+
if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
|
|
7517
|
+
if (scripts.build) checks.push({ name: "build", command: "npm run build" });
|
|
7518
|
+
}
|
|
7519
|
+
if (fs12.existsSync(path9.join(projectPath, "go.mod"))) {
|
|
7520
|
+
checks.push({ name: "test", command: "go test ./..." });
|
|
7521
|
+
checks.push({ name: "build", command: "go build ./..." });
|
|
7522
|
+
}
|
|
7523
|
+
if (fs12.existsSync(path9.join(projectPath, "pyproject.toml")) || fs12.existsSync(path9.join(projectPath, "setup.py"))) {
|
|
7524
|
+
checks.push({ name: "test", command: "python -m pytest" });
|
|
7525
|
+
}
|
|
7526
|
+
return checks;
|
|
7527
|
+
}
|
|
7528
|
+
function executeCheck(check, projectPath) {
|
|
7529
|
+
if (!SAFE_GATE_COMMAND.test(check.command)) {
|
|
7530
|
+
return {
|
|
7531
|
+
name: check.name,
|
|
7532
|
+
passed: false,
|
|
7533
|
+
command: check.command,
|
|
7534
|
+
output: `Blocked: command does not match safe gate pattern. Allowed prefixes: npm, pnpm, yarn, go, python, python3, make, cargo, gradle, mvn`,
|
|
7535
|
+
duration: 0
|
|
7536
|
+
};
|
|
7537
|
+
}
|
|
7538
|
+
const start = Date.now();
|
|
7539
|
+
try {
|
|
7540
|
+
(0, import_child_process2.execSync)(check.command, {
|
|
7541
|
+
cwd: projectPath,
|
|
7542
|
+
stdio: "pipe",
|
|
7543
|
+
timeout: 12e4
|
|
7544
|
+
});
|
|
7545
|
+
return {
|
|
7546
|
+
name: check.name,
|
|
7547
|
+
passed: true,
|
|
7548
|
+
command: check.command,
|
|
7549
|
+
duration: Date.now() - start
|
|
7550
|
+
};
|
|
7551
|
+
} catch (error) {
|
|
7552
|
+
const output = error instanceof Error ? error.stderr?.toString() || error.message : String(error);
|
|
7553
|
+
return {
|
|
7554
|
+
name: check.name,
|
|
7555
|
+
passed: false,
|
|
7556
|
+
command: check.command,
|
|
7557
|
+
output: output.slice(0, 2e3),
|
|
7558
|
+
duration: Date.now() - start
|
|
7559
|
+
};
|
|
7560
|
+
}
|
|
7561
|
+
}
|
|
7248
7562
|
async function runMechanicalGate(projectPath) {
|
|
7249
|
-
const harnessDir =
|
|
7250
|
-
const gateConfigPath =
|
|
7563
|
+
const harnessDir = path9.join(projectPath, HARNESS_DIR);
|
|
7564
|
+
const gateConfigPath = path9.join(harnessDir, GATE_CONFIG_FILE);
|
|
7251
7565
|
try {
|
|
7252
|
-
let checks =
|
|
7253
|
-
if (fs6.existsSync(gateConfigPath)) {
|
|
7254
|
-
const raw = JSON.parse(fs6.readFileSync(gateConfigPath, "utf-8"));
|
|
7255
|
-
const config = GateConfigSchema.safeParse(raw);
|
|
7256
|
-
if (config.success && config.data.checks) {
|
|
7257
|
-
checks = config.data.checks;
|
|
7258
|
-
}
|
|
7259
|
-
}
|
|
7566
|
+
let checks = loadChecksFromConfig(gateConfigPath);
|
|
7260
7567
|
if (checks.length === 0) {
|
|
7261
|
-
|
|
7262
|
-
if (fs6.existsSync(packageJsonPath)) {
|
|
7263
|
-
const pkg = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
|
|
7264
|
-
const scripts = pkg.scripts || {};
|
|
7265
|
-
if (scripts.test) checks.push({ name: "test", command: "npm test" });
|
|
7266
|
-
if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
|
|
7267
|
-
if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
|
|
7268
|
-
if (scripts.build) checks.push({ name: "build", command: "npm run build" });
|
|
7269
|
-
}
|
|
7270
|
-
if (fs6.existsSync(path3.join(projectPath, "go.mod"))) {
|
|
7271
|
-
checks.push({ name: "test", command: "go test ./..." });
|
|
7272
|
-
checks.push({ name: "build", command: "go build ./..." });
|
|
7273
|
-
}
|
|
7274
|
-
if (fs6.existsSync(path3.join(projectPath, "pyproject.toml")) || fs6.existsSync(path3.join(projectPath, "setup.py"))) {
|
|
7275
|
-
checks.push({ name: "test", command: "python -m pytest" });
|
|
7276
|
-
}
|
|
7277
|
-
}
|
|
7278
|
-
const results = [];
|
|
7279
|
-
const 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:.-]+$/;
|
|
7280
|
-
for (const check of checks) {
|
|
7281
|
-
if (!SAFE_GATE_COMMAND.test(check.command)) {
|
|
7282
|
-
results.push({
|
|
7283
|
-
name: check.name,
|
|
7284
|
-
passed: false,
|
|
7285
|
-
command: check.command,
|
|
7286
|
-
output: `Blocked: command does not match safe gate pattern. Allowed prefixes: npm, npx, pnpm, yarn, go, python, python3, make, cargo, gradle, mvn`,
|
|
7287
|
-
duration: 0
|
|
7288
|
-
});
|
|
7289
|
-
continue;
|
|
7290
|
-
}
|
|
7291
|
-
const start = Date.now();
|
|
7292
|
-
try {
|
|
7293
|
-
(0, import_child_process2.execSync)(check.command, {
|
|
7294
|
-
cwd: projectPath,
|
|
7295
|
-
stdio: "pipe",
|
|
7296
|
-
timeout: 12e4
|
|
7297
|
-
});
|
|
7298
|
-
results.push({
|
|
7299
|
-
name: check.name,
|
|
7300
|
-
passed: true,
|
|
7301
|
-
command: check.command,
|
|
7302
|
-
duration: Date.now() - start
|
|
7303
|
-
});
|
|
7304
|
-
} catch (error) {
|
|
7305
|
-
const output = error instanceof Error ? error.stderr?.toString() || error.message : String(error);
|
|
7306
|
-
results.push({
|
|
7307
|
-
name: check.name,
|
|
7308
|
-
passed: false,
|
|
7309
|
-
command: check.command,
|
|
7310
|
-
output: output.slice(0, 2e3),
|
|
7311
|
-
duration: Date.now() - start
|
|
7312
|
-
});
|
|
7313
|
-
}
|
|
7568
|
+
checks = discoverChecksFromProject(projectPath);
|
|
7314
7569
|
}
|
|
7570
|
+
const results = checks.map((check) => executeCheck(check, projectPath));
|
|
7315
7571
|
return (0, import_types.Ok)({
|
|
7316
7572
|
passed: results.length === 0 || results.every((r) => r.passed),
|
|
7317
7573
|
checks: results
|
|
@@ -7325,6 +7581,96 @@ async function runMechanicalGate(projectPath) {
|
|
|
7325
7581
|
}
|
|
7326
7582
|
}
|
|
7327
7583
|
|
|
7584
|
+
// src/state/session-summary.ts
|
|
7585
|
+
var fs13 = __toESM(require("fs"));
|
|
7586
|
+
var path10 = __toESM(require("path"));
|
|
7587
|
+
function formatSummary(data) {
|
|
7588
|
+
const lines = [
|
|
7589
|
+
"## Session Summary",
|
|
7590
|
+
"",
|
|
7591
|
+
`**Session:** ${data.session}`,
|
|
7592
|
+
`**Last active:** ${data.lastActive}`,
|
|
7593
|
+
`**Skill:** ${data.skill}`
|
|
7594
|
+
];
|
|
7595
|
+
if (data.phase) {
|
|
7596
|
+
lines.push(`**Phase:** ${data.phase}`);
|
|
7597
|
+
}
|
|
7598
|
+
lines.push(`**Status:** ${data.status}`);
|
|
7599
|
+
if (data.spec) {
|
|
7600
|
+
lines.push(`**Spec:** ${data.spec}`);
|
|
7601
|
+
}
|
|
7602
|
+
if (data.plan) {
|
|
7603
|
+
lines.push(`**Plan:** ${data.plan}`);
|
|
7604
|
+
}
|
|
7605
|
+
lines.push(`**Key context:** ${data.keyContext}`);
|
|
7606
|
+
lines.push(`**Next step:** ${data.nextStep}`);
|
|
7607
|
+
lines.push("");
|
|
7608
|
+
return lines.join("\n");
|
|
7609
|
+
}
|
|
7610
|
+
function deriveIndexDescription(data) {
|
|
7611
|
+
const skillShort = data.skill.replace("harness-", "");
|
|
7612
|
+
const parts = [skillShort];
|
|
7613
|
+
if (data.phase) {
|
|
7614
|
+
parts.push(`phase ${data.phase}`);
|
|
7615
|
+
}
|
|
7616
|
+
parts.push(data.status.toLowerCase());
|
|
7617
|
+
return parts.join(", ");
|
|
7618
|
+
}
|
|
7619
|
+
function writeSessionSummary(projectPath, sessionSlug, data) {
|
|
7620
|
+
try {
|
|
7621
|
+
const dirResult = resolveSessionDir(projectPath, sessionSlug, { create: true });
|
|
7622
|
+
if (!dirResult.ok) return dirResult;
|
|
7623
|
+
const sessionDir = dirResult.value;
|
|
7624
|
+
const summaryPath = path10.join(sessionDir, SUMMARY_FILE);
|
|
7625
|
+
const content = formatSummary(data);
|
|
7626
|
+
fs13.writeFileSync(summaryPath, content);
|
|
7627
|
+
const description = deriveIndexDescription(data);
|
|
7628
|
+
updateSessionIndex(projectPath, sessionSlug, description);
|
|
7629
|
+
return (0, import_types.Ok)(void 0);
|
|
7630
|
+
} catch (error) {
|
|
7631
|
+
return (0, import_types.Err)(
|
|
7632
|
+
new Error(
|
|
7633
|
+
`Failed to write session summary: ${error instanceof Error ? error.message : String(error)}`
|
|
7634
|
+
)
|
|
7635
|
+
);
|
|
7636
|
+
}
|
|
7637
|
+
}
|
|
7638
|
+
function loadSessionSummary(projectPath, sessionSlug) {
|
|
7639
|
+
try {
|
|
7640
|
+
const dirResult = resolveSessionDir(projectPath, sessionSlug);
|
|
7641
|
+
if (!dirResult.ok) return dirResult;
|
|
7642
|
+
const sessionDir = dirResult.value;
|
|
7643
|
+
const summaryPath = path10.join(sessionDir, SUMMARY_FILE);
|
|
7644
|
+
if (!fs13.existsSync(summaryPath)) {
|
|
7645
|
+
return (0, import_types.Ok)(null);
|
|
7646
|
+
}
|
|
7647
|
+
const content = fs13.readFileSync(summaryPath, "utf-8");
|
|
7648
|
+
return (0, import_types.Ok)(content);
|
|
7649
|
+
} catch (error) {
|
|
7650
|
+
return (0, import_types.Err)(
|
|
7651
|
+
new Error(
|
|
7652
|
+
`Failed to load session summary: ${error instanceof Error ? error.message : String(error)}`
|
|
7653
|
+
)
|
|
7654
|
+
);
|
|
7655
|
+
}
|
|
7656
|
+
}
|
|
7657
|
+
function listActiveSessions(projectPath) {
|
|
7658
|
+
try {
|
|
7659
|
+
const indexPath2 = path10.join(projectPath, HARNESS_DIR, SESSIONS_DIR, SESSION_INDEX_FILE);
|
|
7660
|
+
if (!fs13.existsSync(indexPath2)) {
|
|
7661
|
+
return (0, import_types.Ok)(null);
|
|
7662
|
+
}
|
|
7663
|
+
const content = fs13.readFileSync(indexPath2, "utf-8");
|
|
7664
|
+
return (0, import_types.Ok)(content);
|
|
7665
|
+
} catch (error) {
|
|
7666
|
+
return (0, import_types.Err)(
|
|
7667
|
+
new Error(
|
|
7668
|
+
`Failed to list active sessions: ${error instanceof Error ? error.message : String(error)}`
|
|
7669
|
+
)
|
|
7670
|
+
);
|
|
7671
|
+
}
|
|
7672
|
+
}
|
|
7673
|
+
|
|
7328
7674
|
// src/workflow/runner.ts
|
|
7329
7675
|
async function executeWorkflow(workflow, executor) {
|
|
7330
7676
|
const stepResults = [];
|
|
@@ -7474,7 +7820,7 @@ async function runMultiTurnPipeline(initialContext, turnExecutor, options) {
|
|
|
7474
7820
|
}
|
|
7475
7821
|
|
|
7476
7822
|
// src/security/scanner.ts
|
|
7477
|
-
var
|
|
7823
|
+
var fs15 = __toESM(require("fs/promises"));
|
|
7478
7824
|
|
|
7479
7825
|
// src/security/rules/registry.ts
|
|
7480
7826
|
var RuleRegistry = class {
|
|
@@ -7561,15 +7907,15 @@ function resolveRuleSeverity(ruleId, defaultSeverity, overrides, strict) {
|
|
|
7561
7907
|
}
|
|
7562
7908
|
|
|
7563
7909
|
// src/security/stack-detector.ts
|
|
7564
|
-
var
|
|
7565
|
-
var
|
|
7910
|
+
var fs14 = __toESM(require("fs"));
|
|
7911
|
+
var path11 = __toESM(require("path"));
|
|
7566
7912
|
function detectStack(projectRoot) {
|
|
7567
7913
|
const stacks = [];
|
|
7568
|
-
const pkgJsonPath =
|
|
7569
|
-
if (
|
|
7914
|
+
const pkgJsonPath = path11.join(projectRoot, "package.json");
|
|
7915
|
+
if (fs14.existsSync(pkgJsonPath)) {
|
|
7570
7916
|
stacks.push("node");
|
|
7571
7917
|
try {
|
|
7572
|
-
const pkgJson = JSON.parse(
|
|
7918
|
+
const pkgJson = JSON.parse(fs14.readFileSync(pkgJsonPath, "utf-8"));
|
|
7573
7919
|
const allDeps = {
|
|
7574
7920
|
...pkgJson.dependencies,
|
|
7575
7921
|
...pkgJson.devDependencies
|
|
@@ -7584,13 +7930,13 @@ function detectStack(projectRoot) {
|
|
|
7584
7930
|
} catch {
|
|
7585
7931
|
}
|
|
7586
7932
|
}
|
|
7587
|
-
const goModPath =
|
|
7588
|
-
if (
|
|
7933
|
+
const goModPath = path11.join(projectRoot, "go.mod");
|
|
7934
|
+
if (fs14.existsSync(goModPath)) {
|
|
7589
7935
|
stacks.push("go");
|
|
7590
7936
|
}
|
|
7591
|
-
const requirementsPath =
|
|
7592
|
-
const pyprojectPath =
|
|
7593
|
-
if (
|
|
7937
|
+
const requirementsPath = path11.join(projectRoot, "requirements.txt");
|
|
7938
|
+
const pyprojectPath = path11.join(projectRoot, "pyproject.toml");
|
|
7939
|
+
if (fs14.existsSync(requirementsPath) || fs14.existsSync(pyprojectPath)) {
|
|
7594
7940
|
stacks.push("python");
|
|
7595
7941
|
}
|
|
7596
7942
|
return stacks;
|
|
@@ -8017,7 +8363,7 @@ var SecurityScanner = class {
|
|
|
8017
8363
|
}
|
|
8018
8364
|
async scanFile(filePath) {
|
|
8019
8365
|
if (!this.config.enabled) return [];
|
|
8020
|
-
const content = await
|
|
8366
|
+
const content = await fs15.readFile(filePath, "utf-8");
|
|
8021
8367
|
return this.scanContent(content, filePath, 1);
|
|
8022
8368
|
}
|
|
8023
8369
|
async scanFiles(filePaths) {
|
|
@@ -8042,7 +8388,7 @@ var SecurityScanner = class {
|
|
|
8042
8388
|
};
|
|
8043
8389
|
|
|
8044
8390
|
// src/ci/check-orchestrator.ts
|
|
8045
|
-
var
|
|
8391
|
+
var path12 = __toESM(require("path"));
|
|
8046
8392
|
var ALL_CHECKS = [
|
|
8047
8393
|
"validate",
|
|
8048
8394
|
"deps",
|
|
@@ -8059,7 +8405,7 @@ async function runSingleCheck(name, projectRoot, config) {
|
|
|
8059
8405
|
try {
|
|
8060
8406
|
switch (name) {
|
|
8061
8407
|
case "validate": {
|
|
8062
|
-
const agentsPath =
|
|
8408
|
+
const agentsPath = path12.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
8063
8409
|
const result = await validateAgentsMap(agentsPath);
|
|
8064
8410
|
if (!result.ok) {
|
|
8065
8411
|
issues.push({ severity: "error", message: result.error.message });
|
|
@@ -8114,7 +8460,7 @@ async function runSingleCheck(name, projectRoot, config) {
|
|
|
8114
8460
|
break;
|
|
8115
8461
|
}
|
|
8116
8462
|
case "docs": {
|
|
8117
|
-
const docsDir =
|
|
8463
|
+
const docsDir = path12.join(projectRoot, config.docsDir ?? "docs");
|
|
8118
8464
|
const entropyConfig = config.entropy || {};
|
|
8119
8465
|
const result = await checkDocCoverage("project", {
|
|
8120
8466
|
docsDir,
|
|
@@ -8352,7 +8698,7 @@ async function runCIChecks(input) {
|
|
|
8352
8698
|
}
|
|
8353
8699
|
|
|
8354
8700
|
// src/review/mechanical-checks.ts
|
|
8355
|
-
var
|
|
8701
|
+
var path13 = __toESM(require("path"));
|
|
8356
8702
|
async function runMechanicalChecks(options) {
|
|
8357
8703
|
const { projectRoot, config, skip = [], changedFiles } = options;
|
|
8358
8704
|
const findings = [];
|
|
@@ -8364,7 +8710,7 @@ async function runMechanicalChecks(options) {
|
|
|
8364
8710
|
};
|
|
8365
8711
|
if (!skip.includes("validate")) {
|
|
8366
8712
|
try {
|
|
8367
|
-
const agentsPath =
|
|
8713
|
+
const agentsPath = path13.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
8368
8714
|
const result = await validateAgentsMap(agentsPath);
|
|
8369
8715
|
if (!result.ok) {
|
|
8370
8716
|
statuses.validate = "fail";
|
|
@@ -8401,7 +8747,7 @@ async function runMechanicalChecks(options) {
|
|
|
8401
8747
|
statuses.validate = "fail";
|
|
8402
8748
|
findings.push({
|
|
8403
8749
|
tool: "validate",
|
|
8404
|
-
file:
|
|
8750
|
+
file: path13.join(projectRoot, "AGENTS.md"),
|
|
8405
8751
|
message: err instanceof Error ? err.message : String(err),
|
|
8406
8752
|
severity: "error"
|
|
8407
8753
|
});
|
|
@@ -8465,7 +8811,7 @@ async function runMechanicalChecks(options) {
|
|
|
8465
8811
|
(async () => {
|
|
8466
8812
|
const localFindings = [];
|
|
8467
8813
|
try {
|
|
8468
|
-
const docsDir =
|
|
8814
|
+
const docsDir = path13.join(projectRoot, config.docsDir ?? "docs");
|
|
8469
8815
|
const result = await checkDocCoverage("project", { docsDir });
|
|
8470
8816
|
if (!result.ok) {
|
|
8471
8817
|
statuses["check-docs"] = "warn";
|
|
@@ -8492,7 +8838,7 @@ async function runMechanicalChecks(options) {
|
|
|
8492
8838
|
statuses["check-docs"] = "warn";
|
|
8493
8839
|
localFindings.push({
|
|
8494
8840
|
tool: "check-docs",
|
|
8495
|
-
file:
|
|
8841
|
+
file: path13.join(projectRoot, "docs"),
|
|
8496
8842
|
message: err instanceof Error ? err.message : String(err),
|
|
8497
8843
|
severity: "warning"
|
|
8498
8844
|
});
|
|
@@ -8640,7 +8986,7 @@ function detectChangeType(commitMessage, diff2) {
|
|
|
8640
8986
|
}
|
|
8641
8987
|
|
|
8642
8988
|
// src/review/context-scoper.ts
|
|
8643
|
-
var
|
|
8989
|
+
var path14 = __toESM(require("path"));
|
|
8644
8990
|
var ALL_DOMAINS = ["compliance", "bug", "security", "architecture"];
|
|
8645
8991
|
var SECURITY_PATTERNS = /auth|crypto|password|secret|token|session|cookie|hash|encrypt|decrypt|sql|shell|exec|eval/i;
|
|
8646
8992
|
function computeContextBudget(diffLines) {
|
|
@@ -8648,18 +8994,18 @@ function computeContextBudget(diffLines) {
|
|
|
8648
8994
|
return diffLines;
|
|
8649
8995
|
}
|
|
8650
8996
|
function isWithinProject(absPath, projectRoot) {
|
|
8651
|
-
const resolvedRoot =
|
|
8652
|
-
const resolvedPath =
|
|
8653
|
-
return resolvedPath.startsWith(resolvedRoot) || resolvedPath ===
|
|
8997
|
+
const resolvedRoot = path14.resolve(projectRoot) + path14.sep;
|
|
8998
|
+
const resolvedPath = path14.resolve(absPath);
|
|
8999
|
+
return resolvedPath.startsWith(resolvedRoot) || resolvedPath === path14.resolve(projectRoot);
|
|
8654
9000
|
}
|
|
8655
9001
|
async function readContextFile(projectRoot, filePath, reason) {
|
|
8656
|
-
const absPath =
|
|
9002
|
+
const absPath = path14.isAbsolute(filePath) ? filePath : path14.join(projectRoot, filePath);
|
|
8657
9003
|
if (!isWithinProject(absPath, projectRoot)) return null;
|
|
8658
9004
|
const result = await readFileContent(absPath);
|
|
8659
9005
|
if (!result.ok) return null;
|
|
8660
9006
|
const content = result.value;
|
|
8661
9007
|
const lines = content.split("\n").length;
|
|
8662
|
-
const relPath =
|
|
9008
|
+
const relPath = path14.isAbsolute(filePath) ? path14.relative(projectRoot, filePath) : filePath;
|
|
8663
9009
|
return { path: relPath, content, reason, lines };
|
|
8664
9010
|
}
|
|
8665
9011
|
function extractImportSources2(content) {
|
|
@@ -8674,18 +9020,18 @@ function extractImportSources2(content) {
|
|
|
8674
9020
|
}
|
|
8675
9021
|
async function resolveImportPath2(projectRoot, fromFile, importSource) {
|
|
8676
9022
|
if (!importSource.startsWith(".")) return null;
|
|
8677
|
-
const fromDir =
|
|
8678
|
-
const basePath =
|
|
9023
|
+
const fromDir = path14.dirname(path14.join(projectRoot, fromFile));
|
|
9024
|
+
const basePath = path14.resolve(fromDir, importSource);
|
|
8679
9025
|
if (!isWithinProject(basePath, projectRoot)) return null;
|
|
8680
|
-
const relBase =
|
|
9026
|
+
const relBase = path14.relative(projectRoot, basePath);
|
|
8681
9027
|
const candidates = [
|
|
8682
9028
|
relBase + ".ts",
|
|
8683
9029
|
relBase + ".tsx",
|
|
8684
9030
|
relBase + ".mts",
|
|
8685
|
-
|
|
9031
|
+
path14.join(relBase, "index.ts")
|
|
8686
9032
|
];
|
|
8687
9033
|
for (const candidate of candidates) {
|
|
8688
|
-
const absCandidate =
|
|
9034
|
+
const absCandidate = path14.join(projectRoot, candidate);
|
|
8689
9035
|
if (await fileExists(absCandidate)) {
|
|
8690
9036
|
return candidate;
|
|
8691
9037
|
}
|
|
@@ -8693,10 +9039,10 @@ async function resolveImportPath2(projectRoot, fromFile, importSource) {
|
|
|
8693
9039
|
return null;
|
|
8694
9040
|
}
|
|
8695
9041
|
async function findTestFiles(projectRoot, sourceFile) {
|
|
8696
|
-
const baseName =
|
|
9042
|
+
const baseName = path14.basename(sourceFile, path14.extname(sourceFile));
|
|
8697
9043
|
const pattern = `**/${baseName}.{test,spec}.{ts,tsx,mts}`;
|
|
8698
9044
|
const results = await findFiles(pattern, projectRoot);
|
|
8699
|
-
return results.map((f) =>
|
|
9045
|
+
return results.map((f) => path14.relative(projectRoot, f));
|
|
8700
9046
|
}
|
|
8701
9047
|
async function gatherImportContext(projectRoot, changedFiles, budget) {
|
|
8702
9048
|
const contextFiles = [];
|
|
@@ -9484,7 +9830,7 @@ async function fanOutReview(options) {
|
|
|
9484
9830
|
}
|
|
9485
9831
|
|
|
9486
9832
|
// src/review/validate-findings.ts
|
|
9487
|
-
var
|
|
9833
|
+
var path15 = __toESM(require("path"));
|
|
9488
9834
|
var DOWNGRADE_MAP = {
|
|
9489
9835
|
critical: "important",
|
|
9490
9836
|
important: "suggestion",
|
|
@@ -9505,7 +9851,7 @@ function normalizePath(filePath, projectRoot) {
|
|
|
9505
9851
|
let normalized = filePath;
|
|
9506
9852
|
normalized = normalized.replace(/\\/g, "/");
|
|
9507
9853
|
const normalizedRoot = projectRoot.replace(/\\/g, "/");
|
|
9508
|
-
if (
|
|
9854
|
+
if (path15.isAbsolute(normalized)) {
|
|
9509
9855
|
const root = normalizedRoot.endsWith("/") ? normalizedRoot : normalizedRoot + "/";
|
|
9510
9856
|
if (normalized.startsWith(root)) {
|
|
9511
9857
|
normalized = normalized.slice(root.length);
|
|
@@ -9530,12 +9876,12 @@ function followImportChain(fromFile, fileContents, maxDepth = 2) {
|
|
|
9530
9876
|
while ((match = importRegex.exec(content)) !== null) {
|
|
9531
9877
|
const importPath = match[1];
|
|
9532
9878
|
if (!importPath.startsWith(".")) continue;
|
|
9533
|
-
const dir =
|
|
9534
|
-
let resolved =
|
|
9879
|
+
const dir = path15.dirname(current.file);
|
|
9880
|
+
let resolved = path15.join(dir, importPath).replace(/\\/g, "/");
|
|
9535
9881
|
if (!resolved.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
9536
9882
|
resolved += ".ts";
|
|
9537
9883
|
}
|
|
9538
|
-
resolved =
|
|
9884
|
+
resolved = path15.normalize(resolved).replace(/\\/g, "/");
|
|
9539
9885
|
if (!visited.has(resolved) && current.depth + 1 <= maxDepth) {
|
|
9540
9886
|
queue.push({ file: resolved, depth: current.depth + 1 });
|
|
9541
9887
|
}
|
|
@@ -9552,7 +9898,7 @@ async function validateFindings(options) {
|
|
|
9552
9898
|
if (exclusionSet.isExcluded(normalizedFile, finding.lineRange) || exclusionSet.isExcluded(finding.file, finding.lineRange)) {
|
|
9553
9899
|
continue;
|
|
9554
9900
|
}
|
|
9555
|
-
const absoluteFile =
|
|
9901
|
+
const absoluteFile = path15.isAbsolute(finding.file) ? finding.file : path15.join(projectRoot, finding.file).replace(/\\/g, "/");
|
|
9556
9902
|
if (exclusionSet.isExcluded(absoluteFile, finding.lineRange)) {
|
|
9557
9903
|
continue;
|
|
9558
9904
|
}
|
|
@@ -10035,7 +10381,7 @@ async function runReviewPipeline(options) {
|
|
|
10035
10381
|
}
|
|
10036
10382
|
|
|
10037
10383
|
// src/roadmap/parse.ts
|
|
10038
|
-
var
|
|
10384
|
+
var import_types18 = require("@harness-engineering/types");
|
|
10039
10385
|
var VALID_STATUSES = /* @__PURE__ */ new Set([
|
|
10040
10386
|
"backlog",
|
|
10041
10387
|
"planned",
|
|
@@ -10047,14 +10393,14 @@ var EM_DASH = "\u2014";
|
|
|
10047
10393
|
function parseRoadmap(markdown) {
|
|
10048
10394
|
const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---/);
|
|
10049
10395
|
if (!fmMatch) {
|
|
10050
|
-
return (0,
|
|
10396
|
+
return (0, import_types18.Err)(new Error("Missing or malformed YAML frontmatter"));
|
|
10051
10397
|
}
|
|
10052
10398
|
const fmResult = parseFrontmatter(fmMatch[1]);
|
|
10053
10399
|
if (!fmResult.ok) return fmResult;
|
|
10054
10400
|
const body = markdown.slice(fmMatch[0].length);
|
|
10055
10401
|
const milestonesResult = parseMilestones(body);
|
|
10056
10402
|
if (!milestonesResult.ok) return milestonesResult;
|
|
10057
|
-
return (0,
|
|
10403
|
+
return (0, import_types18.Ok)({
|
|
10058
10404
|
frontmatter: fmResult.value,
|
|
10059
10405
|
milestones: milestonesResult.value
|
|
10060
10406
|
});
|
|
@@ -10073,8 +10419,10 @@ function parseFrontmatter(raw) {
|
|
|
10073
10419
|
const versionStr = map.get("version");
|
|
10074
10420
|
const lastSynced = map.get("last_synced");
|
|
10075
10421
|
const lastManualEdit = map.get("last_manual_edit");
|
|
10422
|
+
const created = map.get("created");
|
|
10423
|
+
const updated = map.get("updated");
|
|
10076
10424
|
if (!project || !versionStr || !lastSynced || !lastManualEdit) {
|
|
10077
|
-
return (0,
|
|
10425
|
+
return (0, import_types18.Err)(
|
|
10078
10426
|
new Error(
|
|
10079
10427
|
"Frontmatter missing required fields: project, version, last_synced, last_manual_edit"
|
|
10080
10428
|
)
|
|
@@ -10082,9 +10430,12 @@ function parseFrontmatter(raw) {
|
|
|
10082
10430
|
}
|
|
10083
10431
|
const version = parseInt(versionStr, 10);
|
|
10084
10432
|
if (isNaN(version)) {
|
|
10085
|
-
return (0,
|
|
10433
|
+
return (0, import_types18.Err)(new Error("Frontmatter version must be a number"));
|
|
10086
10434
|
}
|
|
10087
|
-
|
|
10435
|
+
const fm = { project, version, lastSynced, lastManualEdit };
|
|
10436
|
+
if (created) fm.created = created;
|
|
10437
|
+
if (updated) fm.updated = updated;
|
|
10438
|
+
return (0, import_types18.Ok)(fm);
|
|
10088
10439
|
}
|
|
10089
10440
|
function parseMilestones(body) {
|
|
10090
10441
|
const milestones = [];
|
|
@@ -10092,12 +10443,12 @@ function parseMilestones(body) {
|
|
|
10092
10443
|
const h2Matches = [];
|
|
10093
10444
|
let match;
|
|
10094
10445
|
while ((match = h2Pattern.exec(body)) !== null) {
|
|
10095
|
-
h2Matches.push({ heading: match[1], startIndex: match.index });
|
|
10446
|
+
h2Matches.push({ heading: match[1], startIndex: match.index, fullMatch: match[0] });
|
|
10096
10447
|
}
|
|
10097
10448
|
for (let i = 0; i < h2Matches.length; i++) {
|
|
10098
10449
|
const h2 = h2Matches[i];
|
|
10099
10450
|
const nextStart = i + 1 < h2Matches.length ? h2Matches[i + 1].startIndex : body.length;
|
|
10100
|
-
const sectionBody = body.slice(h2.startIndex + h2.
|
|
10451
|
+
const sectionBody = body.slice(h2.startIndex + h2.fullMatch.length, nextStart);
|
|
10101
10452
|
const isBacklog = h2.heading === "Backlog";
|
|
10102
10453
|
const milestoneName = isBacklog ? "Backlog" : h2.heading.replace(/^Milestone:\s*/, "");
|
|
10103
10454
|
const featuresResult = parseFeatures(sectionBody);
|
|
@@ -10108,28 +10459,25 @@ function parseMilestones(body) {
|
|
|
10108
10459
|
features: featuresResult.value
|
|
10109
10460
|
});
|
|
10110
10461
|
}
|
|
10111
|
-
return (0,
|
|
10462
|
+
return (0, import_types18.Ok)(milestones);
|
|
10112
10463
|
}
|
|
10113
10464
|
function parseFeatures(sectionBody) {
|
|
10114
10465
|
const features = [];
|
|
10115
|
-
const h3Pattern = /^### Feature: (.+)$/gm;
|
|
10466
|
+
const h3Pattern = /^### (?:Feature: )?(.+)$/gm;
|
|
10116
10467
|
const h3Matches = [];
|
|
10117
10468
|
let match;
|
|
10118
10469
|
while ((match = h3Pattern.exec(sectionBody)) !== null) {
|
|
10119
|
-
h3Matches.push({ name: match[1], startIndex: match.index });
|
|
10470
|
+
h3Matches.push({ name: match[1], startIndex: match.index, fullMatch: match[0] });
|
|
10120
10471
|
}
|
|
10121
10472
|
for (let i = 0; i < h3Matches.length; i++) {
|
|
10122
10473
|
const h3 = h3Matches[i];
|
|
10123
10474
|
const nextStart = i + 1 < h3Matches.length ? h3Matches[i + 1].startIndex : sectionBody.length;
|
|
10124
|
-
const featureBody = sectionBody.slice(
|
|
10125
|
-
h3.startIndex + `### Feature: ${h3.name}`.length,
|
|
10126
|
-
nextStart
|
|
10127
|
-
);
|
|
10475
|
+
const featureBody = sectionBody.slice(h3.startIndex + h3.fullMatch.length, nextStart);
|
|
10128
10476
|
const featureResult = parseFeatureFields(h3.name, featureBody);
|
|
10129
10477
|
if (!featureResult.ok) return featureResult;
|
|
10130
10478
|
features.push(featureResult.value);
|
|
10131
10479
|
}
|
|
10132
|
-
return (0,
|
|
10480
|
+
return (0, import_types18.Ok)(features);
|
|
10133
10481
|
}
|
|
10134
10482
|
function parseFeatureFields(name, body) {
|
|
10135
10483
|
const fieldMap = /* @__PURE__ */ new Map();
|
|
@@ -10140,7 +10488,7 @@ function parseFeatureFields(name, body) {
|
|
|
10140
10488
|
}
|
|
10141
10489
|
const statusRaw = fieldMap.get("Status");
|
|
10142
10490
|
if (!statusRaw || !VALID_STATUSES.has(statusRaw)) {
|
|
10143
|
-
return (0,
|
|
10491
|
+
return (0, import_types18.Err)(
|
|
10144
10492
|
new Error(
|
|
10145
10493
|
`Feature "${name}" has invalid status: "${statusRaw ?? "(missing)"}". Valid statuses: ${[...VALID_STATUSES].join(", ")}`
|
|
10146
10494
|
)
|
|
@@ -10149,12 +10497,12 @@ function parseFeatureFields(name, body) {
|
|
|
10149
10497
|
const status = statusRaw;
|
|
10150
10498
|
const specRaw = fieldMap.get("Spec") ?? EM_DASH;
|
|
10151
10499
|
const spec = specRaw === EM_DASH ? null : specRaw;
|
|
10152
|
-
const plansRaw = fieldMap.get("Plans") ?? EM_DASH;
|
|
10153
|
-
const plans = plansRaw === EM_DASH ? [] : plansRaw.split(",").map((p) => p.trim());
|
|
10154
|
-
const blockedByRaw = fieldMap.get("Blocked by") ?? EM_DASH;
|
|
10155
|
-
const blockedBy = blockedByRaw === EM_DASH ? [] : blockedByRaw.split(",").map((b) => b.trim());
|
|
10500
|
+
const plansRaw = fieldMap.get("Plans") ?? fieldMap.get("Plan") ?? EM_DASH;
|
|
10501
|
+
const plans = plansRaw === EM_DASH || plansRaw === "none" ? [] : plansRaw.split(",").map((p) => p.trim());
|
|
10502
|
+
const blockedByRaw = fieldMap.get("Blocked by") ?? fieldMap.get("Blockers") ?? EM_DASH;
|
|
10503
|
+
const blockedBy = blockedByRaw === EM_DASH || blockedByRaw === "none" ? [] : blockedByRaw.split(",").map((b) => b.trim());
|
|
10156
10504
|
const summary = fieldMap.get("Summary") ?? "";
|
|
10157
|
-
return (0,
|
|
10505
|
+
return (0, import_types18.Ok)({ name, status, spec, plans, blockedBy, summary });
|
|
10158
10506
|
}
|
|
10159
10507
|
|
|
10160
10508
|
// src/roadmap/serialize.ts
|
|
@@ -10164,11 +10512,17 @@ function serializeRoadmap(roadmap) {
|
|
|
10164
10512
|
lines.push("---");
|
|
10165
10513
|
lines.push(`project: ${roadmap.frontmatter.project}`);
|
|
10166
10514
|
lines.push(`version: ${roadmap.frontmatter.version}`);
|
|
10515
|
+
if (roadmap.frontmatter.created) {
|
|
10516
|
+
lines.push(`created: ${roadmap.frontmatter.created}`);
|
|
10517
|
+
}
|
|
10518
|
+
if (roadmap.frontmatter.updated) {
|
|
10519
|
+
lines.push(`updated: ${roadmap.frontmatter.updated}`);
|
|
10520
|
+
}
|
|
10167
10521
|
lines.push(`last_synced: ${roadmap.frontmatter.lastSynced}`);
|
|
10168
10522
|
lines.push(`last_manual_edit: ${roadmap.frontmatter.lastManualEdit}`);
|
|
10169
10523
|
lines.push("---");
|
|
10170
10524
|
lines.push("");
|
|
10171
|
-
lines.push("#
|
|
10525
|
+
lines.push("# Roadmap");
|
|
10172
10526
|
for (const milestone of roadmap.milestones) {
|
|
10173
10527
|
lines.push("");
|
|
10174
10528
|
lines.push(serializeMilestoneHeading(milestone));
|
|
@@ -10181,26 +10535,27 @@ function serializeRoadmap(roadmap) {
|
|
|
10181
10535
|
return lines.join("\n");
|
|
10182
10536
|
}
|
|
10183
10537
|
function serializeMilestoneHeading(milestone) {
|
|
10184
|
-
return milestone.isBacklog ? "## Backlog" : `##
|
|
10538
|
+
return milestone.isBacklog ? "## Backlog" : `## ${milestone.name}`;
|
|
10185
10539
|
}
|
|
10186
10540
|
function serializeFeature(feature) {
|
|
10187
10541
|
const spec = feature.spec ?? EM_DASH2;
|
|
10188
10542
|
const plans = feature.plans.length > 0 ? feature.plans.join(", ") : EM_DASH2;
|
|
10189
10543
|
const blockedBy = feature.blockedBy.length > 0 ? feature.blockedBy.join(", ") : EM_DASH2;
|
|
10190
10544
|
return [
|
|
10191
|
-
`###
|
|
10545
|
+
`### ${feature.name}`,
|
|
10546
|
+
"",
|
|
10192
10547
|
`- **Status:** ${feature.status}`,
|
|
10193
10548
|
`- **Spec:** ${spec}`,
|
|
10194
|
-
`- **
|
|
10195
|
-
`- **
|
|
10196
|
-
`- **
|
|
10549
|
+
`- **Summary:** ${feature.summary}`,
|
|
10550
|
+
`- **Blockers:** ${blockedBy}`,
|
|
10551
|
+
`- **Plan:** ${plans}`
|
|
10197
10552
|
];
|
|
10198
10553
|
}
|
|
10199
10554
|
|
|
10200
10555
|
// src/roadmap/sync.ts
|
|
10201
|
-
var
|
|
10202
|
-
var
|
|
10203
|
-
var
|
|
10556
|
+
var fs16 = __toESM(require("fs"));
|
|
10557
|
+
var path16 = __toESM(require("path"));
|
|
10558
|
+
var import_types19 = require("@harness-engineering/types");
|
|
10204
10559
|
function inferStatus(feature, projectPath, allFeatures) {
|
|
10205
10560
|
if (feature.blockedBy.length > 0) {
|
|
10206
10561
|
const blockerNotDone = feature.blockedBy.some((blockerName) => {
|
|
@@ -10214,10 +10569,10 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
10214
10569
|
const featuresWithPlans = allFeatures.filter((f) => f.plans.length > 0);
|
|
10215
10570
|
const useRootState = featuresWithPlans.length <= 1;
|
|
10216
10571
|
if (useRootState) {
|
|
10217
|
-
const rootStatePath =
|
|
10218
|
-
if (
|
|
10572
|
+
const rootStatePath = path16.join(projectPath, ".harness", "state.json");
|
|
10573
|
+
if (fs16.existsSync(rootStatePath)) {
|
|
10219
10574
|
try {
|
|
10220
|
-
const raw =
|
|
10575
|
+
const raw = fs16.readFileSync(rootStatePath, "utf-8");
|
|
10221
10576
|
const state = JSON.parse(raw);
|
|
10222
10577
|
if (state.progress) {
|
|
10223
10578
|
for (const status of Object.values(state.progress)) {
|
|
@@ -10228,16 +10583,16 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
10228
10583
|
}
|
|
10229
10584
|
}
|
|
10230
10585
|
}
|
|
10231
|
-
const sessionsDir =
|
|
10232
|
-
if (
|
|
10586
|
+
const sessionsDir = path16.join(projectPath, ".harness", "sessions");
|
|
10587
|
+
if (fs16.existsSync(sessionsDir)) {
|
|
10233
10588
|
try {
|
|
10234
|
-
const sessionDirs =
|
|
10589
|
+
const sessionDirs = fs16.readdirSync(sessionsDir, { withFileTypes: true });
|
|
10235
10590
|
for (const entry of sessionDirs) {
|
|
10236
10591
|
if (!entry.isDirectory()) continue;
|
|
10237
|
-
const autopilotPath =
|
|
10238
|
-
if (!
|
|
10592
|
+
const autopilotPath = path16.join(sessionsDir, entry.name, "autopilot-state.json");
|
|
10593
|
+
if (!fs16.existsSync(autopilotPath)) continue;
|
|
10239
10594
|
try {
|
|
10240
|
-
const raw =
|
|
10595
|
+
const raw = fs16.readFileSync(autopilotPath, "utf-8");
|
|
10241
10596
|
const autopilot = JSON.parse(raw);
|
|
10242
10597
|
if (!autopilot.phases) continue;
|
|
10243
10598
|
const linkedPhases = autopilot.phases.filter(
|
|
@@ -10284,7 +10639,7 @@ function syncRoadmap(options) {
|
|
|
10284
10639
|
to: inferred
|
|
10285
10640
|
});
|
|
10286
10641
|
}
|
|
10287
|
-
return (0,
|
|
10642
|
+
return (0, import_types19.Ok)(changes);
|
|
10288
10643
|
}
|
|
10289
10644
|
|
|
10290
10645
|
// src/interaction/types.ts
|
|
@@ -10317,17 +10672,17 @@ var EmitInteractionInputSchema = import_zod7.z.object({
|
|
|
10317
10672
|
});
|
|
10318
10673
|
|
|
10319
10674
|
// src/blueprint/scanner.ts
|
|
10320
|
-
var
|
|
10321
|
-
var
|
|
10675
|
+
var fs17 = __toESM(require("fs/promises"));
|
|
10676
|
+
var path17 = __toESM(require("path"));
|
|
10322
10677
|
var ProjectScanner = class {
|
|
10323
10678
|
constructor(rootDir) {
|
|
10324
10679
|
this.rootDir = rootDir;
|
|
10325
10680
|
}
|
|
10326
10681
|
async scan() {
|
|
10327
|
-
let projectName =
|
|
10682
|
+
let projectName = path17.basename(this.rootDir);
|
|
10328
10683
|
try {
|
|
10329
|
-
const pkgPath =
|
|
10330
|
-
const pkgRaw = await
|
|
10684
|
+
const pkgPath = path17.join(this.rootDir, "package.json");
|
|
10685
|
+
const pkgRaw = await fs17.readFile(pkgPath, "utf-8");
|
|
10331
10686
|
const pkg = JSON.parse(pkgRaw);
|
|
10332
10687
|
if (pkg.name) projectName = pkg.name;
|
|
10333
10688
|
} catch {
|
|
@@ -10368,8 +10723,8 @@ var ProjectScanner = class {
|
|
|
10368
10723
|
};
|
|
10369
10724
|
|
|
10370
10725
|
// src/blueprint/generator.ts
|
|
10371
|
-
var
|
|
10372
|
-
var
|
|
10726
|
+
var fs18 = __toESM(require("fs/promises"));
|
|
10727
|
+
var path18 = __toESM(require("path"));
|
|
10373
10728
|
var ejs = __toESM(require("ejs"));
|
|
10374
10729
|
|
|
10375
10730
|
// src/blueprint/templates.ts
|
|
@@ -10453,19 +10808,19 @@ var BlueprintGenerator = class {
|
|
|
10453
10808
|
styles: STYLES,
|
|
10454
10809
|
scripts: SCRIPTS
|
|
10455
10810
|
});
|
|
10456
|
-
await
|
|
10457
|
-
await
|
|
10811
|
+
await fs18.mkdir(options.outputDir, { recursive: true });
|
|
10812
|
+
await fs18.writeFile(path18.join(options.outputDir, "index.html"), html);
|
|
10458
10813
|
}
|
|
10459
10814
|
};
|
|
10460
10815
|
|
|
10461
10816
|
// src/update-checker.ts
|
|
10462
|
-
var
|
|
10463
|
-
var
|
|
10817
|
+
var fs19 = __toESM(require("fs"));
|
|
10818
|
+
var path19 = __toESM(require("path"));
|
|
10464
10819
|
var os = __toESM(require("os"));
|
|
10465
10820
|
var import_child_process3 = require("child_process");
|
|
10466
10821
|
function getStatePath() {
|
|
10467
10822
|
const home = process.env["HOME"] || os.homedir();
|
|
10468
|
-
return
|
|
10823
|
+
return path19.join(home, ".harness", "update-check.json");
|
|
10469
10824
|
}
|
|
10470
10825
|
function isUpdateCheckEnabled(configInterval) {
|
|
10471
10826
|
if (process.env["HARNESS_NO_UPDATE_CHECK"] === "1") return false;
|
|
@@ -10478,7 +10833,7 @@ function shouldRunCheck(state, intervalMs) {
|
|
|
10478
10833
|
}
|
|
10479
10834
|
function readCheckState() {
|
|
10480
10835
|
try {
|
|
10481
|
-
const raw =
|
|
10836
|
+
const raw = fs19.readFileSync(getStatePath(), "utf-8");
|
|
10482
10837
|
const parsed = JSON.parse(raw);
|
|
10483
10838
|
if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
|
|
10484
10839
|
const state = parsed;
|
|
@@ -10495,7 +10850,7 @@ function readCheckState() {
|
|
|
10495
10850
|
}
|
|
10496
10851
|
function spawnBackgroundCheck(currentVersion) {
|
|
10497
10852
|
const statePath = getStatePath();
|
|
10498
|
-
const stateDir =
|
|
10853
|
+
const stateDir = path19.dirname(statePath);
|
|
10499
10854
|
const script = `
|
|
10500
10855
|
const { execSync } = require('child_process');
|
|
10501
10856
|
const fs = require('fs');
|
|
@@ -10627,6 +10982,7 @@ var VERSION = "0.11.0";
|
|
|
10627
10982
|
ViolationSchema,
|
|
10628
10983
|
addProvenance,
|
|
10629
10984
|
analyzeDiff,
|
|
10985
|
+
analyzeLearningPatterns,
|
|
10630
10986
|
appendFailure,
|
|
10631
10987
|
appendLearning,
|
|
10632
10988
|
applyFixes,
|
|
@@ -10635,6 +10991,7 @@ var VERSION = "0.11.0";
|
|
|
10635
10991
|
archModule,
|
|
10636
10992
|
architecture,
|
|
10637
10993
|
archiveFailures,
|
|
10994
|
+
archiveLearnings,
|
|
10638
10995
|
archiveStream,
|
|
10639
10996
|
buildDependencyGraph,
|
|
10640
10997
|
buildExclusionSet,
|
|
@@ -10642,6 +10999,8 @@ var VERSION = "0.11.0";
|
|
|
10642
10999
|
checkDocCoverage,
|
|
10643
11000
|
checkEligibility,
|
|
10644
11001
|
classifyFinding,
|
|
11002
|
+
clearFailuresCache,
|
|
11003
|
+
clearLearningsCache,
|
|
10645
11004
|
configureFeedback,
|
|
10646
11005
|
constraintRuleId,
|
|
10647
11006
|
contextBudget,
|
|
@@ -10697,16 +11056,20 @@ var VERSION = "0.11.0";
|
|
|
10697
11056
|
injectionRules,
|
|
10698
11057
|
isSmallSuggestion,
|
|
10699
11058
|
isUpdateCheckEnabled,
|
|
11059
|
+
listActiveSessions,
|
|
10700
11060
|
listStreams,
|
|
11061
|
+
loadBudgetedLearnings,
|
|
10701
11062
|
loadFailures,
|
|
10702
11063
|
loadHandoff,
|
|
10703
11064
|
loadRelevantLearnings,
|
|
11065
|
+
loadSessionSummary,
|
|
10704
11066
|
loadState,
|
|
10705
11067
|
loadStreamIndex,
|
|
10706
11068
|
logAgentAction,
|
|
10707
11069
|
migrateToStreams,
|
|
10708
11070
|
networkRules,
|
|
10709
11071
|
nodeRules,
|
|
11072
|
+
parseDateFromEntry,
|
|
10710
11073
|
parseDiff,
|
|
10711
11074
|
parseManifest,
|
|
10712
11075
|
parseRoadmap,
|
|
@@ -10714,6 +11077,7 @@ var VERSION = "0.11.0";
|
|
|
10714
11077
|
parseSize,
|
|
10715
11078
|
pathTraversalRules,
|
|
10716
11079
|
previewFix,
|
|
11080
|
+
pruneLearnings,
|
|
10717
11081
|
reactRules,
|
|
10718
11082
|
readCheckState,
|
|
10719
11083
|
readLockfile,
|
|
@@ -10725,6 +11089,7 @@ var VERSION = "0.11.0";
|
|
|
10725
11089
|
resolveFileToLayer,
|
|
10726
11090
|
resolveModelTier,
|
|
10727
11091
|
resolveRuleSeverity,
|
|
11092
|
+
resolveSessionDir,
|
|
10728
11093
|
resolveStreamPath,
|
|
10729
11094
|
resolveThresholds,
|
|
10730
11095
|
runAll,
|
|
@@ -10751,6 +11116,7 @@ var VERSION = "0.11.0";
|
|
|
10751
11116
|
syncRoadmap,
|
|
10752
11117
|
touchStream,
|
|
10753
11118
|
trackAction,
|
|
11119
|
+
updateSessionIndex,
|
|
10754
11120
|
validateAgentsMap,
|
|
10755
11121
|
validateBoundaries,
|
|
10756
11122
|
validateCommitMessage,
|
|
@@ -10763,6 +11129,7 @@ var VERSION = "0.11.0";
|
|
|
10763
11129
|
violationId,
|
|
10764
11130
|
writeConfig,
|
|
10765
11131
|
writeLockfile,
|
|
11132
|
+
writeSessionSummary,
|
|
10766
11133
|
xssRules,
|
|
10767
11134
|
...require("@harness-engineering/types")
|
|
10768
11135
|
});
|