@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.mjs
CHANGED
|
@@ -10058,6 +10058,7 @@ var VALID_STATUSES = /* @__PURE__ */ new Set([
|
|
|
10058
10058
|
"blocked"
|
|
10059
10059
|
]);
|
|
10060
10060
|
var EM_DASH = "\u2014";
|
|
10061
|
+
var VALID_PRIORITIES = /* @__PURE__ */ new Set(["P0", "P1", "P2", "P3"]);
|
|
10061
10062
|
function parseRoadmap(markdown) {
|
|
10062
10063
|
const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---/);
|
|
10063
10064
|
if (!fmMatch) {
|
|
@@ -10068,9 +10069,12 @@ function parseRoadmap(markdown) {
|
|
|
10068
10069
|
const body = markdown.slice(fmMatch[0].length);
|
|
10069
10070
|
const milestonesResult = parseMilestones(body);
|
|
10070
10071
|
if (!milestonesResult.ok) return milestonesResult;
|
|
10072
|
+
const historyResult = parseAssignmentHistory(body);
|
|
10073
|
+
if (!historyResult.ok) return historyResult;
|
|
10071
10074
|
return Ok2({
|
|
10072
10075
|
frontmatter: fmResult.value,
|
|
10073
|
-
milestones: milestonesResult.value
|
|
10076
|
+
milestones: milestonesResult.value,
|
|
10077
|
+
assignmentHistory: historyResult.value
|
|
10074
10078
|
});
|
|
10075
10079
|
}
|
|
10076
10080
|
function parseFrontmatter2(raw) {
|
|
@@ -10110,12 +10114,17 @@ function parseMilestones(body) {
|
|
|
10110
10114
|
const h2Pattern = /^## (.+)$/gm;
|
|
10111
10115
|
const h2Matches = [];
|
|
10112
10116
|
let match;
|
|
10117
|
+
let bodyEnd = body.length;
|
|
10113
10118
|
while ((match = h2Pattern.exec(body)) !== null) {
|
|
10119
|
+
if (match[1] === "Assignment History") {
|
|
10120
|
+
bodyEnd = match.index;
|
|
10121
|
+
break;
|
|
10122
|
+
}
|
|
10114
10123
|
h2Matches.push({ heading: match[1], startIndex: match.index, fullMatch: match[0] });
|
|
10115
10124
|
}
|
|
10116
10125
|
for (let i = 0; i < h2Matches.length; i++) {
|
|
10117
10126
|
const h2 = h2Matches[i];
|
|
10118
|
-
const nextStart = i + 1 < h2Matches.length ? h2Matches[i + 1].startIndex :
|
|
10127
|
+
const nextStart = i + 1 < h2Matches.length ? h2Matches[i + 1].startIndex : bodyEnd;
|
|
10119
10128
|
const sectionBody = body.slice(h2.startIndex + h2.fullMatch.length, nextStart);
|
|
10120
10129
|
const isBacklog = h2.heading === "Backlog";
|
|
10121
10130
|
const milestoneName = isBacklog ? "Backlog" : h2.heading.replace(/^Milestone:\s*/, "");
|
|
@@ -10181,15 +10190,60 @@ function parseFeatureFields(name, body) {
|
|
|
10181
10190
|
const specRaw = fieldMap.get("Spec") ?? EM_DASH;
|
|
10182
10191
|
const plans = parseListField(fieldMap, "Plans", "Plan");
|
|
10183
10192
|
const blockedBy = parseListField(fieldMap, "Blocked by", "Blockers");
|
|
10193
|
+
const assigneeRaw = fieldMap.get("Assignee") ?? EM_DASH;
|
|
10194
|
+
const priorityRaw = fieldMap.get("Priority") ?? EM_DASH;
|
|
10195
|
+
const externalIdRaw = fieldMap.get("External-ID") ?? EM_DASH;
|
|
10196
|
+
if (priorityRaw !== EM_DASH && !VALID_PRIORITIES.has(priorityRaw)) {
|
|
10197
|
+
return Err2(
|
|
10198
|
+
new Error(
|
|
10199
|
+
`Feature "${name}" has invalid priority: "${priorityRaw}". Valid priorities: ${[...VALID_PRIORITIES].join(", ")}`
|
|
10200
|
+
)
|
|
10201
|
+
);
|
|
10202
|
+
}
|
|
10184
10203
|
return Ok2({
|
|
10185
10204
|
name,
|
|
10186
10205
|
status: statusRaw,
|
|
10187
10206
|
spec: specRaw === EM_DASH ? null : specRaw,
|
|
10188
10207
|
plans,
|
|
10189
10208
|
blockedBy,
|
|
10190
|
-
summary: fieldMap.get("Summary") ?? ""
|
|
10209
|
+
summary: fieldMap.get("Summary") ?? "",
|
|
10210
|
+
assignee: assigneeRaw === EM_DASH ? null : assigneeRaw,
|
|
10211
|
+
priority: priorityRaw === EM_DASH ? null : priorityRaw,
|
|
10212
|
+
externalId: externalIdRaw === EM_DASH ? null : externalIdRaw
|
|
10191
10213
|
});
|
|
10192
10214
|
}
|
|
10215
|
+
function parseAssignmentHistory(body) {
|
|
10216
|
+
const historyMatch = body.match(/^## Assignment History\s*\n/m);
|
|
10217
|
+
if (!historyMatch || historyMatch.index === void 0) return Ok2([]);
|
|
10218
|
+
const historyStart = historyMatch.index + historyMatch[0].length;
|
|
10219
|
+
const rawHistoryBody = body.slice(historyStart);
|
|
10220
|
+
const nextH2 = rawHistoryBody.search(/^## /m);
|
|
10221
|
+
const historyBody = nextH2 === -1 ? rawHistoryBody : rawHistoryBody.slice(0, nextH2);
|
|
10222
|
+
const records = [];
|
|
10223
|
+
const lines = historyBody.split("\n");
|
|
10224
|
+
let pastHeader = false;
|
|
10225
|
+
for (const line of lines) {
|
|
10226
|
+
const trimmed = line.trim();
|
|
10227
|
+
if (!trimmed.startsWith("|")) continue;
|
|
10228
|
+
if (!pastHeader) {
|
|
10229
|
+
if (trimmed.match(/^\|[-\s|]+\|$/)) {
|
|
10230
|
+
pastHeader = true;
|
|
10231
|
+
}
|
|
10232
|
+
continue;
|
|
10233
|
+
}
|
|
10234
|
+
const cells = trimmed.split("|").map((c) => c.trim()).filter((c) => c.length > 0);
|
|
10235
|
+
if (cells.length < 4) continue;
|
|
10236
|
+
const action = cells[2];
|
|
10237
|
+
if (!["assigned", "completed", "unassigned"].includes(action)) continue;
|
|
10238
|
+
records.push({
|
|
10239
|
+
feature: cells[0],
|
|
10240
|
+
assignee: cells[1],
|
|
10241
|
+
action,
|
|
10242
|
+
date: cells[3]
|
|
10243
|
+
});
|
|
10244
|
+
}
|
|
10245
|
+
return Ok2(records);
|
|
10246
|
+
}
|
|
10193
10247
|
|
|
10194
10248
|
// src/roadmap/serialize.ts
|
|
10195
10249
|
var EM_DASH2 = "\u2014";
|
|
@@ -10217,6 +10271,10 @@ function serializeRoadmap(roadmap) {
|
|
|
10217
10271
|
lines.push(...serializeFeature(feature));
|
|
10218
10272
|
}
|
|
10219
10273
|
}
|
|
10274
|
+
if (roadmap.assignmentHistory && roadmap.assignmentHistory.length > 0) {
|
|
10275
|
+
lines.push("");
|
|
10276
|
+
lines.push(...serializeAssignmentHistory(roadmap.assignmentHistory));
|
|
10277
|
+
}
|
|
10220
10278
|
lines.push("");
|
|
10221
10279
|
return lines.join("\n");
|
|
10222
10280
|
}
|
|
@@ -10227,7 +10285,7 @@ function serializeFeature(feature) {
|
|
|
10227
10285
|
const spec = feature.spec ?? EM_DASH2;
|
|
10228
10286
|
const plans = feature.plans.length > 0 ? feature.plans.join(", ") : EM_DASH2;
|
|
10229
10287
|
const blockedBy = feature.blockedBy.length > 0 ? feature.blockedBy.join(", ") : EM_DASH2;
|
|
10230
|
-
|
|
10288
|
+
const lines = [
|
|
10231
10289
|
`### ${feature.name}`,
|
|
10232
10290
|
"",
|
|
10233
10291
|
`- **Status:** ${feature.status}`,
|
|
@@ -10236,12 +10294,45 @@ function serializeFeature(feature) {
|
|
|
10236
10294
|
`- **Blockers:** ${blockedBy}`,
|
|
10237
10295
|
`- **Plan:** ${plans}`
|
|
10238
10296
|
];
|
|
10297
|
+
const hasExtended = feature.assignee !== null || feature.priority !== null || feature.externalId !== null;
|
|
10298
|
+
if (hasExtended) {
|
|
10299
|
+
lines.push(`- **Assignee:** ${feature.assignee ?? EM_DASH2}`);
|
|
10300
|
+
lines.push(`- **Priority:** ${feature.priority ?? EM_DASH2}`);
|
|
10301
|
+
lines.push(`- **External-ID:** ${feature.externalId ?? EM_DASH2}`);
|
|
10302
|
+
}
|
|
10303
|
+
return lines;
|
|
10304
|
+
}
|
|
10305
|
+
function serializeAssignmentHistory(records) {
|
|
10306
|
+
const lines = [
|
|
10307
|
+
"## Assignment History",
|
|
10308
|
+
"| Feature | Assignee | Action | Date |",
|
|
10309
|
+
"|---------|----------|--------|------|"
|
|
10310
|
+
];
|
|
10311
|
+
for (const record of records) {
|
|
10312
|
+
lines.push(`| ${record.feature} | ${record.assignee} | ${record.action} | ${record.date} |`);
|
|
10313
|
+
}
|
|
10314
|
+
return lines;
|
|
10239
10315
|
}
|
|
10240
10316
|
|
|
10241
10317
|
// src/roadmap/sync.ts
|
|
10242
10318
|
import * as fs19 from "fs";
|
|
10243
10319
|
import * as path19 from "path";
|
|
10244
10320
|
import { Ok as Ok3 } from "@harness-engineering/types";
|
|
10321
|
+
|
|
10322
|
+
// src/roadmap/status-rank.ts
|
|
10323
|
+
var STATUS_RANK = {
|
|
10324
|
+
backlog: 0,
|
|
10325
|
+
planned: 1,
|
|
10326
|
+
blocked: 1,
|
|
10327
|
+
// lateral to planned — sync can move to/from blocked freely
|
|
10328
|
+
"in-progress": 2,
|
|
10329
|
+
done: 3
|
|
10330
|
+
};
|
|
10331
|
+
function isRegression(from, to) {
|
|
10332
|
+
return STATUS_RANK[to] < STATUS_RANK[from];
|
|
10333
|
+
}
|
|
10334
|
+
|
|
10335
|
+
// src/roadmap/sync.ts
|
|
10245
10336
|
function inferStatus(feature, projectPath, allFeatures) {
|
|
10246
10337
|
if (feature.blockedBy.length > 0) {
|
|
10247
10338
|
const blockerNotDone = feature.blockedBy.some((blockerName) => {
|
|
@@ -10308,17 +10399,6 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
10308
10399
|
if (anyStarted) return "in-progress";
|
|
10309
10400
|
return null;
|
|
10310
10401
|
}
|
|
10311
|
-
var STATUS_RANK = {
|
|
10312
|
-
backlog: 0,
|
|
10313
|
-
planned: 1,
|
|
10314
|
-
blocked: 1,
|
|
10315
|
-
// lateral to planned — sync can move to/from blocked freely
|
|
10316
|
-
"in-progress": 2,
|
|
10317
|
-
done: 3
|
|
10318
|
-
};
|
|
10319
|
-
function isRegression(from, to) {
|
|
10320
|
-
return STATUS_RANK[to] < STATUS_RANK[from];
|
|
10321
|
-
}
|
|
10322
10402
|
function syncRoadmap(options) {
|
|
10323
10403
|
const { projectPath, roadmap, forceSync } = options;
|
|
10324
10404
|
const allFeatures = roadmap.milestones.flatMap((m) => m.features);
|
|
@@ -10349,6 +10429,487 @@ function applySyncChanges(roadmap, changes) {
|
|
|
10349
10429
|
roadmap.frontmatter.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
|
|
10350
10430
|
}
|
|
10351
10431
|
|
|
10432
|
+
// src/roadmap/tracker-sync.ts
|
|
10433
|
+
function resolveReverseStatus(externalStatus, labels, config) {
|
|
10434
|
+
const reverseMap = config.reverseStatusMap;
|
|
10435
|
+
if (!reverseMap) return null;
|
|
10436
|
+
if (reverseMap[externalStatus]) {
|
|
10437
|
+
return reverseMap[externalStatus];
|
|
10438
|
+
}
|
|
10439
|
+
const statusLabels = ["in-progress", "blocked", "planned"];
|
|
10440
|
+
const matchingLabels = labels.filter((l) => statusLabels.includes(l));
|
|
10441
|
+
if (matchingLabels.length === 1) {
|
|
10442
|
+
const compoundKey = `${externalStatus}:${matchingLabels[0]}`;
|
|
10443
|
+
if (reverseMap[compoundKey]) {
|
|
10444
|
+
return reverseMap[compoundKey];
|
|
10445
|
+
}
|
|
10446
|
+
}
|
|
10447
|
+
return null;
|
|
10448
|
+
}
|
|
10449
|
+
|
|
10450
|
+
// src/roadmap/adapters/github-issues.ts
|
|
10451
|
+
import { Ok as Ok4, Err as Err3 } from "@harness-engineering/types";
|
|
10452
|
+
function parseExternalId(externalId) {
|
|
10453
|
+
const match = externalId.match(/^github:([^/]+)\/([^#]+)#(\d+)$/);
|
|
10454
|
+
if (!match) return null;
|
|
10455
|
+
return { owner: match[1], repo: match[2], number: parseInt(match[3], 10) };
|
|
10456
|
+
}
|
|
10457
|
+
function buildExternalId(owner, repo, number) {
|
|
10458
|
+
return `github:${owner}/${repo}#${number}`;
|
|
10459
|
+
}
|
|
10460
|
+
function labelsForStatus(status, config) {
|
|
10461
|
+
const base = config.labels ?? [];
|
|
10462
|
+
const externalStatus = config.statusMap[status];
|
|
10463
|
+
if (externalStatus === "open" && status !== "backlog") {
|
|
10464
|
+
return [...base, status];
|
|
10465
|
+
}
|
|
10466
|
+
return [...base];
|
|
10467
|
+
}
|
|
10468
|
+
var RETRY_DEFAULTS = { maxRetries: 5, baseDelayMs: 1e3 };
|
|
10469
|
+
function sleep(ms) {
|
|
10470
|
+
return new Promise((resolve5) => setTimeout(resolve5, ms));
|
|
10471
|
+
}
|
|
10472
|
+
async function fetchWithRetry(fetchFn, input, init, opts = RETRY_DEFAULTS) {
|
|
10473
|
+
let lastResponse;
|
|
10474
|
+
for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
|
|
10475
|
+
const response = await fetchFn(input, init);
|
|
10476
|
+
if (response.status !== 403 && response.status !== 429) return response;
|
|
10477
|
+
lastResponse = response;
|
|
10478
|
+
if (attempt === opts.maxRetries) break;
|
|
10479
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
10480
|
+
let delayMs;
|
|
10481
|
+
if (retryAfter) {
|
|
10482
|
+
const seconds = parseInt(retryAfter, 10);
|
|
10483
|
+
delayMs = isNaN(seconds) ? opts.baseDelayMs : seconds * 1e3;
|
|
10484
|
+
} else {
|
|
10485
|
+
delayMs = opts.baseDelayMs * Math.pow(2, attempt) + Math.random() * 500;
|
|
10486
|
+
}
|
|
10487
|
+
await sleep(delayMs);
|
|
10488
|
+
}
|
|
10489
|
+
return lastResponse;
|
|
10490
|
+
}
|
|
10491
|
+
var GitHubIssuesSyncAdapter = class {
|
|
10492
|
+
token;
|
|
10493
|
+
config;
|
|
10494
|
+
fetchFn;
|
|
10495
|
+
apiBase;
|
|
10496
|
+
owner;
|
|
10497
|
+
repo;
|
|
10498
|
+
retryOpts;
|
|
10499
|
+
constructor(options) {
|
|
10500
|
+
this.token = options.token;
|
|
10501
|
+
this.config = options.config;
|
|
10502
|
+
this.fetchFn = options.fetchFn ?? globalThis.fetch;
|
|
10503
|
+
this.apiBase = options.apiBase ?? "https://api.github.com";
|
|
10504
|
+
this.retryOpts = {
|
|
10505
|
+
maxRetries: options.maxRetries ?? RETRY_DEFAULTS.maxRetries,
|
|
10506
|
+
baseDelayMs: options.baseDelayMs ?? RETRY_DEFAULTS.baseDelayMs
|
|
10507
|
+
};
|
|
10508
|
+
const repoParts = (options.config.repo ?? "").split("/");
|
|
10509
|
+
if (repoParts.length !== 2 || !repoParts[0] || !repoParts[1]) {
|
|
10510
|
+
throw new Error(`Invalid repo format: "${options.config.repo}". Expected "owner/repo".`);
|
|
10511
|
+
}
|
|
10512
|
+
this.owner = repoParts[0];
|
|
10513
|
+
this.repo = repoParts[1];
|
|
10514
|
+
}
|
|
10515
|
+
headers() {
|
|
10516
|
+
return {
|
|
10517
|
+
Authorization: `Bearer ${this.token}`,
|
|
10518
|
+
Accept: "application/vnd.github+json",
|
|
10519
|
+
"Content-Type": "application/json",
|
|
10520
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
10521
|
+
};
|
|
10522
|
+
}
|
|
10523
|
+
/**
|
|
10524
|
+
* Close an issue if the feature status maps to 'closed'.
|
|
10525
|
+
* GitHub Issues API doesn't accept state on POST — requires a follow-up PATCH.
|
|
10526
|
+
*/
|
|
10527
|
+
async closeIfDone(issueNumber, featureStatus) {
|
|
10528
|
+
const externalStatus = this.config.statusMap[featureStatus];
|
|
10529
|
+
if (externalStatus !== "closed") return;
|
|
10530
|
+
await fetchWithRetry(
|
|
10531
|
+
this.fetchFn,
|
|
10532
|
+
`${this.apiBase}/repos/${this.owner}/${this.repo}/issues/${issueNumber}`,
|
|
10533
|
+
{ method: "PATCH", headers: this.headers(), body: JSON.stringify({ state: "closed" }) },
|
|
10534
|
+
this.retryOpts
|
|
10535
|
+
);
|
|
10536
|
+
}
|
|
10537
|
+
async createTicket(feature, milestone) {
|
|
10538
|
+
try {
|
|
10539
|
+
const labels = labelsForStatus(feature.status, this.config);
|
|
10540
|
+
const body = [
|
|
10541
|
+
feature.summary,
|
|
10542
|
+
"",
|
|
10543
|
+
`**Milestone:** ${milestone}`,
|
|
10544
|
+
feature.spec ? `**Spec:** ${feature.spec}` : ""
|
|
10545
|
+
].filter(Boolean).join("\n");
|
|
10546
|
+
const response = await fetchWithRetry(
|
|
10547
|
+
this.fetchFn,
|
|
10548
|
+
`${this.apiBase}/repos/${this.owner}/${this.repo}/issues`,
|
|
10549
|
+
{
|
|
10550
|
+
method: "POST",
|
|
10551
|
+
headers: this.headers(),
|
|
10552
|
+
body: JSON.stringify({ title: feature.name, body, labels })
|
|
10553
|
+
},
|
|
10554
|
+
this.retryOpts
|
|
10555
|
+
);
|
|
10556
|
+
if (!response.ok) {
|
|
10557
|
+
const text = await response.text();
|
|
10558
|
+
return Err3(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
10559
|
+
}
|
|
10560
|
+
const data = await response.json();
|
|
10561
|
+
const externalId = buildExternalId(this.owner, this.repo, data.number);
|
|
10562
|
+
await this.closeIfDone(data.number, feature.status);
|
|
10563
|
+
return Ok4({ externalId, url: data.html_url });
|
|
10564
|
+
} catch (error) {
|
|
10565
|
+
return Err3(error instanceof Error ? error : new Error(String(error)));
|
|
10566
|
+
}
|
|
10567
|
+
}
|
|
10568
|
+
async updateTicket(externalId, changes) {
|
|
10569
|
+
try {
|
|
10570
|
+
const parsed = parseExternalId(externalId);
|
|
10571
|
+
if (!parsed) return Err3(new Error(`Invalid externalId format: "${externalId}"`));
|
|
10572
|
+
const patch = {};
|
|
10573
|
+
if (changes.name !== void 0) patch.title = changes.name;
|
|
10574
|
+
if (changes.summary !== void 0) {
|
|
10575
|
+
const body = [changes.summary, "", changes.spec ? `**Spec:** ${changes.spec}` : ""].filter(Boolean).join("\n");
|
|
10576
|
+
patch.body = body;
|
|
10577
|
+
}
|
|
10578
|
+
if (changes.status !== void 0) {
|
|
10579
|
+
const externalStatus = this.config.statusMap[changes.status];
|
|
10580
|
+
patch.state = externalStatus;
|
|
10581
|
+
patch.labels = labelsForStatus(changes.status, this.config);
|
|
10582
|
+
}
|
|
10583
|
+
const response = await fetchWithRetry(
|
|
10584
|
+
this.fetchFn,
|
|
10585
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}`,
|
|
10586
|
+
{
|
|
10587
|
+
method: "PATCH",
|
|
10588
|
+
headers: this.headers(),
|
|
10589
|
+
body: JSON.stringify(patch)
|
|
10590
|
+
},
|
|
10591
|
+
this.retryOpts
|
|
10592
|
+
);
|
|
10593
|
+
if (!response.ok) {
|
|
10594
|
+
const text = await response.text();
|
|
10595
|
+
return Err3(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
10596
|
+
}
|
|
10597
|
+
const data = await response.json();
|
|
10598
|
+
return Ok4({ externalId, url: data.html_url });
|
|
10599
|
+
} catch (error) {
|
|
10600
|
+
return Err3(error instanceof Error ? error : new Error(String(error)));
|
|
10601
|
+
}
|
|
10602
|
+
}
|
|
10603
|
+
async fetchTicketState(externalId) {
|
|
10604
|
+
try {
|
|
10605
|
+
const parsed = parseExternalId(externalId);
|
|
10606
|
+
if (!parsed) return Err3(new Error(`Invalid externalId format: "${externalId}"`));
|
|
10607
|
+
const response = await fetchWithRetry(
|
|
10608
|
+
this.fetchFn,
|
|
10609
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}`,
|
|
10610
|
+
{
|
|
10611
|
+
method: "GET",
|
|
10612
|
+
headers: this.headers()
|
|
10613
|
+
},
|
|
10614
|
+
this.retryOpts
|
|
10615
|
+
);
|
|
10616
|
+
if (!response.ok) {
|
|
10617
|
+
const text = await response.text();
|
|
10618
|
+
return Err3(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
10619
|
+
}
|
|
10620
|
+
const data = await response.json();
|
|
10621
|
+
return Ok4({
|
|
10622
|
+
externalId,
|
|
10623
|
+
status: data.state,
|
|
10624
|
+
labels: data.labels.map((l) => l.name),
|
|
10625
|
+
assignee: data.assignee ? `@${data.assignee.login}` : null
|
|
10626
|
+
});
|
|
10627
|
+
} catch (error) {
|
|
10628
|
+
return Err3(error instanceof Error ? error : new Error(String(error)));
|
|
10629
|
+
}
|
|
10630
|
+
}
|
|
10631
|
+
async fetchAllTickets() {
|
|
10632
|
+
try {
|
|
10633
|
+
const filterLabels = this.config.labels ?? [];
|
|
10634
|
+
const labelsParam = filterLabels.length > 0 ? `&labels=${filterLabels.join(",")}` : "";
|
|
10635
|
+
const tickets = [];
|
|
10636
|
+
let page = 1;
|
|
10637
|
+
const perPage = 100;
|
|
10638
|
+
while (true) {
|
|
10639
|
+
const response = await fetchWithRetry(
|
|
10640
|
+
this.fetchFn,
|
|
10641
|
+
`${this.apiBase}/repos/${this.owner}/${this.repo}/issues?state=all&per_page=${perPage}&page=${page}${labelsParam}`,
|
|
10642
|
+
{
|
|
10643
|
+
method: "GET",
|
|
10644
|
+
headers: this.headers()
|
|
10645
|
+
},
|
|
10646
|
+
this.retryOpts
|
|
10647
|
+
);
|
|
10648
|
+
if (!response.ok) {
|
|
10649
|
+
const text = await response.text();
|
|
10650
|
+
return Err3(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
10651
|
+
}
|
|
10652
|
+
const data = await response.json();
|
|
10653
|
+
const issues = data.filter((d) => !d.pull_request);
|
|
10654
|
+
for (const issue of issues) {
|
|
10655
|
+
tickets.push({
|
|
10656
|
+
externalId: buildExternalId(this.owner, this.repo, issue.number),
|
|
10657
|
+
status: issue.state,
|
|
10658
|
+
labels: issue.labels.map((l) => l.name),
|
|
10659
|
+
assignee: issue.assignee ? `@${issue.assignee.login}` : null
|
|
10660
|
+
});
|
|
10661
|
+
}
|
|
10662
|
+
if (data.length < perPage) break;
|
|
10663
|
+
page++;
|
|
10664
|
+
}
|
|
10665
|
+
return Ok4(tickets);
|
|
10666
|
+
} catch (error) {
|
|
10667
|
+
return Err3(error instanceof Error ? error : new Error(String(error)));
|
|
10668
|
+
}
|
|
10669
|
+
}
|
|
10670
|
+
async assignTicket(externalId, assignee) {
|
|
10671
|
+
try {
|
|
10672
|
+
const parsed = parseExternalId(externalId);
|
|
10673
|
+
if (!parsed) return Err3(new Error(`Invalid externalId format: "${externalId}"`));
|
|
10674
|
+
const login = assignee.startsWith("@") ? assignee.slice(1) : assignee;
|
|
10675
|
+
const response = await fetchWithRetry(
|
|
10676
|
+
this.fetchFn,
|
|
10677
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}/assignees`,
|
|
10678
|
+
{
|
|
10679
|
+
method: "POST",
|
|
10680
|
+
headers: this.headers(),
|
|
10681
|
+
body: JSON.stringify({ assignees: [login] })
|
|
10682
|
+
},
|
|
10683
|
+
this.retryOpts
|
|
10684
|
+
);
|
|
10685
|
+
if (!response.ok) {
|
|
10686
|
+
const text = await response.text();
|
|
10687
|
+
return Err3(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
10688
|
+
}
|
|
10689
|
+
return Ok4(void 0);
|
|
10690
|
+
} catch (error) {
|
|
10691
|
+
return Err3(error instanceof Error ? error : new Error(String(error)));
|
|
10692
|
+
}
|
|
10693
|
+
}
|
|
10694
|
+
};
|
|
10695
|
+
|
|
10696
|
+
// src/roadmap/sync-engine.ts
|
|
10697
|
+
import * as fs20 from "fs";
|
|
10698
|
+
function emptySyncResult() {
|
|
10699
|
+
return { created: [], updated: [], assignmentChanges: [], errors: [] };
|
|
10700
|
+
}
|
|
10701
|
+
async function syncToExternal(roadmap, adapter, _config) {
|
|
10702
|
+
const result = emptySyncResult();
|
|
10703
|
+
for (const milestone of roadmap.milestones) {
|
|
10704
|
+
for (const feature of milestone.features) {
|
|
10705
|
+
if (!feature.externalId) {
|
|
10706
|
+
const createResult = await adapter.createTicket(feature, milestone.name);
|
|
10707
|
+
if (createResult.ok) {
|
|
10708
|
+
feature.externalId = createResult.value.externalId;
|
|
10709
|
+
result.created.push(createResult.value);
|
|
10710
|
+
} else {
|
|
10711
|
+
result.errors.push({ featureOrId: feature.name, error: createResult.error });
|
|
10712
|
+
}
|
|
10713
|
+
} else {
|
|
10714
|
+
const updateResult = await adapter.updateTicket(feature.externalId, feature);
|
|
10715
|
+
if (updateResult.ok) {
|
|
10716
|
+
result.updated.push(feature.externalId);
|
|
10717
|
+
} else {
|
|
10718
|
+
result.errors.push({ featureOrId: feature.externalId, error: updateResult.error });
|
|
10719
|
+
}
|
|
10720
|
+
}
|
|
10721
|
+
}
|
|
10722
|
+
}
|
|
10723
|
+
return result;
|
|
10724
|
+
}
|
|
10725
|
+
async function syncFromExternal(roadmap, adapter, config, options) {
|
|
10726
|
+
const result = emptySyncResult();
|
|
10727
|
+
const forceSync = options?.forceSync ?? false;
|
|
10728
|
+
const featureByExternalId = /* @__PURE__ */ new Map();
|
|
10729
|
+
for (const milestone of roadmap.milestones) {
|
|
10730
|
+
for (const feature of milestone.features) {
|
|
10731
|
+
if (feature.externalId) {
|
|
10732
|
+
featureByExternalId.set(feature.externalId, feature);
|
|
10733
|
+
}
|
|
10734
|
+
}
|
|
10735
|
+
}
|
|
10736
|
+
if (featureByExternalId.size === 0) return result;
|
|
10737
|
+
const fetchResult = await adapter.fetchAllTickets();
|
|
10738
|
+
if (!fetchResult.ok) {
|
|
10739
|
+
result.errors.push({ featureOrId: "*", error: fetchResult.error });
|
|
10740
|
+
return result;
|
|
10741
|
+
}
|
|
10742
|
+
for (const ticketState of fetchResult.value) {
|
|
10743
|
+
const feature = featureByExternalId.get(ticketState.externalId);
|
|
10744
|
+
if (!feature) continue;
|
|
10745
|
+
if (ticketState.assignee !== feature.assignee) {
|
|
10746
|
+
result.assignmentChanges.push({
|
|
10747
|
+
feature: feature.name,
|
|
10748
|
+
from: feature.assignee,
|
|
10749
|
+
to: ticketState.assignee
|
|
10750
|
+
});
|
|
10751
|
+
feature.assignee = ticketState.assignee;
|
|
10752
|
+
}
|
|
10753
|
+
const resolvedStatus = resolveReverseStatus(ticketState.status, ticketState.labels, config);
|
|
10754
|
+
if (resolvedStatus && resolvedStatus !== feature.status) {
|
|
10755
|
+
const newStatus = resolvedStatus;
|
|
10756
|
+
if (!forceSync && isRegression(feature.status, newStatus)) {
|
|
10757
|
+
continue;
|
|
10758
|
+
}
|
|
10759
|
+
feature.status = newStatus;
|
|
10760
|
+
}
|
|
10761
|
+
}
|
|
10762
|
+
return result;
|
|
10763
|
+
}
|
|
10764
|
+
var syncMutex = Promise.resolve();
|
|
10765
|
+
async function fullSync(roadmapPath, adapter, config, options) {
|
|
10766
|
+
const previousSync = syncMutex;
|
|
10767
|
+
let releaseMutex;
|
|
10768
|
+
syncMutex = new Promise((resolve5) => {
|
|
10769
|
+
releaseMutex = resolve5;
|
|
10770
|
+
});
|
|
10771
|
+
await previousSync;
|
|
10772
|
+
try {
|
|
10773
|
+
const raw = fs20.readFileSync(roadmapPath, "utf-8");
|
|
10774
|
+
const parseResult = parseRoadmap(raw);
|
|
10775
|
+
if (!parseResult.ok) {
|
|
10776
|
+
return {
|
|
10777
|
+
...emptySyncResult(),
|
|
10778
|
+
errors: [{ featureOrId: "*", error: parseResult.error }]
|
|
10779
|
+
};
|
|
10780
|
+
}
|
|
10781
|
+
const roadmap = parseResult.value;
|
|
10782
|
+
const pushResult = await syncToExternal(roadmap, adapter, config);
|
|
10783
|
+
const pullResult = await syncFromExternal(roadmap, adapter, config, options);
|
|
10784
|
+
fs20.writeFileSync(roadmapPath, serializeRoadmap(roadmap), "utf-8");
|
|
10785
|
+
return {
|
|
10786
|
+
created: pushResult.created,
|
|
10787
|
+
updated: pushResult.updated,
|
|
10788
|
+
assignmentChanges: pullResult.assignmentChanges,
|
|
10789
|
+
errors: [...pushResult.errors, ...pullResult.errors]
|
|
10790
|
+
};
|
|
10791
|
+
} finally {
|
|
10792
|
+
releaseMutex();
|
|
10793
|
+
}
|
|
10794
|
+
}
|
|
10795
|
+
|
|
10796
|
+
// src/roadmap/pilot-scoring.ts
|
|
10797
|
+
var PRIORITY_RANK = {
|
|
10798
|
+
P0: 0,
|
|
10799
|
+
P1: 1,
|
|
10800
|
+
P2: 2,
|
|
10801
|
+
P3: 3
|
|
10802
|
+
};
|
|
10803
|
+
var POSITION_WEIGHT = 0.5;
|
|
10804
|
+
var DEPENDENTS_WEIGHT = 0.3;
|
|
10805
|
+
var AFFINITY_WEIGHT = 0.2;
|
|
10806
|
+
function scoreRoadmapCandidates(roadmap, options) {
|
|
10807
|
+
const allFeatures = roadmap.milestones.flatMap((m) => m.features);
|
|
10808
|
+
const allFeatureNames = new Set(allFeatures.map((f) => f.name.toLowerCase()));
|
|
10809
|
+
const doneFeatures = new Set(
|
|
10810
|
+
allFeatures.filter((f) => f.status === "done").map((f) => f.name.toLowerCase())
|
|
10811
|
+
);
|
|
10812
|
+
const dependentsCount = /* @__PURE__ */ new Map();
|
|
10813
|
+
for (const feature of allFeatures) {
|
|
10814
|
+
for (const blocker of feature.blockedBy) {
|
|
10815
|
+
const key = blocker.toLowerCase();
|
|
10816
|
+
dependentsCount.set(key, (dependentsCount.get(key) ?? 0) + 1);
|
|
10817
|
+
}
|
|
10818
|
+
}
|
|
10819
|
+
const maxDependents = Math.max(1, ...dependentsCount.values());
|
|
10820
|
+
const milestoneMap = /* @__PURE__ */ new Map();
|
|
10821
|
+
for (const ms of roadmap.milestones) {
|
|
10822
|
+
milestoneMap.set(
|
|
10823
|
+
ms.name,
|
|
10824
|
+
ms.features.map((f) => f.name.toLowerCase())
|
|
10825
|
+
);
|
|
10826
|
+
}
|
|
10827
|
+
const userCompletedFeatures = /* @__PURE__ */ new Set();
|
|
10828
|
+
if (options?.currentUser) {
|
|
10829
|
+
const user = options.currentUser.toLowerCase();
|
|
10830
|
+
for (const record of roadmap.assignmentHistory) {
|
|
10831
|
+
if (record.action === "completed" && record.assignee.toLowerCase() === user) {
|
|
10832
|
+
userCompletedFeatures.add(record.feature.toLowerCase());
|
|
10833
|
+
}
|
|
10834
|
+
}
|
|
10835
|
+
}
|
|
10836
|
+
let totalPositions = 0;
|
|
10837
|
+
for (const ms of roadmap.milestones) {
|
|
10838
|
+
totalPositions += ms.features.length;
|
|
10839
|
+
}
|
|
10840
|
+
totalPositions = Math.max(1, totalPositions);
|
|
10841
|
+
const candidates = [];
|
|
10842
|
+
let globalPosition = 0;
|
|
10843
|
+
for (const ms of roadmap.milestones) {
|
|
10844
|
+
for (let featureIdx = 0; featureIdx < ms.features.length; featureIdx++) {
|
|
10845
|
+
const feature = ms.features[featureIdx];
|
|
10846
|
+
globalPosition++;
|
|
10847
|
+
if (feature.status !== "planned" && feature.status !== "backlog") continue;
|
|
10848
|
+
const isBlocked = feature.blockedBy.some((blocker) => {
|
|
10849
|
+
const key = blocker.toLowerCase();
|
|
10850
|
+
return allFeatureNames.has(key) && !doneFeatures.has(key);
|
|
10851
|
+
});
|
|
10852
|
+
if (isBlocked) continue;
|
|
10853
|
+
const positionScore = 1 - (globalPosition - 1) / totalPositions;
|
|
10854
|
+
const deps = dependentsCount.get(feature.name.toLowerCase()) ?? 0;
|
|
10855
|
+
const dependentsScore = deps / maxDependents;
|
|
10856
|
+
let affinityScore = 0;
|
|
10857
|
+
if (userCompletedFeatures.size > 0) {
|
|
10858
|
+
const completedBlockers = feature.blockedBy.filter(
|
|
10859
|
+
(b) => userCompletedFeatures.has(b.toLowerCase())
|
|
10860
|
+
);
|
|
10861
|
+
if (completedBlockers.length > 0) {
|
|
10862
|
+
affinityScore = 1;
|
|
10863
|
+
} else {
|
|
10864
|
+
const siblings = milestoneMap.get(ms.name) ?? [];
|
|
10865
|
+
const completedSiblings = siblings.filter((s) => userCompletedFeatures.has(s));
|
|
10866
|
+
if (completedSiblings.length > 0) {
|
|
10867
|
+
affinityScore = 0.5;
|
|
10868
|
+
}
|
|
10869
|
+
}
|
|
10870
|
+
}
|
|
10871
|
+
const weightedScore = POSITION_WEIGHT * positionScore + DEPENDENTS_WEIGHT * dependentsScore + AFFINITY_WEIGHT * affinityScore;
|
|
10872
|
+
const priorityTier = feature.priority ? PRIORITY_RANK[feature.priority] : null;
|
|
10873
|
+
candidates.push({
|
|
10874
|
+
feature,
|
|
10875
|
+
milestone: ms.name,
|
|
10876
|
+
positionScore,
|
|
10877
|
+
dependentsScore,
|
|
10878
|
+
affinityScore,
|
|
10879
|
+
weightedScore,
|
|
10880
|
+
priorityTier
|
|
10881
|
+
});
|
|
10882
|
+
}
|
|
10883
|
+
}
|
|
10884
|
+
candidates.sort((a, b) => {
|
|
10885
|
+
if (a.priorityTier !== null && b.priorityTier === null) return -1;
|
|
10886
|
+
if (a.priorityTier === null && b.priorityTier !== null) return 1;
|
|
10887
|
+
if (a.priorityTier !== null && b.priorityTier !== null) {
|
|
10888
|
+
if (a.priorityTier !== b.priorityTier) return a.priorityTier - b.priorityTier;
|
|
10889
|
+
}
|
|
10890
|
+
return b.weightedScore - a.weightedScore;
|
|
10891
|
+
});
|
|
10892
|
+
return candidates;
|
|
10893
|
+
}
|
|
10894
|
+
function assignFeature(roadmap, feature, assignee, date) {
|
|
10895
|
+
if (feature.assignee === assignee) return;
|
|
10896
|
+
if (feature.assignee !== null) {
|
|
10897
|
+
roadmap.assignmentHistory.push({
|
|
10898
|
+
feature: feature.name,
|
|
10899
|
+
assignee: feature.assignee,
|
|
10900
|
+
action: "unassigned",
|
|
10901
|
+
date
|
|
10902
|
+
});
|
|
10903
|
+
}
|
|
10904
|
+
feature.assignee = assignee;
|
|
10905
|
+
roadmap.assignmentHistory.push({
|
|
10906
|
+
feature: feature.name,
|
|
10907
|
+
assignee,
|
|
10908
|
+
action: "assigned",
|
|
10909
|
+
date
|
|
10910
|
+
});
|
|
10911
|
+
}
|
|
10912
|
+
|
|
10352
10913
|
// src/interaction/types.ts
|
|
10353
10914
|
import { z as z7 } from "zod";
|
|
10354
10915
|
var InteractionTypeSchema = z7.enum(["question", "confirmation", "transition"]);
|
|
@@ -10379,17 +10940,18 @@ var EmitInteractionInputSchema = z7.object({
|
|
|
10379
10940
|
});
|
|
10380
10941
|
|
|
10381
10942
|
// src/blueprint/scanner.ts
|
|
10382
|
-
import * as
|
|
10943
|
+
import * as fs21 from "fs/promises";
|
|
10383
10944
|
import * as path20 from "path";
|
|
10384
10945
|
var ProjectScanner = class {
|
|
10385
10946
|
constructor(rootDir) {
|
|
10386
10947
|
this.rootDir = rootDir;
|
|
10387
10948
|
}
|
|
10949
|
+
rootDir;
|
|
10388
10950
|
async scan() {
|
|
10389
10951
|
let projectName = path20.basename(this.rootDir);
|
|
10390
10952
|
try {
|
|
10391
10953
|
const pkgPath = path20.join(this.rootDir, "package.json");
|
|
10392
|
-
const pkgRaw = await
|
|
10954
|
+
const pkgRaw = await fs21.readFile(pkgPath, "utf-8");
|
|
10393
10955
|
const pkg = JSON.parse(pkgRaw);
|
|
10394
10956
|
if (pkg.name) projectName = pkg.name;
|
|
10395
10957
|
} catch {
|
|
@@ -10430,7 +10992,7 @@ var ProjectScanner = class {
|
|
|
10430
10992
|
};
|
|
10431
10993
|
|
|
10432
10994
|
// src/blueprint/generator.ts
|
|
10433
|
-
import * as
|
|
10995
|
+
import * as fs22 from "fs/promises";
|
|
10434
10996
|
import * as path21 from "path";
|
|
10435
10997
|
import * as ejs from "ejs";
|
|
10436
10998
|
|
|
@@ -10515,13 +11077,13 @@ var BlueprintGenerator = class {
|
|
|
10515
11077
|
styles: STYLES,
|
|
10516
11078
|
scripts: SCRIPTS
|
|
10517
11079
|
});
|
|
10518
|
-
await
|
|
10519
|
-
await
|
|
11080
|
+
await fs22.mkdir(options.outputDir, { recursive: true });
|
|
11081
|
+
await fs22.writeFile(path21.join(options.outputDir, "index.html"), html);
|
|
10520
11082
|
}
|
|
10521
11083
|
};
|
|
10522
11084
|
|
|
10523
11085
|
// src/update-checker.ts
|
|
10524
|
-
import * as
|
|
11086
|
+
import * as fs23 from "fs";
|
|
10525
11087
|
import * as path22 from "path";
|
|
10526
11088
|
import * as os from "os";
|
|
10527
11089
|
import { spawn } from "child_process";
|
|
@@ -10540,7 +11102,7 @@ function shouldRunCheck(state, intervalMs) {
|
|
|
10540
11102
|
}
|
|
10541
11103
|
function readCheckState() {
|
|
10542
11104
|
try {
|
|
10543
|
-
const raw =
|
|
11105
|
+
const raw = fs23.readFileSync(getStatePath(), "utf-8");
|
|
10544
11106
|
const parsed = JSON.parse(raw);
|
|
10545
11107
|
if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
|
|
10546
11108
|
const state = parsed;
|
|
@@ -11052,7 +11614,7 @@ function getModelPrice(model, dataset) {
|
|
|
11052
11614
|
}
|
|
11053
11615
|
|
|
11054
11616
|
// src/pricing/cache.ts
|
|
11055
|
-
import * as
|
|
11617
|
+
import * as fs24 from "fs/promises";
|
|
11056
11618
|
import * as path23 from "path";
|
|
11057
11619
|
|
|
11058
11620
|
// src/pricing/fallback.json
|
|
@@ -11113,7 +11675,7 @@ function getStalenessMarkerPath(projectRoot) {
|
|
|
11113
11675
|
}
|
|
11114
11676
|
async function readDiskCache(projectRoot) {
|
|
11115
11677
|
try {
|
|
11116
|
-
const raw = await
|
|
11678
|
+
const raw = await fs24.readFile(getCachePath(projectRoot), "utf-8");
|
|
11117
11679
|
return JSON.parse(raw);
|
|
11118
11680
|
} catch {
|
|
11119
11681
|
return null;
|
|
@@ -11121,8 +11683,8 @@ async function readDiskCache(projectRoot) {
|
|
|
11121
11683
|
}
|
|
11122
11684
|
async function writeDiskCache(projectRoot, data) {
|
|
11123
11685
|
const cachePath = getCachePath(projectRoot);
|
|
11124
|
-
await
|
|
11125
|
-
await
|
|
11686
|
+
await fs24.mkdir(path23.dirname(cachePath), { recursive: true });
|
|
11687
|
+
await fs24.writeFile(cachePath, JSON.stringify(data, null, 2));
|
|
11126
11688
|
}
|
|
11127
11689
|
async function fetchFromNetwork() {
|
|
11128
11690
|
try {
|
|
@@ -11149,7 +11711,7 @@ function loadFallbackDataset() {
|
|
|
11149
11711
|
async function checkAndWarnStaleness(projectRoot) {
|
|
11150
11712
|
const markerPath = getStalenessMarkerPath(projectRoot);
|
|
11151
11713
|
try {
|
|
11152
|
-
const raw = await
|
|
11714
|
+
const raw = await fs24.readFile(markerPath, "utf-8");
|
|
11153
11715
|
const marker = JSON.parse(raw);
|
|
11154
11716
|
const firstUse = new Date(marker.firstFallbackUse).getTime();
|
|
11155
11717
|
const now = Date.now();
|
|
@@ -11161,8 +11723,8 @@ async function checkAndWarnStaleness(projectRoot) {
|
|
|
11161
11723
|
}
|
|
11162
11724
|
} catch {
|
|
11163
11725
|
try {
|
|
11164
|
-
await
|
|
11165
|
-
await
|
|
11726
|
+
await fs24.mkdir(path23.dirname(markerPath), { recursive: true });
|
|
11727
|
+
await fs24.writeFile(
|
|
11166
11728
|
markerPath,
|
|
11167
11729
|
JSON.stringify({ firstFallbackUse: (/* @__PURE__ */ new Date()).toISOString() })
|
|
11168
11730
|
);
|
|
@@ -11172,7 +11734,7 @@ async function checkAndWarnStaleness(projectRoot) {
|
|
|
11172
11734
|
}
|
|
11173
11735
|
async function clearStalenessMarker(projectRoot) {
|
|
11174
11736
|
try {
|
|
11175
|
-
await
|
|
11737
|
+
await fs24.unlink(getStalenessMarkerPath(projectRoot));
|
|
11176
11738
|
} catch {
|
|
11177
11739
|
}
|
|
11178
11740
|
}
|
|
@@ -11342,7 +11904,7 @@ function aggregateByDay(records) {
|
|
|
11342
11904
|
}
|
|
11343
11905
|
|
|
11344
11906
|
// src/usage/jsonl-reader.ts
|
|
11345
|
-
import * as
|
|
11907
|
+
import * as fs25 from "fs";
|
|
11346
11908
|
import * as path24 from "path";
|
|
11347
11909
|
function parseLine(line, lineNumber) {
|
|
11348
11910
|
let entry;
|
|
@@ -11385,7 +11947,7 @@ function readCostRecords(projectRoot) {
|
|
|
11385
11947
|
const costsFile = path24.join(projectRoot, ".harness", "metrics", "costs.jsonl");
|
|
11386
11948
|
let raw;
|
|
11387
11949
|
try {
|
|
11388
|
-
raw =
|
|
11950
|
+
raw = fs25.readFileSync(costsFile, "utf-8");
|
|
11389
11951
|
} catch {
|
|
11390
11952
|
return [];
|
|
11391
11953
|
}
|
|
@@ -11403,7 +11965,7 @@ function readCostRecords(projectRoot) {
|
|
|
11403
11965
|
}
|
|
11404
11966
|
|
|
11405
11967
|
// src/usage/cc-parser.ts
|
|
11406
|
-
import * as
|
|
11968
|
+
import * as fs26 from "fs";
|
|
11407
11969
|
import * as path25 from "path";
|
|
11408
11970
|
import * as os2 from "os";
|
|
11409
11971
|
function extractUsage(entry) {
|
|
@@ -11451,7 +12013,7 @@ function parseCCLine(line, filePath, lineNumber) {
|
|
|
11451
12013
|
function readCCFile(filePath) {
|
|
11452
12014
|
let raw;
|
|
11453
12015
|
try {
|
|
11454
|
-
raw =
|
|
12016
|
+
raw = fs26.readFileSync(filePath, "utf-8");
|
|
11455
12017
|
} catch {
|
|
11456
12018
|
return [];
|
|
11457
12019
|
}
|
|
@@ -11476,7 +12038,7 @@ function parseCCRecords() {
|
|
|
11476
12038
|
const projectsDir = path25.join(homeDir, ".claude", "projects");
|
|
11477
12039
|
let projectDirs;
|
|
11478
12040
|
try {
|
|
11479
|
-
projectDirs =
|
|
12041
|
+
projectDirs = fs26.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path25.join(projectsDir, d.name));
|
|
11480
12042
|
} catch {
|
|
11481
12043
|
return [];
|
|
11482
12044
|
}
|
|
@@ -11484,7 +12046,7 @@ function parseCCRecords() {
|
|
|
11484
12046
|
for (const dir of projectDirs) {
|
|
11485
12047
|
let files;
|
|
11486
12048
|
try {
|
|
11487
|
-
files =
|
|
12049
|
+
files = fs26.readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => path25.join(dir, f));
|
|
11488
12050
|
} catch {
|
|
11489
12051
|
continue;
|
|
11490
12052
|
}
|
|
@@ -11542,6 +12104,7 @@ export {
|
|
|
11542
12104
|
ForbiddenImportCollector,
|
|
11543
12105
|
GateConfigSchema,
|
|
11544
12106
|
GateResultSchema,
|
|
12107
|
+
GitHubIssuesSyncAdapter,
|
|
11545
12108
|
HandoffSchema,
|
|
11546
12109
|
HarnessStateSchema,
|
|
11547
12110
|
InteractionTypeSchema,
|
|
@@ -11563,6 +12126,7 @@ export {
|
|
|
11563
12126
|
RuleRegistry,
|
|
11564
12127
|
SECURITY_DESCRIPTOR,
|
|
11565
12128
|
STALENESS_WARNING_DAYS,
|
|
12129
|
+
STATUS_RANK,
|
|
11566
12130
|
SecurityConfigSchema,
|
|
11567
12131
|
SecurityScanner,
|
|
11568
12132
|
SharableBoundaryConfigSchema,
|
|
@@ -11596,6 +12160,7 @@ export {
|
|
|
11596
12160
|
archiveLearnings,
|
|
11597
12161
|
archiveSession,
|
|
11598
12162
|
archiveStream,
|
|
12163
|
+
assignFeature,
|
|
11599
12164
|
buildDependencyGraph,
|
|
11600
12165
|
buildExclusionSet,
|
|
11601
12166
|
buildSnapshot,
|
|
@@ -11660,6 +12225,7 @@ export {
|
|
|
11660
12225
|
formatGitHubSummary,
|
|
11661
12226
|
formatOutline,
|
|
11662
12227
|
formatTerminalOutput,
|
|
12228
|
+
fullSync,
|
|
11663
12229
|
generateAgentsMap,
|
|
11664
12230
|
generateSuggestions,
|
|
11665
12231
|
getActionEmitter,
|
|
@@ -11677,6 +12243,7 @@ export {
|
|
|
11677
12243
|
injectionRules,
|
|
11678
12244
|
insecureDefaultsRules,
|
|
11679
12245
|
isDuplicateFinding,
|
|
12246
|
+
isRegression,
|
|
11680
12247
|
isSmallSuggestion,
|
|
11681
12248
|
isUpdateCheckEnabled,
|
|
11682
12249
|
listActiveSessions,
|
|
@@ -11730,6 +12297,7 @@ export {
|
|
|
11730
12297
|
resetParserCache,
|
|
11731
12298
|
resolveFileToLayer,
|
|
11732
12299
|
resolveModelTier,
|
|
12300
|
+
resolveReverseStatus,
|
|
11733
12301
|
resolveRuleSeverity,
|
|
11734
12302
|
resolveSessionDir,
|
|
11735
12303
|
resolveStreamPath,
|
|
@@ -11750,6 +12318,7 @@ export {
|
|
|
11750
12318
|
saveStreamIndex,
|
|
11751
12319
|
scanForInjection,
|
|
11752
12320
|
scopeContext,
|
|
12321
|
+
scoreRoadmapCandidates,
|
|
11753
12322
|
searchSymbols,
|
|
11754
12323
|
secretRules,
|
|
11755
12324
|
serializeRoadmap,
|
|
@@ -11758,7 +12327,9 @@ export {
|
|
|
11758
12327
|
shouldRunCheck,
|
|
11759
12328
|
spawnBackgroundCheck,
|
|
11760
12329
|
syncConstraintNodes,
|
|
12330
|
+
syncFromExternal,
|
|
11761
12331
|
syncRoadmap,
|
|
12332
|
+
syncToExternal,
|
|
11762
12333
|
tagUncitedFindings,
|
|
11763
12334
|
touchStream,
|
|
11764
12335
|
trackAction,
|