@harness-engineering/core 0.16.0 → 0.17.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 +145 -2
- package/dist/index.d.ts +145 -2
- package/dist/index.js +616 -36
- package/dist/index.mjs +607 -36
- package/package.json +14 -14
package/dist/index.js
CHANGED
|
@@ -75,6 +75,7 @@ __export(index_exports, {
|
|
|
75
75
|
ForbiddenImportCollector: () => ForbiddenImportCollector,
|
|
76
76
|
GateConfigSchema: () => GateConfigSchema,
|
|
77
77
|
GateResultSchema: () => GateResultSchema,
|
|
78
|
+
GitHubIssuesSyncAdapter: () => GitHubIssuesSyncAdapter,
|
|
78
79
|
HandoffSchema: () => HandoffSchema,
|
|
79
80
|
HarnessStateSchema: () => HarnessStateSchema,
|
|
80
81
|
InteractionTypeSchema: () => InteractionTypeSchema,
|
|
@@ -96,6 +97,7 @@ __export(index_exports, {
|
|
|
96
97
|
RuleRegistry: () => RuleRegistry,
|
|
97
98
|
SECURITY_DESCRIPTOR: () => SECURITY_DESCRIPTOR,
|
|
98
99
|
STALENESS_WARNING_DAYS: () => STALENESS_WARNING_DAYS,
|
|
100
|
+
STATUS_RANK: () => STATUS_RANK,
|
|
99
101
|
SecurityConfigSchema: () => SecurityConfigSchema,
|
|
100
102
|
SecurityScanner: () => SecurityScanner,
|
|
101
103
|
SharableBoundaryConfigSchema: () => SharableBoundaryConfigSchema,
|
|
@@ -129,6 +131,7 @@ __export(index_exports, {
|
|
|
129
131
|
archiveLearnings: () => archiveLearnings,
|
|
130
132
|
archiveSession: () => archiveSession,
|
|
131
133
|
archiveStream: () => archiveStream,
|
|
134
|
+
assignFeature: () => assignFeature,
|
|
132
135
|
buildDependencyGraph: () => buildDependencyGraph,
|
|
133
136
|
buildExclusionSet: () => buildExclusionSet,
|
|
134
137
|
buildSnapshot: () => buildSnapshot,
|
|
@@ -193,6 +196,7 @@ __export(index_exports, {
|
|
|
193
196
|
formatGitHubSummary: () => formatGitHubSummary,
|
|
194
197
|
formatOutline: () => formatOutline,
|
|
195
198
|
formatTerminalOutput: () => formatTerminalOutput,
|
|
199
|
+
fullSync: () => fullSync,
|
|
196
200
|
generateAgentsMap: () => generateAgentsMap,
|
|
197
201
|
generateSuggestions: () => generateSuggestions,
|
|
198
202
|
getActionEmitter: () => getActionEmitter,
|
|
@@ -210,6 +214,7 @@ __export(index_exports, {
|
|
|
210
214
|
injectionRules: () => injectionRules,
|
|
211
215
|
insecureDefaultsRules: () => insecureDefaultsRules,
|
|
212
216
|
isDuplicateFinding: () => isDuplicateFinding,
|
|
217
|
+
isRegression: () => isRegression,
|
|
213
218
|
isSmallSuggestion: () => isSmallSuggestion,
|
|
214
219
|
isUpdateCheckEnabled: () => isUpdateCheckEnabled,
|
|
215
220
|
listActiveSessions: () => listActiveSessions,
|
|
@@ -263,6 +268,7 @@ __export(index_exports, {
|
|
|
263
268
|
resetParserCache: () => resetParserCache,
|
|
264
269
|
resolveFileToLayer: () => resolveFileToLayer,
|
|
265
270
|
resolveModelTier: () => resolveModelTier,
|
|
271
|
+
resolveReverseStatus: () => resolveReverseStatus,
|
|
266
272
|
resolveRuleSeverity: () => resolveRuleSeverity,
|
|
267
273
|
resolveSessionDir: () => resolveSessionDir,
|
|
268
274
|
resolveStreamPath: () => resolveStreamPath,
|
|
@@ -283,6 +289,7 @@ __export(index_exports, {
|
|
|
283
289
|
saveStreamIndex: () => saveStreamIndex,
|
|
284
290
|
scanForInjection: () => scanForInjection,
|
|
285
291
|
scopeContext: () => scopeContext,
|
|
292
|
+
scoreRoadmapCandidates: () => scoreRoadmapCandidates,
|
|
286
293
|
searchSymbols: () => searchSymbols,
|
|
287
294
|
secretRules: () => secretRules,
|
|
288
295
|
serializeRoadmap: () => serializeRoadmap,
|
|
@@ -291,7 +298,9 @@ __export(index_exports, {
|
|
|
291
298
|
shouldRunCheck: () => shouldRunCheck,
|
|
292
299
|
spawnBackgroundCheck: () => spawnBackgroundCheck,
|
|
293
300
|
syncConstraintNodes: () => syncConstraintNodes,
|
|
301
|
+
syncFromExternal: () => syncFromExternal,
|
|
294
302
|
syncRoadmap: () => syncRoadmap,
|
|
303
|
+
syncToExternal: () => syncToExternal,
|
|
295
304
|
tagUncitedFindings: () => tagUncitedFindings,
|
|
296
305
|
touchStream: () => touchStream,
|
|
297
306
|
trackAction: () => trackAction,
|
|
@@ -12158,6 +12167,7 @@ var VALID_STATUSES = /* @__PURE__ */ new Set([
|
|
|
12158
12167
|
"blocked"
|
|
12159
12168
|
]);
|
|
12160
12169
|
var EM_DASH = "\u2014";
|
|
12170
|
+
var VALID_PRIORITIES = /* @__PURE__ */ new Set(["P0", "P1", "P2", "P3"]);
|
|
12161
12171
|
function parseRoadmap(markdown) {
|
|
12162
12172
|
const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---/);
|
|
12163
12173
|
if (!fmMatch) {
|
|
@@ -12168,9 +12178,12 @@ function parseRoadmap(markdown) {
|
|
|
12168
12178
|
const body = markdown.slice(fmMatch[0].length);
|
|
12169
12179
|
const milestonesResult = parseMilestones(body);
|
|
12170
12180
|
if (!milestonesResult.ok) return milestonesResult;
|
|
12181
|
+
const historyResult = parseAssignmentHistory(body);
|
|
12182
|
+
if (!historyResult.ok) return historyResult;
|
|
12171
12183
|
return (0, import_types19.Ok)({
|
|
12172
12184
|
frontmatter: fmResult.value,
|
|
12173
|
-
milestones: milestonesResult.value
|
|
12185
|
+
milestones: milestonesResult.value,
|
|
12186
|
+
assignmentHistory: historyResult.value
|
|
12174
12187
|
});
|
|
12175
12188
|
}
|
|
12176
12189
|
function parseFrontmatter2(raw) {
|
|
@@ -12210,12 +12223,17 @@ function parseMilestones(body) {
|
|
|
12210
12223
|
const h2Pattern = /^## (.+)$/gm;
|
|
12211
12224
|
const h2Matches = [];
|
|
12212
12225
|
let match;
|
|
12226
|
+
let bodyEnd = body.length;
|
|
12213
12227
|
while ((match = h2Pattern.exec(body)) !== null) {
|
|
12228
|
+
if (match[1] === "Assignment History") {
|
|
12229
|
+
bodyEnd = match.index;
|
|
12230
|
+
break;
|
|
12231
|
+
}
|
|
12214
12232
|
h2Matches.push({ heading: match[1], startIndex: match.index, fullMatch: match[0] });
|
|
12215
12233
|
}
|
|
12216
12234
|
for (let i = 0; i < h2Matches.length; i++) {
|
|
12217
12235
|
const h2 = h2Matches[i];
|
|
12218
|
-
const nextStart = i + 1 < h2Matches.length ? h2Matches[i + 1].startIndex :
|
|
12236
|
+
const nextStart = i + 1 < h2Matches.length ? h2Matches[i + 1].startIndex : bodyEnd;
|
|
12219
12237
|
const sectionBody = body.slice(h2.startIndex + h2.fullMatch.length, nextStart);
|
|
12220
12238
|
const isBacklog = h2.heading === "Backlog";
|
|
12221
12239
|
const milestoneName = isBacklog ? "Backlog" : h2.heading.replace(/^Milestone:\s*/, "");
|
|
@@ -12281,15 +12299,60 @@ function parseFeatureFields(name, body) {
|
|
|
12281
12299
|
const specRaw = fieldMap.get("Spec") ?? EM_DASH;
|
|
12282
12300
|
const plans = parseListField(fieldMap, "Plans", "Plan");
|
|
12283
12301
|
const blockedBy = parseListField(fieldMap, "Blocked by", "Blockers");
|
|
12302
|
+
const assigneeRaw = fieldMap.get("Assignee") ?? EM_DASH;
|
|
12303
|
+
const priorityRaw = fieldMap.get("Priority") ?? EM_DASH;
|
|
12304
|
+
const externalIdRaw = fieldMap.get("External-ID") ?? EM_DASH;
|
|
12305
|
+
if (priorityRaw !== EM_DASH && !VALID_PRIORITIES.has(priorityRaw)) {
|
|
12306
|
+
return (0, import_types19.Err)(
|
|
12307
|
+
new Error(
|
|
12308
|
+
`Feature "${name}" has invalid priority: "${priorityRaw}". Valid priorities: ${[...VALID_PRIORITIES].join(", ")}`
|
|
12309
|
+
)
|
|
12310
|
+
);
|
|
12311
|
+
}
|
|
12284
12312
|
return (0, import_types19.Ok)({
|
|
12285
12313
|
name,
|
|
12286
12314
|
status: statusRaw,
|
|
12287
12315
|
spec: specRaw === EM_DASH ? null : specRaw,
|
|
12288
12316
|
plans,
|
|
12289
12317
|
blockedBy,
|
|
12290
|
-
summary: fieldMap.get("Summary") ?? ""
|
|
12318
|
+
summary: fieldMap.get("Summary") ?? "",
|
|
12319
|
+
assignee: assigneeRaw === EM_DASH ? null : assigneeRaw,
|
|
12320
|
+
priority: priorityRaw === EM_DASH ? null : priorityRaw,
|
|
12321
|
+
externalId: externalIdRaw === EM_DASH ? null : externalIdRaw
|
|
12291
12322
|
});
|
|
12292
12323
|
}
|
|
12324
|
+
function parseAssignmentHistory(body) {
|
|
12325
|
+
const historyMatch = body.match(/^## Assignment History\s*\n/m);
|
|
12326
|
+
if (!historyMatch || historyMatch.index === void 0) return (0, import_types19.Ok)([]);
|
|
12327
|
+
const historyStart = historyMatch.index + historyMatch[0].length;
|
|
12328
|
+
const rawHistoryBody = body.slice(historyStart);
|
|
12329
|
+
const nextH2 = rawHistoryBody.search(/^## /m);
|
|
12330
|
+
const historyBody = nextH2 === -1 ? rawHistoryBody : rawHistoryBody.slice(0, nextH2);
|
|
12331
|
+
const records = [];
|
|
12332
|
+
const lines = historyBody.split("\n");
|
|
12333
|
+
let pastHeader = false;
|
|
12334
|
+
for (const line of lines) {
|
|
12335
|
+
const trimmed = line.trim();
|
|
12336
|
+
if (!trimmed.startsWith("|")) continue;
|
|
12337
|
+
if (!pastHeader) {
|
|
12338
|
+
if (trimmed.match(/^\|[-\s|]+\|$/)) {
|
|
12339
|
+
pastHeader = true;
|
|
12340
|
+
}
|
|
12341
|
+
continue;
|
|
12342
|
+
}
|
|
12343
|
+
const cells = trimmed.split("|").map((c) => c.trim()).filter((c) => c.length > 0);
|
|
12344
|
+
if (cells.length < 4) continue;
|
|
12345
|
+
const action = cells[2];
|
|
12346
|
+
if (!["assigned", "completed", "unassigned"].includes(action)) continue;
|
|
12347
|
+
records.push({
|
|
12348
|
+
feature: cells[0],
|
|
12349
|
+
assignee: cells[1],
|
|
12350
|
+
action,
|
|
12351
|
+
date: cells[3]
|
|
12352
|
+
});
|
|
12353
|
+
}
|
|
12354
|
+
return (0, import_types19.Ok)(records);
|
|
12355
|
+
}
|
|
12293
12356
|
|
|
12294
12357
|
// src/roadmap/serialize.ts
|
|
12295
12358
|
var EM_DASH2 = "\u2014";
|
|
@@ -12317,6 +12380,10 @@ function serializeRoadmap(roadmap) {
|
|
|
12317
12380
|
lines.push(...serializeFeature(feature));
|
|
12318
12381
|
}
|
|
12319
12382
|
}
|
|
12383
|
+
if (roadmap.assignmentHistory && roadmap.assignmentHistory.length > 0) {
|
|
12384
|
+
lines.push("");
|
|
12385
|
+
lines.push(...serializeAssignmentHistory(roadmap.assignmentHistory));
|
|
12386
|
+
}
|
|
12320
12387
|
lines.push("");
|
|
12321
12388
|
return lines.join("\n");
|
|
12322
12389
|
}
|
|
@@ -12327,7 +12394,7 @@ function serializeFeature(feature) {
|
|
|
12327
12394
|
const spec = feature.spec ?? EM_DASH2;
|
|
12328
12395
|
const plans = feature.plans.length > 0 ? feature.plans.join(", ") : EM_DASH2;
|
|
12329
12396
|
const blockedBy = feature.blockedBy.length > 0 ? feature.blockedBy.join(", ") : EM_DASH2;
|
|
12330
|
-
|
|
12397
|
+
const lines = [
|
|
12331
12398
|
`### ${feature.name}`,
|
|
12332
12399
|
"",
|
|
12333
12400
|
`- **Status:** ${feature.status}`,
|
|
@@ -12336,12 +12403,45 @@ function serializeFeature(feature) {
|
|
|
12336
12403
|
`- **Blockers:** ${blockedBy}`,
|
|
12337
12404
|
`- **Plan:** ${plans}`
|
|
12338
12405
|
];
|
|
12406
|
+
const hasExtended = feature.assignee !== null || feature.priority !== null || feature.externalId !== null;
|
|
12407
|
+
if (hasExtended) {
|
|
12408
|
+
lines.push(`- **Assignee:** ${feature.assignee ?? EM_DASH2}`);
|
|
12409
|
+
lines.push(`- **Priority:** ${feature.priority ?? EM_DASH2}`);
|
|
12410
|
+
lines.push(`- **External-ID:** ${feature.externalId ?? EM_DASH2}`);
|
|
12411
|
+
}
|
|
12412
|
+
return lines;
|
|
12413
|
+
}
|
|
12414
|
+
function serializeAssignmentHistory(records) {
|
|
12415
|
+
const lines = [
|
|
12416
|
+
"## Assignment History",
|
|
12417
|
+
"| Feature | Assignee | Action | Date |",
|
|
12418
|
+
"|---------|----------|--------|------|"
|
|
12419
|
+
];
|
|
12420
|
+
for (const record of records) {
|
|
12421
|
+
lines.push(`| ${record.feature} | ${record.assignee} | ${record.action} | ${record.date} |`);
|
|
12422
|
+
}
|
|
12423
|
+
return lines;
|
|
12339
12424
|
}
|
|
12340
12425
|
|
|
12341
12426
|
// src/roadmap/sync.ts
|
|
12342
12427
|
var fs19 = __toESM(require("fs"));
|
|
12343
12428
|
var path19 = __toESM(require("path"));
|
|
12344
12429
|
var import_types20 = require("@harness-engineering/types");
|
|
12430
|
+
|
|
12431
|
+
// src/roadmap/status-rank.ts
|
|
12432
|
+
var STATUS_RANK = {
|
|
12433
|
+
backlog: 0,
|
|
12434
|
+
planned: 1,
|
|
12435
|
+
blocked: 1,
|
|
12436
|
+
// lateral to planned — sync can move to/from blocked freely
|
|
12437
|
+
"in-progress": 2,
|
|
12438
|
+
done: 3
|
|
12439
|
+
};
|
|
12440
|
+
function isRegression(from, to) {
|
|
12441
|
+
return STATUS_RANK[to] < STATUS_RANK[from];
|
|
12442
|
+
}
|
|
12443
|
+
|
|
12444
|
+
// src/roadmap/sync.ts
|
|
12345
12445
|
function inferStatus(feature, projectPath, allFeatures) {
|
|
12346
12446
|
if (feature.blockedBy.length > 0) {
|
|
12347
12447
|
const blockerNotDone = feature.blockedBy.some((blockerName) => {
|
|
@@ -12408,17 +12508,6 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
12408
12508
|
if (anyStarted) return "in-progress";
|
|
12409
12509
|
return null;
|
|
12410
12510
|
}
|
|
12411
|
-
var STATUS_RANK = {
|
|
12412
|
-
backlog: 0,
|
|
12413
|
-
planned: 1,
|
|
12414
|
-
blocked: 1,
|
|
12415
|
-
// lateral to planned — sync can move to/from blocked freely
|
|
12416
|
-
"in-progress": 2,
|
|
12417
|
-
done: 3
|
|
12418
|
-
};
|
|
12419
|
-
function isRegression(from, to) {
|
|
12420
|
-
return STATUS_RANK[to] < STATUS_RANK[from];
|
|
12421
|
-
}
|
|
12422
12511
|
function syncRoadmap(options) {
|
|
12423
12512
|
const { projectPath, roadmap, forceSync } = options;
|
|
12424
12513
|
const allFeatures = roadmap.milestones.flatMap((m) => m.features);
|
|
@@ -12449,6 +12538,487 @@ function applySyncChanges(roadmap, changes) {
|
|
|
12449
12538
|
roadmap.frontmatter.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
|
|
12450
12539
|
}
|
|
12451
12540
|
|
|
12541
|
+
// src/roadmap/tracker-sync.ts
|
|
12542
|
+
function resolveReverseStatus(externalStatus, labels, config) {
|
|
12543
|
+
const reverseMap = config.reverseStatusMap;
|
|
12544
|
+
if (!reverseMap) return null;
|
|
12545
|
+
if (reverseMap[externalStatus]) {
|
|
12546
|
+
return reverseMap[externalStatus];
|
|
12547
|
+
}
|
|
12548
|
+
const statusLabels = ["in-progress", "blocked", "planned"];
|
|
12549
|
+
const matchingLabels = labels.filter((l) => statusLabels.includes(l));
|
|
12550
|
+
if (matchingLabels.length === 1) {
|
|
12551
|
+
const compoundKey = `${externalStatus}:${matchingLabels[0]}`;
|
|
12552
|
+
if (reverseMap[compoundKey]) {
|
|
12553
|
+
return reverseMap[compoundKey];
|
|
12554
|
+
}
|
|
12555
|
+
}
|
|
12556
|
+
return null;
|
|
12557
|
+
}
|
|
12558
|
+
|
|
12559
|
+
// src/roadmap/adapters/github-issues.ts
|
|
12560
|
+
var import_types21 = require("@harness-engineering/types");
|
|
12561
|
+
function parseExternalId(externalId) {
|
|
12562
|
+
const match = externalId.match(/^github:([^/]+)\/([^#]+)#(\d+)$/);
|
|
12563
|
+
if (!match) return null;
|
|
12564
|
+
return { owner: match[1], repo: match[2], number: parseInt(match[3], 10) };
|
|
12565
|
+
}
|
|
12566
|
+
function buildExternalId(owner, repo, number) {
|
|
12567
|
+
return `github:${owner}/${repo}#${number}`;
|
|
12568
|
+
}
|
|
12569
|
+
function labelsForStatus(status, config) {
|
|
12570
|
+
const base = config.labels ?? [];
|
|
12571
|
+
const externalStatus = config.statusMap[status];
|
|
12572
|
+
if (externalStatus === "open" && status !== "backlog") {
|
|
12573
|
+
return [...base, status];
|
|
12574
|
+
}
|
|
12575
|
+
return [...base];
|
|
12576
|
+
}
|
|
12577
|
+
var RETRY_DEFAULTS = { maxRetries: 5, baseDelayMs: 1e3 };
|
|
12578
|
+
function sleep(ms) {
|
|
12579
|
+
return new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
12580
|
+
}
|
|
12581
|
+
async function fetchWithRetry(fetchFn, input, init, opts = RETRY_DEFAULTS) {
|
|
12582
|
+
let lastResponse;
|
|
12583
|
+
for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
|
|
12584
|
+
const response = await fetchFn(input, init);
|
|
12585
|
+
if (response.status !== 403 && response.status !== 429) return response;
|
|
12586
|
+
lastResponse = response;
|
|
12587
|
+
if (attempt === opts.maxRetries) break;
|
|
12588
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
12589
|
+
let delayMs;
|
|
12590
|
+
if (retryAfter) {
|
|
12591
|
+
const seconds = parseInt(retryAfter, 10);
|
|
12592
|
+
delayMs = isNaN(seconds) ? opts.baseDelayMs : seconds * 1e3;
|
|
12593
|
+
} else {
|
|
12594
|
+
delayMs = opts.baseDelayMs * Math.pow(2, attempt) + Math.random() * 500;
|
|
12595
|
+
}
|
|
12596
|
+
await sleep(delayMs);
|
|
12597
|
+
}
|
|
12598
|
+
return lastResponse;
|
|
12599
|
+
}
|
|
12600
|
+
var GitHubIssuesSyncAdapter = class {
|
|
12601
|
+
token;
|
|
12602
|
+
config;
|
|
12603
|
+
fetchFn;
|
|
12604
|
+
apiBase;
|
|
12605
|
+
owner;
|
|
12606
|
+
repo;
|
|
12607
|
+
retryOpts;
|
|
12608
|
+
constructor(options) {
|
|
12609
|
+
this.token = options.token;
|
|
12610
|
+
this.config = options.config;
|
|
12611
|
+
this.fetchFn = options.fetchFn ?? globalThis.fetch;
|
|
12612
|
+
this.apiBase = options.apiBase ?? "https://api.github.com";
|
|
12613
|
+
this.retryOpts = {
|
|
12614
|
+
maxRetries: options.maxRetries ?? RETRY_DEFAULTS.maxRetries,
|
|
12615
|
+
baseDelayMs: options.baseDelayMs ?? RETRY_DEFAULTS.baseDelayMs
|
|
12616
|
+
};
|
|
12617
|
+
const repoParts = (options.config.repo ?? "").split("/");
|
|
12618
|
+
if (repoParts.length !== 2 || !repoParts[0] || !repoParts[1]) {
|
|
12619
|
+
throw new Error(`Invalid repo format: "${options.config.repo}". Expected "owner/repo".`);
|
|
12620
|
+
}
|
|
12621
|
+
this.owner = repoParts[0];
|
|
12622
|
+
this.repo = repoParts[1];
|
|
12623
|
+
}
|
|
12624
|
+
headers() {
|
|
12625
|
+
return {
|
|
12626
|
+
Authorization: `Bearer ${this.token}`,
|
|
12627
|
+
Accept: "application/vnd.github+json",
|
|
12628
|
+
"Content-Type": "application/json",
|
|
12629
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
12630
|
+
};
|
|
12631
|
+
}
|
|
12632
|
+
/**
|
|
12633
|
+
* Close an issue if the feature status maps to 'closed'.
|
|
12634
|
+
* GitHub Issues API doesn't accept state on POST — requires a follow-up PATCH.
|
|
12635
|
+
*/
|
|
12636
|
+
async closeIfDone(issueNumber, featureStatus) {
|
|
12637
|
+
const externalStatus = this.config.statusMap[featureStatus];
|
|
12638
|
+
if (externalStatus !== "closed") return;
|
|
12639
|
+
await fetchWithRetry(
|
|
12640
|
+
this.fetchFn,
|
|
12641
|
+
`${this.apiBase}/repos/${this.owner}/${this.repo}/issues/${issueNumber}`,
|
|
12642
|
+
{ method: "PATCH", headers: this.headers(), body: JSON.stringify({ state: "closed" }) },
|
|
12643
|
+
this.retryOpts
|
|
12644
|
+
);
|
|
12645
|
+
}
|
|
12646
|
+
async createTicket(feature, milestone) {
|
|
12647
|
+
try {
|
|
12648
|
+
const labels = labelsForStatus(feature.status, this.config);
|
|
12649
|
+
const body = [
|
|
12650
|
+
feature.summary,
|
|
12651
|
+
"",
|
|
12652
|
+
`**Milestone:** ${milestone}`,
|
|
12653
|
+
feature.spec ? `**Spec:** ${feature.spec}` : ""
|
|
12654
|
+
].filter(Boolean).join("\n");
|
|
12655
|
+
const response = await fetchWithRetry(
|
|
12656
|
+
this.fetchFn,
|
|
12657
|
+
`${this.apiBase}/repos/${this.owner}/${this.repo}/issues`,
|
|
12658
|
+
{
|
|
12659
|
+
method: "POST",
|
|
12660
|
+
headers: this.headers(),
|
|
12661
|
+
body: JSON.stringify({ title: feature.name, body, labels })
|
|
12662
|
+
},
|
|
12663
|
+
this.retryOpts
|
|
12664
|
+
);
|
|
12665
|
+
if (!response.ok) {
|
|
12666
|
+
const text = await response.text();
|
|
12667
|
+
return (0, import_types21.Err)(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12668
|
+
}
|
|
12669
|
+
const data = await response.json();
|
|
12670
|
+
const externalId = buildExternalId(this.owner, this.repo, data.number);
|
|
12671
|
+
await this.closeIfDone(data.number, feature.status);
|
|
12672
|
+
return (0, import_types21.Ok)({ externalId, url: data.html_url });
|
|
12673
|
+
} catch (error) {
|
|
12674
|
+
return (0, import_types21.Err)(error instanceof Error ? error : new Error(String(error)));
|
|
12675
|
+
}
|
|
12676
|
+
}
|
|
12677
|
+
async updateTicket(externalId, changes) {
|
|
12678
|
+
try {
|
|
12679
|
+
const parsed = parseExternalId(externalId);
|
|
12680
|
+
if (!parsed) return (0, import_types21.Err)(new Error(`Invalid externalId format: "${externalId}"`));
|
|
12681
|
+
const patch = {};
|
|
12682
|
+
if (changes.name !== void 0) patch.title = changes.name;
|
|
12683
|
+
if (changes.summary !== void 0) {
|
|
12684
|
+
const body = [changes.summary, "", changes.spec ? `**Spec:** ${changes.spec}` : ""].filter(Boolean).join("\n");
|
|
12685
|
+
patch.body = body;
|
|
12686
|
+
}
|
|
12687
|
+
if (changes.status !== void 0) {
|
|
12688
|
+
const externalStatus = this.config.statusMap[changes.status];
|
|
12689
|
+
patch.state = externalStatus;
|
|
12690
|
+
patch.labels = labelsForStatus(changes.status, this.config);
|
|
12691
|
+
}
|
|
12692
|
+
const response = await fetchWithRetry(
|
|
12693
|
+
this.fetchFn,
|
|
12694
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}`,
|
|
12695
|
+
{
|
|
12696
|
+
method: "PATCH",
|
|
12697
|
+
headers: this.headers(),
|
|
12698
|
+
body: JSON.stringify(patch)
|
|
12699
|
+
},
|
|
12700
|
+
this.retryOpts
|
|
12701
|
+
);
|
|
12702
|
+
if (!response.ok) {
|
|
12703
|
+
const text = await response.text();
|
|
12704
|
+
return (0, import_types21.Err)(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12705
|
+
}
|
|
12706
|
+
const data = await response.json();
|
|
12707
|
+
return (0, import_types21.Ok)({ externalId, url: data.html_url });
|
|
12708
|
+
} catch (error) {
|
|
12709
|
+
return (0, import_types21.Err)(error instanceof Error ? error : new Error(String(error)));
|
|
12710
|
+
}
|
|
12711
|
+
}
|
|
12712
|
+
async fetchTicketState(externalId) {
|
|
12713
|
+
try {
|
|
12714
|
+
const parsed = parseExternalId(externalId);
|
|
12715
|
+
if (!parsed) return (0, import_types21.Err)(new Error(`Invalid externalId format: "${externalId}"`));
|
|
12716
|
+
const response = await fetchWithRetry(
|
|
12717
|
+
this.fetchFn,
|
|
12718
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}`,
|
|
12719
|
+
{
|
|
12720
|
+
method: "GET",
|
|
12721
|
+
headers: this.headers()
|
|
12722
|
+
},
|
|
12723
|
+
this.retryOpts
|
|
12724
|
+
);
|
|
12725
|
+
if (!response.ok) {
|
|
12726
|
+
const text = await response.text();
|
|
12727
|
+
return (0, import_types21.Err)(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12728
|
+
}
|
|
12729
|
+
const data = await response.json();
|
|
12730
|
+
return (0, import_types21.Ok)({
|
|
12731
|
+
externalId,
|
|
12732
|
+
status: data.state,
|
|
12733
|
+
labels: data.labels.map((l) => l.name),
|
|
12734
|
+
assignee: data.assignee ? `@${data.assignee.login}` : null
|
|
12735
|
+
});
|
|
12736
|
+
} catch (error) {
|
|
12737
|
+
return (0, import_types21.Err)(error instanceof Error ? error : new Error(String(error)));
|
|
12738
|
+
}
|
|
12739
|
+
}
|
|
12740
|
+
async fetchAllTickets() {
|
|
12741
|
+
try {
|
|
12742
|
+
const filterLabels = this.config.labels ?? [];
|
|
12743
|
+
const labelsParam = filterLabels.length > 0 ? `&labels=${filterLabels.join(",")}` : "";
|
|
12744
|
+
const tickets = [];
|
|
12745
|
+
let page = 1;
|
|
12746
|
+
const perPage = 100;
|
|
12747
|
+
while (true) {
|
|
12748
|
+
const response = await fetchWithRetry(
|
|
12749
|
+
this.fetchFn,
|
|
12750
|
+
`${this.apiBase}/repos/${this.owner}/${this.repo}/issues?state=all&per_page=${perPage}&page=${page}${labelsParam}`,
|
|
12751
|
+
{
|
|
12752
|
+
method: "GET",
|
|
12753
|
+
headers: this.headers()
|
|
12754
|
+
},
|
|
12755
|
+
this.retryOpts
|
|
12756
|
+
);
|
|
12757
|
+
if (!response.ok) {
|
|
12758
|
+
const text = await response.text();
|
|
12759
|
+
return (0, import_types21.Err)(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12760
|
+
}
|
|
12761
|
+
const data = await response.json();
|
|
12762
|
+
const issues = data.filter((d) => !d.pull_request);
|
|
12763
|
+
for (const issue of issues) {
|
|
12764
|
+
tickets.push({
|
|
12765
|
+
externalId: buildExternalId(this.owner, this.repo, issue.number),
|
|
12766
|
+
status: issue.state,
|
|
12767
|
+
labels: issue.labels.map((l) => l.name),
|
|
12768
|
+
assignee: issue.assignee ? `@${issue.assignee.login}` : null
|
|
12769
|
+
});
|
|
12770
|
+
}
|
|
12771
|
+
if (data.length < perPage) break;
|
|
12772
|
+
page++;
|
|
12773
|
+
}
|
|
12774
|
+
return (0, import_types21.Ok)(tickets);
|
|
12775
|
+
} catch (error) {
|
|
12776
|
+
return (0, import_types21.Err)(error instanceof Error ? error : new Error(String(error)));
|
|
12777
|
+
}
|
|
12778
|
+
}
|
|
12779
|
+
async assignTicket(externalId, assignee) {
|
|
12780
|
+
try {
|
|
12781
|
+
const parsed = parseExternalId(externalId);
|
|
12782
|
+
if (!parsed) return (0, import_types21.Err)(new Error(`Invalid externalId format: "${externalId}"`));
|
|
12783
|
+
const login = assignee.startsWith("@") ? assignee.slice(1) : assignee;
|
|
12784
|
+
const response = await fetchWithRetry(
|
|
12785
|
+
this.fetchFn,
|
|
12786
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}/assignees`,
|
|
12787
|
+
{
|
|
12788
|
+
method: "POST",
|
|
12789
|
+
headers: this.headers(),
|
|
12790
|
+
body: JSON.stringify({ assignees: [login] })
|
|
12791
|
+
},
|
|
12792
|
+
this.retryOpts
|
|
12793
|
+
);
|
|
12794
|
+
if (!response.ok) {
|
|
12795
|
+
const text = await response.text();
|
|
12796
|
+
return (0, import_types21.Err)(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12797
|
+
}
|
|
12798
|
+
return (0, import_types21.Ok)(void 0);
|
|
12799
|
+
} catch (error) {
|
|
12800
|
+
return (0, import_types21.Err)(error instanceof Error ? error : new Error(String(error)));
|
|
12801
|
+
}
|
|
12802
|
+
}
|
|
12803
|
+
};
|
|
12804
|
+
|
|
12805
|
+
// src/roadmap/sync-engine.ts
|
|
12806
|
+
var fs20 = __toESM(require("fs"));
|
|
12807
|
+
function emptySyncResult() {
|
|
12808
|
+
return { created: [], updated: [], assignmentChanges: [], errors: [] };
|
|
12809
|
+
}
|
|
12810
|
+
async function syncToExternal(roadmap, adapter, _config) {
|
|
12811
|
+
const result = emptySyncResult();
|
|
12812
|
+
for (const milestone of roadmap.milestones) {
|
|
12813
|
+
for (const feature of milestone.features) {
|
|
12814
|
+
if (!feature.externalId) {
|
|
12815
|
+
const createResult = await adapter.createTicket(feature, milestone.name);
|
|
12816
|
+
if (createResult.ok) {
|
|
12817
|
+
feature.externalId = createResult.value.externalId;
|
|
12818
|
+
result.created.push(createResult.value);
|
|
12819
|
+
} else {
|
|
12820
|
+
result.errors.push({ featureOrId: feature.name, error: createResult.error });
|
|
12821
|
+
}
|
|
12822
|
+
} else {
|
|
12823
|
+
const updateResult = await adapter.updateTicket(feature.externalId, feature);
|
|
12824
|
+
if (updateResult.ok) {
|
|
12825
|
+
result.updated.push(feature.externalId);
|
|
12826
|
+
} else {
|
|
12827
|
+
result.errors.push({ featureOrId: feature.externalId, error: updateResult.error });
|
|
12828
|
+
}
|
|
12829
|
+
}
|
|
12830
|
+
}
|
|
12831
|
+
}
|
|
12832
|
+
return result;
|
|
12833
|
+
}
|
|
12834
|
+
async function syncFromExternal(roadmap, adapter, config, options) {
|
|
12835
|
+
const result = emptySyncResult();
|
|
12836
|
+
const forceSync = options?.forceSync ?? false;
|
|
12837
|
+
const featureByExternalId = /* @__PURE__ */ new Map();
|
|
12838
|
+
for (const milestone of roadmap.milestones) {
|
|
12839
|
+
for (const feature of milestone.features) {
|
|
12840
|
+
if (feature.externalId) {
|
|
12841
|
+
featureByExternalId.set(feature.externalId, feature);
|
|
12842
|
+
}
|
|
12843
|
+
}
|
|
12844
|
+
}
|
|
12845
|
+
if (featureByExternalId.size === 0) return result;
|
|
12846
|
+
const fetchResult = await adapter.fetchAllTickets();
|
|
12847
|
+
if (!fetchResult.ok) {
|
|
12848
|
+
result.errors.push({ featureOrId: "*", error: fetchResult.error });
|
|
12849
|
+
return result;
|
|
12850
|
+
}
|
|
12851
|
+
for (const ticketState of fetchResult.value) {
|
|
12852
|
+
const feature = featureByExternalId.get(ticketState.externalId);
|
|
12853
|
+
if (!feature) continue;
|
|
12854
|
+
if (ticketState.assignee !== feature.assignee) {
|
|
12855
|
+
result.assignmentChanges.push({
|
|
12856
|
+
feature: feature.name,
|
|
12857
|
+
from: feature.assignee,
|
|
12858
|
+
to: ticketState.assignee
|
|
12859
|
+
});
|
|
12860
|
+
feature.assignee = ticketState.assignee;
|
|
12861
|
+
}
|
|
12862
|
+
const resolvedStatus = resolveReverseStatus(ticketState.status, ticketState.labels, config);
|
|
12863
|
+
if (resolvedStatus && resolvedStatus !== feature.status) {
|
|
12864
|
+
const newStatus = resolvedStatus;
|
|
12865
|
+
if (!forceSync && isRegression(feature.status, newStatus)) {
|
|
12866
|
+
continue;
|
|
12867
|
+
}
|
|
12868
|
+
feature.status = newStatus;
|
|
12869
|
+
}
|
|
12870
|
+
}
|
|
12871
|
+
return result;
|
|
12872
|
+
}
|
|
12873
|
+
var syncMutex = Promise.resolve();
|
|
12874
|
+
async function fullSync(roadmapPath, adapter, config, options) {
|
|
12875
|
+
const previousSync = syncMutex;
|
|
12876
|
+
let releaseMutex;
|
|
12877
|
+
syncMutex = new Promise((resolve7) => {
|
|
12878
|
+
releaseMutex = resolve7;
|
|
12879
|
+
});
|
|
12880
|
+
await previousSync;
|
|
12881
|
+
try {
|
|
12882
|
+
const raw = fs20.readFileSync(roadmapPath, "utf-8");
|
|
12883
|
+
const parseResult = parseRoadmap(raw);
|
|
12884
|
+
if (!parseResult.ok) {
|
|
12885
|
+
return {
|
|
12886
|
+
...emptySyncResult(),
|
|
12887
|
+
errors: [{ featureOrId: "*", error: parseResult.error }]
|
|
12888
|
+
};
|
|
12889
|
+
}
|
|
12890
|
+
const roadmap = parseResult.value;
|
|
12891
|
+
const pushResult = await syncToExternal(roadmap, adapter, config);
|
|
12892
|
+
const pullResult = await syncFromExternal(roadmap, adapter, config, options);
|
|
12893
|
+
fs20.writeFileSync(roadmapPath, serializeRoadmap(roadmap), "utf-8");
|
|
12894
|
+
return {
|
|
12895
|
+
created: pushResult.created,
|
|
12896
|
+
updated: pushResult.updated,
|
|
12897
|
+
assignmentChanges: pullResult.assignmentChanges,
|
|
12898
|
+
errors: [...pushResult.errors, ...pullResult.errors]
|
|
12899
|
+
};
|
|
12900
|
+
} finally {
|
|
12901
|
+
releaseMutex();
|
|
12902
|
+
}
|
|
12903
|
+
}
|
|
12904
|
+
|
|
12905
|
+
// src/roadmap/pilot-scoring.ts
|
|
12906
|
+
var PRIORITY_RANK = {
|
|
12907
|
+
P0: 0,
|
|
12908
|
+
P1: 1,
|
|
12909
|
+
P2: 2,
|
|
12910
|
+
P3: 3
|
|
12911
|
+
};
|
|
12912
|
+
var POSITION_WEIGHT = 0.5;
|
|
12913
|
+
var DEPENDENTS_WEIGHT = 0.3;
|
|
12914
|
+
var AFFINITY_WEIGHT = 0.2;
|
|
12915
|
+
function scoreRoadmapCandidates(roadmap, options) {
|
|
12916
|
+
const allFeatures = roadmap.milestones.flatMap((m) => m.features);
|
|
12917
|
+
const allFeatureNames = new Set(allFeatures.map((f) => f.name.toLowerCase()));
|
|
12918
|
+
const doneFeatures = new Set(
|
|
12919
|
+
allFeatures.filter((f) => f.status === "done").map((f) => f.name.toLowerCase())
|
|
12920
|
+
);
|
|
12921
|
+
const dependentsCount = /* @__PURE__ */ new Map();
|
|
12922
|
+
for (const feature of allFeatures) {
|
|
12923
|
+
for (const blocker of feature.blockedBy) {
|
|
12924
|
+
const key = blocker.toLowerCase();
|
|
12925
|
+
dependentsCount.set(key, (dependentsCount.get(key) ?? 0) + 1);
|
|
12926
|
+
}
|
|
12927
|
+
}
|
|
12928
|
+
const maxDependents = Math.max(1, ...dependentsCount.values());
|
|
12929
|
+
const milestoneMap = /* @__PURE__ */ new Map();
|
|
12930
|
+
for (const ms of roadmap.milestones) {
|
|
12931
|
+
milestoneMap.set(
|
|
12932
|
+
ms.name,
|
|
12933
|
+
ms.features.map((f) => f.name.toLowerCase())
|
|
12934
|
+
);
|
|
12935
|
+
}
|
|
12936
|
+
const userCompletedFeatures = /* @__PURE__ */ new Set();
|
|
12937
|
+
if (options?.currentUser) {
|
|
12938
|
+
const user = options.currentUser.toLowerCase();
|
|
12939
|
+
for (const record of roadmap.assignmentHistory) {
|
|
12940
|
+
if (record.action === "completed" && record.assignee.toLowerCase() === user) {
|
|
12941
|
+
userCompletedFeatures.add(record.feature.toLowerCase());
|
|
12942
|
+
}
|
|
12943
|
+
}
|
|
12944
|
+
}
|
|
12945
|
+
let totalPositions = 0;
|
|
12946
|
+
for (const ms of roadmap.milestones) {
|
|
12947
|
+
totalPositions += ms.features.length;
|
|
12948
|
+
}
|
|
12949
|
+
totalPositions = Math.max(1, totalPositions);
|
|
12950
|
+
const candidates = [];
|
|
12951
|
+
let globalPosition = 0;
|
|
12952
|
+
for (const ms of roadmap.milestones) {
|
|
12953
|
+
for (let featureIdx = 0; featureIdx < ms.features.length; featureIdx++) {
|
|
12954
|
+
const feature = ms.features[featureIdx];
|
|
12955
|
+
globalPosition++;
|
|
12956
|
+
if (feature.status !== "planned" && feature.status !== "backlog") continue;
|
|
12957
|
+
const isBlocked = feature.blockedBy.some((blocker) => {
|
|
12958
|
+
const key = blocker.toLowerCase();
|
|
12959
|
+
return allFeatureNames.has(key) && !doneFeatures.has(key);
|
|
12960
|
+
});
|
|
12961
|
+
if (isBlocked) continue;
|
|
12962
|
+
const positionScore = 1 - (globalPosition - 1) / totalPositions;
|
|
12963
|
+
const deps = dependentsCount.get(feature.name.toLowerCase()) ?? 0;
|
|
12964
|
+
const dependentsScore = deps / maxDependents;
|
|
12965
|
+
let affinityScore = 0;
|
|
12966
|
+
if (userCompletedFeatures.size > 0) {
|
|
12967
|
+
const completedBlockers = feature.blockedBy.filter(
|
|
12968
|
+
(b) => userCompletedFeatures.has(b.toLowerCase())
|
|
12969
|
+
);
|
|
12970
|
+
if (completedBlockers.length > 0) {
|
|
12971
|
+
affinityScore = 1;
|
|
12972
|
+
} else {
|
|
12973
|
+
const siblings = milestoneMap.get(ms.name) ?? [];
|
|
12974
|
+
const completedSiblings = siblings.filter((s) => userCompletedFeatures.has(s));
|
|
12975
|
+
if (completedSiblings.length > 0) {
|
|
12976
|
+
affinityScore = 0.5;
|
|
12977
|
+
}
|
|
12978
|
+
}
|
|
12979
|
+
}
|
|
12980
|
+
const weightedScore = POSITION_WEIGHT * positionScore + DEPENDENTS_WEIGHT * dependentsScore + AFFINITY_WEIGHT * affinityScore;
|
|
12981
|
+
const priorityTier = feature.priority ? PRIORITY_RANK[feature.priority] : null;
|
|
12982
|
+
candidates.push({
|
|
12983
|
+
feature,
|
|
12984
|
+
milestone: ms.name,
|
|
12985
|
+
positionScore,
|
|
12986
|
+
dependentsScore,
|
|
12987
|
+
affinityScore,
|
|
12988
|
+
weightedScore,
|
|
12989
|
+
priorityTier
|
|
12990
|
+
});
|
|
12991
|
+
}
|
|
12992
|
+
}
|
|
12993
|
+
candidates.sort((a, b) => {
|
|
12994
|
+
if (a.priorityTier !== null && b.priorityTier === null) return -1;
|
|
12995
|
+
if (a.priorityTier === null && b.priorityTier !== null) return 1;
|
|
12996
|
+
if (a.priorityTier !== null && b.priorityTier !== null) {
|
|
12997
|
+
if (a.priorityTier !== b.priorityTier) return a.priorityTier - b.priorityTier;
|
|
12998
|
+
}
|
|
12999
|
+
return b.weightedScore - a.weightedScore;
|
|
13000
|
+
});
|
|
13001
|
+
return candidates;
|
|
13002
|
+
}
|
|
13003
|
+
function assignFeature(roadmap, feature, assignee, date) {
|
|
13004
|
+
if (feature.assignee === assignee) return;
|
|
13005
|
+
if (feature.assignee !== null) {
|
|
13006
|
+
roadmap.assignmentHistory.push({
|
|
13007
|
+
feature: feature.name,
|
|
13008
|
+
assignee: feature.assignee,
|
|
13009
|
+
action: "unassigned",
|
|
13010
|
+
date
|
|
13011
|
+
});
|
|
13012
|
+
}
|
|
13013
|
+
feature.assignee = assignee;
|
|
13014
|
+
roadmap.assignmentHistory.push({
|
|
13015
|
+
feature: feature.name,
|
|
13016
|
+
assignee,
|
|
13017
|
+
action: "assigned",
|
|
13018
|
+
date
|
|
13019
|
+
});
|
|
13020
|
+
}
|
|
13021
|
+
|
|
12452
13022
|
// src/interaction/types.ts
|
|
12453
13023
|
var import_zod8 = require("zod");
|
|
12454
13024
|
var InteractionTypeSchema = import_zod8.z.enum(["question", "confirmation", "transition"]);
|
|
@@ -12479,17 +13049,18 @@ var EmitInteractionInputSchema = import_zod8.z.object({
|
|
|
12479
13049
|
});
|
|
12480
13050
|
|
|
12481
13051
|
// src/blueprint/scanner.ts
|
|
12482
|
-
var
|
|
13052
|
+
var fs21 = __toESM(require("fs/promises"));
|
|
12483
13053
|
var path20 = __toESM(require("path"));
|
|
12484
13054
|
var ProjectScanner = class {
|
|
12485
13055
|
constructor(rootDir) {
|
|
12486
13056
|
this.rootDir = rootDir;
|
|
12487
13057
|
}
|
|
13058
|
+
rootDir;
|
|
12488
13059
|
async scan() {
|
|
12489
13060
|
let projectName = path20.basename(this.rootDir);
|
|
12490
13061
|
try {
|
|
12491
13062
|
const pkgPath = path20.join(this.rootDir, "package.json");
|
|
12492
|
-
const pkgRaw = await
|
|
13063
|
+
const pkgRaw = await fs21.readFile(pkgPath, "utf-8");
|
|
12493
13064
|
const pkg = JSON.parse(pkgRaw);
|
|
12494
13065
|
if (pkg.name) projectName = pkg.name;
|
|
12495
13066
|
} catch {
|
|
@@ -12530,7 +13101,7 @@ var ProjectScanner = class {
|
|
|
12530
13101
|
};
|
|
12531
13102
|
|
|
12532
13103
|
// src/blueprint/generator.ts
|
|
12533
|
-
var
|
|
13104
|
+
var fs22 = __toESM(require("fs/promises"));
|
|
12534
13105
|
var path21 = __toESM(require("path"));
|
|
12535
13106
|
var ejs = __toESM(require("ejs"));
|
|
12536
13107
|
|
|
@@ -12615,13 +13186,13 @@ var BlueprintGenerator = class {
|
|
|
12615
13186
|
styles: STYLES,
|
|
12616
13187
|
scripts: SCRIPTS
|
|
12617
13188
|
});
|
|
12618
|
-
await
|
|
12619
|
-
await
|
|
13189
|
+
await fs22.mkdir(options.outputDir, { recursive: true });
|
|
13190
|
+
await fs22.writeFile(path21.join(options.outputDir, "index.html"), html);
|
|
12620
13191
|
}
|
|
12621
13192
|
};
|
|
12622
13193
|
|
|
12623
13194
|
// src/update-checker.ts
|
|
12624
|
-
var
|
|
13195
|
+
var fs23 = __toESM(require("fs"));
|
|
12625
13196
|
var path22 = __toESM(require("path"));
|
|
12626
13197
|
var os = __toESM(require("os"));
|
|
12627
13198
|
var import_child_process3 = require("child_process");
|
|
@@ -12640,7 +13211,7 @@ function shouldRunCheck(state, intervalMs) {
|
|
|
12640
13211
|
}
|
|
12641
13212
|
function readCheckState() {
|
|
12642
13213
|
try {
|
|
12643
|
-
const raw =
|
|
13214
|
+
const raw = fs23.readFileSync(getStatePath(), "utf-8");
|
|
12644
13215
|
const parsed = JSON.parse(raw);
|
|
12645
13216
|
if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
|
|
12646
13217
|
const state = parsed;
|
|
@@ -13153,7 +13724,7 @@ function getModelPrice(model, dataset) {
|
|
|
13153
13724
|
}
|
|
13154
13725
|
|
|
13155
13726
|
// src/pricing/cache.ts
|
|
13156
|
-
var
|
|
13727
|
+
var fs24 = __toESM(require("fs/promises"));
|
|
13157
13728
|
var path23 = __toESM(require("path"));
|
|
13158
13729
|
|
|
13159
13730
|
// src/pricing/fallback.json
|
|
@@ -13214,7 +13785,7 @@ function getStalenessMarkerPath(projectRoot) {
|
|
|
13214
13785
|
}
|
|
13215
13786
|
async function readDiskCache(projectRoot) {
|
|
13216
13787
|
try {
|
|
13217
|
-
const raw = await
|
|
13788
|
+
const raw = await fs24.readFile(getCachePath(projectRoot), "utf-8");
|
|
13218
13789
|
return JSON.parse(raw);
|
|
13219
13790
|
} catch {
|
|
13220
13791
|
return null;
|
|
@@ -13222,8 +13793,8 @@ async function readDiskCache(projectRoot) {
|
|
|
13222
13793
|
}
|
|
13223
13794
|
async function writeDiskCache(projectRoot, data) {
|
|
13224
13795
|
const cachePath = getCachePath(projectRoot);
|
|
13225
|
-
await
|
|
13226
|
-
await
|
|
13796
|
+
await fs24.mkdir(path23.dirname(cachePath), { recursive: true });
|
|
13797
|
+
await fs24.writeFile(cachePath, JSON.stringify(data, null, 2));
|
|
13227
13798
|
}
|
|
13228
13799
|
async function fetchFromNetwork() {
|
|
13229
13800
|
try {
|
|
@@ -13250,7 +13821,7 @@ function loadFallbackDataset() {
|
|
|
13250
13821
|
async function checkAndWarnStaleness(projectRoot) {
|
|
13251
13822
|
const markerPath = getStalenessMarkerPath(projectRoot);
|
|
13252
13823
|
try {
|
|
13253
|
-
const raw = await
|
|
13824
|
+
const raw = await fs24.readFile(markerPath, "utf-8");
|
|
13254
13825
|
const marker = JSON.parse(raw);
|
|
13255
13826
|
const firstUse = new Date(marker.firstFallbackUse).getTime();
|
|
13256
13827
|
const now = Date.now();
|
|
@@ -13262,8 +13833,8 @@ async function checkAndWarnStaleness(projectRoot) {
|
|
|
13262
13833
|
}
|
|
13263
13834
|
} catch {
|
|
13264
13835
|
try {
|
|
13265
|
-
await
|
|
13266
|
-
await
|
|
13836
|
+
await fs24.mkdir(path23.dirname(markerPath), { recursive: true });
|
|
13837
|
+
await fs24.writeFile(
|
|
13267
13838
|
markerPath,
|
|
13268
13839
|
JSON.stringify({ firstFallbackUse: (/* @__PURE__ */ new Date()).toISOString() })
|
|
13269
13840
|
);
|
|
@@ -13273,7 +13844,7 @@ async function checkAndWarnStaleness(projectRoot) {
|
|
|
13273
13844
|
}
|
|
13274
13845
|
async function clearStalenessMarker(projectRoot) {
|
|
13275
13846
|
try {
|
|
13276
|
-
await
|
|
13847
|
+
await fs24.unlink(getStalenessMarkerPath(projectRoot));
|
|
13277
13848
|
} catch {
|
|
13278
13849
|
}
|
|
13279
13850
|
}
|
|
@@ -13443,7 +14014,7 @@ function aggregateByDay(records) {
|
|
|
13443
14014
|
}
|
|
13444
14015
|
|
|
13445
14016
|
// src/usage/jsonl-reader.ts
|
|
13446
|
-
var
|
|
14017
|
+
var fs25 = __toESM(require("fs"));
|
|
13447
14018
|
var path24 = __toESM(require("path"));
|
|
13448
14019
|
function parseLine(line, lineNumber) {
|
|
13449
14020
|
let entry;
|
|
@@ -13486,7 +14057,7 @@ function readCostRecords(projectRoot) {
|
|
|
13486
14057
|
const costsFile = path24.join(projectRoot, ".harness", "metrics", "costs.jsonl");
|
|
13487
14058
|
let raw;
|
|
13488
14059
|
try {
|
|
13489
|
-
raw =
|
|
14060
|
+
raw = fs25.readFileSync(costsFile, "utf-8");
|
|
13490
14061
|
} catch {
|
|
13491
14062
|
return [];
|
|
13492
14063
|
}
|
|
@@ -13504,7 +14075,7 @@ function readCostRecords(projectRoot) {
|
|
|
13504
14075
|
}
|
|
13505
14076
|
|
|
13506
14077
|
// src/usage/cc-parser.ts
|
|
13507
|
-
var
|
|
14078
|
+
var fs26 = __toESM(require("fs"));
|
|
13508
14079
|
var path25 = __toESM(require("path"));
|
|
13509
14080
|
var os2 = __toESM(require("os"));
|
|
13510
14081
|
function extractUsage(entry) {
|
|
@@ -13552,7 +14123,7 @@ function parseCCLine(line, filePath, lineNumber) {
|
|
|
13552
14123
|
function readCCFile(filePath) {
|
|
13553
14124
|
let raw;
|
|
13554
14125
|
try {
|
|
13555
|
-
raw =
|
|
14126
|
+
raw = fs26.readFileSync(filePath, "utf-8");
|
|
13556
14127
|
} catch {
|
|
13557
14128
|
return [];
|
|
13558
14129
|
}
|
|
@@ -13577,7 +14148,7 @@ function parseCCRecords() {
|
|
|
13577
14148
|
const projectsDir = path25.join(homeDir, ".claude", "projects");
|
|
13578
14149
|
let projectDirs;
|
|
13579
14150
|
try {
|
|
13580
|
-
projectDirs =
|
|
14151
|
+
projectDirs = fs26.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path25.join(projectsDir, d.name));
|
|
13581
14152
|
} catch {
|
|
13582
14153
|
return [];
|
|
13583
14154
|
}
|
|
@@ -13585,7 +14156,7 @@ function parseCCRecords() {
|
|
|
13585
14156
|
for (const dir of projectDirs) {
|
|
13586
14157
|
let files;
|
|
13587
14158
|
try {
|
|
13588
|
-
files =
|
|
14159
|
+
files = fs26.readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => path25.join(dir, f));
|
|
13589
14160
|
} catch {
|
|
13590
14161
|
continue;
|
|
13591
14162
|
}
|
|
@@ -13644,6 +14215,7 @@ var VERSION = "0.15.0";
|
|
|
13644
14215
|
ForbiddenImportCollector,
|
|
13645
14216
|
GateConfigSchema,
|
|
13646
14217
|
GateResultSchema,
|
|
14218
|
+
GitHubIssuesSyncAdapter,
|
|
13647
14219
|
HandoffSchema,
|
|
13648
14220
|
HarnessStateSchema,
|
|
13649
14221
|
InteractionTypeSchema,
|
|
@@ -13665,6 +14237,7 @@ var VERSION = "0.15.0";
|
|
|
13665
14237
|
RuleRegistry,
|
|
13666
14238
|
SECURITY_DESCRIPTOR,
|
|
13667
14239
|
STALENESS_WARNING_DAYS,
|
|
14240
|
+
STATUS_RANK,
|
|
13668
14241
|
SecurityConfigSchema,
|
|
13669
14242
|
SecurityScanner,
|
|
13670
14243
|
SharableBoundaryConfigSchema,
|
|
@@ -13698,6 +14271,7 @@ var VERSION = "0.15.0";
|
|
|
13698
14271
|
archiveLearnings,
|
|
13699
14272
|
archiveSession,
|
|
13700
14273
|
archiveStream,
|
|
14274
|
+
assignFeature,
|
|
13701
14275
|
buildDependencyGraph,
|
|
13702
14276
|
buildExclusionSet,
|
|
13703
14277
|
buildSnapshot,
|
|
@@ -13762,6 +14336,7 @@ var VERSION = "0.15.0";
|
|
|
13762
14336
|
formatGitHubSummary,
|
|
13763
14337
|
formatOutline,
|
|
13764
14338
|
formatTerminalOutput,
|
|
14339
|
+
fullSync,
|
|
13765
14340
|
generateAgentsMap,
|
|
13766
14341
|
generateSuggestions,
|
|
13767
14342
|
getActionEmitter,
|
|
@@ -13779,6 +14354,7 @@ var VERSION = "0.15.0";
|
|
|
13779
14354
|
injectionRules,
|
|
13780
14355
|
insecureDefaultsRules,
|
|
13781
14356
|
isDuplicateFinding,
|
|
14357
|
+
isRegression,
|
|
13782
14358
|
isSmallSuggestion,
|
|
13783
14359
|
isUpdateCheckEnabled,
|
|
13784
14360
|
listActiveSessions,
|
|
@@ -13832,6 +14408,7 @@ var VERSION = "0.15.0";
|
|
|
13832
14408
|
resetParserCache,
|
|
13833
14409
|
resolveFileToLayer,
|
|
13834
14410
|
resolveModelTier,
|
|
14411
|
+
resolveReverseStatus,
|
|
13835
14412
|
resolveRuleSeverity,
|
|
13836
14413
|
resolveSessionDir,
|
|
13837
14414
|
resolveStreamPath,
|
|
@@ -13852,6 +14429,7 @@ var VERSION = "0.15.0";
|
|
|
13852
14429
|
saveStreamIndex,
|
|
13853
14430
|
scanForInjection,
|
|
13854
14431
|
scopeContext,
|
|
14432
|
+
scoreRoadmapCandidates,
|
|
13855
14433
|
searchSymbols,
|
|
13856
14434
|
secretRules,
|
|
13857
14435
|
serializeRoadmap,
|
|
@@ -13860,7 +14438,9 @@ var VERSION = "0.15.0";
|
|
|
13860
14438
|
shouldRunCheck,
|
|
13861
14439
|
spawnBackgroundCheck,
|
|
13862
14440
|
syncConstraintNodes,
|
|
14441
|
+
syncFromExternal,
|
|
13863
14442
|
syncRoadmap,
|
|
14443
|
+
syncToExternal,
|
|
13864
14444
|
tagUncitedFindings,
|
|
13865
14445
|
touchStream,
|
|
13866
14446
|
trackAction,
|