@harness-engineering/cli 1.16.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +204 -0
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/skill.yaml +52 -0
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +204 -0
- package/dist/agents/skills/codex/harness-roadmap-pilot/skill.yaml +52 -0
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +204 -0
- package/dist/agents/skills/cursor/harness-roadmap-pilot/skill.yaml +52 -0
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +204 -0
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/skill.yaml +52 -0
- package/dist/agents/skills/package.json +5 -5
- package/dist/{agents-md-VYDFPIRW.js → agents-md-DUYNKHJZ.js} +1 -1
- package/dist/{architecture-K5HSRBGB.js → architecture-UBO5KKUV.js} +2 -2
- package/dist/bin/harness-mcp.js +13 -12
- package/dist/bin/harness.js +18 -15
- package/dist/{check-phase-gate-5AS6SXL6.js → check-phase-gate-OSHN2AEL.js} +3 -3
- package/dist/{chunk-JOP2NDNB.js → chunk-2DMIQ35P.js} +151 -79
- package/dist/{chunk-TF6ZLHJV.js → chunk-5FM64G6D.js} +2 -2
- package/dist/{chunk-5ZXHMCPL.js → chunk-ABQUCXRE.js} +2 -1
- package/dist/{chunk-AV6KMDO5.js → chunk-APNPXLB2.js} +4 -4
- package/dist/{chunk-FTMXDOR6.js → chunk-CZZXE6BL.js} +1 -1
- package/dist/{chunk-SFRGPAK6.js → chunk-GWXP3JVA.js} +3 -3
- package/dist/{chunk-SHYWICGA.js → chunk-OA3MOZGG.js} +22 -22
- package/dist/{chunk-RWZPHW4H.js → chunk-OHZVGIPE.js} +9 -9
- package/dist/{chunk-C7DTKLPW.js → chunk-QSRRBNLY.js} +8 -8
- package/dist/{chunk-QDF7COPQ.js → chunk-TG7IUJ3J.js} +1 -1
- package/dist/{chunk-DNDBFIZN.js → chunk-TZIHFNEG.js} +7 -7
- package/dist/{chunk-ZJMU7MEV.js → chunk-UX3JHYEA.js} +1 -1
- package/dist/{chunk-ALFKNAZW.js → chunk-VF23UTNB.js} +545 -36
- package/dist/{chunk-7MJAPE3Z.js → chunk-YLN34N65.js} +1 -0
- package/dist/{chunk-OCDDCGDE.js → chunk-ZA2I7S3E.js} +20 -1
- package/dist/{ci-workflow-CRWU723U.js → ci-workflow-FJZMNZPT.js} +1 -1
- package/dist/{dist-4LPXJYVZ.js → dist-MF5BK5AD.js} +19 -1
- package/dist/{dist-B26DFXMP.js → dist-U7EAO6T2.js} +110 -60
- package/dist/{docs-4JRHTLUZ.js → docs-WZHW4N4P.js} +3 -3
- package/dist/{engine-3G3VIM6L.js → engine-VS6ZJ2VZ.js} +2 -2
- package/dist/{entropy-G6CZ2A6P.js → entropy-FCIGJIIT.js} +2 -2
- package/dist/{feedback-QYKQ65HB.js → feedback-O3FYTZIE.js} +1 -1
- package/dist/{generate-agent-definitions-SAAOAPT4.js → generate-agent-definitions-EYG263XD.js} +1 -1
- package/dist/{graph-loader-2M2HXDQI.js → graph-loader-KMHDQYDT.js} +1 -1
- package/dist/index.d.ts +70 -11
- package/dist/index.js +15 -15
- package/dist/{loader-VCOK3PF7.js → loader-B4XWX4K6.js} +1 -1
- package/dist/{mcp-YENEPHBW.js → mcp-DVVUODN7.js} +12 -12
- package/dist/{performance-UBCFI2UP.js → performance-NMJDV6HF.js} +3 -3
- package/dist/{review-pipeline-IQAVCWAX.js → review-pipeline-MSEJWTKM.js} +1 -1
- package/dist/{runtime-PYFFIESU.js → runtime-YHVLJNPG.js} +1 -1
- package/dist/{security-ZDADTPYW.js → security-HTDKKGMX.js} +1 -1
- package/dist/{validate-VRTUHALQ.js → validate-SPSTH2YW.js} +2 -2
- package/dist/{validate-cross-check-4Y6NHNK3.js → validate-cross-check-YTDWIMFI.js} +1 -1
- package/package.json +21 -21
|
@@ -1862,22 +1862,23 @@ import * as path17 from "path";
|
|
|
1862
1862
|
import * as path18 from "path";
|
|
1863
1863
|
import * as fs19 from "fs";
|
|
1864
1864
|
import * as path19 from "path";
|
|
1865
|
+
import * as fs20 from "fs";
|
|
1865
1866
|
import { z as z7 } from "zod";
|
|
1866
|
-
import * as fs20 from "fs/promises";
|
|
1867
|
-
import * as path20 from "path";
|
|
1868
1867
|
import * as fs21 from "fs/promises";
|
|
1868
|
+
import * as path20 from "path";
|
|
1869
|
+
import * as fs22 from "fs/promises";
|
|
1869
1870
|
import * as path21 from "path";
|
|
1870
1871
|
import * as ejs from "ejs";
|
|
1871
|
-
import * as
|
|
1872
|
+
import * as fs23 from "fs";
|
|
1872
1873
|
import * as path22 from "path";
|
|
1873
1874
|
import * as os from "os";
|
|
1874
1875
|
import { spawn } from "child_process";
|
|
1875
1876
|
import Parser from "web-tree-sitter";
|
|
1876
|
-
import * as
|
|
1877
|
+
import * as fs24 from "fs/promises";
|
|
1877
1878
|
import * as path23 from "path";
|
|
1878
|
-
import * as fs24 from "fs";
|
|
1879
|
-
import * as path24 from "path";
|
|
1880
1879
|
import * as fs25 from "fs";
|
|
1880
|
+
import * as path24 from "path";
|
|
1881
|
+
import * as fs26 from "fs";
|
|
1881
1882
|
import * as path25 from "path";
|
|
1882
1883
|
import * as os2 from "os";
|
|
1883
1884
|
async function validateFileStructure(projectPath, conventions) {
|
|
@@ -11587,6 +11588,7 @@ var VALID_STATUSES = /* @__PURE__ */ new Set([
|
|
|
11587
11588
|
"blocked"
|
|
11588
11589
|
]);
|
|
11589
11590
|
var EM_DASH = "\u2014";
|
|
11591
|
+
var VALID_PRIORITIES = /* @__PURE__ */ new Set(["P0", "P1", "P2", "P3"]);
|
|
11590
11592
|
function parseRoadmap(markdown) {
|
|
11591
11593
|
const fmMatch = markdown.match(/^---\n([\s\S]*?)\n---/);
|
|
11592
11594
|
if (!fmMatch) {
|
|
@@ -11597,9 +11599,12 @@ function parseRoadmap(markdown) {
|
|
|
11597
11599
|
const body = markdown.slice(fmMatch[0].length);
|
|
11598
11600
|
const milestonesResult = parseMilestones(body);
|
|
11599
11601
|
if (!milestonesResult.ok) return milestonesResult;
|
|
11602
|
+
const historyResult = parseAssignmentHistory(body);
|
|
11603
|
+
if (!historyResult.ok) return historyResult;
|
|
11600
11604
|
return Ok({
|
|
11601
11605
|
frontmatter: fmResult.value,
|
|
11602
|
-
milestones: milestonesResult.value
|
|
11606
|
+
milestones: milestonesResult.value,
|
|
11607
|
+
assignmentHistory: historyResult.value
|
|
11603
11608
|
});
|
|
11604
11609
|
}
|
|
11605
11610
|
function parseFrontmatter2(raw) {
|
|
@@ -11639,12 +11644,17 @@ function parseMilestones(body) {
|
|
|
11639
11644
|
const h2Pattern = /^## (.+)$/gm;
|
|
11640
11645
|
const h2Matches = [];
|
|
11641
11646
|
let match;
|
|
11647
|
+
let bodyEnd = body.length;
|
|
11642
11648
|
while ((match = h2Pattern.exec(body)) !== null) {
|
|
11649
|
+
if (match[1] === "Assignment History") {
|
|
11650
|
+
bodyEnd = match.index;
|
|
11651
|
+
break;
|
|
11652
|
+
}
|
|
11643
11653
|
h2Matches.push({ heading: match[1], startIndex: match.index, fullMatch: match[0] });
|
|
11644
11654
|
}
|
|
11645
11655
|
for (let i = 0; i < h2Matches.length; i++) {
|
|
11646
11656
|
const h2 = h2Matches[i];
|
|
11647
|
-
const nextStart = i + 1 < h2Matches.length ? h2Matches[i + 1].startIndex :
|
|
11657
|
+
const nextStart = i + 1 < h2Matches.length ? h2Matches[i + 1].startIndex : bodyEnd;
|
|
11648
11658
|
const sectionBody = body.slice(h2.startIndex + h2.fullMatch.length, nextStart);
|
|
11649
11659
|
const isBacklog = h2.heading === "Backlog";
|
|
11650
11660
|
const milestoneName = isBacklog ? "Backlog" : h2.heading.replace(/^Milestone:\s*/, "");
|
|
@@ -11710,15 +11720,60 @@ function parseFeatureFields(name, body) {
|
|
|
11710
11720
|
const specRaw = fieldMap.get("Spec") ?? EM_DASH;
|
|
11711
11721
|
const plans = parseListField(fieldMap, "Plans", "Plan");
|
|
11712
11722
|
const blockedBy = parseListField(fieldMap, "Blocked by", "Blockers");
|
|
11723
|
+
const assigneeRaw = fieldMap.get("Assignee") ?? EM_DASH;
|
|
11724
|
+
const priorityRaw = fieldMap.get("Priority") ?? EM_DASH;
|
|
11725
|
+
const externalIdRaw = fieldMap.get("External-ID") ?? EM_DASH;
|
|
11726
|
+
if (priorityRaw !== EM_DASH && !VALID_PRIORITIES.has(priorityRaw)) {
|
|
11727
|
+
return Err(
|
|
11728
|
+
new Error(
|
|
11729
|
+
`Feature "${name}" has invalid priority: "${priorityRaw}". Valid priorities: ${[...VALID_PRIORITIES].join(", ")}`
|
|
11730
|
+
)
|
|
11731
|
+
);
|
|
11732
|
+
}
|
|
11713
11733
|
return Ok({
|
|
11714
11734
|
name,
|
|
11715
11735
|
status: statusRaw,
|
|
11716
11736
|
spec: specRaw === EM_DASH ? null : specRaw,
|
|
11717
11737
|
plans,
|
|
11718
11738
|
blockedBy,
|
|
11719
|
-
summary: fieldMap.get("Summary") ?? ""
|
|
11739
|
+
summary: fieldMap.get("Summary") ?? "",
|
|
11740
|
+
assignee: assigneeRaw === EM_DASH ? null : assigneeRaw,
|
|
11741
|
+
priority: priorityRaw === EM_DASH ? null : priorityRaw,
|
|
11742
|
+
externalId: externalIdRaw === EM_DASH ? null : externalIdRaw
|
|
11720
11743
|
});
|
|
11721
11744
|
}
|
|
11745
|
+
function parseAssignmentHistory(body) {
|
|
11746
|
+
const historyMatch = body.match(/^## Assignment History\s*\n/m);
|
|
11747
|
+
if (!historyMatch || historyMatch.index === void 0) return Ok([]);
|
|
11748
|
+
const historyStart = historyMatch.index + historyMatch[0].length;
|
|
11749
|
+
const rawHistoryBody = body.slice(historyStart);
|
|
11750
|
+
const nextH2 = rawHistoryBody.search(/^## /m);
|
|
11751
|
+
const historyBody = nextH2 === -1 ? rawHistoryBody : rawHistoryBody.slice(0, nextH2);
|
|
11752
|
+
const records = [];
|
|
11753
|
+
const lines = historyBody.split("\n");
|
|
11754
|
+
let pastHeader = false;
|
|
11755
|
+
for (const line of lines) {
|
|
11756
|
+
const trimmed = line.trim();
|
|
11757
|
+
if (!trimmed.startsWith("|")) continue;
|
|
11758
|
+
if (!pastHeader) {
|
|
11759
|
+
if (trimmed.match(/^\|[-\s|]+\|$/)) {
|
|
11760
|
+
pastHeader = true;
|
|
11761
|
+
}
|
|
11762
|
+
continue;
|
|
11763
|
+
}
|
|
11764
|
+
const cells = trimmed.split("|").map((c) => c.trim()).filter((c) => c.length > 0);
|
|
11765
|
+
if (cells.length < 4) continue;
|
|
11766
|
+
const action = cells[2];
|
|
11767
|
+
if (!["assigned", "completed", "unassigned"].includes(action)) continue;
|
|
11768
|
+
records.push({
|
|
11769
|
+
feature: cells[0],
|
|
11770
|
+
assignee: cells[1],
|
|
11771
|
+
action,
|
|
11772
|
+
date: cells[3]
|
|
11773
|
+
});
|
|
11774
|
+
}
|
|
11775
|
+
return Ok(records);
|
|
11776
|
+
}
|
|
11722
11777
|
var EM_DASH2 = "\u2014";
|
|
11723
11778
|
function serializeRoadmap(roadmap) {
|
|
11724
11779
|
const lines = [];
|
|
@@ -11744,6 +11799,10 @@ function serializeRoadmap(roadmap) {
|
|
|
11744
11799
|
lines.push(...serializeFeature(feature));
|
|
11745
11800
|
}
|
|
11746
11801
|
}
|
|
11802
|
+
if (roadmap.assignmentHistory && roadmap.assignmentHistory.length > 0) {
|
|
11803
|
+
lines.push("");
|
|
11804
|
+
lines.push(...serializeAssignmentHistory(roadmap.assignmentHistory));
|
|
11805
|
+
}
|
|
11747
11806
|
lines.push("");
|
|
11748
11807
|
return lines.join("\n");
|
|
11749
11808
|
}
|
|
@@ -11754,7 +11813,7 @@ function serializeFeature(feature) {
|
|
|
11754
11813
|
const spec = feature.spec ?? EM_DASH2;
|
|
11755
11814
|
const plans = feature.plans.length > 0 ? feature.plans.join(", ") : EM_DASH2;
|
|
11756
11815
|
const blockedBy = feature.blockedBy.length > 0 ? feature.blockedBy.join(", ") : EM_DASH2;
|
|
11757
|
-
|
|
11816
|
+
const lines = [
|
|
11758
11817
|
`### ${feature.name}`,
|
|
11759
11818
|
"",
|
|
11760
11819
|
`- **Status:** ${feature.status}`,
|
|
@@ -11763,6 +11822,35 @@ function serializeFeature(feature) {
|
|
|
11763
11822
|
`- **Blockers:** ${blockedBy}`,
|
|
11764
11823
|
`- **Plan:** ${plans}`
|
|
11765
11824
|
];
|
|
11825
|
+
const hasExtended = feature.assignee !== null || feature.priority !== null || feature.externalId !== null;
|
|
11826
|
+
if (hasExtended) {
|
|
11827
|
+
lines.push(`- **Assignee:** ${feature.assignee ?? EM_DASH2}`);
|
|
11828
|
+
lines.push(`- **Priority:** ${feature.priority ?? EM_DASH2}`);
|
|
11829
|
+
lines.push(`- **External-ID:** ${feature.externalId ?? EM_DASH2}`);
|
|
11830
|
+
}
|
|
11831
|
+
return lines;
|
|
11832
|
+
}
|
|
11833
|
+
function serializeAssignmentHistory(records) {
|
|
11834
|
+
const lines = [
|
|
11835
|
+
"## Assignment History",
|
|
11836
|
+
"| Feature | Assignee | Action | Date |",
|
|
11837
|
+
"|---------|----------|--------|------|"
|
|
11838
|
+
];
|
|
11839
|
+
for (const record of records) {
|
|
11840
|
+
lines.push(`| ${record.feature} | ${record.assignee} | ${record.action} | ${record.date} |`);
|
|
11841
|
+
}
|
|
11842
|
+
return lines;
|
|
11843
|
+
}
|
|
11844
|
+
var STATUS_RANK = {
|
|
11845
|
+
backlog: 0,
|
|
11846
|
+
planned: 1,
|
|
11847
|
+
blocked: 1,
|
|
11848
|
+
// lateral to planned — sync can move to/from blocked freely
|
|
11849
|
+
"in-progress": 2,
|
|
11850
|
+
done: 3
|
|
11851
|
+
};
|
|
11852
|
+
function isRegression(from, to) {
|
|
11853
|
+
return STATUS_RANK[to] < STATUS_RANK[from];
|
|
11766
11854
|
}
|
|
11767
11855
|
function inferStatus(feature, projectPath, allFeatures) {
|
|
11768
11856
|
if (feature.blockedBy.length > 0) {
|
|
@@ -11830,17 +11918,6 @@ function inferStatus(feature, projectPath, allFeatures) {
|
|
|
11830
11918
|
if (anyStarted) return "in-progress";
|
|
11831
11919
|
return null;
|
|
11832
11920
|
}
|
|
11833
|
-
var STATUS_RANK = {
|
|
11834
|
-
backlog: 0,
|
|
11835
|
-
planned: 1,
|
|
11836
|
-
blocked: 1,
|
|
11837
|
-
// lateral to planned — sync can move to/from blocked freely
|
|
11838
|
-
"in-progress": 2,
|
|
11839
|
-
done: 3
|
|
11840
|
-
};
|
|
11841
|
-
function isRegression(from, to) {
|
|
11842
|
-
return STATUS_RANK[to] < STATUS_RANK[from];
|
|
11843
|
-
}
|
|
11844
11921
|
function syncRoadmap(options) {
|
|
11845
11922
|
const { projectPath, roadmap, forceSync } = options;
|
|
11846
11923
|
const allFeatures = roadmap.milestones.flatMap((m) => m.features);
|
|
@@ -11870,6 +11947,428 @@ function applySyncChanges(roadmap, changes) {
|
|
|
11870
11947
|
}
|
|
11871
11948
|
roadmap.frontmatter.lastSynced = (/* @__PURE__ */ new Date()).toISOString();
|
|
11872
11949
|
}
|
|
11950
|
+
function resolveReverseStatus(externalStatus, labels, config) {
|
|
11951
|
+
const reverseMap = config.reverseStatusMap;
|
|
11952
|
+
if (!reverseMap) return null;
|
|
11953
|
+
if (reverseMap[externalStatus]) {
|
|
11954
|
+
return reverseMap[externalStatus];
|
|
11955
|
+
}
|
|
11956
|
+
const statusLabels = ["in-progress", "blocked", "planned"];
|
|
11957
|
+
const matchingLabels = labels.filter((l) => statusLabels.includes(l));
|
|
11958
|
+
if (matchingLabels.length === 1) {
|
|
11959
|
+
const compoundKey = `${externalStatus}:${matchingLabels[0]}`;
|
|
11960
|
+
if (reverseMap[compoundKey]) {
|
|
11961
|
+
return reverseMap[compoundKey];
|
|
11962
|
+
}
|
|
11963
|
+
}
|
|
11964
|
+
return null;
|
|
11965
|
+
}
|
|
11966
|
+
function parseExternalId(externalId) {
|
|
11967
|
+
const match = externalId.match(/^github:([^/]+)\/([^#]+)#(\d+)$/);
|
|
11968
|
+
if (!match) return null;
|
|
11969
|
+
return { owner: match[1], repo: match[2], number: parseInt(match[3], 10) };
|
|
11970
|
+
}
|
|
11971
|
+
function buildExternalId(owner, repo, number) {
|
|
11972
|
+
return `github:${owner}/${repo}#${number}`;
|
|
11973
|
+
}
|
|
11974
|
+
function labelsForStatus(status, config) {
|
|
11975
|
+
const base = config.labels ?? [];
|
|
11976
|
+
const externalStatus = config.statusMap[status];
|
|
11977
|
+
if (externalStatus === "open" && status !== "backlog") {
|
|
11978
|
+
return [...base, status];
|
|
11979
|
+
}
|
|
11980
|
+
return [...base];
|
|
11981
|
+
}
|
|
11982
|
+
var GitHubIssuesSyncAdapter = class {
|
|
11983
|
+
token;
|
|
11984
|
+
config;
|
|
11985
|
+
fetchFn;
|
|
11986
|
+
apiBase;
|
|
11987
|
+
owner;
|
|
11988
|
+
repo;
|
|
11989
|
+
constructor(options) {
|
|
11990
|
+
this.token = options.token;
|
|
11991
|
+
this.config = options.config;
|
|
11992
|
+
this.fetchFn = options.fetchFn ?? globalThis.fetch;
|
|
11993
|
+
this.apiBase = options.apiBase ?? "https://api.github.com";
|
|
11994
|
+
const repoParts = (options.config.repo ?? "").split("/");
|
|
11995
|
+
if (repoParts.length !== 2 || !repoParts[0] || !repoParts[1]) {
|
|
11996
|
+
throw new Error(`Invalid repo format: "${options.config.repo}". Expected "owner/repo".`);
|
|
11997
|
+
}
|
|
11998
|
+
this.owner = repoParts[0];
|
|
11999
|
+
this.repo = repoParts[1];
|
|
12000
|
+
}
|
|
12001
|
+
headers() {
|
|
12002
|
+
return {
|
|
12003
|
+
Authorization: `Bearer ${this.token}`,
|
|
12004
|
+
Accept: "application/vnd.github+json",
|
|
12005
|
+
"Content-Type": "application/json",
|
|
12006
|
+
"X-GitHub-Api-Version": "2022-11-28"
|
|
12007
|
+
};
|
|
12008
|
+
}
|
|
12009
|
+
async createTicket(feature, milestone) {
|
|
12010
|
+
try {
|
|
12011
|
+
const labels = labelsForStatus(feature.status, this.config);
|
|
12012
|
+
const body = [
|
|
12013
|
+
feature.summary,
|
|
12014
|
+
"",
|
|
12015
|
+
`**Milestone:** ${milestone}`,
|
|
12016
|
+
feature.spec ? `**Spec:** ${feature.spec}` : ""
|
|
12017
|
+
].filter(Boolean).join("\n");
|
|
12018
|
+
const response = await this.fetchFn(
|
|
12019
|
+
`${this.apiBase}/repos/${this.owner}/${this.repo}/issues`,
|
|
12020
|
+
{
|
|
12021
|
+
method: "POST",
|
|
12022
|
+
headers: this.headers(),
|
|
12023
|
+
body: JSON.stringify({
|
|
12024
|
+
title: feature.name,
|
|
12025
|
+
body,
|
|
12026
|
+
labels
|
|
12027
|
+
})
|
|
12028
|
+
}
|
|
12029
|
+
);
|
|
12030
|
+
if (!response.ok) {
|
|
12031
|
+
const text = await response.text();
|
|
12032
|
+
return Err(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12033
|
+
}
|
|
12034
|
+
const data = await response.json();
|
|
12035
|
+
const externalId = buildExternalId(this.owner, this.repo, data.number);
|
|
12036
|
+
return Ok({ externalId, url: data.html_url });
|
|
12037
|
+
} catch (error) {
|
|
12038
|
+
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
12039
|
+
}
|
|
12040
|
+
}
|
|
12041
|
+
async updateTicket(externalId, changes) {
|
|
12042
|
+
try {
|
|
12043
|
+
const parsed = parseExternalId(externalId);
|
|
12044
|
+
if (!parsed) return Err(new Error(`Invalid externalId format: "${externalId}"`));
|
|
12045
|
+
const patch = {};
|
|
12046
|
+
if (changes.name !== void 0) patch.title = changes.name;
|
|
12047
|
+
if (changes.summary !== void 0) {
|
|
12048
|
+
const body = [changes.summary, "", changes.spec ? `**Spec:** ${changes.spec}` : ""].filter(Boolean).join("\n");
|
|
12049
|
+
patch.body = body;
|
|
12050
|
+
}
|
|
12051
|
+
if (changes.status !== void 0) {
|
|
12052
|
+
const externalStatus = this.config.statusMap[changes.status];
|
|
12053
|
+
patch.state = externalStatus;
|
|
12054
|
+
patch.labels = labelsForStatus(changes.status, this.config);
|
|
12055
|
+
}
|
|
12056
|
+
const response = await this.fetchFn(
|
|
12057
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}`,
|
|
12058
|
+
{
|
|
12059
|
+
method: "PATCH",
|
|
12060
|
+
headers: this.headers(),
|
|
12061
|
+
body: JSON.stringify(patch)
|
|
12062
|
+
}
|
|
12063
|
+
);
|
|
12064
|
+
if (!response.ok) {
|
|
12065
|
+
const text = await response.text();
|
|
12066
|
+
return Err(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12067
|
+
}
|
|
12068
|
+
const data = await response.json();
|
|
12069
|
+
return Ok({ externalId, url: data.html_url });
|
|
12070
|
+
} catch (error) {
|
|
12071
|
+
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
12072
|
+
}
|
|
12073
|
+
}
|
|
12074
|
+
async fetchTicketState(externalId) {
|
|
12075
|
+
try {
|
|
12076
|
+
const parsed = parseExternalId(externalId);
|
|
12077
|
+
if (!parsed) return Err(new Error(`Invalid externalId format: "${externalId}"`));
|
|
12078
|
+
const response = await this.fetchFn(
|
|
12079
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}`,
|
|
12080
|
+
{
|
|
12081
|
+
method: "GET",
|
|
12082
|
+
headers: this.headers()
|
|
12083
|
+
}
|
|
12084
|
+
);
|
|
12085
|
+
if (!response.ok) {
|
|
12086
|
+
const text = await response.text();
|
|
12087
|
+
return Err(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12088
|
+
}
|
|
12089
|
+
const data = await response.json();
|
|
12090
|
+
return Ok({
|
|
12091
|
+
externalId,
|
|
12092
|
+
status: data.state,
|
|
12093
|
+
labels: data.labels.map((l) => l.name),
|
|
12094
|
+
assignee: data.assignee ? `@${data.assignee.login}` : null
|
|
12095
|
+
});
|
|
12096
|
+
} catch (error) {
|
|
12097
|
+
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
12098
|
+
}
|
|
12099
|
+
}
|
|
12100
|
+
async fetchAllTickets() {
|
|
12101
|
+
try {
|
|
12102
|
+
const filterLabels = this.config.labels ?? [];
|
|
12103
|
+
const labelsParam = filterLabels.length > 0 ? `&labels=${filterLabels.join(",")}` : "";
|
|
12104
|
+
const tickets = [];
|
|
12105
|
+
let page = 1;
|
|
12106
|
+
const perPage = 100;
|
|
12107
|
+
while (true) {
|
|
12108
|
+
const response = await this.fetchFn(
|
|
12109
|
+
`${this.apiBase}/repos/${this.owner}/${this.repo}/issues?state=all&per_page=${perPage}&page=${page}${labelsParam}`,
|
|
12110
|
+
{
|
|
12111
|
+
method: "GET",
|
|
12112
|
+
headers: this.headers()
|
|
12113
|
+
}
|
|
12114
|
+
);
|
|
12115
|
+
if (!response.ok) {
|
|
12116
|
+
const text = await response.text();
|
|
12117
|
+
return Err(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12118
|
+
}
|
|
12119
|
+
const data = await response.json();
|
|
12120
|
+
const issues = data.filter((d) => !d.pull_request);
|
|
12121
|
+
for (const issue of issues) {
|
|
12122
|
+
tickets.push({
|
|
12123
|
+
externalId: buildExternalId(this.owner, this.repo, issue.number),
|
|
12124
|
+
status: issue.state,
|
|
12125
|
+
labels: issue.labels.map((l) => l.name),
|
|
12126
|
+
assignee: issue.assignee ? `@${issue.assignee.login}` : null
|
|
12127
|
+
});
|
|
12128
|
+
}
|
|
12129
|
+
if (data.length < perPage) break;
|
|
12130
|
+
page++;
|
|
12131
|
+
}
|
|
12132
|
+
return Ok(tickets);
|
|
12133
|
+
} catch (error) {
|
|
12134
|
+
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
12135
|
+
}
|
|
12136
|
+
}
|
|
12137
|
+
async assignTicket(externalId, assignee) {
|
|
12138
|
+
try {
|
|
12139
|
+
const parsed = parseExternalId(externalId);
|
|
12140
|
+
if (!parsed) return Err(new Error(`Invalid externalId format: "${externalId}"`));
|
|
12141
|
+
const login = assignee.startsWith("@") ? assignee.slice(1) : assignee;
|
|
12142
|
+
const response = await this.fetchFn(
|
|
12143
|
+
`${this.apiBase}/repos/${parsed.owner}/${parsed.repo}/issues/${parsed.number}/assignees`,
|
|
12144
|
+
{
|
|
12145
|
+
method: "POST",
|
|
12146
|
+
headers: this.headers(),
|
|
12147
|
+
body: JSON.stringify({ assignees: [login] })
|
|
12148
|
+
}
|
|
12149
|
+
);
|
|
12150
|
+
if (!response.ok) {
|
|
12151
|
+
const text = await response.text();
|
|
12152
|
+
return Err(new Error(`GitHub API error ${response.status}: ${text}`));
|
|
12153
|
+
}
|
|
12154
|
+
return Ok(void 0);
|
|
12155
|
+
} catch (error) {
|
|
12156
|
+
return Err(error instanceof Error ? error : new Error(String(error)));
|
|
12157
|
+
}
|
|
12158
|
+
}
|
|
12159
|
+
};
|
|
12160
|
+
function emptySyncResult() {
|
|
12161
|
+
return { created: [], updated: [], assignmentChanges: [], errors: [] };
|
|
12162
|
+
}
|
|
12163
|
+
async function syncToExternal(roadmap, adapter, _config) {
|
|
12164
|
+
const result = emptySyncResult();
|
|
12165
|
+
for (const milestone of roadmap.milestones) {
|
|
12166
|
+
for (const feature of milestone.features) {
|
|
12167
|
+
if (!feature.externalId) {
|
|
12168
|
+
const createResult = await adapter.createTicket(feature, milestone.name);
|
|
12169
|
+
if (createResult.ok) {
|
|
12170
|
+
feature.externalId = createResult.value.externalId;
|
|
12171
|
+
result.created.push(createResult.value);
|
|
12172
|
+
} else {
|
|
12173
|
+
result.errors.push({ featureOrId: feature.name, error: createResult.error });
|
|
12174
|
+
}
|
|
12175
|
+
} else {
|
|
12176
|
+
const updateResult = await adapter.updateTicket(feature.externalId, feature);
|
|
12177
|
+
if (updateResult.ok) {
|
|
12178
|
+
result.updated.push(feature.externalId);
|
|
12179
|
+
} else {
|
|
12180
|
+
result.errors.push({ featureOrId: feature.externalId, error: updateResult.error });
|
|
12181
|
+
}
|
|
12182
|
+
}
|
|
12183
|
+
}
|
|
12184
|
+
}
|
|
12185
|
+
return result;
|
|
12186
|
+
}
|
|
12187
|
+
async function syncFromExternal(roadmap, adapter, config, options) {
|
|
12188
|
+
const result = emptySyncResult();
|
|
12189
|
+
const forceSync = options?.forceSync ?? false;
|
|
12190
|
+
const featureByExternalId = /* @__PURE__ */ new Map();
|
|
12191
|
+
for (const milestone of roadmap.milestones) {
|
|
12192
|
+
for (const feature of milestone.features) {
|
|
12193
|
+
if (feature.externalId) {
|
|
12194
|
+
featureByExternalId.set(feature.externalId, feature);
|
|
12195
|
+
}
|
|
12196
|
+
}
|
|
12197
|
+
}
|
|
12198
|
+
if (featureByExternalId.size === 0) return result;
|
|
12199
|
+
const fetchResult = await adapter.fetchAllTickets();
|
|
12200
|
+
if (!fetchResult.ok) {
|
|
12201
|
+
result.errors.push({ featureOrId: "*", error: fetchResult.error });
|
|
12202
|
+
return result;
|
|
12203
|
+
}
|
|
12204
|
+
for (const ticketState of fetchResult.value) {
|
|
12205
|
+
const feature = featureByExternalId.get(ticketState.externalId);
|
|
12206
|
+
if (!feature) continue;
|
|
12207
|
+
if (ticketState.assignee !== feature.assignee) {
|
|
12208
|
+
result.assignmentChanges.push({
|
|
12209
|
+
feature: feature.name,
|
|
12210
|
+
from: feature.assignee,
|
|
12211
|
+
to: ticketState.assignee
|
|
12212
|
+
});
|
|
12213
|
+
feature.assignee = ticketState.assignee;
|
|
12214
|
+
}
|
|
12215
|
+
const resolvedStatus = resolveReverseStatus(ticketState.status, ticketState.labels, config);
|
|
12216
|
+
if (resolvedStatus && resolvedStatus !== feature.status) {
|
|
12217
|
+
const newStatus = resolvedStatus;
|
|
12218
|
+
if (!forceSync && isRegression(feature.status, newStatus)) {
|
|
12219
|
+
continue;
|
|
12220
|
+
}
|
|
12221
|
+
feature.status = newStatus;
|
|
12222
|
+
}
|
|
12223
|
+
}
|
|
12224
|
+
return result;
|
|
12225
|
+
}
|
|
12226
|
+
var syncMutex = Promise.resolve();
|
|
12227
|
+
async function fullSync(roadmapPath, adapter, config, options) {
|
|
12228
|
+
const previousSync = syncMutex;
|
|
12229
|
+
let releaseMutex;
|
|
12230
|
+
syncMutex = new Promise((resolve5) => {
|
|
12231
|
+
releaseMutex = resolve5;
|
|
12232
|
+
});
|
|
12233
|
+
await previousSync;
|
|
12234
|
+
try {
|
|
12235
|
+
const raw = fs20.readFileSync(roadmapPath, "utf-8");
|
|
12236
|
+
const parseResult = parseRoadmap(raw);
|
|
12237
|
+
if (!parseResult.ok) {
|
|
12238
|
+
return {
|
|
12239
|
+
...emptySyncResult(),
|
|
12240
|
+
errors: [{ featureOrId: "*", error: parseResult.error }]
|
|
12241
|
+
};
|
|
12242
|
+
}
|
|
12243
|
+
const roadmap = parseResult.value;
|
|
12244
|
+
const pushResult = await syncToExternal(roadmap, adapter, config);
|
|
12245
|
+
const pullResult = await syncFromExternal(roadmap, adapter, config, options);
|
|
12246
|
+
fs20.writeFileSync(roadmapPath, serializeRoadmap(roadmap), "utf-8");
|
|
12247
|
+
return {
|
|
12248
|
+
created: pushResult.created,
|
|
12249
|
+
updated: pushResult.updated,
|
|
12250
|
+
assignmentChanges: pullResult.assignmentChanges,
|
|
12251
|
+
errors: [...pushResult.errors, ...pullResult.errors]
|
|
12252
|
+
};
|
|
12253
|
+
} finally {
|
|
12254
|
+
releaseMutex();
|
|
12255
|
+
}
|
|
12256
|
+
}
|
|
12257
|
+
var PRIORITY_RANK = {
|
|
12258
|
+
P0: 0,
|
|
12259
|
+
P1: 1,
|
|
12260
|
+
P2: 2,
|
|
12261
|
+
P3: 3
|
|
12262
|
+
};
|
|
12263
|
+
var POSITION_WEIGHT = 0.5;
|
|
12264
|
+
var DEPENDENTS_WEIGHT = 0.3;
|
|
12265
|
+
var AFFINITY_WEIGHT = 0.2;
|
|
12266
|
+
function scoreRoadmapCandidates(roadmap, options) {
|
|
12267
|
+
const allFeatures = roadmap.milestones.flatMap((m) => m.features);
|
|
12268
|
+
const allFeatureNames = new Set(allFeatures.map((f) => f.name.toLowerCase()));
|
|
12269
|
+
const doneFeatures = new Set(
|
|
12270
|
+
allFeatures.filter((f) => f.status === "done").map((f) => f.name.toLowerCase())
|
|
12271
|
+
);
|
|
12272
|
+
const dependentsCount = /* @__PURE__ */ new Map();
|
|
12273
|
+
for (const feature of allFeatures) {
|
|
12274
|
+
for (const blocker of feature.blockedBy) {
|
|
12275
|
+
const key = blocker.toLowerCase();
|
|
12276
|
+
dependentsCount.set(key, (dependentsCount.get(key) ?? 0) + 1);
|
|
12277
|
+
}
|
|
12278
|
+
}
|
|
12279
|
+
const maxDependents = Math.max(1, ...dependentsCount.values());
|
|
12280
|
+
const milestoneMap = /* @__PURE__ */ new Map();
|
|
12281
|
+
for (const ms of roadmap.milestones) {
|
|
12282
|
+
milestoneMap.set(
|
|
12283
|
+
ms.name,
|
|
12284
|
+
ms.features.map((f) => f.name.toLowerCase())
|
|
12285
|
+
);
|
|
12286
|
+
}
|
|
12287
|
+
const userCompletedFeatures = /* @__PURE__ */ new Set();
|
|
12288
|
+
if (options?.currentUser) {
|
|
12289
|
+
const user = options.currentUser.toLowerCase();
|
|
12290
|
+
for (const record of roadmap.assignmentHistory) {
|
|
12291
|
+
if (record.action === "completed" && record.assignee.toLowerCase() === user) {
|
|
12292
|
+
userCompletedFeatures.add(record.feature.toLowerCase());
|
|
12293
|
+
}
|
|
12294
|
+
}
|
|
12295
|
+
}
|
|
12296
|
+
let totalPositions = 0;
|
|
12297
|
+
for (const ms of roadmap.milestones) {
|
|
12298
|
+
totalPositions += ms.features.length;
|
|
12299
|
+
}
|
|
12300
|
+
totalPositions = Math.max(1, totalPositions);
|
|
12301
|
+
const candidates = [];
|
|
12302
|
+
let globalPosition = 0;
|
|
12303
|
+
for (const ms of roadmap.milestones) {
|
|
12304
|
+
for (let featureIdx = 0; featureIdx < ms.features.length; featureIdx++) {
|
|
12305
|
+
const feature = ms.features[featureIdx];
|
|
12306
|
+
globalPosition++;
|
|
12307
|
+
if (feature.status !== "planned" && feature.status !== "backlog") continue;
|
|
12308
|
+
const isBlocked = feature.blockedBy.some((blocker) => {
|
|
12309
|
+
const key = blocker.toLowerCase();
|
|
12310
|
+
return allFeatureNames.has(key) && !doneFeatures.has(key);
|
|
12311
|
+
});
|
|
12312
|
+
if (isBlocked) continue;
|
|
12313
|
+
const positionScore = 1 - (globalPosition - 1) / totalPositions;
|
|
12314
|
+
const deps = dependentsCount.get(feature.name.toLowerCase()) ?? 0;
|
|
12315
|
+
const dependentsScore = deps / maxDependents;
|
|
12316
|
+
let affinityScore = 0;
|
|
12317
|
+
if (userCompletedFeatures.size > 0) {
|
|
12318
|
+
const completedBlockers = feature.blockedBy.filter(
|
|
12319
|
+
(b) => userCompletedFeatures.has(b.toLowerCase())
|
|
12320
|
+
);
|
|
12321
|
+
if (completedBlockers.length > 0) {
|
|
12322
|
+
affinityScore = 1;
|
|
12323
|
+
} else {
|
|
12324
|
+
const siblings = milestoneMap.get(ms.name) ?? [];
|
|
12325
|
+
const completedSiblings = siblings.filter((s) => userCompletedFeatures.has(s));
|
|
12326
|
+
if (completedSiblings.length > 0) {
|
|
12327
|
+
affinityScore = 0.5;
|
|
12328
|
+
}
|
|
12329
|
+
}
|
|
12330
|
+
}
|
|
12331
|
+
const weightedScore = POSITION_WEIGHT * positionScore + DEPENDENTS_WEIGHT * dependentsScore + AFFINITY_WEIGHT * affinityScore;
|
|
12332
|
+
const priorityTier = feature.priority ? PRIORITY_RANK[feature.priority] : null;
|
|
12333
|
+
candidates.push({
|
|
12334
|
+
feature,
|
|
12335
|
+
milestone: ms.name,
|
|
12336
|
+
positionScore,
|
|
12337
|
+
dependentsScore,
|
|
12338
|
+
affinityScore,
|
|
12339
|
+
weightedScore,
|
|
12340
|
+
priorityTier
|
|
12341
|
+
});
|
|
12342
|
+
}
|
|
12343
|
+
}
|
|
12344
|
+
candidates.sort((a, b) => {
|
|
12345
|
+
if (a.priorityTier !== null && b.priorityTier === null) return -1;
|
|
12346
|
+
if (a.priorityTier === null && b.priorityTier !== null) return 1;
|
|
12347
|
+
if (a.priorityTier !== null && b.priorityTier !== null) {
|
|
12348
|
+
if (a.priorityTier !== b.priorityTier) return a.priorityTier - b.priorityTier;
|
|
12349
|
+
}
|
|
12350
|
+
return b.weightedScore - a.weightedScore;
|
|
12351
|
+
});
|
|
12352
|
+
return candidates;
|
|
12353
|
+
}
|
|
12354
|
+
function assignFeature(roadmap, feature, assignee, date) {
|
|
12355
|
+
if (feature.assignee === assignee) return;
|
|
12356
|
+
if (feature.assignee !== null) {
|
|
12357
|
+
roadmap.assignmentHistory.push({
|
|
12358
|
+
feature: feature.name,
|
|
12359
|
+
assignee: feature.assignee,
|
|
12360
|
+
action: "unassigned",
|
|
12361
|
+
date
|
|
12362
|
+
});
|
|
12363
|
+
}
|
|
12364
|
+
feature.assignee = assignee;
|
|
12365
|
+
roadmap.assignmentHistory.push({
|
|
12366
|
+
feature: feature.name,
|
|
12367
|
+
assignee,
|
|
12368
|
+
action: "assigned",
|
|
12369
|
+
date
|
|
12370
|
+
});
|
|
12371
|
+
}
|
|
11873
12372
|
var InteractionTypeSchema = z7.enum(["question", "confirmation", "transition"]);
|
|
11874
12373
|
var QuestionSchema = z7.object({
|
|
11875
12374
|
text: z7.string(),
|
|
@@ -11900,11 +12399,12 @@ var ProjectScanner = class {
|
|
|
11900
12399
|
constructor(rootDir) {
|
|
11901
12400
|
this.rootDir = rootDir;
|
|
11902
12401
|
}
|
|
12402
|
+
rootDir;
|
|
11903
12403
|
async scan() {
|
|
11904
12404
|
let projectName = path20.basename(this.rootDir);
|
|
11905
12405
|
try {
|
|
11906
12406
|
const pkgPath = path20.join(this.rootDir, "package.json");
|
|
11907
|
-
const pkgRaw = await
|
|
12407
|
+
const pkgRaw = await fs21.readFile(pkgPath, "utf-8");
|
|
11908
12408
|
const pkg = JSON.parse(pkgRaw);
|
|
11909
12409
|
if (pkg.name) projectName = pkg.name;
|
|
11910
12410
|
} catch {
|
|
@@ -12017,8 +12517,8 @@ var BlueprintGenerator = class {
|
|
|
12017
12517
|
styles: STYLES,
|
|
12018
12518
|
scripts: SCRIPTS
|
|
12019
12519
|
});
|
|
12020
|
-
await
|
|
12021
|
-
await
|
|
12520
|
+
await fs22.mkdir(options.outputDir, { recursive: true });
|
|
12521
|
+
await fs22.writeFile(path21.join(options.outputDir, "index.html"), html);
|
|
12022
12522
|
}
|
|
12023
12523
|
};
|
|
12024
12524
|
function getStatePath() {
|
|
@@ -12036,7 +12536,7 @@ function shouldRunCheck(state, intervalMs) {
|
|
|
12036
12536
|
}
|
|
12037
12537
|
function readCheckState() {
|
|
12038
12538
|
try {
|
|
12039
|
-
const raw =
|
|
12539
|
+
const raw = fs23.readFileSync(getStatePath(), "utf-8");
|
|
12040
12540
|
const parsed = JSON.parse(raw);
|
|
12041
12541
|
if (typeof parsed === "object" && parsed !== null && "lastCheckTime" in parsed && typeof parsed.lastCheckTime === "number" && "currentVersion" in parsed && typeof parsed.currentVersion === "string") {
|
|
12042
12542
|
const state = parsed;
|
|
@@ -12588,7 +13088,7 @@ function getStalenessMarkerPath(projectRoot) {
|
|
|
12588
13088
|
}
|
|
12589
13089
|
async function readDiskCache(projectRoot) {
|
|
12590
13090
|
try {
|
|
12591
|
-
const raw = await
|
|
13091
|
+
const raw = await fs24.readFile(getCachePath(projectRoot), "utf-8");
|
|
12592
13092
|
return JSON.parse(raw);
|
|
12593
13093
|
} catch {
|
|
12594
13094
|
return null;
|
|
@@ -12596,8 +13096,8 @@ async function readDiskCache(projectRoot) {
|
|
|
12596
13096
|
}
|
|
12597
13097
|
async function writeDiskCache(projectRoot, data) {
|
|
12598
13098
|
const cachePath = getCachePath(projectRoot);
|
|
12599
|
-
await
|
|
12600
|
-
await
|
|
13099
|
+
await fs24.mkdir(path23.dirname(cachePath), { recursive: true });
|
|
13100
|
+
await fs24.writeFile(cachePath, JSON.stringify(data, null, 2));
|
|
12601
13101
|
}
|
|
12602
13102
|
async function fetchFromNetwork() {
|
|
12603
13103
|
try {
|
|
@@ -12624,7 +13124,7 @@ function loadFallbackDataset() {
|
|
|
12624
13124
|
async function checkAndWarnStaleness(projectRoot) {
|
|
12625
13125
|
const markerPath = getStalenessMarkerPath(projectRoot);
|
|
12626
13126
|
try {
|
|
12627
|
-
const raw = await
|
|
13127
|
+
const raw = await fs24.readFile(markerPath, "utf-8");
|
|
12628
13128
|
const marker = JSON.parse(raw);
|
|
12629
13129
|
const firstUse = new Date(marker.firstFallbackUse).getTime();
|
|
12630
13130
|
const now = Date.now();
|
|
@@ -12636,8 +13136,8 @@ async function checkAndWarnStaleness(projectRoot) {
|
|
|
12636
13136
|
}
|
|
12637
13137
|
} catch {
|
|
12638
13138
|
try {
|
|
12639
|
-
await
|
|
12640
|
-
await
|
|
13139
|
+
await fs24.mkdir(path23.dirname(markerPath), { recursive: true });
|
|
13140
|
+
await fs24.writeFile(
|
|
12641
13141
|
markerPath,
|
|
12642
13142
|
JSON.stringify({ firstFallbackUse: (/* @__PURE__ */ new Date()).toISOString() })
|
|
12643
13143
|
);
|
|
@@ -12647,7 +13147,7 @@ async function checkAndWarnStaleness(projectRoot) {
|
|
|
12647
13147
|
}
|
|
12648
13148
|
async function clearStalenessMarker(projectRoot) {
|
|
12649
13149
|
try {
|
|
12650
|
-
await
|
|
13150
|
+
await fs24.unlink(getStalenessMarkerPath(projectRoot));
|
|
12651
13151
|
} catch {
|
|
12652
13152
|
}
|
|
12653
13153
|
}
|
|
@@ -12852,7 +13352,7 @@ function readCostRecords(projectRoot) {
|
|
|
12852
13352
|
const costsFile = path24.join(projectRoot, ".harness", "metrics", "costs.jsonl");
|
|
12853
13353
|
let raw;
|
|
12854
13354
|
try {
|
|
12855
|
-
raw =
|
|
13355
|
+
raw = fs25.readFileSync(costsFile, "utf-8");
|
|
12856
13356
|
} catch {
|
|
12857
13357
|
return [];
|
|
12858
13358
|
}
|
|
@@ -12913,7 +13413,7 @@ function parseCCLine(line, filePath, lineNumber) {
|
|
|
12913
13413
|
function readCCFile(filePath) {
|
|
12914
13414
|
let raw;
|
|
12915
13415
|
try {
|
|
12916
|
-
raw =
|
|
13416
|
+
raw = fs26.readFileSync(filePath, "utf-8");
|
|
12917
13417
|
} catch {
|
|
12918
13418
|
return [];
|
|
12919
13419
|
}
|
|
@@ -12938,7 +13438,7 @@ function parseCCRecords() {
|
|
|
12938
13438
|
const projectsDir = path25.join(homeDir, ".claude", "projects");
|
|
12939
13439
|
let projectDirs;
|
|
12940
13440
|
try {
|
|
12941
|
-
projectDirs =
|
|
13441
|
+
projectDirs = fs26.readdirSync(projectsDir, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => path25.join(projectsDir, d.name));
|
|
12942
13442
|
} catch {
|
|
12943
13443
|
return [];
|
|
12944
13444
|
}
|
|
@@ -12946,7 +13446,7 @@ function parseCCRecords() {
|
|
|
12946
13446
|
for (const dir of projectDirs) {
|
|
12947
13447
|
let files;
|
|
12948
13448
|
try {
|
|
12949
|
-
files =
|
|
13449
|
+
files = fs26.readdirSync(dir).filter((f) => f.endsWith(".jsonl")).map((f) => path25.join(dir, f));
|
|
12950
13450
|
} catch {
|
|
12951
13451
|
continue;
|
|
12952
13452
|
}
|
|
@@ -13205,8 +13705,17 @@ export {
|
|
|
13205
13705
|
runReviewPipeline,
|
|
13206
13706
|
parseRoadmap,
|
|
13207
13707
|
serializeRoadmap,
|
|
13708
|
+
STATUS_RANK,
|
|
13709
|
+
isRegression,
|
|
13208
13710
|
syncRoadmap,
|
|
13209
13711
|
applySyncChanges,
|
|
13712
|
+
resolveReverseStatus,
|
|
13713
|
+
GitHubIssuesSyncAdapter,
|
|
13714
|
+
syncToExternal,
|
|
13715
|
+
syncFromExternal,
|
|
13716
|
+
fullSync,
|
|
13717
|
+
scoreRoadmapCandidates,
|
|
13718
|
+
assignFeature,
|
|
13210
13719
|
InteractionTypeSchema,
|
|
13211
13720
|
QuestionSchema,
|
|
13212
13721
|
ConfirmationSchema,
|