@harness-engineering/core 0.21.0 → 0.21.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/index.d.mts +24 -8
- package/dist/index.d.ts +24 -8
- package/dist/index.js +594 -524
- package/dist/index.mjs +597 -529
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -168,6 +168,7 @@ __export(index_exports, {
|
|
|
168
168
|
clearFailuresCache: () => clearFailuresCache,
|
|
169
169
|
clearLearningsCache: () => clearLearningsCache,
|
|
170
170
|
clearTaint: () => clearTaint,
|
|
171
|
+
computeContentHash: () => computeContentHash,
|
|
171
172
|
computeOverallSeverity: () => computeOverallSeverity,
|
|
172
173
|
computeScanExitCode: () => computeScanExitCode,
|
|
173
174
|
configureFeedback: () => configureFeedback,
|
|
@@ -261,6 +262,7 @@ __export(index_exports, {
|
|
|
261
262
|
migrateToStreams: () => migrateToStreams,
|
|
262
263
|
networkRules: () => networkRules,
|
|
263
264
|
nodeRules: () => nodeRules,
|
|
265
|
+
normalizeLearningContent: () => normalizeLearningContent,
|
|
264
266
|
parseCCRecords: () => parseCCRecords,
|
|
265
267
|
parseDateFromEntry: () => parseDateFromEntry,
|
|
266
268
|
parseDiff: () => parseDiff,
|
|
@@ -371,17 +373,17 @@ var import_node_path = require("path");
|
|
|
371
373
|
var import_glob = require("glob");
|
|
372
374
|
var accessAsync = (0, import_util.promisify)(import_fs.access);
|
|
373
375
|
var readFileAsync = (0, import_util.promisify)(import_fs.readFile);
|
|
374
|
-
async function fileExists(
|
|
376
|
+
async function fileExists(path31) {
|
|
375
377
|
try {
|
|
376
|
-
await accessAsync(
|
|
378
|
+
await accessAsync(path31, import_fs.constants.F_OK);
|
|
377
379
|
return true;
|
|
378
380
|
} catch {
|
|
379
381
|
return false;
|
|
380
382
|
}
|
|
381
383
|
}
|
|
382
|
-
async function readFileContent(
|
|
384
|
+
async function readFileContent(path31) {
|
|
383
385
|
try {
|
|
384
|
-
const content = await readFileAsync(
|
|
386
|
+
const content = await readFileAsync(path31, "utf-8");
|
|
385
387
|
return (0, import_types.Ok)(content);
|
|
386
388
|
} catch (error) {
|
|
387
389
|
return (0, import_types.Err)(error);
|
|
@@ -432,15 +434,15 @@ function validateConfig(data, schema) {
|
|
|
432
434
|
let message = "Configuration validation failed";
|
|
433
435
|
const suggestions = [];
|
|
434
436
|
if (firstError) {
|
|
435
|
-
const
|
|
436
|
-
const pathDisplay =
|
|
437
|
+
const path31 = firstError.path.join(".");
|
|
438
|
+
const pathDisplay = path31 ? ` at "${path31}"` : "";
|
|
437
439
|
if (firstError.code === "invalid_type") {
|
|
438
440
|
const received = firstError.received;
|
|
439
441
|
const expected = firstError.expected;
|
|
440
442
|
if (received === "undefined") {
|
|
441
443
|
code = "MISSING_FIELD";
|
|
442
444
|
message = `Missing required field${pathDisplay}: ${firstError.message}`;
|
|
443
|
-
suggestions.push(`Field "${
|
|
445
|
+
suggestions.push(`Field "${path31}" is required and must be of type "${expected}"`);
|
|
444
446
|
} else {
|
|
445
447
|
code = "INVALID_TYPE";
|
|
446
448
|
message = `Invalid type${pathDisplay}: ${firstError.message}`;
|
|
@@ -656,27 +658,27 @@ function extractSections(content) {
|
|
|
656
658
|
}
|
|
657
659
|
return sections.map((section) => buildAgentMapSection(section, lines));
|
|
658
660
|
}
|
|
659
|
-
function isExternalLink(
|
|
660
|
-
return
|
|
661
|
+
function isExternalLink(path31) {
|
|
662
|
+
return path31.startsWith("http://") || path31.startsWith("https://") || path31.startsWith("#") || path31.startsWith("mailto:");
|
|
661
663
|
}
|
|
662
664
|
function resolveLinkPath(linkPath, baseDir) {
|
|
663
665
|
return linkPath.startsWith(".") ? (0, import_path.join)(baseDir, linkPath) : linkPath;
|
|
664
666
|
}
|
|
665
|
-
async function validateAgentsMap(
|
|
666
|
-
const contentResult = await readFileContent(
|
|
667
|
+
async function validateAgentsMap(path31 = "./AGENTS.md") {
|
|
668
|
+
const contentResult = await readFileContent(path31);
|
|
667
669
|
if (!contentResult.ok) {
|
|
668
670
|
return (0, import_types.Err)(
|
|
669
671
|
createError(
|
|
670
672
|
"PARSE_ERROR",
|
|
671
673
|
`Failed to read AGENTS.md: ${contentResult.error.message}`,
|
|
672
|
-
{ path:
|
|
674
|
+
{ path: path31 },
|
|
673
675
|
["Ensure the file exists", "Check file permissions"]
|
|
674
676
|
)
|
|
675
677
|
);
|
|
676
678
|
}
|
|
677
679
|
const content = contentResult.value;
|
|
678
680
|
const sections = extractSections(content);
|
|
679
|
-
const baseDir = (0, import_path.dirname)(
|
|
681
|
+
const baseDir = (0, import_path.dirname)(path31);
|
|
680
682
|
const sectionTitles = sections.map((s) => s.title);
|
|
681
683
|
const missingSections = REQUIRED_SECTIONS.filter(
|
|
682
684
|
(required) => !sectionTitles.some((title) => title.toLowerCase().includes(required.toLowerCase()))
|
|
@@ -817,8 +819,8 @@ async function checkDocCoverage(domain, options = {}) {
|
|
|
817
819
|
|
|
818
820
|
// src/context/knowledge-map.ts
|
|
819
821
|
var import_path3 = require("path");
|
|
820
|
-
function suggestFix(
|
|
821
|
-
const targetName = (0, import_path3.basename)(
|
|
822
|
+
function suggestFix(path31, existingFiles) {
|
|
823
|
+
const targetName = (0, import_path3.basename)(path31).toLowerCase();
|
|
822
824
|
const similar = existingFiles.find((file) => {
|
|
823
825
|
const fileName = (0, import_path3.basename)(file).toLowerCase();
|
|
824
826
|
return fileName.includes(targetName) || targetName.includes(fileName);
|
|
@@ -826,7 +828,7 @@ function suggestFix(path28, existingFiles) {
|
|
|
826
828
|
if (similar) {
|
|
827
829
|
return `Did you mean "${similar}"?`;
|
|
828
830
|
}
|
|
829
|
-
return `Create the file "${
|
|
831
|
+
return `Create the file "${path31}" or remove the link`;
|
|
830
832
|
}
|
|
831
833
|
async function validateKnowledgeMap(rootDir = process.cwd()) {
|
|
832
834
|
const agentsPath = (0, import_path3.join)(rootDir, "AGENTS.md");
|
|
@@ -1432,8 +1434,8 @@ function createBoundaryValidator(schema, name) {
|
|
|
1432
1434
|
return (0, import_types.Ok)(result.data);
|
|
1433
1435
|
}
|
|
1434
1436
|
const suggestions = result.error.issues.map((issue) => {
|
|
1435
|
-
const
|
|
1436
|
-
return
|
|
1437
|
+
const path31 = issue.path.join(".");
|
|
1438
|
+
return path31 ? `${path31}: ${issue.message}` : issue.message;
|
|
1437
1439
|
});
|
|
1438
1440
|
return (0, import_types.Err)(
|
|
1439
1441
|
createError(
|
|
@@ -2065,11 +2067,11 @@ function processExportListSpecifiers(exportDecl, exports2) {
|
|
|
2065
2067
|
var TypeScriptParser = class {
|
|
2066
2068
|
name = "typescript";
|
|
2067
2069
|
extensions = [".ts", ".tsx", ".mts", ".cts"];
|
|
2068
|
-
async parseFile(
|
|
2069
|
-
const contentResult = await readFileContent(
|
|
2070
|
+
async parseFile(path31) {
|
|
2071
|
+
const contentResult = await readFileContent(path31);
|
|
2070
2072
|
if (!contentResult.ok) {
|
|
2071
2073
|
return (0, import_types.Err)(
|
|
2072
|
-
createParseError("NOT_FOUND", `File not found: ${
|
|
2074
|
+
createParseError("NOT_FOUND", `File not found: ${path31}`, { path: path31 }, [
|
|
2073
2075
|
"Check that the file exists",
|
|
2074
2076
|
"Verify the path is correct"
|
|
2075
2077
|
])
|
|
@@ -2079,7 +2081,7 @@ var TypeScriptParser = class {
|
|
|
2079
2081
|
const ast = (0, import_typescript_estree.parse)(contentResult.value, {
|
|
2080
2082
|
loc: true,
|
|
2081
2083
|
range: true,
|
|
2082
|
-
jsx:
|
|
2084
|
+
jsx: path31.endsWith(".tsx"),
|
|
2083
2085
|
errorOnUnknownASTType: false
|
|
2084
2086
|
});
|
|
2085
2087
|
return (0, import_types.Ok)({
|
|
@@ -2090,7 +2092,7 @@ var TypeScriptParser = class {
|
|
|
2090
2092
|
} catch (e) {
|
|
2091
2093
|
const error = e;
|
|
2092
2094
|
return (0, import_types.Err)(
|
|
2093
|
-
createParseError("SYNTAX_ERROR", `Failed to parse ${
|
|
2095
|
+
createParseError("SYNTAX_ERROR", `Failed to parse ${path31}: ${error.message}`, { path: path31 }, [
|
|
2094
2096
|
"Check for syntax errors in the file",
|
|
2095
2097
|
"Ensure valid TypeScript syntax"
|
|
2096
2098
|
])
|
|
@@ -2275,22 +2277,22 @@ function extractInlineRefs(content) {
|
|
|
2275
2277
|
}
|
|
2276
2278
|
return refs;
|
|
2277
2279
|
}
|
|
2278
|
-
async function parseDocumentationFile(
|
|
2279
|
-
const contentResult = await readFileContent(
|
|
2280
|
+
async function parseDocumentationFile(path31) {
|
|
2281
|
+
const contentResult = await readFileContent(path31);
|
|
2280
2282
|
if (!contentResult.ok) {
|
|
2281
2283
|
return (0, import_types.Err)(
|
|
2282
2284
|
createEntropyError(
|
|
2283
2285
|
"PARSE_ERROR",
|
|
2284
|
-
`Failed to read documentation file: ${
|
|
2285
|
-
{ file:
|
|
2286
|
+
`Failed to read documentation file: ${path31}`,
|
|
2287
|
+
{ file: path31 },
|
|
2286
2288
|
["Check that the file exists"]
|
|
2287
2289
|
)
|
|
2288
2290
|
);
|
|
2289
2291
|
}
|
|
2290
2292
|
const content = contentResult.value;
|
|
2291
|
-
const type =
|
|
2293
|
+
const type = path31.endsWith(".md") ? "markdown" : "text";
|
|
2292
2294
|
return (0, import_types.Ok)({
|
|
2293
|
-
path:
|
|
2295
|
+
path: path31,
|
|
2294
2296
|
type,
|
|
2295
2297
|
content,
|
|
2296
2298
|
codeBlocks: extractCodeBlocks(content),
|
|
@@ -7353,8 +7355,7 @@ function parseListField(fieldMap, ...keys) {
|
|
|
7353
7355
|
if (raw === EM_DASH || raw === "none") return [];
|
|
7354
7356
|
return raw.split(",").map((s) => s.trim());
|
|
7355
7357
|
}
|
|
7356
|
-
function
|
|
7357
|
-
const fieldMap = extractFieldMap(body);
|
|
7358
|
+
function validateStatus(name, fieldMap) {
|
|
7358
7359
|
const statusRaw = fieldMap.get("Status");
|
|
7359
7360
|
if (!statusRaw || !VALID_STATUSES.has(statusRaw)) {
|
|
7360
7361
|
return (0, import_types13.Err)(
|
|
@@ -7363,12 +7364,10 @@ function parseFeatureFields(name, body) {
|
|
|
7363
7364
|
)
|
|
7364
7365
|
);
|
|
7365
7366
|
}
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
const assigneeRaw = fieldMap.get("Assignee") ?? EM_DASH;
|
|
7367
|
+
return (0, import_types13.Ok)(statusRaw);
|
|
7368
|
+
}
|
|
7369
|
+
function validatePriority(name, fieldMap) {
|
|
7370
7370
|
const priorityRaw = fieldMap.get("Priority") ?? EM_DASH;
|
|
7371
|
-
const externalIdRaw = fieldMap.get("External-ID") ?? EM_DASH;
|
|
7372
7371
|
if (priorityRaw !== EM_DASH && !VALID_PRIORITIES.has(priorityRaw)) {
|
|
7373
7372
|
return (0, import_types13.Err)(
|
|
7374
7373
|
new Error(
|
|
@@ -7376,16 +7375,28 @@ function parseFeatureFields(name, body) {
|
|
|
7376
7375
|
)
|
|
7377
7376
|
);
|
|
7378
7377
|
}
|
|
7378
|
+
return (0, import_types13.Ok)(priorityRaw === EM_DASH ? null : priorityRaw);
|
|
7379
|
+
}
|
|
7380
|
+
function optionalField(fieldMap, key) {
|
|
7381
|
+
const raw = fieldMap.get(key) ?? EM_DASH;
|
|
7382
|
+
return raw === EM_DASH ? null : raw;
|
|
7383
|
+
}
|
|
7384
|
+
function parseFeatureFields(name, body) {
|
|
7385
|
+
const fieldMap = extractFieldMap(body);
|
|
7386
|
+
const statusResult = validateStatus(name, fieldMap);
|
|
7387
|
+
if (!statusResult.ok) return statusResult;
|
|
7388
|
+
const priorityResult = validatePriority(name, fieldMap);
|
|
7389
|
+
if (!priorityResult.ok) return priorityResult;
|
|
7379
7390
|
return (0, import_types13.Ok)({
|
|
7380
7391
|
name,
|
|
7381
|
-
status:
|
|
7382
|
-
spec:
|
|
7383
|
-
plans,
|
|
7384
|
-
blockedBy,
|
|
7392
|
+
status: statusResult.value,
|
|
7393
|
+
spec: optionalField(fieldMap, "Spec"),
|
|
7394
|
+
plans: parseListField(fieldMap, "Plans", "Plan"),
|
|
7395
|
+
blockedBy: parseListField(fieldMap, "Blocked by", "Blockers"),
|
|
7385
7396
|
summary: fieldMap.get("Summary") ?? "",
|
|
7386
|
-
assignee:
|
|
7387
|
-
priority:
|
|
7388
|
-
externalId:
|
|
7397
|
+
assignee: optionalField(fieldMap, "Assignee"),
|
|
7398
|
+
priority: priorityResult.value,
|
|
7399
|
+
externalId: optionalField(fieldMap, "External-ID")
|
|
7389
7400
|
});
|
|
7390
7401
|
}
|
|
7391
7402
|
function parseAssignmentHistory(body) {
|
|
@@ -7451,82 +7462,16 @@ var PredictionEngine = class {
|
|
|
7451
7462
|
const firstDate = new Date(snapshots[0].capturedAt).getTime();
|
|
7452
7463
|
const lastSnapshot = snapshots[snapshots.length - 1];
|
|
7453
7464
|
const currentT = (new Date(lastSnapshot.capturedAt).getTime() - firstDate) / (7 * 24 * 60 * 60 * 1e3);
|
|
7454
|
-
const baselines =
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
const timeSeries = this.extractTimeSeries(snapshots, category, firstDate);
|
|
7463
|
-
baselines[category] = this.forecastCategory(
|
|
7464
|
-
category,
|
|
7465
|
-
timeSeries,
|
|
7466
|
-
currentT,
|
|
7467
|
-
threshold,
|
|
7468
|
-
opts.horizon
|
|
7469
|
-
);
|
|
7470
|
-
}
|
|
7465
|
+
const baselines = this.computeBaselines(
|
|
7466
|
+
categoriesToProcess,
|
|
7467
|
+
thresholds,
|
|
7468
|
+
snapshots,
|
|
7469
|
+
firstDate,
|
|
7470
|
+
currentT,
|
|
7471
|
+
opts.horizon
|
|
7472
|
+
);
|
|
7471
7473
|
const specImpacts = this.computeSpecImpacts(opts);
|
|
7472
|
-
const categories =
|
|
7473
|
-
for (const category of ALL_CATEGORIES2) {
|
|
7474
|
-
const baseline = baselines[category];
|
|
7475
|
-
const threshold = thresholds[category];
|
|
7476
|
-
if (!specImpacts || specImpacts.length === 0) {
|
|
7477
|
-
categories[category] = {
|
|
7478
|
-
baseline,
|
|
7479
|
-
adjusted: baseline,
|
|
7480
|
-
contributingFeatures: []
|
|
7481
|
-
};
|
|
7482
|
-
continue;
|
|
7483
|
-
}
|
|
7484
|
-
let totalDelta = 0;
|
|
7485
|
-
const contributing = [];
|
|
7486
|
-
for (const impact of specImpacts) {
|
|
7487
|
-
const delta = impact.deltas?.[category] ?? 0;
|
|
7488
|
-
if (delta !== 0) {
|
|
7489
|
-
totalDelta += delta;
|
|
7490
|
-
contributing.push({
|
|
7491
|
-
name: impact.featureName,
|
|
7492
|
-
specPath: impact.specPath,
|
|
7493
|
-
delta
|
|
7494
|
-
});
|
|
7495
|
-
}
|
|
7496
|
-
}
|
|
7497
|
-
if (totalDelta === 0) {
|
|
7498
|
-
categories[category] = {
|
|
7499
|
-
baseline,
|
|
7500
|
-
adjusted: baseline,
|
|
7501
|
-
contributingFeatures: []
|
|
7502
|
-
};
|
|
7503
|
-
continue;
|
|
7504
|
-
}
|
|
7505
|
-
const adjusted = {
|
|
7506
|
-
...baseline,
|
|
7507
|
-
projectedValue4w: baseline.projectedValue4w + totalDelta,
|
|
7508
|
-
projectedValue8w: baseline.projectedValue8w + totalDelta,
|
|
7509
|
-
projectedValue12w: baseline.projectedValue12w + totalDelta
|
|
7510
|
-
};
|
|
7511
|
-
const adjustedFit = {
|
|
7512
|
-
slope: baseline.regression.slope,
|
|
7513
|
-
intercept: baseline.regression.intercept + totalDelta,
|
|
7514
|
-
rSquared: baseline.regression.rSquared,
|
|
7515
|
-
dataPoints: baseline.regression.dataPoints
|
|
7516
|
-
};
|
|
7517
|
-
adjusted.thresholdCrossingWeeks = weeksUntilThreshold(adjustedFit, currentT, threshold);
|
|
7518
|
-
adjusted.regression = {
|
|
7519
|
-
slope: adjustedFit.slope,
|
|
7520
|
-
intercept: adjustedFit.intercept,
|
|
7521
|
-
rSquared: adjustedFit.rSquared,
|
|
7522
|
-
dataPoints: adjustedFit.dataPoints
|
|
7523
|
-
};
|
|
7524
|
-
categories[category] = {
|
|
7525
|
-
baseline,
|
|
7526
|
-
adjusted,
|
|
7527
|
-
contributingFeatures: contributing
|
|
7528
|
-
};
|
|
7529
|
-
}
|
|
7474
|
+
const categories = this.computeAdjustedForecasts(baselines, thresholds, specImpacts, currentT);
|
|
7530
7475
|
const warnings = this.generateWarnings(
|
|
7531
7476
|
categories,
|
|
7532
7477
|
opts.horizon
|
|
@@ -7568,6 +7513,76 @@ var PredictionEngine = class {
|
|
|
7568
7513
|
}
|
|
7569
7514
|
return base;
|
|
7570
7515
|
}
|
|
7516
|
+
computeBaselines(categoriesToProcess, thresholds, snapshots, firstDate, currentT, horizon) {
|
|
7517
|
+
const baselines = {};
|
|
7518
|
+
for (const category of ALL_CATEGORIES2) {
|
|
7519
|
+
const threshold = thresholds[category];
|
|
7520
|
+
if (!categoriesToProcess.includes(category)) {
|
|
7521
|
+
baselines[category] = this.zeroForecast(category, threshold);
|
|
7522
|
+
continue;
|
|
7523
|
+
}
|
|
7524
|
+
const timeSeries = this.extractTimeSeries(snapshots, category, firstDate);
|
|
7525
|
+
baselines[category] = this.forecastCategory(
|
|
7526
|
+
category,
|
|
7527
|
+
timeSeries,
|
|
7528
|
+
currentT,
|
|
7529
|
+
threshold,
|
|
7530
|
+
horizon
|
|
7531
|
+
);
|
|
7532
|
+
}
|
|
7533
|
+
return baselines;
|
|
7534
|
+
}
|
|
7535
|
+
computeAdjustedForecasts(baselines, thresholds, specImpacts, currentT) {
|
|
7536
|
+
const categories = {};
|
|
7537
|
+
for (const category of ALL_CATEGORIES2) {
|
|
7538
|
+
const baseline = baselines[category];
|
|
7539
|
+
categories[category] = this.adjustForecastForCategory(
|
|
7540
|
+
category,
|
|
7541
|
+
baseline,
|
|
7542
|
+
thresholds[category],
|
|
7543
|
+
specImpacts,
|
|
7544
|
+
currentT
|
|
7545
|
+
);
|
|
7546
|
+
}
|
|
7547
|
+
return categories;
|
|
7548
|
+
}
|
|
7549
|
+
adjustForecastForCategory(category, baseline, threshold, specImpacts, currentT) {
|
|
7550
|
+
if (!specImpacts || specImpacts.length === 0) {
|
|
7551
|
+
return { baseline, adjusted: baseline, contributingFeatures: [] };
|
|
7552
|
+
}
|
|
7553
|
+
let totalDelta = 0;
|
|
7554
|
+
const contributing = [];
|
|
7555
|
+
for (const impact of specImpacts) {
|
|
7556
|
+
const delta = impact.deltas?.[category] ?? 0;
|
|
7557
|
+
if (delta !== 0) {
|
|
7558
|
+
totalDelta += delta;
|
|
7559
|
+
contributing.push({ name: impact.featureName, specPath: impact.specPath, delta });
|
|
7560
|
+
}
|
|
7561
|
+
}
|
|
7562
|
+
if (totalDelta === 0) {
|
|
7563
|
+
return { baseline, adjusted: baseline, contributingFeatures: [] };
|
|
7564
|
+
}
|
|
7565
|
+
const adjusted = {
|
|
7566
|
+
...baseline,
|
|
7567
|
+
projectedValue4w: baseline.projectedValue4w + totalDelta,
|
|
7568
|
+
projectedValue8w: baseline.projectedValue8w + totalDelta,
|
|
7569
|
+
projectedValue12w: baseline.projectedValue12w + totalDelta
|
|
7570
|
+
};
|
|
7571
|
+
const adjustedFit = {
|
|
7572
|
+
slope: baseline.regression.slope,
|
|
7573
|
+
intercept: baseline.regression.intercept + totalDelta,
|
|
7574
|
+
rSquared: baseline.regression.rSquared,
|
|
7575
|
+
dataPoints: baseline.regression.dataPoints
|
|
7576
|
+
};
|
|
7577
|
+
adjusted.thresholdCrossingWeeks = weeksUntilThreshold(adjustedFit, currentT, threshold);
|
|
7578
|
+
adjusted.regression = {
|
|
7579
|
+
slope: adjustedFit.slope,
|
|
7580
|
+
intercept: adjustedFit.intercept,
|
|
7581
|
+
rSquared: adjustedFit.rSquared,
|
|
7582
|
+
dataPoints: adjustedFit.dataPoints
|
|
7583
|
+
};
|
|
7584
|
+
return { baseline, adjusted, contributingFeatures: contributing };
|
|
7585
|
+
}
|
|
7571
7586
|
/**
|
|
7572
7587
|
* Extract time series for a single category from snapshots.
|
|
7573
7588
|
* Returns array of { t (weeks from first), value } sorted oldest first.
|
|
@@ -8392,7 +8407,7 @@ async function saveState(projectPath, state, stream, session) {
|
|
|
8392
8407
|
}
|
|
8393
8408
|
}
|
|
8394
8409
|
|
|
8395
|
-
// src/state/learnings.ts
|
|
8410
|
+
// src/state/learnings-content.ts
|
|
8396
8411
|
var fs11 = __toESM(require("fs"));
|
|
8397
8412
|
var path8 = __toESM(require("path"));
|
|
8398
8413
|
var crypto = __toESM(require("crypto"));
|
|
@@ -8403,6 +8418,25 @@ function parseFrontmatter2(line) {
|
|
|
8403
8418
|
const tags = match[2] ? match[2].split(",").filter(Boolean) : [];
|
|
8404
8419
|
return { hash, tags };
|
|
8405
8420
|
}
|
|
8421
|
+
function parseDateFromEntry(entry) {
|
|
8422
|
+
const match = entry.match(/(\d{4}-\d{2}-\d{2})/);
|
|
8423
|
+
return match ? match[1] ?? null : null;
|
|
8424
|
+
}
|
|
8425
|
+
function extractIndexEntry(entry) {
|
|
8426
|
+
const lines = entry.split("\n");
|
|
8427
|
+
const summary = lines[0] ?? entry;
|
|
8428
|
+
const tags = [];
|
|
8429
|
+
const skillMatch = entry.match(/\[skill:([^\]]+)\]/);
|
|
8430
|
+
if (skillMatch?.[1]) tags.push(skillMatch[1]);
|
|
8431
|
+
const outcomeMatch = entry.match(/\[outcome:([^\]]+)\]/);
|
|
8432
|
+
if (outcomeMatch?.[1]) tags.push(outcomeMatch[1]);
|
|
8433
|
+
return {
|
|
8434
|
+
hash: computeEntryHash(entry),
|
|
8435
|
+
tags,
|
|
8436
|
+
summary,
|
|
8437
|
+
fullText: entry
|
|
8438
|
+
};
|
|
8439
|
+
}
|
|
8406
8440
|
function computeEntryHash(text) {
|
|
8407
8441
|
return crypto.createHash("sha256").update(text).digest("hex").slice(0, 8);
|
|
8408
8442
|
}
|
|
@@ -8437,8 +8471,8 @@ function saveContentHashes(stateDir, index) {
|
|
|
8437
8471
|
const hashesPath = path8.join(stateDir, CONTENT_HASHES_FILE);
|
|
8438
8472
|
fs11.writeFileSync(hashesPath, JSON.stringify(index, null, 2) + "\n");
|
|
8439
8473
|
}
|
|
8440
|
-
function rebuildContentHashes(stateDir) {
|
|
8441
|
-
const learningsPath = path8.join(stateDir,
|
|
8474
|
+
function rebuildContentHashes(stateDir, learningsFile) {
|
|
8475
|
+
const learningsPath = path8.join(stateDir, learningsFile);
|
|
8442
8476
|
if (!fs11.existsSync(learningsPath)) return {};
|
|
8443
8477
|
const content = fs11.readFileSync(learningsPath, "utf-8");
|
|
8444
8478
|
const lines = content.split("\n");
|
|
@@ -8459,43 +8493,125 @@ function rebuildContentHashes(stateDir) {
|
|
|
8459
8493
|
saveContentHashes(stateDir, index);
|
|
8460
8494
|
return index;
|
|
8461
8495
|
}
|
|
8462
|
-
function
|
|
8463
|
-
const
|
|
8464
|
-
const
|
|
8465
|
-
|
|
8466
|
-
|
|
8467
|
-
|
|
8468
|
-
|
|
8469
|
-
|
|
8470
|
-
|
|
8471
|
-
|
|
8472
|
-
|
|
8473
|
-
|
|
8474
|
-
|
|
8475
|
-
|
|
8496
|
+
function analyzeLearningPatterns(entries) {
|
|
8497
|
+
const tagGroups = /* @__PURE__ */ new Map();
|
|
8498
|
+
for (const entry of entries) {
|
|
8499
|
+
const tagMatches = entry.matchAll(/\[(skill:[^\]]+)\]|\[(outcome:[^\]]+)\]/g);
|
|
8500
|
+
for (const match of tagMatches) {
|
|
8501
|
+
const tag = match[1] ?? match[2];
|
|
8502
|
+
if (tag) {
|
|
8503
|
+
const group = tagGroups.get(tag) ?? [];
|
|
8504
|
+
group.push(entry);
|
|
8505
|
+
tagGroups.set(tag, group);
|
|
8506
|
+
}
|
|
8507
|
+
}
|
|
8508
|
+
}
|
|
8509
|
+
const patterns = [];
|
|
8510
|
+
for (const [tag, groupEntries] of tagGroups) {
|
|
8511
|
+
if (groupEntries.length >= 3) {
|
|
8512
|
+
patterns.push({ tag, count: groupEntries.length, entries: groupEntries });
|
|
8513
|
+
}
|
|
8514
|
+
}
|
|
8515
|
+
return patterns.sort((a, b) => b.count - a.count);
|
|
8476
8516
|
}
|
|
8517
|
+
function estimateTokens(text) {
|
|
8518
|
+
return Math.ceil(text.length / 4);
|
|
8519
|
+
}
|
|
8520
|
+
function scoreRelevance(entry, intent) {
|
|
8521
|
+
if (!intent || intent.trim() === "") return 0;
|
|
8522
|
+
const intentWords = intent.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
8523
|
+
if (intentWords.length === 0) return 0;
|
|
8524
|
+
const entryLower = entry.toLowerCase();
|
|
8525
|
+
const matches = intentWords.filter((word) => entryLower.includes(word));
|
|
8526
|
+
return matches.length / intentWords.length;
|
|
8527
|
+
}
|
|
8528
|
+
|
|
8529
|
+
// src/state/learnings-loader.ts
|
|
8530
|
+
var fs12 = __toESM(require("fs"));
|
|
8531
|
+
var path9 = __toESM(require("path"));
|
|
8477
8532
|
var learningsCacheMap = /* @__PURE__ */ new Map();
|
|
8478
8533
|
function clearLearningsCache() {
|
|
8479
8534
|
learningsCacheMap.clear();
|
|
8480
8535
|
}
|
|
8536
|
+
function invalidateLearningsCacheEntry(key) {
|
|
8537
|
+
learningsCacheMap.delete(key);
|
|
8538
|
+
}
|
|
8539
|
+
async function loadRelevantLearnings(projectPath, skillName, stream, session) {
|
|
8540
|
+
try {
|
|
8541
|
+
const dirResult = await getStateDir(projectPath, stream, session);
|
|
8542
|
+
if (!dirResult.ok) return dirResult;
|
|
8543
|
+
const stateDir = dirResult.value;
|
|
8544
|
+
const learningsPath = path9.join(stateDir, LEARNINGS_FILE);
|
|
8545
|
+
if (!fs12.existsSync(learningsPath)) {
|
|
8546
|
+
return (0, import_types.Ok)([]);
|
|
8547
|
+
}
|
|
8548
|
+
const stats = fs12.statSync(learningsPath);
|
|
8549
|
+
const cacheKey = learningsPath;
|
|
8550
|
+
const cached = learningsCacheMap.get(cacheKey);
|
|
8551
|
+
let entries;
|
|
8552
|
+
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
8553
|
+
entries = cached.entries;
|
|
8554
|
+
} else {
|
|
8555
|
+
const content = fs12.readFileSync(learningsPath, "utf-8");
|
|
8556
|
+
const lines = content.split("\n");
|
|
8557
|
+
entries = [];
|
|
8558
|
+
let currentBlock = [];
|
|
8559
|
+
for (const line of lines) {
|
|
8560
|
+
if (line.startsWith("# ")) continue;
|
|
8561
|
+
if (/^<!--\s+hash:[a-f0-9]+/.test(line)) continue;
|
|
8562
|
+
const isDatedBullet = /^- \*\*\d{4}-\d{2}-\d{2}/.test(line);
|
|
8563
|
+
const isHeading = /^## \d{4}-\d{2}-\d{2}/.test(line);
|
|
8564
|
+
if (isDatedBullet || isHeading) {
|
|
8565
|
+
if (currentBlock.length > 0) {
|
|
8566
|
+
entries.push(currentBlock.join("\n"));
|
|
8567
|
+
}
|
|
8568
|
+
currentBlock = [line];
|
|
8569
|
+
} else if (line.trim() !== "" && currentBlock.length > 0) {
|
|
8570
|
+
currentBlock.push(line);
|
|
8571
|
+
}
|
|
8572
|
+
}
|
|
8573
|
+
if (currentBlock.length > 0) {
|
|
8574
|
+
entries.push(currentBlock.join("\n"));
|
|
8575
|
+
}
|
|
8576
|
+
learningsCacheMap.set(cacheKey, { mtimeMs: stats.mtimeMs, entries });
|
|
8577
|
+
evictIfNeeded(learningsCacheMap);
|
|
8578
|
+
}
|
|
8579
|
+
if (!skillName) {
|
|
8580
|
+
return (0, import_types.Ok)(entries);
|
|
8581
|
+
}
|
|
8582
|
+
const filtered = entries.filter((entry) => entry.includes(`[skill:${skillName}]`));
|
|
8583
|
+
return (0, import_types.Ok)(filtered);
|
|
8584
|
+
} catch (error) {
|
|
8585
|
+
return (0, import_types.Err)(
|
|
8586
|
+
new Error(
|
|
8587
|
+
`Failed to load learnings: ${error instanceof Error ? error.message : String(error)}`
|
|
8588
|
+
)
|
|
8589
|
+
);
|
|
8590
|
+
}
|
|
8591
|
+
}
|
|
8592
|
+
|
|
8593
|
+
// src/state/learnings.ts
|
|
8594
|
+
var fs13 = __toESM(require("fs"));
|
|
8595
|
+
var path10 = __toESM(require("path"));
|
|
8596
|
+
var crypto2 = __toESM(require("crypto"));
|
|
8481
8597
|
async function appendLearning(projectPath, learning, skillName, outcome, stream, session) {
|
|
8482
8598
|
try {
|
|
8483
8599
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
8484
8600
|
if (!dirResult.ok) return dirResult;
|
|
8485
8601
|
const stateDir = dirResult.value;
|
|
8486
|
-
const learningsPath =
|
|
8487
|
-
|
|
8602
|
+
const learningsPath = path10.join(stateDir, LEARNINGS_FILE);
|
|
8603
|
+
fs13.mkdirSync(stateDir, { recursive: true });
|
|
8488
8604
|
const normalizedContent = normalizeLearningContent(learning);
|
|
8489
8605
|
const contentHash = computeContentHash(normalizedContent);
|
|
8490
|
-
const hashesPath =
|
|
8606
|
+
const hashesPath = path10.join(stateDir, CONTENT_HASHES_FILE);
|
|
8491
8607
|
let contentHashes;
|
|
8492
|
-
if (
|
|
8608
|
+
if (fs13.existsSync(hashesPath)) {
|
|
8493
8609
|
contentHashes = loadContentHashes(stateDir);
|
|
8494
|
-
if (Object.keys(contentHashes).length === 0 &&
|
|
8495
|
-
contentHashes = rebuildContentHashes(stateDir);
|
|
8610
|
+
if (Object.keys(contentHashes).length === 0 && fs13.existsSync(learningsPath)) {
|
|
8611
|
+
contentHashes = rebuildContentHashes(stateDir, LEARNINGS_FILE);
|
|
8496
8612
|
}
|
|
8497
|
-
} else if (
|
|
8498
|
-
contentHashes = rebuildContentHashes(stateDir);
|
|
8613
|
+
} else if (fs13.existsSync(learningsPath)) {
|
|
8614
|
+
contentHashes = rebuildContentHashes(stateDir, LEARNINGS_FILE);
|
|
8499
8615
|
} else {
|
|
8500
8616
|
contentHashes = {};
|
|
8501
8617
|
}
|
|
@@ -8514,7 +8630,7 @@ async function appendLearning(projectPath, learning, skillName, outcome, stream,
|
|
|
8514
8630
|
} else {
|
|
8515
8631
|
bulletLine = `- **${timestamp}:** ${learning}`;
|
|
8516
8632
|
}
|
|
8517
|
-
const hash =
|
|
8633
|
+
const hash = crypto2.createHash("sha256").update(bulletLine).digest("hex").slice(0, 8);
|
|
8518
8634
|
const tagsStr = fmTags.length > 0 ? ` tags:${fmTags.join(",")}` : "";
|
|
8519
8635
|
const frontmatter = `<!-- hash:${hash}${tagsStr} -->`;
|
|
8520
8636
|
const entry = `
|
|
@@ -8522,19 +8638,19 @@ ${frontmatter}
|
|
|
8522
8638
|
${bulletLine}
|
|
8523
8639
|
`;
|
|
8524
8640
|
let existingLineCount;
|
|
8525
|
-
if (!
|
|
8526
|
-
|
|
8641
|
+
if (!fs13.existsSync(learningsPath)) {
|
|
8642
|
+
fs13.writeFileSync(learningsPath, `# Learnings
|
|
8527
8643
|
${entry}`);
|
|
8528
8644
|
existingLineCount = 1;
|
|
8529
8645
|
} else {
|
|
8530
|
-
const existingContent =
|
|
8646
|
+
const existingContent = fs13.readFileSync(learningsPath, "utf-8");
|
|
8531
8647
|
existingLineCount = existingContent.split("\n").length;
|
|
8532
|
-
|
|
8648
|
+
fs13.appendFileSync(learningsPath, entry);
|
|
8533
8649
|
}
|
|
8534
8650
|
const bulletLine_lineNum = existingLineCount + 2;
|
|
8535
8651
|
contentHashes[contentHash] = { date: timestamp ?? "", line: bulletLine_lineNum };
|
|
8536
8652
|
saveContentHashes(stateDir, contentHashes);
|
|
8537
|
-
|
|
8653
|
+
invalidateLearningsCacheEntry(learningsPath);
|
|
8538
8654
|
return (0, import_types.Ok)(void 0);
|
|
8539
8655
|
} catch (error) {
|
|
8540
8656
|
return (0, import_types.Err)(
|
|
@@ -8544,42 +8660,6 @@ ${entry}`);
|
|
|
8544
8660
|
);
|
|
8545
8661
|
}
|
|
8546
8662
|
}
|
|
8547
|
-
function estimateTokens(text) {
|
|
8548
|
-
return Math.ceil(text.length / 4);
|
|
8549
|
-
}
|
|
8550
|
-
function scoreRelevance(entry, intent) {
|
|
8551
|
-
if (!intent || intent.trim() === "") return 0;
|
|
8552
|
-
const intentWords = intent.toLowerCase().split(/\s+/).filter((w) => w.length > 2);
|
|
8553
|
-
if (intentWords.length === 0) return 0;
|
|
8554
|
-
const entryLower = entry.toLowerCase();
|
|
8555
|
-
const matches = intentWords.filter((word) => entryLower.includes(word));
|
|
8556
|
-
return matches.length / intentWords.length;
|
|
8557
|
-
}
|
|
8558
|
-
function parseDateFromEntry(entry) {
|
|
8559
|
-
const match = entry.match(/(\d{4}-\d{2}-\d{2})/);
|
|
8560
|
-
return match ? match[1] ?? null : null;
|
|
8561
|
-
}
|
|
8562
|
-
function analyzeLearningPatterns(entries) {
|
|
8563
|
-
const tagGroups = /* @__PURE__ */ new Map();
|
|
8564
|
-
for (const entry of entries) {
|
|
8565
|
-
const tagMatches = entry.matchAll(/\[(skill:[^\]]+)\]|\[(outcome:[^\]]+)\]/g);
|
|
8566
|
-
for (const match of tagMatches) {
|
|
8567
|
-
const tag = match[1] ?? match[2];
|
|
8568
|
-
if (tag) {
|
|
8569
|
-
const group = tagGroups.get(tag) ?? [];
|
|
8570
|
-
group.push(entry);
|
|
8571
|
-
tagGroups.set(tag, group);
|
|
8572
|
-
}
|
|
8573
|
-
}
|
|
8574
|
-
}
|
|
8575
|
-
const patterns = [];
|
|
8576
|
-
for (const [tag, groupEntries] of tagGroups) {
|
|
8577
|
-
if (groupEntries.length >= 3) {
|
|
8578
|
-
patterns.push({ tag, count: groupEntries.length, entries: groupEntries });
|
|
8579
|
-
}
|
|
8580
|
-
}
|
|
8581
|
-
return patterns.sort((a, b) => b.count - a.count);
|
|
8582
|
-
}
|
|
8583
8663
|
async function loadBudgetedLearnings(projectPath, options) {
|
|
8584
8664
|
const { intent, tokenBudget = 1e3, skill, session, stream, depth = "summary" } = options;
|
|
8585
8665
|
if (depth === "index") {
|
|
@@ -8643,11 +8723,11 @@ async function loadIndexEntries(projectPath, skillName, stream, session) {
|
|
|
8643
8723
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
8644
8724
|
if (!dirResult.ok) return dirResult;
|
|
8645
8725
|
const stateDir = dirResult.value;
|
|
8646
|
-
const learningsPath =
|
|
8647
|
-
if (!
|
|
8726
|
+
const learningsPath = path10.join(stateDir, LEARNINGS_FILE);
|
|
8727
|
+
if (!fs13.existsSync(learningsPath)) {
|
|
8648
8728
|
return (0, import_types.Ok)([]);
|
|
8649
8729
|
}
|
|
8650
|
-
const content =
|
|
8730
|
+
const content = fs13.readFileSync(learningsPath, "utf-8");
|
|
8651
8731
|
const lines = content.split("\n");
|
|
8652
8732
|
const indexEntries = [];
|
|
8653
8733
|
let pendingFrontmatter = null;
|
|
@@ -8700,74 +8780,25 @@ async function loadIndexEntries(projectPath, skillName, stream, session) {
|
|
|
8700
8780
|
);
|
|
8701
8781
|
}
|
|
8702
8782
|
}
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
const stateDir = dirResult.value;
|
|
8708
|
-
const learningsPath = path8.join(stateDir, LEARNINGS_FILE);
|
|
8709
|
-
if (!fs11.existsSync(learningsPath)) {
|
|
8710
|
-
return (0, import_types.Ok)([]);
|
|
8711
|
-
}
|
|
8712
|
-
const stats = fs11.statSync(learningsPath);
|
|
8713
|
-
const cacheKey = learningsPath;
|
|
8714
|
-
const cached = learningsCacheMap.get(cacheKey);
|
|
8715
|
-
let entries;
|
|
8716
|
-
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
8717
|
-
entries = cached.entries;
|
|
8718
|
-
} else {
|
|
8719
|
-
const content = fs11.readFileSync(learningsPath, "utf-8");
|
|
8720
|
-
const lines = content.split("\n");
|
|
8721
|
-
entries = [];
|
|
8722
|
-
let currentBlock = [];
|
|
8723
|
-
for (const line of lines) {
|
|
8724
|
-
if (line.startsWith("# ")) continue;
|
|
8725
|
-
if (/^<!--\s+hash:[a-f0-9]+/.test(line)) continue;
|
|
8726
|
-
const isDatedBullet = /^- \*\*\d{4}-\d{2}-\d{2}/.test(line);
|
|
8727
|
-
const isHeading = /^## \d{4}-\d{2}-\d{2}/.test(line);
|
|
8728
|
-
if (isDatedBullet || isHeading) {
|
|
8729
|
-
if (currentBlock.length > 0) {
|
|
8730
|
-
entries.push(currentBlock.join("\n"));
|
|
8731
|
-
}
|
|
8732
|
-
currentBlock = [line];
|
|
8733
|
-
} else if (line.trim() !== "" && currentBlock.length > 0) {
|
|
8734
|
-
currentBlock.push(line);
|
|
8735
|
-
}
|
|
8736
|
-
}
|
|
8737
|
-
if (currentBlock.length > 0) {
|
|
8738
|
-
entries.push(currentBlock.join("\n"));
|
|
8739
|
-
}
|
|
8740
|
-
learningsCacheMap.set(cacheKey, { mtimeMs: stats.mtimeMs, entries });
|
|
8741
|
-
evictIfNeeded(learningsCacheMap);
|
|
8742
|
-
}
|
|
8743
|
-
if (!skillName) {
|
|
8744
|
-
return (0, import_types.Ok)(entries);
|
|
8745
|
-
}
|
|
8746
|
-
const filtered = entries.filter((entry) => entry.includes(`[skill:${skillName}]`));
|
|
8747
|
-
return (0, import_types.Ok)(filtered);
|
|
8748
|
-
} catch (error) {
|
|
8749
|
-
return (0, import_types.Err)(
|
|
8750
|
-
new Error(
|
|
8751
|
-
`Failed to load learnings: ${error instanceof Error ? error.message : String(error)}`
|
|
8752
|
-
)
|
|
8753
|
-
);
|
|
8754
|
-
}
|
|
8755
|
-
}
|
|
8783
|
+
|
|
8784
|
+
// src/state/learnings-lifecycle.ts
|
|
8785
|
+
var fs14 = __toESM(require("fs"));
|
|
8786
|
+
var path11 = __toESM(require("path"));
|
|
8756
8787
|
async function archiveLearnings(projectPath, entries, stream) {
|
|
8757
8788
|
try {
|
|
8758
8789
|
const dirResult = await getStateDir(projectPath, stream);
|
|
8759
8790
|
if (!dirResult.ok) return dirResult;
|
|
8760
8791
|
const stateDir = dirResult.value;
|
|
8761
|
-
const archiveDir =
|
|
8762
|
-
|
|
8792
|
+
const archiveDir = path11.join(stateDir, "learnings-archive");
|
|
8793
|
+
fs14.mkdirSync(archiveDir, { recursive: true });
|
|
8763
8794
|
const now = /* @__PURE__ */ new Date();
|
|
8764
8795
|
const yearMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
|
|
8765
|
-
const archivePath =
|
|
8796
|
+
const archivePath = path11.join(archiveDir, `${yearMonth}.md`);
|
|
8766
8797
|
const archiveContent = entries.join("\n\n") + "\n";
|
|
8767
|
-
if (
|
|
8768
|
-
|
|
8798
|
+
if (fs14.existsSync(archivePath)) {
|
|
8799
|
+
fs14.appendFileSync(archivePath, "\n" + archiveContent);
|
|
8769
8800
|
} else {
|
|
8770
|
-
|
|
8801
|
+
fs14.writeFileSync(archivePath, `# Learnings Archive
|
|
8771
8802
|
|
|
8772
8803
|
${archiveContent}`);
|
|
8773
8804
|
}
|
|
@@ -8785,8 +8816,8 @@ async function pruneLearnings(projectPath, stream) {
|
|
|
8785
8816
|
const dirResult = await getStateDir(projectPath, stream);
|
|
8786
8817
|
if (!dirResult.ok) return dirResult;
|
|
8787
8818
|
const stateDir = dirResult.value;
|
|
8788
|
-
const learningsPath =
|
|
8789
|
-
if (!
|
|
8819
|
+
const learningsPath = path11.join(stateDir, LEARNINGS_FILE);
|
|
8820
|
+
if (!fs14.existsSync(learningsPath)) {
|
|
8790
8821
|
return (0, import_types.Ok)({ kept: 0, archived: 0, patterns: [] });
|
|
8791
8822
|
}
|
|
8792
8823
|
const loadResult = await loadRelevantLearnings(projectPath, void 0, stream);
|
|
@@ -8817,8 +8848,8 @@ async function pruneLearnings(projectPath, stream) {
|
|
|
8817
8848
|
if (!archiveResult.ok) return archiveResult;
|
|
8818
8849
|
}
|
|
8819
8850
|
const newContent = "# Learnings\n\n" + toKeep.join("\n\n") + "\n";
|
|
8820
|
-
|
|
8821
|
-
|
|
8851
|
+
fs14.writeFileSync(learningsPath, newContent);
|
|
8852
|
+
invalidateLearningsCacheEntry(learningsPath);
|
|
8822
8853
|
return (0, import_types.Ok)({
|
|
8823
8854
|
kept: toKeep.length,
|
|
8824
8855
|
archived: toArchive.length,
|
|
@@ -8862,21 +8893,21 @@ async function promoteSessionLearnings(projectPath, sessionSlug, stream) {
|
|
|
8862
8893
|
const dirResult = await getStateDir(projectPath, stream);
|
|
8863
8894
|
if (!dirResult.ok) return dirResult;
|
|
8864
8895
|
const stateDir = dirResult.value;
|
|
8865
|
-
const globalPath =
|
|
8866
|
-
const existingGlobal =
|
|
8896
|
+
const globalPath = path11.join(stateDir, LEARNINGS_FILE);
|
|
8897
|
+
const existingGlobal = fs14.existsSync(globalPath) ? fs14.readFileSync(globalPath, "utf-8") : "";
|
|
8867
8898
|
const newEntries = toPromote.filter((entry) => !existingGlobal.includes(entry.trim()));
|
|
8868
8899
|
if (newEntries.length === 0) {
|
|
8869
8900
|
return (0, import_types.Ok)({ promoted: 0, skipped: skipped + toPromote.length });
|
|
8870
8901
|
}
|
|
8871
8902
|
const promotedContent = newEntries.join("\n\n") + "\n";
|
|
8872
8903
|
if (!existingGlobal) {
|
|
8873
|
-
|
|
8904
|
+
fs14.writeFileSync(globalPath, `# Learnings
|
|
8874
8905
|
|
|
8875
8906
|
${promotedContent}`);
|
|
8876
8907
|
} else {
|
|
8877
|
-
|
|
8908
|
+
fs14.appendFileSync(globalPath, "\n\n" + promotedContent);
|
|
8878
8909
|
}
|
|
8879
|
-
|
|
8910
|
+
invalidateLearningsCacheEntry(globalPath);
|
|
8880
8911
|
return (0, import_types.Ok)({
|
|
8881
8912
|
promoted: newEntries.length,
|
|
8882
8913
|
skipped: skipped + (toPromote.length - newEntries.length)
|
|
@@ -8896,8 +8927,8 @@ async function countLearningEntries(projectPath, stream) {
|
|
|
8896
8927
|
}
|
|
8897
8928
|
|
|
8898
8929
|
// src/state/failures.ts
|
|
8899
|
-
var
|
|
8900
|
-
var
|
|
8930
|
+
var fs15 = __toESM(require("fs"));
|
|
8931
|
+
var path12 = __toESM(require("path"));
|
|
8901
8932
|
var failuresCacheMap = /* @__PURE__ */ new Map();
|
|
8902
8933
|
function clearFailuresCache() {
|
|
8903
8934
|
failuresCacheMap.clear();
|
|
@@ -8908,17 +8939,17 @@ async function appendFailure(projectPath, description, skillName, type, stream,
|
|
|
8908
8939
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
8909
8940
|
if (!dirResult.ok) return dirResult;
|
|
8910
8941
|
const stateDir = dirResult.value;
|
|
8911
|
-
const failuresPath =
|
|
8912
|
-
|
|
8942
|
+
const failuresPath = path12.join(stateDir, FAILURES_FILE);
|
|
8943
|
+
fs15.mkdirSync(stateDir, { recursive: true });
|
|
8913
8944
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
8914
8945
|
const entry = `
|
|
8915
8946
|
- **${timestamp} [skill:${skillName}] [type:${type}]:** ${description}
|
|
8916
8947
|
`;
|
|
8917
|
-
if (!
|
|
8918
|
-
|
|
8948
|
+
if (!fs15.existsSync(failuresPath)) {
|
|
8949
|
+
fs15.writeFileSync(failuresPath, `# Failures
|
|
8919
8950
|
${entry}`);
|
|
8920
8951
|
} else {
|
|
8921
|
-
|
|
8952
|
+
fs15.appendFileSync(failuresPath, entry);
|
|
8922
8953
|
}
|
|
8923
8954
|
failuresCacheMap.delete(failuresPath);
|
|
8924
8955
|
return (0, import_types.Ok)(void 0);
|
|
@@ -8935,17 +8966,17 @@ async function loadFailures(projectPath, stream, session) {
|
|
|
8935
8966
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
8936
8967
|
if (!dirResult.ok) return dirResult;
|
|
8937
8968
|
const stateDir = dirResult.value;
|
|
8938
|
-
const failuresPath =
|
|
8939
|
-
if (!
|
|
8969
|
+
const failuresPath = path12.join(stateDir, FAILURES_FILE);
|
|
8970
|
+
if (!fs15.existsSync(failuresPath)) {
|
|
8940
8971
|
return (0, import_types.Ok)([]);
|
|
8941
8972
|
}
|
|
8942
|
-
const stats =
|
|
8973
|
+
const stats = fs15.statSync(failuresPath);
|
|
8943
8974
|
const cacheKey = failuresPath;
|
|
8944
8975
|
const cached = failuresCacheMap.get(cacheKey);
|
|
8945
8976
|
if (cached && cached.mtimeMs === stats.mtimeMs) {
|
|
8946
8977
|
return (0, import_types.Ok)(cached.entries);
|
|
8947
8978
|
}
|
|
8948
|
-
const content =
|
|
8979
|
+
const content = fs15.readFileSync(failuresPath, "utf-8");
|
|
8949
8980
|
const entries = [];
|
|
8950
8981
|
for (const line of content.split("\n")) {
|
|
8951
8982
|
const match = line.match(FAILURE_LINE_REGEX);
|
|
@@ -8974,20 +9005,20 @@ async function archiveFailures(projectPath, stream, session) {
|
|
|
8974
9005
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
8975
9006
|
if (!dirResult.ok) return dirResult;
|
|
8976
9007
|
const stateDir = dirResult.value;
|
|
8977
|
-
const failuresPath =
|
|
8978
|
-
if (!
|
|
9008
|
+
const failuresPath = path12.join(stateDir, FAILURES_FILE);
|
|
9009
|
+
if (!fs15.existsSync(failuresPath)) {
|
|
8979
9010
|
return (0, import_types.Ok)(void 0);
|
|
8980
9011
|
}
|
|
8981
|
-
const archiveDir =
|
|
8982
|
-
|
|
9012
|
+
const archiveDir = path12.join(stateDir, "archive");
|
|
9013
|
+
fs15.mkdirSync(archiveDir, { recursive: true });
|
|
8983
9014
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
8984
9015
|
let archiveName = `failures-${date}.md`;
|
|
8985
9016
|
let counter = 2;
|
|
8986
|
-
while (
|
|
9017
|
+
while (fs15.existsSync(path12.join(archiveDir, archiveName))) {
|
|
8987
9018
|
archiveName = `failures-${date}-${counter}.md`;
|
|
8988
9019
|
counter++;
|
|
8989
9020
|
}
|
|
8990
|
-
|
|
9021
|
+
fs15.renameSync(failuresPath, path12.join(archiveDir, archiveName));
|
|
8991
9022
|
failuresCacheMap.delete(failuresPath);
|
|
8992
9023
|
return (0, import_types.Ok)(void 0);
|
|
8993
9024
|
} catch (error) {
|
|
@@ -9000,16 +9031,16 @@ async function archiveFailures(projectPath, stream, session) {
|
|
|
9000
9031
|
}
|
|
9001
9032
|
|
|
9002
9033
|
// src/state/handoff.ts
|
|
9003
|
-
var
|
|
9004
|
-
var
|
|
9034
|
+
var fs16 = __toESM(require("fs"));
|
|
9035
|
+
var path13 = __toESM(require("path"));
|
|
9005
9036
|
async function saveHandoff(projectPath, handoff, stream, session) {
|
|
9006
9037
|
try {
|
|
9007
9038
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
9008
9039
|
if (!dirResult.ok) return dirResult;
|
|
9009
9040
|
const stateDir = dirResult.value;
|
|
9010
|
-
const handoffPath =
|
|
9011
|
-
|
|
9012
|
-
|
|
9041
|
+
const handoffPath = path13.join(stateDir, HANDOFF_FILE);
|
|
9042
|
+
fs16.mkdirSync(stateDir, { recursive: true });
|
|
9043
|
+
fs16.writeFileSync(handoffPath, JSON.stringify(handoff, null, 2));
|
|
9013
9044
|
return (0, import_types.Ok)(void 0);
|
|
9014
9045
|
} catch (error) {
|
|
9015
9046
|
return (0, import_types.Err)(
|
|
@@ -9022,11 +9053,11 @@ async function loadHandoff(projectPath, stream, session) {
|
|
|
9022
9053
|
const dirResult = await getStateDir(projectPath, stream, session);
|
|
9023
9054
|
if (!dirResult.ok) return dirResult;
|
|
9024
9055
|
const stateDir = dirResult.value;
|
|
9025
|
-
const handoffPath =
|
|
9026
|
-
if (!
|
|
9056
|
+
const handoffPath = path13.join(stateDir, HANDOFF_FILE);
|
|
9057
|
+
if (!fs16.existsSync(handoffPath)) {
|
|
9027
9058
|
return (0, import_types.Ok)(null);
|
|
9028
9059
|
}
|
|
9029
|
-
const raw =
|
|
9060
|
+
const raw = fs16.readFileSync(handoffPath, "utf-8");
|
|
9030
9061
|
const parsed = JSON.parse(raw);
|
|
9031
9062
|
const result = HandoffSchema.safeParse(parsed);
|
|
9032
9063
|
if (!result.success) {
|
|
@@ -9041,33 +9072,33 @@ async function loadHandoff(projectPath, stream, session) {
|
|
|
9041
9072
|
}
|
|
9042
9073
|
|
|
9043
9074
|
// src/state/mechanical-gate.ts
|
|
9044
|
-
var
|
|
9045
|
-
var
|
|
9075
|
+
var fs17 = __toESM(require("fs"));
|
|
9076
|
+
var path14 = __toESM(require("path"));
|
|
9046
9077
|
var import_child_process2 = require("child_process");
|
|
9047
9078
|
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:.-]+$/;
|
|
9048
9079
|
function loadChecksFromConfig(gateConfigPath) {
|
|
9049
|
-
if (!
|
|
9050
|
-
const raw = JSON.parse(
|
|
9080
|
+
if (!fs17.existsSync(gateConfigPath)) return [];
|
|
9081
|
+
const raw = JSON.parse(fs17.readFileSync(gateConfigPath, "utf-8"));
|
|
9051
9082
|
const config = GateConfigSchema.safeParse(raw);
|
|
9052
9083
|
if (config.success && config.data.checks) return config.data.checks;
|
|
9053
9084
|
return [];
|
|
9054
9085
|
}
|
|
9055
9086
|
function discoverChecksFromProject(projectPath) {
|
|
9056
9087
|
const checks = [];
|
|
9057
|
-
const packageJsonPath =
|
|
9058
|
-
if (
|
|
9059
|
-
const pkg = JSON.parse(
|
|
9088
|
+
const packageJsonPath = path14.join(projectPath, "package.json");
|
|
9089
|
+
if (fs17.existsSync(packageJsonPath)) {
|
|
9090
|
+
const pkg = JSON.parse(fs17.readFileSync(packageJsonPath, "utf-8"));
|
|
9060
9091
|
const scripts = pkg.scripts || {};
|
|
9061
9092
|
if (scripts.test) checks.push({ name: "test", command: "npm test" });
|
|
9062
9093
|
if (scripts.lint) checks.push({ name: "lint", command: "npm run lint" });
|
|
9063
9094
|
if (scripts.typecheck) checks.push({ name: "typecheck", command: "npm run typecheck" });
|
|
9064
9095
|
if (scripts.build) checks.push({ name: "build", command: "npm run build" });
|
|
9065
9096
|
}
|
|
9066
|
-
if (
|
|
9097
|
+
if (fs17.existsSync(path14.join(projectPath, "go.mod"))) {
|
|
9067
9098
|
checks.push({ name: "test", command: "go test ./..." });
|
|
9068
9099
|
checks.push({ name: "build", command: "go build ./..." });
|
|
9069
9100
|
}
|
|
9070
|
-
if (
|
|
9101
|
+
if (fs17.existsSync(path14.join(projectPath, "pyproject.toml")) || fs17.existsSync(path14.join(projectPath, "setup.py"))) {
|
|
9071
9102
|
checks.push({ name: "test", command: "python -m pytest" });
|
|
9072
9103
|
}
|
|
9073
9104
|
return checks;
|
|
@@ -9107,8 +9138,8 @@ function executeCheck(check, projectPath) {
|
|
|
9107
9138
|
}
|
|
9108
9139
|
}
|
|
9109
9140
|
async function runMechanicalGate(projectPath) {
|
|
9110
|
-
const harnessDir =
|
|
9111
|
-
const gateConfigPath =
|
|
9141
|
+
const harnessDir = path14.join(projectPath, HARNESS_DIR);
|
|
9142
|
+
const gateConfigPath = path14.join(harnessDir, GATE_CONFIG_FILE);
|
|
9112
9143
|
try {
|
|
9113
9144
|
let checks = loadChecksFromConfig(gateConfigPath);
|
|
9114
9145
|
if (checks.length === 0) {
|
|
@@ -9129,8 +9160,8 @@ async function runMechanicalGate(projectPath) {
|
|
|
9129
9160
|
}
|
|
9130
9161
|
|
|
9131
9162
|
// src/state/session-summary.ts
|
|
9132
|
-
var
|
|
9133
|
-
var
|
|
9163
|
+
var fs18 = __toESM(require("fs"));
|
|
9164
|
+
var path15 = __toESM(require("path"));
|
|
9134
9165
|
function formatSummary(data) {
|
|
9135
9166
|
const lines = [
|
|
9136
9167
|
"## Session Summary",
|
|
@@ -9168,9 +9199,9 @@ function writeSessionSummary(projectPath, sessionSlug, data) {
|
|
|
9168
9199
|
const dirResult = resolveSessionDir(projectPath, sessionSlug, { create: true });
|
|
9169
9200
|
if (!dirResult.ok) return dirResult;
|
|
9170
9201
|
const sessionDir = dirResult.value;
|
|
9171
|
-
const summaryPath =
|
|
9202
|
+
const summaryPath = path15.join(sessionDir, SUMMARY_FILE);
|
|
9172
9203
|
const content = formatSummary(data);
|
|
9173
|
-
|
|
9204
|
+
fs18.writeFileSync(summaryPath, content);
|
|
9174
9205
|
const description = deriveIndexDescription(data);
|
|
9175
9206
|
updateSessionIndex(projectPath, sessionSlug, description);
|
|
9176
9207
|
return (0, import_types.Ok)(void 0);
|
|
@@ -9187,11 +9218,11 @@ function loadSessionSummary(projectPath, sessionSlug) {
|
|
|
9187
9218
|
const dirResult = resolveSessionDir(projectPath, sessionSlug);
|
|
9188
9219
|
if (!dirResult.ok) return dirResult;
|
|
9189
9220
|
const sessionDir = dirResult.value;
|
|
9190
|
-
const summaryPath =
|
|
9191
|
-
if (!
|
|
9221
|
+
const summaryPath = path15.join(sessionDir, SUMMARY_FILE);
|
|
9222
|
+
if (!fs18.existsSync(summaryPath)) {
|
|
9192
9223
|
return (0, import_types.Ok)(null);
|
|
9193
9224
|
}
|
|
9194
|
-
const content =
|
|
9225
|
+
const content = fs18.readFileSync(summaryPath, "utf-8");
|
|
9195
9226
|
return (0, import_types.Ok)(content);
|
|
9196
9227
|
} catch (error) {
|
|
9197
9228
|
return (0, import_types.Err)(
|
|
@@ -9203,11 +9234,11 @@ function loadSessionSummary(projectPath, sessionSlug) {
|
|
|
9203
9234
|
}
|
|
9204
9235
|
function listActiveSessions(projectPath) {
|
|
9205
9236
|
try {
|
|
9206
|
-
const indexPath2 =
|
|
9207
|
-
if (!
|
|
9237
|
+
const indexPath2 = path15.join(projectPath, HARNESS_DIR, SESSIONS_DIR, SESSION_INDEX_FILE);
|
|
9238
|
+
if (!fs18.existsSync(indexPath2)) {
|
|
9208
9239
|
return (0, import_types.Ok)(null);
|
|
9209
9240
|
}
|
|
9210
|
-
const content =
|
|
9241
|
+
const content = fs18.readFileSync(indexPath2, "utf-8");
|
|
9211
9242
|
return (0, import_types.Ok)(content);
|
|
9212
9243
|
} catch (error) {
|
|
9213
9244
|
return (0, import_types.Err)(
|
|
@@ -9219,8 +9250,8 @@ function listActiveSessions(projectPath) {
|
|
|
9219
9250
|
}
|
|
9220
9251
|
|
|
9221
9252
|
// src/state/session-sections.ts
|
|
9222
|
-
var
|
|
9223
|
-
var
|
|
9253
|
+
var fs19 = __toESM(require("fs"));
|
|
9254
|
+
var path16 = __toESM(require("path"));
|
|
9224
9255
|
var import_types19 = require("@harness-engineering/types");
|
|
9225
9256
|
function emptySections() {
|
|
9226
9257
|
const sections = {};
|
|
@@ -9233,12 +9264,12 @@ async function loadSessionState(projectPath, sessionSlug) {
|
|
|
9233
9264
|
const dirResult = resolveSessionDir(projectPath, sessionSlug);
|
|
9234
9265
|
if (!dirResult.ok) return dirResult;
|
|
9235
9266
|
const sessionDir = dirResult.value;
|
|
9236
|
-
const filePath =
|
|
9237
|
-
if (!
|
|
9267
|
+
const filePath = path16.join(sessionDir, SESSION_STATE_FILE);
|
|
9268
|
+
if (!fs19.existsSync(filePath)) {
|
|
9238
9269
|
return (0, import_types.Ok)(emptySections());
|
|
9239
9270
|
}
|
|
9240
9271
|
try {
|
|
9241
|
-
const raw =
|
|
9272
|
+
const raw = fs19.readFileSync(filePath, "utf-8");
|
|
9242
9273
|
const parsed = JSON.parse(raw);
|
|
9243
9274
|
const sections = emptySections();
|
|
9244
9275
|
for (const name of import_types19.SESSION_SECTION_NAMES) {
|
|
@@ -9259,9 +9290,9 @@ async function saveSessionState(projectPath, sessionSlug, sections) {
|
|
|
9259
9290
|
const dirResult = resolveSessionDir(projectPath, sessionSlug, { create: true });
|
|
9260
9291
|
if (!dirResult.ok) return dirResult;
|
|
9261
9292
|
const sessionDir = dirResult.value;
|
|
9262
|
-
const filePath =
|
|
9293
|
+
const filePath = path16.join(sessionDir, SESSION_STATE_FILE);
|
|
9263
9294
|
try {
|
|
9264
|
-
|
|
9295
|
+
fs19.writeFileSync(filePath, JSON.stringify(sections, null, 2));
|
|
9265
9296
|
return (0, import_types.Ok)(void 0);
|
|
9266
9297
|
} catch (error) {
|
|
9267
9298
|
return (0, import_types.Err)(
|
|
@@ -9315,32 +9346,32 @@ function generateEntryId() {
|
|
|
9315
9346
|
}
|
|
9316
9347
|
|
|
9317
9348
|
// src/state/session-archive.ts
|
|
9318
|
-
var
|
|
9319
|
-
var
|
|
9349
|
+
var fs20 = __toESM(require("fs"));
|
|
9350
|
+
var path17 = __toESM(require("path"));
|
|
9320
9351
|
async function archiveSession(projectPath, sessionSlug) {
|
|
9321
9352
|
const dirResult = resolveSessionDir(projectPath, sessionSlug);
|
|
9322
9353
|
if (!dirResult.ok) return dirResult;
|
|
9323
9354
|
const sessionDir = dirResult.value;
|
|
9324
|
-
if (!
|
|
9355
|
+
if (!fs20.existsSync(sessionDir)) {
|
|
9325
9356
|
return (0, import_types.Err)(new Error(`Session '${sessionSlug}' not found at ${sessionDir}`));
|
|
9326
9357
|
}
|
|
9327
|
-
const archiveBase =
|
|
9358
|
+
const archiveBase = path17.join(projectPath, HARNESS_DIR, ARCHIVE_DIR, "sessions");
|
|
9328
9359
|
try {
|
|
9329
|
-
|
|
9360
|
+
fs20.mkdirSync(archiveBase, { recursive: true });
|
|
9330
9361
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
9331
9362
|
let archiveName = `${sessionSlug}-${date}`;
|
|
9332
9363
|
let counter = 1;
|
|
9333
|
-
while (
|
|
9364
|
+
while (fs20.existsSync(path17.join(archiveBase, archiveName))) {
|
|
9334
9365
|
archiveName = `${sessionSlug}-${date}-${counter}`;
|
|
9335
9366
|
counter++;
|
|
9336
9367
|
}
|
|
9337
|
-
const dest =
|
|
9368
|
+
const dest = path17.join(archiveBase, archiveName);
|
|
9338
9369
|
try {
|
|
9339
|
-
|
|
9370
|
+
fs20.renameSync(sessionDir, dest);
|
|
9340
9371
|
} catch (renameErr) {
|
|
9341
9372
|
if (renameErr instanceof Error && "code" in renameErr && renameErr.code === "EXDEV") {
|
|
9342
|
-
|
|
9343
|
-
|
|
9373
|
+
fs20.cpSync(sessionDir, dest, { recursive: true });
|
|
9374
|
+
fs20.rmSync(sessionDir, { recursive: true });
|
|
9344
9375
|
} else {
|
|
9345
9376
|
throw renameErr;
|
|
9346
9377
|
}
|
|
@@ -9356,8 +9387,8 @@ async function archiveSession(projectPath, sessionSlug) {
|
|
|
9356
9387
|
}
|
|
9357
9388
|
|
|
9358
9389
|
// src/state/events.ts
|
|
9359
|
-
var
|
|
9360
|
-
var
|
|
9390
|
+
var fs21 = __toESM(require("fs"));
|
|
9391
|
+
var path18 = __toESM(require("path"));
|
|
9361
9392
|
var import_zod8 = require("zod");
|
|
9362
9393
|
var SkillEventSchema = import_zod8.z.object({
|
|
9363
9394
|
timestamp: import_zod8.z.string(),
|
|
@@ -9378,8 +9409,8 @@ function loadKnownHashes(eventsPath) {
|
|
|
9378
9409
|
const cached = knownHashesCache.get(eventsPath);
|
|
9379
9410
|
if (cached) return cached;
|
|
9380
9411
|
const hashes = /* @__PURE__ */ new Set();
|
|
9381
|
-
if (
|
|
9382
|
-
const content =
|
|
9412
|
+
if (fs21.existsSync(eventsPath)) {
|
|
9413
|
+
const content = fs21.readFileSync(eventsPath, "utf-8");
|
|
9383
9414
|
const lines = content.split("\n").filter((line) => line.trim() !== "");
|
|
9384
9415
|
for (const line of lines) {
|
|
9385
9416
|
try {
|
|
@@ -9402,8 +9433,8 @@ async function emitEvent(projectPath, event, options) {
|
|
|
9402
9433
|
const dirResult = await getStateDir(projectPath, options?.stream, options?.session);
|
|
9403
9434
|
if (!dirResult.ok) return dirResult;
|
|
9404
9435
|
const stateDir = dirResult.value;
|
|
9405
|
-
const eventsPath =
|
|
9406
|
-
|
|
9436
|
+
const eventsPath = path18.join(stateDir, EVENTS_FILE);
|
|
9437
|
+
fs21.mkdirSync(stateDir, { recursive: true });
|
|
9407
9438
|
const contentHash = computeEventHash(event, options?.session);
|
|
9408
9439
|
const knownHashes = loadKnownHashes(eventsPath);
|
|
9409
9440
|
if (knownHashes.has(contentHash)) {
|
|
@@ -9417,7 +9448,7 @@ async function emitEvent(projectPath, event, options) {
|
|
|
9417
9448
|
if (options?.session) {
|
|
9418
9449
|
fullEvent.session = options.session;
|
|
9419
9450
|
}
|
|
9420
|
-
|
|
9451
|
+
fs21.appendFileSync(eventsPath, JSON.stringify(fullEvent) + "\n");
|
|
9421
9452
|
knownHashes.add(contentHash);
|
|
9422
9453
|
return (0, import_types.Ok)({ written: true });
|
|
9423
9454
|
} catch (error) {
|
|
@@ -9431,11 +9462,11 @@ async function loadEvents(projectPath, options) {
|
|
|
9431
9462
|
const dirResult = await getStateDir(projectPath, options?.stream, options?.session);
|
|
9432
9463
|
if (!dirResult.ok) return dirResult;
|
|
9433
9464
|
const stateDir = dirResult.value;
|
|
9434
|
-
const eventsPath =
|
|
9435
|
-
if (!
|
|
9465
|
+
const eventsPath = path18.join(stateDir, EVENTS_FILE);
|
|
9466
|
+
if (!fs21.existsSync(eventsPath)) {
|
|
9436
9467
|
return (0, import_types.Ok)([]);
|
|
9437
9468
|
}
|
|
9438
|
-
const content =
|
|
9469
|
+
const content = fs21.readFileSync(eventsPath, "utf-8");
|
|
9439
9470
|
const lines = content.split("\n").filter((line) => line.trim() !== "");
|
|
9440
9471
|
const events = [];
|
|
9441
9472
|
for (const line of lines) {
|
|
@@ -9649,7 +9680,7 @@ async function runMultiTurnPipeline(initialContext, turnExecutor, options) {
|
|
|
9649
9680
|
}
|
|
9650
9681
|
|
|
9651
9682
|
// src/security/scanner.ts
|
|
9652
|
-
var
|
|
9683
|
+
var fs23 = __toESM(require("fs/promises"));
|
|
9653
9684
|
var import_minimatch5 = require("minimatch");
|
|
9654
9685
|
|
|
9655
9686
|
// src/security/rules/registry.ts
|
|
@@ -9737,15 +9768,15 @@ function resolveRuleSeverity(ruleId, defaultSeverity, overrides, strict) {
|
|
|
9737
9768
|
}
|
|
9738
9769
|
|
|
9739
9770
|
// src/security/stack-detector.ts
|
|
9740
|
-
var
|
|
9741
|
-
var
|
|
9771
|
+
var fs22 = __toESM(require("fs"));
|
|
9772
|
+
var path19 = __toESM(require("path"));
|
|
9742
9773
|
function detectStack(projectRoot) {
|
|
9743
9774
|
const stacks = [];
|
|
9744
|
-
const pkgJsonPath =
|
|
9745
|
-
if (
|
|
9775
|
+
const pkgJsonPath = path19.join(projectRoot, "package.json");
|
|
9776
|
+
if (fs22.existsSync(pkgJsonPath)) {
|
|
9746
9777
|
stacks.push("node");
|
|
9747
9778
|
try {
|
|
9748
|
-
const pkgJson = JSON.parse(
|
|
9779
|
+
const pkgJson = JSON.parse(fs22.readFileSync(pkgJsonPath, "utf-8"));
|
|
9749
9780
|
const allDeps = {
|
|
9750
9781
|
...pkgJson.dependencies,
|
|
9751
9782
|
...pkgJson.devDependencies
|
|
@@ -9760,13 +9791,13 @@ function detectStack(projectRoot) {
|
|
|
9760
9791
|
} catch {
|
|
9761
9792
|
}
|
|
9762
9793
|
}
|
|
9763
|
-
const goModPath =
|
|
9764
|
-
if (
|
|
9794
|
+
const goModPath = path19.join(projectRoot, "go.mod");
|
|
9795
|
+
if (fs22.existsSync(goModPath)) {
|
|
9765
9796
|
stacks.push("go");
|
|
9766
9797
|
}
|
|
9767
|
-
const requirementsPath =
|
|
9768
|
-
const pyprojectPath =
|
|
9769
|
-
if (
|
|
9798
|
+
const requirementsPath = path19.join(projectRoot, "requirements.txt");
|
|
9799
|
+
const pyprojectPath = path19.join(projectRoot, "pyproject.toml");
|
|
9800
|
+
if (fs22.existsSync(requirementsPath) || fs22.existsSync(pyprojectPath)) {
|
|
9770
9801
|
stacks.push("python");
|
|
9771
9802
|
}
|
|
9772
9803
|
return stacks;
|
|
@@ -10597,7 +10628,7 @@ var SecurityScanner = class {
|
|
|
10597
10628
|
}
|
|
10598
10629
|
async scanFile(filePath) {
|
|
10599
10630
|
if (!this.config.enabled) return [];
|
|
10600
|
-
const content = await
|
|
10631
|
+
const content = await fs23.readFile(filePath, "utf-8");
|
|
10601
10632
|
return this.scanContentForFile(content, filePath, 1);
|
|
10602
10633
|
}
|
|
10603
10634
|
scanContentForFile(content, filePath, startLine = 1) {
|
|
@@ -11100,7 +11131,7 @@ function mapSecurityFindings(secFindings, existing) {
|
|
|
11100
11131
|
}
|
|
11101
11132
|
|
|
11102
11133
|
// src/ci/check-orchestrator.ts
|
|
11103
|
-
var
|
|
11134
|
+
var path20 = __toESM(require("path"));
|
|
11104
11135
|
var import_graph = require("@harness-engineering/graph");
|
|
11105
11136
|
var ALL_CHECKS = [
|
|
11106
11137
|
"validate",
|
|
@@ -11115,7 +11146,7 @@ var ALL_CHECKS = [
|
|
|
11115
11146
|
];
|
|
11116
11147
|
async function runValidateCheck(projectRoot, config) {
|
|
11117
11148
|
const issues = [];
|
|
11118
|
-
const agentsPath =
|
|
11149
|
+
const agentsPath = path20.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
11119
11150
|
const result = await validateAgentsMap(agentsPath);
|
|
11120
11151
|
if (!result.ok) {
|
|
11121
11152
|
issues.push({ severity: "error", message: result.error.message });
|
|
@@ -11172,7 +11203,7 @@ async function runDepsCheck(projectRoot, config) {
|
|
|
11172
11203
|
}
|
|
11173
11204
|
async function runDocsCheck(projectRoot, config) {
|
|
11174
11205
|
const issues = [];
|
|
11175
|
-
const docsDir =
|
|
11206
|
+
const docsDir = path20.join(projectRoot, config.docsDir ?? "docs");
|
|
11176
11207
|
const entropyConfig = config.entropy || {};
|
|
11177
11208
|
const result = await checkDocCoverage("project", {
|
|
11178
11209
|
docsDir,
|
|
@@ -11357,7 +11388,7 @@ async function runTraceabilityCheck(projectRoot, config) {
|
|
|
11357
11388
|
const issues = [];
|
|
11358
11389
|
const traceConfig = config.traceability || {};
|
|
11359
11390
|
if (traceConfig.enabled === false) return issues;
|
|
11360
|
-
const graphDir =
|
|
11391
|
+
const graphDir = path20.join(projectRoot, ".harness", "graph");
|
|
11361
11392
|
const store = new import_graph.GraphStore();
|
|
11362
11393
|
const loaded = await store.load(graphDir);
|
|
11363
11394
|
if (!loaded) {
|
|
@@ -11486,7 +11517,7 @@ async function runCIChecks(input) {
|
|
|
11486
11517
|
}
|
|
11487
11518
|
|
|
11488
11519
|
// src/review/mechanical-checks.ts
|
|
11489
|
-
var
|
|
11520
|
+
var path21 = __toESM(require("path"));
|
|
11490
11521
|
async function runMechanicalChecks(options) {
|
|
11491
11522
|
const { projectRoot, config, skip = [], changedFiles } = options;
|
|
11492
11523
|
const findings = [];
|
|
@@ -11498,7 +11529,7 @@ async function runMechanicalChecks(options) {
|
|
|
11498
11529
|
};
|
|
11499
11530
|
if (!skip.includes("validate")) {
|
|
11500
11531
|
try {
|
|
11501
|
-
const agentsPath =
|
|
11532
|
+
const agentsPath = path21.join(projectRoot, config.agentsMapPath ?? "AGENTS.md");
|
|
11502
11533
|
const result = await validateAgentsMap(agentsPath);
|
|
11503
11534
|
if (!result.ok) {
|
|
11504
11535
|
statuses.validate = "fail";
|
|
@@ -11535,7 +11566,7 @@ async function runMechanicalChecks(options) {
|
|
|
11535
11566
|
statuses.validate = "fail";
|
|
11536
11567
|
findings.push({
|
|
11537
11568
|
tool: "validate",
|
|
11538
|
-
file:
|
|
11569
|
+
file: path21.join(projectRoot, "AGENTS.md"),
|
|
11539
11570
|
message: err instanceof Error ? err.message : String(err),
|
|
11540
11571
|
severity: "error"
|
|
11541
11572
|
});
|
|
@@ -11599,7 +11630,7 @@ async function runMechanicalChecks(options) {
|
|
|
11599
11630
|
(async () => {
|
|
11600
11631
|
const localFindings = [];
|
|
11601
11632
|
try {
|
|
11602
|
-
const docsDir =
|
|
11633
|
+
const docsDir = path21.join(projectRoot, config.docsDir ?? "docs");
|
|
11603
11634
|
const result = await checkDocCoverage("project", { docsDir });
|
|
11604
11635
|
if (!result.ok) {
|
|
11605
11636
|
statuses["check-docs"] = "warn";
|
|
@@ -11626,7 +11657,7 @@ async function runMechanicalChecks(options) {
|
|
|
11626
11657
|
statuses["check-docs"] = "warn";
|
|
11627
11658
|
localFindings.push({
|
|
11628
11659
|
tool: "check-docs",
|
|
11629
|
-
file:
|
|
11660
|
+
file: path21.join(projectRoot, "docs"),
|
|
11630
11661
|
message: err instanceof Error ? err.message : String(err),
|
|
11631
11662
|
severity: "warning"
|
|
11632
11663
|
});
|
|
@@ -11774,7 +11805,7 @@ function detectChangeType(commitMessage, diff2) {
|
|
|
11774
11805
|
}
|
|
11775
11806
|
|
|
11776
11807
|
// src/review/context-scoper.ts
|
|
11777
|
-
var
|
|
11808
|
+
var path22 = __toESM(require("path"));
|
|
11778
11809
|
var ALL_DOMAINS = ["compliance", "bug", "security", "architecture"];
|
|
11779
11810
|
var SECURITY_PATTERNS = /auth|crypto|password|secret|token|session|cookie|hash|encrypt|decrypt|sql|shell|exec|eval/i;
|
|
11780
11811
|
function computeContextBudget(diffLines) {
|
|
@@ -11782,18 +11813,18 @@ function computeContextBudget(diffLines) {
|
|
|
11782
11813
|
return diffLines;
|
|
11783
11814
|
}
|
|
11784
11815
|
function isWithinProject(absPath, projectRoot) {
|
|
11785
|
-
const resolvedRoot =
|
|
11786
|
-
const resolvedPath =
|
|
11787
|
-
return resolvedPath.startsWith(resolvedRoot) || resolvedPath ===
|
|
11816
|
+
const resolvedRoot = path22.resolve(projectRoot) + path22.sep;
|
|
11817
|
+
const resolvedPath = path22.resolve(absPath);
|
|
11818
|
+
return resolvedPath.startsWith(resolvedRoot) || resolvedPath === path22.resolve(projectRoot);
|
|
11788
11819
|
}
|
|
11789
11820
|
async function readContextFile(projectRoot, filePath, reason) {
|
|
11790
|
-
const absPath =
|
|
11821
|
+
const absPath = path22.isAbsolute(filePath) ? filePath : path22.join(projectRoot, filePath);
|
|
11791
11822
|
if (!isWithinProject(absPath, projectRoot)) return null;
|
|
11792
11823
|
const result = await readFileContent(absPath);
|
|
11793
11824
|
if (!result.ok) return null;
|
|
11794
11825
|
const content = result.value;
|
|
11795
11826
|
const lines = content.split("\n").length;
|
|
11796
|
-
const relPath =
|
|
11827
|
+
const relPath = path22.isAbsolute(filePath) ? relativePosix(projectRoot, filePath) : filePath;
|
|
11797
11828
|
return { path: relPath, content, reason, lines };
|
|
11798
11829
|
}
|
|
11799
11830
|
function extractImportSources2(content) {
|
|
@@ -11808,18 +11839,18 @@ function extractImportSources2(content) {
|
|
|
11808
11839
|
}
|
|
11809
11840
|
async function resolveImportPath2(projectRoot, fromFile, importSource) {
|
|
11810
11841
|
if (!importSource.startsWith(".")) return null;
|
|
11811
|
-
const fromDir =
|
|
11812
|
-
const basePath =
|
|
11842
|
+
const fromDir = path22.dirname(path22.join(projectRoot, fromFile));
|
|
11843
|
+
const basePath = path22.resolve(fromDir, importSource);
|
|
11813
11844
|
if (!isWithinProject(basePath, projectRoot)) return null;
|
|
11814
11845
|
const relBase = relativePosix(projectRoot, basePath);
|
|
11815
11846
|
const candidates = [
|
|
11816
11847
|
relBase + ".ts",
|
|
11817
11848
|
relBase + ".tsx",
|
|
11818
11849
|
relBase + ".mts",
|
|
11819
|
-
|
|
11850
|
+
path22.join(relBase, "index.ts")
|
|
11820
11851
|
];
|
|
11821
11852
|
for (const candidate of candidates) {
|
|
11822
|
-
const absCandidate =
|
|
11853
|
+
const absCandidate = path22.join(projectRoot, candidate);
|
|
11823
11854
|
if (await fileExists(absCandidate)) {
|
|
11824
11855
|
return candidate;
|
|
11825
11856
|
}
|
|
@@ -11827,7 +11858,7 @@ async function resolveImportPath2(projectRoot, fromFile, importSource) {
|
|
|
11827
11858
|
return null;
|
|
11828
11859
|
}
|
|
11829
11860
|
async function findTestFiles(projectRoot, sourceFile) {
|
|
11830
|
-
const baseName =
|
|
11861
|
+
const baseName = path22.basename(sourceFile, path22.extname(sourceFile));
|
|
11831
11862
|
const pattern = `**/${baseName}.{test,spec}.{ts,tsx,mts}`;
|
|
11832
11863
|
const results = await findFiles(pattern, projectRoot);
|
|
11833
11864
|
return results.map((f) => relativePosix(projectRoot, f));
|
|
@@ -12636,7 +12667,7 @@ async function fanOutReview(options) {
|
|
|
12636
12667
|
}
|
|
12637
12668
|
|
|
12638
12669
|
// src/review/validate-findings.ts
|
|
12639
|
-
var
|
|
12670
|
+
var path23 = __toESM(require("path"));
|
|
12640
12671
|
var DOWNGRADE_MAP = {
|
|
12641
12672
|
critical: "important",
|
|
12642
12673
|
important: "suggestion",
|
|
@@ -12657,7 +12688,7 @@ function normalizePath(filePath, projectRoot) {
|
|
|
12657
12688
|
let normalized = filePath;
|
|
12658
12689
|
normalized = normalized.replace(/\\/g, "/");
|
|
12659
12690
|
const normalizedRoot = projectRoot.replace(/\\/g, "/");
|
|
12660
|
-
if (
|
|
12691
|
+
if (path23.isAbsolute(normalized)) {
|
|
12661
12692
|
const root = normalizedRoot.endsWith("/") ? normalizedRoot : normalizedRoot + "/";
|
|
12662
12693
|
if (normalized.startsWith(root)) {
|
|
12663
12694
|
normalized = normalized.slice(root.length);
|
|
@@ -12682,12 +12713,12 @@ function followImportChain(fromFile, fileContents, maxDepth = 2) {
|
|
|
12682
12713
|
while ((match = importRegex.exec(content)) !== null) {
|
|
12683
12714
|
const importPath = match[1];
|
|
12684
12715
|
if (!importPath.startsWith(".")) continue;
|
|
12685
|
-
const dir =
|
|
12686
|
-
let resolved =
|
|
12716
|
+
const dir = path23.dirname(current.file);
|
|
12717
|
+
let resolved = path23.join(dir, importPath).replace(/\\/g, "/");
|
|
12687
12718
|
if (!resolved.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
12688
12719
|
resolved += ".ts";
|
|
12689
12720
|
}
|
|
12690
|
-
resolved =
|
|
12721
|
+
resolved = path23.normalize(resolved).replace(/\\/g, "/");
|
|
12691
12722
|
if (!visited.has(resolved) && current.depth + 1 <= maxDepth) {
|
|
12692
12723
|
queue.push({ file: resolved, depth: current.depth + 1 });
|
|
12693
12724
|
}
|
|
@@ -12704,7 +12735,7 @@ async function validateFindings(options) {
|
|
|
12704
12735
|
if (exclusionSet.isExcluded(normalizedFile, finding.lineRange) || exclusionSet.isExcluded(finding.file, finding.lineRange)) {
|
|
12705
12736
|
continue;
|
|
12706
12737
|
}
|
|
12707
|
-
const absoluteFile =
|
|
12738
|
+
const absoluteFile = path23.isAbsolute(finding.file) ? finding.file : path23.join(projectRoot, finding.file).replace(/\\/g, "/");
|
|
12708
12739
|
if (exclusionSet.isExcluded(absoluteFile, finding.lineRange)) {
|
|
12709
12740
|
continue;
|
|
12710
12741
|
}
|
|
@@ -13387,8 +13418,8 @@ function serializeAssignmentHistory(records) {
|
|
|
13387
13418
|
}
|
|
13388
13419
|
|
|
13389
13420
|
// src/roadmap/sync.ts
|
|
13390
|
-
var
|
|
13391
|
-
var
|
|
13421
|
+
var fs24 = __toESM(require("fs"));
|
|
13422
|
+
var path24 = __toESM(require("path"));
|
|
13392
13423
|
var import_types24 = require("@harness-engineering/types");
|
|
13393
13424
|
|
|
13394
13425
|
// src/roadmap/status-rank.ts
|
|
@@ -13418,10 +13449,10 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
13418
13449
|
const featuresWithPlans = allFeatures.filter((f) => f.plans.length > 0);
|
|
13419
13450
|
const useRootState = featuresWithPlans.length <= 1;
|
|
13420
13451
|
if (useRootState) {
|
|
13421
|
-
const rootStatePath =
|
|
13422
|
-
if (
|
|
13452
|
+
const rootStatePath = path24.join(projectPath, ".harness", "state.json");
|
|
13453
|
+
if (fs24.existsSync(rootStatePath)) {
|
|
13423
13454
|
try {
|
|
13424
|
-
const raw =
|
|
13455
|
+
const raw = fs24.readFileSync(rootStatePath, "utf-8");
|
|
13425
13456
|
const state = JSON.parse(raw);
|
|
13426
13457
|
if (state.progress) {
|
|
13427
13458
|
for (const status of Object.values(state.progress)) {
|
|
@@ -13432,16 +13463,16 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
13432
13463
|
}
|
|
13433
13464
|
}
|
|
13434
13465
|
}
|
|
13435
|
-
const sessionsDir =
|
|
13436
|
-
if (
|
|
13466
|
+
const sessionsDir = path24.join(projectPath, ".harness", "sessions");
|
|
13467
|
+
if (fs24.existsSync(sessionsDir)) {
|
|
13437
13468
|
try {
|
|
13438
|
-
const sessionDirs =
|
|
13469
|
+
const sessionDirs = fs24.readdirSync(sessionsDir, { withFileTypes: true });
|
|
13439
13470
|
for (const entry of sessionDirs) {
|
|
13440
13471
|
if (!entry.isDirectory()) continue;
|
|
13441
|
-
const autopilotPath =
|
|
13442
|
-
if (!
|
|
13472
|
+
const autopilotPath = path24.join(sessionsDir, entry.name, "autopilot-state.json");
|
|
13473
|
+
if (!fs24.existsSync(autopilotPath)) continue;
|
|
13443
13474
|
try {
|
|
13444
|
-
const raw =
|
|
13475
|
+
const raw = fs24.readFileSync(autopilotPath, "utf-8");
|
|
13445
13476
|
const autopilot = JSON.parse(raw);
|
|
13446
13477
|
if (!autopilot.phases) continue;
|
|
13447
13478
|
const linkedPhases = autopilot.phases.filter(
|
|
@@ -13843,42 +13874,54 @@ var GitHubIssuesSyncAdapter = class {
|
|
|
13843
13874
|
};
|
|
13844
13875
|
|
|
13845
13876
|
// src/roadmap/sync-engine.ts
|
|
13846
|
-
var
|
|
13877
|
+
var fs25 = __toESM(require("fs"));
|
|
13847
13878
|
function emptySyncResult() {
|
|
13848
13879
|
return { created: [], updated: [], assignmentChanges: [], errors: [] };
|
|
13849
13880
|
}
|
|
13850
|
-
|
|
13851
|
-
const
|
|
13852
|
-
|
|
13881
|
+
function buildDedupIndex(tickets, config) {
|
|
13882
|
+
const index = /* @__PURE__ */ new Map();
|
|
13883
|
+
if (!tickets) return index;
|
|
13853
13884
|
const configLabels = new Set((config.labels ?? []).map((l) => l.toLowerCase()));
|
|
13854
|
-
|
|
13855
|
-
|
|
13856
|
-
|
|
13857
|
-
|
|
13858
|
-
|
|
13859
|
-
|
|
13860
|
-
|
|
13861
|
-
existingByTitle.set(key, ticket);
|
|
13862
|
-
}
|
|
13885
|
+
for (const ticket of tickets) {
|
|
13886
|
+
const hasConfigLabels = configLabels.size === 0 || ticket.labels.some((l) => configLabels.has(l.toLowerCase()));
|
|
13887
|
+
if (!hasConfigLabels) continue;
|
|
13888
|
+
const key = ticket.title.toLowerCase();
|
|
13889
|
+
const prev = index.get(key);
|
|
13890
|
+
if (!prev || prev.status === "closed" && ticket.status === "open") {
|
|
13891
|
+
index.set(key, ticket);
|
|
13863
13892
|
}
|
|
13864
13893
|
}
|
|
13894
|
+
return index;
|
|
13895
|
+
}
|
|
13896
|
+
async function resolveExternalId(feature, milestone, adapter, dedupIndex, result) {
|
|
13897
|
+
if (feature.externalId) return true;
|
|
13898
|
+
const existing = dedupIndex.get(feature.name.toLowerCase());
|
|
13899
|
+
if (existing) {
|
|
13900
|
+
feature.externalId = existing.externalId;
|
|
13901
|
+
return true;
|
|
13902
|
+
}
|
|
13903
|
+
const createResult = await adapter.createTicket(feature, milestone);
|
|
13904
|
+
if (createResult.ok) {
|
|
13905
|
+
feature.externalId = createResult.value.externalId;
|
|
13906
|
+
result.created.push(createResult.value);
|
|
13907
|
+
} else {
|
|
13908
|
+
result.errors.push({ featureOrId: feature.name, error: createResult.error });
|
|
13909
|
+
}
|
|
13910
|
+
return false;
|
|
13911
|
+
}
|
|
13912
|
+
async function syncToExternal(roadmap, adapter, config, prefetchedTickets) {
|
|
13913
|
+
const result = emptySyncResult();
|
|
13914
|
+
const dedupIndex = buildDedupIndex(prefetchedTickets, config);
|
|
13865
13915
|
for (const milestone of roadmap.milestones) {
|
|
13866
13916
|
for (const feature of milestone.features) {
|
|
13867
|
-
|
|
13868
|
-
|
|
13869
|
-
|
|
13870
|
-
|
|
13871
|
-
|
|
13872
|
-
|
|
13873
|
-
|
|
13874
|
-
|
|
13875
|
-
result.created.push(createResult.value);
|
|
13876
|
-
} else {
|
|
13877
|
-
result.errors.push({ featureOrId: feature.name, error: createResult.error });
|
|
13878
|
-
}
|
|
13879
|
-
continue;
|
|
13880
|
-
}
|
|
13881
|
-
}
|
|
13917
|
+
const shouldUpdate = await resolveExternalId(
|
|
13918
|
+
feature,
|
|
13919
|
+
milestone.name,
|
|
13920
|
+
adapter,
|
|
13921
|
+
dedupIndex,
|
|
13922
|
+
result
|
|
13923
|
+
);
|
|
13924
|
+
if (!shouldUpdate) continue;
|
|
13882
13925
|
const updateResult = await adapter.updateTicket(feature.externalId, feature, milestone.name);
|
|
13883
13926
|
if (updateResult.ok) {
|
|
13884
13927
|
result.updated.push(feature.externalId);
|
|
@@ -13929,6 +13972,9 @@ async function syncFromExternal(roadmap, adapter, config, options, prefetchedTic
|
|
|
13929
13972
|
if (!forceSync && isRegression(feature.status, newStatus)) {
|
|
13930
13973
|
continue;
|
|
13931
13974
|
}
|
|
13975
|
+
if (!forceSync && feature.status === "blocked" && newStatus === "planned") {
|
|
13976
|
+
continue;
|
|
13977
|
+
}
|
|
13932
13978
|
feature.status = newStatus;
|
|
13933
13979
|
}
|
|
13934
13980
|
}
|
|
@@ -13943,7 +13989,7 @@ async function fullSync(roadmapPath, adapter, config, options) {
|
|
|
13943
13989
|
});
|
|
13944
13990
|
await previousSync;
|
|
13945
13991
|
try {
|
|
13946
|
-
const raw =
|
|
13992
|
+
const raw = fs25.readFileSync(roadmapPath, "utf-8");
|
|
13947
13993
|
const parseResult = parseRoadmap(raw);
|
|
13948
13994
|
if (!parseResult.ok) {
|
|
13949
13995
|
return {
|
|
@@ -13956,7 +14002,7 @@ async function fullSync(roadmapPath, adapter, config, options) {
|
|
|
13956
14002
|
const tickets = fetchResult.ok ? fetchResult.value : void 0;
|
|
13957
14003
|
const pushResult = await syncToExternal(roadmap, adapter, config, tickets);
|
|
13958
14004
|
const pullResult = await syncFromExternal(roadmap, adapter, config, options, tickets);
|
|
13959
|
-
|
|
14005
|
+
fs25.writeFileSync(roadmapPath, serializeRoadmap(roadmap), "utf-8");
|
|
13960
14006
|
return {
|
|
13961
14007
|
created: pushResult.created,
|
|
13962
14008
|
updated: pushResult.updated,
|
|
@@ -14115,18 +14161,18 @@ var EmitInteractionInputSchema = import_zod10.z.object({
|
|
|
14115
14161
|
});
|
|
14116
14162
|
|
|
14117
14163
|
// src/blueprint/scanner.ts
|
|
14118
|
-
var
|
|
14119
|
-
var
|
|
14164
|
+
var fs26 = __toESM(require("fs/promises"));
|
|
14165
|
+
var path25 = __toESM(require("path"));
|
|
14120
14166
|
var ProjectScanner = class {
|
|
14121
14167
|
constructor(rootDir) {
|
|
14122
14168
|
this.rootDir = rootDir;
|
|
14123
14169
|
}
|
|
14124
14170
|
rootDir;
|
|
14125
14171
|
async scan() {
|
|
14126
|
-
let projectName =
|
|
14172
|
+
let projectName = path25.basename(this.rootDir);
|
|
14127
14173
|
try {
|
|
14128
|
-
const pkgPath =
|
|
14129
|
-
const pkgRaw = await
|
|
14174
|
+
const pkgPath = path25.join(this.rootDir, "package.json");
|
|
14175
|
+
const pkgRaw = await fs26.readFile(pkgPath, "utf-8");
|
|
14130
14176
|
const pkg = JSON.parse(pkgRaw);
|
|
14131
14177
|
if (pkg.name) projectName = pkg.name;
|
|
14132
14178
|
} catch {
|
|
@@ -14167,8 +14213,8 @@ var ProjectScanner = class {
|
|
|
14167
14213
|
};
|
|
14168
14214
|
|
|
14169
14215
|
// src/blueprint/generator.ts
|
|
14170
|
-
var
|
|
14171
|
-
var
|
|
14216
|
+
var fs27 = __toESM(require("fs/promises"));
|
|
14217
|
+
var path26 = __toESM(require("path"));
|
|
14172
14218
|
var ejs = __toESM(require("ejs"));
|
|
14173
14219
|
|
|
14174
14220
|
// src/blueprint/templates.ts
|
|
@@ -14252,19 +14298,19 @@ var BlueprintGenerator = class {
|
|
|
14252
14298
|
styles: STYLES,
|
|
14253
14299
|
scripts: SCRIPTS
|
|
14254
14300
|
});
|
|
14255
|
-
await
|
|
14256
|
-
await
|
|
14301
|
+
await fs27.mkdir(options.outputDir, { recursive: true });
|
|
14302
|
+
await fs27.writeFile(path26.join(options.outputDir, "index.html"), html);
|
|
14257
14303
|
}
|
|
14258
14304
|
};
|
|
14259
14305
|
|
|
14260
14306
|
// src/update-checker.ts
|
|
14261
|
-
var
|
|
14262
|
-
var
|
|
14307
|
+
var fs28 = __toESM(require("fs"));
|
|
14308
|
+
var path27 = __toESM(require("path"));
|
|
14263
14309
|
var os = __toESM(require("os"));
|
|
14264
14310
|
var import_child_process3 = require("child_process");
|
|
14265
14311
|
function getStatePath() {
|
|
14266
14312
|
const home = process.env["HOME"] || os.homedir();
|
|
14267
|
-
return
|
|
14313
|
+
return path27.join(home, ".harness", "update-check.json");
|
|
14268
14314
|
}
|
|
14269
14315
|
function isUpdateCheckEnabled(configInterval) {
|
|
14270
14316
|
if (process.env["HARNESS_NO_UPDATE_CHECK"] === "1") return false;
|
|
@@ -14277,7 +14323,7 @@ function shouldRunCheck(state, intervalMs) {
|
|
|
14277
14323
|
}
|
|
14278
14324
|
function readCheckState() {
|
|
14279
14325
|
try {
|
|
14280
|
-
const raw =
|
|
14326
|
+
const raw = fs28.readFileSync(getStatePath(), "utf-8");
|
|
14281
14327
|
const parsed = JSON.parse(raw);
|
|
14282
14328
|
if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
|
|
14283
14329
|
const state = parsed;
|
|
@@ -14294,7 +14340,7 @@ function readCheckState() {
|
|
|
14294
14340
|
}
|
|
14295
14341
|
function spawnBackgroundCheck(currentVersion) {
|
|
14296
14342
|
const statePath = getStatePath();
|
|
14297
|
-
const stateDir =
|
|
14343
|
+
const stateDir = path27.dirname(statePath);
|
|
14298
14344
|
const script = `
|
|
14299
14345
|
const { execSync } = require('child_process');
|
|
14300
14346
|
const fs = require('fs');
|
|
@@ -14384,9 +14430,9 @@ async function resolveWasmPath(grammarName) {
|
|
|
14384
14430
|
const { createRequire } = await import("module");
|
|
14385
14431
|
const require2 = createRequire(import_meta.url ?? __filename);
|
|
14386
14432
|
const pkgPath = require2.resolve("tree-sitter-wasms/package.json");
|
|
14387
|
-
const
|
|
14388
|
-
const pkgDir =
|
|
14389
|
-
return
|
|
14433
|
+
const path31 = await import("path");
|
|
14434
|
+
const pkgDir = path31.dirname(pkgPath);
|
|
14435
|
+
return path31.join(pkgDir, "out", `${grammarName}.wasm`);
|
|
14390
14436
|
}
|
|
14391
14437
|
async function loadLanguage(lang) {
|
|
14392
14438
|
const grammarName = GRAMMAR_MAP[lang];
|
|
@@ -14790,8 +14836,8 @@ function getModelPrice(model, dataset) {
|
|
|
14790
14836
|
}
|
|
14791
14837
|
|
|
14792
14838
|
// src/pricing/cache.ts
|
|
14793
|
-
var
|
|
14794
|
-
var
|
|
14839
|
+
var fs29 = __toESM(require("fs/promises"));
|
|
14840
|
+
var path28 = __toESM(require("path"));
|
|
14795
14841
|
|
|
14796
14842
|
// src/pricing/fallback.json
|
|
14797
14843
|
var fallback_default = {
|
|
@@ -14844,14 +14890,14 @@ var LITELLM_PRICING_URL = "https://raw.githubusercontent.com/BerriAI/litellm/mai
|
|
|
14844
14890
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
14845
14891
|
var STALENESS_WARNING_DAYS = 7;
|
|
14846
14892
|
function getCachePath(projectRoot) {
|
|
14847
|
-
return
|
|
14893
|
+
return path28.join(projectRoot, ".harness", "cache", "pricing.json");
|
|
14848
14894
|
}
|
|
14849
14895
|
function getStalenessMarkerPath(projectRoot) {
|
|
14850
|
-
return
|
|
14896
|
+
return path28.join(projectRoot, ".harness", "cache", "staleness-marker.json");
|
|
14851
14897
|
}
|
|
14852
14898
|
async function readDiskCache(projectRoot) {
|
|
14853
14899
|
try {
|
|
14854
|
-
const raw = await
|
|
14900
|
+
const raw = await fs29.readFile(getCachePath(projectRoot), "utf-8");
|
|
14855
14901
|
return JSON.parse(raw);
|
|
14856
14902
|
} catch {
|
|
14857
14903
|
return null;
|
|
@@ -14859,8 +14905,8 @@ async function readDiskCache(projectRoot) {
|
|
|
14859
14905
|
}
|
|
14860
14906
|
async function writeDiskCache(projectRoot, data) {
|
|
14861
14907
|
const cachePath = getCachePath(projectRoot);
|
|
14862
|
-
await
|
|
14863
|
-
await
|
|
14908
|
+
await fs29.mkdir(path28.dirname(cachePath), { recursive: true });
|
|
14909
|
+
await fs29.writeFile(cachePath, JSON.stringify(data, null, 2));
|
|
14864
14910
|
}
|
|
14865
14911
|
async function fetchFromNetwork() {
|
|
14866
14912
|
try {
|
|
@@ -14887,7 +14933,7 @@ function loadFallbackDataset() {
|
|
|
14887
14933
|
async function checkAndWarnStaleness(projectRoot) {
|
|
14888
14934
|
const markerPath = getStalenessMarkerPath(projectRoot);
|
|
14889
14935
|
try {
|
|
14890
|
-
const raw = await
|
|
14936
|
+
const raw = await fs29.readFile(markerPath, "utf-8");
|
|
14891
14937
|
const marker = JSON.parse(raw);
|
|
14892
14938
|
const firstUse = new Date(marker.firstFallbackUse).getTime();
|
|
14893
14939
|
const now = Date.now();
|
|
@@ -14899,8 +14945,8 @@ async function checkAndWarnStaleness(projectRoot) {
|
|
|
14899
14945
|
}
|
|
14900
14946
|
} catch {
|
|
14901
14947
|
try {
|
|
14902
|
-
await
|
|
14903
|
-
await
|
|
14948
|
+
await fs29.mkdir(path28.dirname(markerPath), { recursive: true });
|
|
14949
|
+
await fs29.writeFile(
|
|
14904
14950
|
markerPath,
|
|
14905
14951
|
JSON.stringify({ firstFallbackUse: (/* @__PURE__ */ new Date()).toISOString() })
|
|
14906
14952
|
);
|
|
@@ -14910,7 +14956,7 @@ async function checkAndWarnStaleness(projectRoot) {
|
|
|
14910
14956
|
}
|
|
14911
14957
|
async function clearStalenessMarker(projectRoot) {
|
|
14912
14958
|
try {
|
|
14913
|
-
await
|
|
14959
|
+
await fs29.unlink(getStalenessMarkerPath(projectRoot));
|
|
14914
14960
|
} catch {
|
|
14915
14961
|
}
|
|
14916
14962
|
}
|
|
@@ -14956,8 +15002,7 @@ function calculateCost(record, dataset) {
|
|
|
14956
15002
|
}
|
|
14957
15003
|
|
|
14958
15004
|
// src/usage/aggregator.ts
|
|
14959
|
-
function
|
|
14960
|
-
if (records.length === 0) return [];
|
|
15005
|
+
function bucketRecordsBySession(records) {
|
|
14961
15006
|
const sessionMap = /* @__PURE__ */ new Map();
|
|
14962
15007
|
for (const record of records) {
|
|
14963
15008
|
const tagged = record;
|
|
@@ -14973,58 +15018,104 @@ function aggregateBySession(records) {
|
|
|
14973
15018
|
}
|
|
14974
15019
|
bucket.allRecords.push(tagged);
|
|
14975
15020
|
}
|
|
15021
|
+
return sessionMap;
|
|
15022
|
+
}
|
|
15023
|
+
function accumulateCost(running, recordCost) {
|
|
15024
|
+
if (recordCost != null && running != null) {
|
|
15025
|
+
return running + recordCost;
|
|
15026
|
+
}
|
|
15027
|
+
if (recordCost == null) {
|
|
15028
|
+
return null;
|
|
15029
|
+
}
|
|
15030
|
+
return running;
|
|
15031
|
+
}
|
|
15032
|
+
function sumRecordTokens(tokenSource) {
|
|
15033
|
+
const tokens = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
15034
|
+
let cacheCreation;
|
|
15035
|
+
let cacheRead;
|
|
15036
|
+
let costMicroUSD = 0;
|
|
15037
|
+
let model;
|
|
15038
|
+
for (const r of tokenSource) {
|
|
15039
|
+
tokens.inputTokens += r.tokens.inputTokens;
|
|
15040
|
+
tokens.outputTokens += r.tokens.outputTokens;
|
|
15041
|
+
tokens.totalTokens += r.tokens.totalTokens;
|
|
15042
|
+
if (r.cacheCreationTokens != null) {
|
|
15043
|
+
cacheCreation = (cacheCreation ?? 0) + r.cacheCreationTokens;
|
|
15044
|
+
}
|
|
15045
|
+
if (r.cacheReadTokens != null) {
|
|
15046
|
+
cacheRead = (cacheRead ?? 0) + r.cacheReadTokens;
|
|
15047
|
+
}
|
|
15048
|
+
costMicroUSD = accumulateCost(costMicroUSD, r.costMicroUSD);
|
|
15049
|
+
if (!model && r.model) {
|
|
15050
|
+
model = r.model;
|
|
15051
|
+
}
|
|
15052
|
+
}
|
|
15053
|
+
return { tokens, cacheCreation, cacheRead, costMicroUSD, model };
|
|
15054
|
+
}
|
|
15055
|
+
function findModel(records) {
|
|
15056
|
+
for (const r of records) {
|
|
15057
|
+
if (r.model) return r.model;
|
|
15058
|
+
}
|
|
15059
|
+
return void 0;
|
|
15060
|
+
}
|
|
15061
|
+
function determineSource(hasHarness, hasCC) {
|
|
15062
|
+
if (hasHarness && hasCC) return "merged";
|
|
15063
|
+
if (hasCC) return "claude-code";
|
|
15064
|
+
return "harness";
|
|
15065
|
+
}
|
|
15066
|
+
function applyOptionalFields(session, totals, model) {
|
|
15067
|
+
if (model) session.model = model;
|
|
15068
|
+
if (totals.cacheCreation != null) session.cacheCreationTokens = totals.cacheCreation;
|
|
15069
|
+
if (totals.cacheRead != null) session.cacheReadTokens = totals.cacheRead;
|
|
15070
|
+
}
|
|
15071
|
+
function buildSessionUsage(sessionId, bucket) {
|
|
15072
|
+
const hasHarness = bucket.harnessRecords.length > 0;
|
|
15073
|
+
const hasCC = bucket.ccRecords.length > 0;
|
|
15074
|
+
const tokenSource = hasHarness ? bucket.harnessRecords : bucket.ccRecords;
|
|
15075
|
+
const totals = sumRecordTokens(tokenSource);
|
|
15076
|
+
const model = totals.model ?? (hasCC ? findModel(bucket.ccRecords) : void 0);
|
|
15077
|
+
const timestamps = bucket.allRecords.map((r) => r.timestamp).sort();
|
|
15078
|
+
const session = {
|
|
15079
|
+
sessionId,
|
|
15080
|
+
firstTimestamp: timestamps[0] ?? "",
|
|
15081
|
+
lastTimestamp: timestamps[timestamps.length - 1] ?? "",
|
|
15082
|
+
tokens: totals.tokens,
|
|
15083
|
+
costMicroUSD: totals.costMicroUSD,
|
|
15084
|
+
source: determineSource(hasHarness, hasCC)
|
|
15085
|
+
};
|
|
15086
|
+
applyOptionalFields(session, totals, model);
|
|
15087
|
+
return session;
|
|
15088
|
+
}
|
|
15089
|
+
function accumulateIntoDayBucket(day, record) {
|
|
15090
|
+
day.sessions.add(record.sessionId);
|
|
15091
|
+
day.tokens.inputTokens += record.tokens.inputTokens;
|
|
15092
|
+
day.tokens.outputTokens += record.tokens.outputTokens;
|
|
15093
|
+
day.tokens.totalTokens += record.tokens.totalTokens;
|
|
15094
|
+
if (record.cacheCreationTokens != null) {
|
|
15095
|
+
day.cacheCreation = (day.cacheCreation ?? 0) + record.cacheCreationTokens;
|
|
15096
|
+
}
|
|
15097
|
+
if (record.cacheReadTokens != null) {
|
|
15098
|
+
day.cacheRead = (day.cacheRead ?? 0) + record.cacheReadTokens;
|
|
15099
|
+
}
|
|
15100
|
+
day.costMicroUSD = accumulateCost(day.costMicroUSD, record.costMicroUSD);
|
|
15101
|
+
if (record.model) {
|
|
15102
|
+
day.models.add(record.model);
|
|
15103
|
+
}
|
|
15104
|
+
}
|
|
15105
|
+
function createDayBucket() {
|
|
15106
|
+
return {
|
|
15107
|
+
sessions: /* @__PURE__ */ new Set(),
|
|
15108
|
+
tokens: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
15109
|
+
costMicroUSD: 0,
|
|
15110
|
+
models: /* @__PURE__ */ new Set()
|
|
15111
|
+
};
|
|
15112
|
+
}
|
|
15113
|
+
function aggregateBySession(records) {
|
|
15114
|
+
if (records.length === 0) return [];
|
|
15115
|
+
const sessionMap = bucketRecordsBySession(records);
|
|
14976
15116
|
const results = [];
|
|
14977
15117
|
for (const [sessionId, bucket] of sessionMap) {
|
|
14978
|
-
|
|
14979
|
-
const hasCC = bucket.ccRecords.length > 0;
|
|
14980
|
-
const isMerged = hasHarness && hasCC;
|
|
14981
|
-
const tokenSource = hasHarness ? bucket.harnessRecords : bucket.ccRecords;
|
|
14982
|
-
const tokens = { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
14983
|
-
let cacheCreation;
|
|
14984
|
-
let cacheRead;
|
|
14985
|
-
let costMicroUSD = 0;
|
|
14986
|
-
let model;
|
|
14987
|
-
for (const r of tokenSource) {
|
|
14988
|
-
tokens.inputTokens += r.tokens.inputTokens;
|
|
14989
|
-
tokens.outputTokens += r.tokens.outputTokens;
|
|
14990
|
-
tokens.totalTokens += r.tokens.totalTokens;
|
|
14991
|
-
if (r.cacheCreationTokens != null) {
|
|
14992
|
-
cacheCreation = (cacheCreation ?? 0) + r.cacheCreationTokens;
|
|
14993
|
-
}
|
|
14994
|
-
if (r.cacheReadTokens != null) {
|
|
14995
|
-
cacheRead = (cacheRead ?? 0) + r.cacheReadTokens;
|
|
14996
|
-
}
|
|
14997
|
-
if (r.costMicroUSD != null && costMicroUSD != null) {
|
|
14998
|
-
costMicroUSD += r.costMicroUSD;
|
|
14999
|
-
} else if (r.costMicroUSD == null) {
|
|
15000
|
-
costMicroUSD = null;
|
|
15001
|
-
}
|
|
15002
|
-
if (!model && r.model) {
|
|
15003
|
-
model = r.model;
|
|
15004
|
-
}
|
|
15005
|
-
}
|
|
15006
|
-
if (!model && hasCC) {
|
|
15007
|
-
for (const r of bucket.ccRecords) {
|
|
15008
|
-
if (r.model) {
|
|
15009
|
-
model = r.model;
|
|
15010
|
-
break;
|
|
15011
|
-
}
|
|
15012
|
-
}
|
|
15013
|
-
}
|
|
15014
|
-
const timestamps = bucket.allRecords.map((r) => r.timestamp).sort();
|
|
15015
|
-
const source = isMerged ? "merged" : hasCC ? "claude-code" : "harness";
|
|
15016
|
-
const session = {
|
|
15017
|
-
sessionId,
|
|
15018
|
-
firstTimestamp: timestamps[0] ?? "",
|
|
15019
|
-
lastTimestamp: timestamps[timestamps.length - 1] ?? "",
|
|
15020
|
-
tokens,
|
|
15021
|
-
costMicroUSD,
|
|
15022
|
-
source
|
|
15023
|
-
};
|
|
15024
|
-
if (model) session.model = model;
|
|
15025
|
-
if (cacheCreation != null) session.cacheCreationTokens = cacheCreation;
|
|
15026
|
-
if (cacheRead != null) session.cacheReadTokens = cacheRead;
|
|
15027
|
-
results.push(session);
|
|
15118
|
+
results.push(buildSessionUsage(sessionId, bucket));
|
|
15028
15119
|
}
|
|
15029
15120
|
results.sort((a, b) => b.firstTimestamp.localeCompare(a.firstTimestamp));
|
|
15030
15121
|
return results;
|
|
@@ -15035,32 +15126,9 @@ function aggregateByDay(records) {
|
|
|
15035
15126
|
for (const record of records) {
|
|
15036
15127
|
const date = record.timestamp.slice(0, 10);
|
|
15037
15128
|
if (!dayMap.has(date)) {
|
|
15038
|
-
dayMap.set(date,
|
|
15039
|
-
sessions: /* @__PURE__ */ new Set(),
|
|
15040
|
-
tokens: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
15041
|
-
costMicroUSD: 0,
|
|
15042
|
-
models: /* @__PURE__ */ new Set()
|
|
15043
|
-
});
|
|
15044
|
-
}
|
|
15045
|
-
const day = dayMap.get(date);
|
|
15046
|
-
day.sessions.add(record.sessionId);
|
|
15047
|
-
day.tokens.inputTokens += record.tokens.inputTokens;
|
|
15048
|
-
day.tokens.outputTokens += record.tokens.outputTokens;
|
|
15049
|
-
day.tokens.totalTokens += record.tokens.totalTokens;
|
|
15050
|
-
if (record.cacheCreationTokens != null) {
|
|
15051
|
-
day.cacheCreation = (day.cacheCreation ?? 0) + record.cacheCreationTokens;
|
|
15052
|
-
}
|
|
15053
|
-
if (record.cacheReadTokens != null) {
|
|
15054
|
-
day.cacheRead = (day.cacheRead ?? 0) + record.cacheReadTokens;
|
|
15055
|
-
}
|
|
15056
|
-
if (record.costMicroUSD != null && day.costMicroUSD != null) {
|
|
15057
|
-
day.costMicroUSD += record.costMicroUSD;
|
|
15058
|
-
} else if (record.costMicroUSD == null) {
|
|
15059
|
-
day.costMicroUSD = null;
|
|
15060
|
-
}
|
|
15061
|
-
if (record.model) {
|
|
15062
|
-
day.models.add(record.model);
|
|
15129
|
+
dayMap.set(date, createDayBucket());
|
|
15063
15130
|
}
|
|
15131
|
+
accumulateIntoDayBucket(dayMap.get(date), record);
|
|
15064
15132
|
}
|
|
15065
15133
|
const results = [];
|
|
15066
15134
|
for (const [date, day] of dayMap) {
|
|
@@ -15080,8 +15148,8 @@ function aggregateByDay(records) {
|
|
|
15080
15148
|
}
|
|
15081
15149
|
|
|
15082
15150
|
// src/usage/jsonl-reader.ts
|
|
15083
|
-
var
|
|
15084
|
-
var
|
|
15151
|
+
var fs30 = __toESM(require("fs"));
|
|
15152
|
+
var path29 = __toESM(require("path"));
|
|
15085
15153
|
function parseLine(line, lineNumber) {
|
|
15086
15154
|
let entry;
|
|
15087
15155
|
try {
|
|
@@ -15120,10 +15188,10 @@ function parseLine(line, lineNumber) {
|
|
|
15120
15188
|
return record;
|
|
15121
15189
|
}
|
|
15122
15190
|
function readCostRecords(projectRoot) {
|
|
15123
|
-
const costsFile =
|
|
15191
|
+
const costsFile = path29.join(projectRoot, ".harness", "metrics", "costs.jsonl");
|
|
15124
15192
|
let raw;
|
|
15125
15193
|
try {
|
|
15126
|
-
raw =
|
|
15194
|
+
raw = fs30.readFileSync(costsFile, "utf-8");
|
|
15127
15195
|
} catch {
|
|
15128
15196
|
return [];
|
|
15129
15197
|
}
|
|
@@ -15141,8 +15209,8 @@ function readCostRecords(projectRoot) {
|
|
|
15141
15209
|
}
|
|
15142
15210
|
|
|
15143
15211
|
// src/usage/cc-parser.ts
|
|
15144
|
-
var
|
|
15145
|
-
var
|
|
15212
|
+
var fs31 = __toESM(require("fs"));
|
|
15213
|
+
var path30 = __toESM(require("path"));
|
|
15146
15214
|
var os2 = __toESM(require("os"));
|
|
15147
15215
|
function extractUsage(entry) {
|
|
15148
15216
|
if (entry.type !== "assistant") return null;
|
|
@@ -15175,7 +15243,7 @@ function parseCCLine(line, filePath, lineNumber) {
|
|
|
15175
15243
|
entry = JSON.parse(line);
|
|
15176
15244
|
} catch {
|
|
15177
15245
|
console.warn(
|
|
15178
|
-
`[harness usage] Skipping malformed CC JSONL line ${lineNumber} in ${
|
|
15246
|
+
`[harness usage] Skipping malformed CC JSONL line ${lineNumber} in ${path30.basename(filePath)}`
|
|
15179
15247
|
);
|
|
15180
15248
|
return null;
|
|
15181
15249
|
}
|
|
@@ -15189,7 +15257,7 @@ function parseCCLine(line, filePath, lineNumber) {
|
|
|
15189
15257
|
function readCCFile(filePath) {
|
|
15190
15258
|
let raw;
|
|
15191
15259
|
try {
|
|
15192
|
-
raw =
|
|
15260
|
+
raw = fs31.readFileSync(filePath, "utf-8");
|
|
15193
15261
|
} catch {
|
|
15194
15262
|
return [];
|
|
15195
15263
|
}
|
|
@@ -15211,10 +15279,10 @@ function readCCFile(filePath) {
|
|
|
15211
15279
|
}
|
|
15212
15280
|
function parseCCRecords() {
|
|
15213
15281
|
const homeDir = process.env.HOME ?? os2.homedir();
|
|
15214
|
-
const projectsDir =
|
|
15282
|
+
const projectsDir = path30.join(homeDir, ".claude", "projects");
|
|
15215
15283
|
let projectDirs;
|
|
15216
15284
|
try {
|
|
15217
|
-
projectDirs =
|
|
15285
|
+
projectDirs = fs31.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path30.join(projectsDir, d.name));
|
|
15218
15286
|
} catch {
|
|
15219
15287
|
return [];
|
|
15220
15288
|
}
|
|
@@ -15222,7 +15290,7 @@ function parseCCRecords() {
|
|
|
15222
15290
|
for (const dir of projectDirs) {
|
|
15223
15291
|
let files;
|
|
15224
15292
|
try {
|
|
15225
|
-
files =
|
|
15293
|
+
files = fs31.readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => path30.join(dir, f));
|
|
15226
15294
|
} catch {
|
|
15227
15295
|
continue;
|
|
15228
15296
|
}
|
|
@@ -15374,6 +15442,7 @@ var VERSION = "0.15.0";
|
|
|
15374
15442
|
clearFailuresCache,
|
|
15375
15443
|
clearLearningsCache,
|
|
15376
15444
|
clearTaint,
|
|
15445
|
+
computeContentHash,
|
|
15377
15446
|
computeOverallSeverity,
|
|
15378
15447
|
computeScanExitCode,
|
|
15379
15448
|
configureFeedback,
|
|
@@ -15467,6 +15536,7 @@ var VERSION = "0.15.0";
|
|
|
15467
15536
|
migrateToStreams,
|
|
15468
15537
|
networkRules,
|
|
15469
15538
|
nodeRules,
|
|
15539
|
+
normalizeLearningContent,
|
|
15470
15540
|
parseCCRecords,
|
|
15471
15541
|
parseDateFromEntry,
|
|
15472
15542
|
parseDiff,
|