hyper-pm 0.1.4 → 0.1.6
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/README.md +23 -11
- package/dist/index.cjs +1530 -886
- package/dist/main.cjs +1530 -886
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -11540,6 +11540,103 @@ var normalizeTicketBranchListFromPayloadValue = (value) => {
|
|
|
11540
11540
|
return normalizeTicketBranchListFromStrings(strings);
|
|
11541
11541
|
};
|
|
11542
11542
|
|
|
11543
|
+
// src/lib/ticket-depends-on.ts
|
|
11544
|
+
var normalizeTicketDependsOnIds = (ids) => {
|
|
11545
|
+
const out = [];
|
|
11546
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11547
|
+
for (const raw of ids) {
|
|
11548
|
+
const t = raw.trim();
|
|
11549
|
+
if (t === "") continue;
|
|
11550
|
+
if (seen.has(t)) continue;
|
|
11551
|
+
seen.add(t);
|
|
11552
|
+
out.push(t);
|
|
11553
|
+
}
|
|
11554
|
+
return out;
|
|
11555
|
+
};
|
|
11556
|
+
var ticketDependsOnListsEqual = (a, b) => {
|
|
11557
|
+
const an = normalizeTicketDependsOnIds(a ?? []);
|
|
11558
|
+
const bn = normalizeTicketDependsOnIds(b ?? []);
|
|
11559
|
+
if (an.length !== bn.length) return false;
|
|
11560
|
+
return an.every((x, i) => x === bn[i]);
|
|
11561
|
+
};
|
|
11562
|
+
var parseTicketDependsOnFromPayloadValue = (value) => {
|
|
11563
|
+
if (!Array.isArray(value)) return void 0;
|
|
11564
|
+
const strings = [];
|
|
11565
|
+
for (const x of value) {
|
|
11566
|
+
if (typeof x !== "string") return void 0;
|
|
11567
|
+
strings.push(x);
|
|
11568
|
+
}
|
|
11569
|
+
return normalizeTicketDependsOnIds(strings);
|
|
11570
|
+
};
|
|
11571
|
+
var parseTicketDependsOnFromFenceValue = (value) => {
|
|
11572
|
+
if (!Array.isArray(value)) return void 0;
|
|
11573
|
+
const strings = [];
|
|
11574
|
+
for (const x of value) {
|
|
11575
|
+
if (typeof x === "string") strings.push(x);
|
|
11576
|
+
}
|
|
11577
|
+
return normalizeTicketDependsOnIds(strings);
|
|
11578
|
+
};
|
|
11579
|
+
var wouldTicketDependsOnCreateCycle = (params) => {
|
|
11580
|
+
const { fromTicketId, nextDependsOn, successorsFor } = params;
|
|
11581
|
+
const dfsFromPrerequisite = (start) => {
|
|
11582
|
+
const stack = [start];
|
|
11583
|
+
const visited = /* @__PURE__ */ new Set();
|
|
11584
|
+
while (stack.length > 0) {
|
|
11585
|
+
const node = stack.pop();
|
|
11586
|
+
if (node === fromTicketId) return true;
|
|
11587
|
+
if (visited.has(node)) continue;
|
|
11588
|
+
visited.add(node);
|
|
11589
|
+
const next = successorsFor(node) ?? [];
|
|
11590
|
+
for (let i = next.length - 1; i >= 0; i -= 1) {
|
|
11591
|
+
stack.push(next[i]);
|
|
11592
|
+
}
|
|
11593
|
+
}
|
|
11594
|
+
return false;
|
|
11595
|
+
};
|
|
11596
|
+
for (const p of nextDependsOn) {
|
|
11597
|
+
if (dfsFromPrerequisite(p)) return true;
|
|
11598
|
+
}
|
|
11599
|
+
return false;
|
|
11600
|
+
};
|
|
11601
|
+
var ticketDependsOnSuccessorsForProjection = (projection, fromTicketId, nextDependsOn) => {
|
|
11602
|
+
return (ticketId) => {
|
|
11603
|
+
if (ticketId === fromTicketId) {
|
|
11604
|
+
return nextDependsOn.length > 0 ? nextDependsOn : void 0;
|
|
11605
|
+
}
|
|
11606
|
+
const row = projection.tickets.get(ticketId);
|
|
11607
|
+
if (!row || row.deleted) return void 0;
|
|
11608
|
+
return row.dependsOn;
|
|
11609
|
+
};
|
|
11610
|
+
};
|
|
11611
|
+
var validateTicketDependsOnForWrite = (params) => {
|
|
11612
|
+
const { projection, fromTicketId, nextDependsOn } = params;
|
|
11613
|
+
for (const id of nextDependsOn) {
|
|
11614
|
+
if (id === fromTicketId) {
|
|
11615
|
+
return `Ticket cannot depend on itself (${id})`;
|
|
11616
|
+
}
|
|
11617
|
+
const row = projection.tickets.get(id);
|
|
11618
|
+
if (row === void 0) {
|
|
11619
|
+
return `Dependency ticket not found: ${id}`;
|
|
11620
|
+
}
|
|
11621
|
+
if (row.deleted) {
|
|
11622
|
+
return `Dependency ticket deleted: ${id}`;
|
|
11623
|
+
}
|
|
11624
|
+
}
|
|
11625
|
+
const successorsFor = ticketDependsOnSuccessorsForProjection(
|
|
11626
|
+
projection,
|
|
11627
|
+
fromTicketId,
|
|
11628
|
+
nextDependsOn
|
|
11629
|
+
);
|
|
11630
|
+
if (wouldTicketDependsOnCreateCycle({
|
|
11631
|
+
fromTicketId,
|
|
11632
|
+
nextDependsOn,
|
|
11633
|
+
successorsFor
|
|
11634
|
+
})) {
|
|
11635
|
+
return "Ticket dependencies would create a cycle";
|
|
11636
|
+
}
|
|
11637
|
+
return void 0;
|
|
11638
|
+
};
|
|
11639
|
+
|
|
11543
11640
|
// src/lib/ticket-planning-fields.ts
|
|
11544
11641
|
var PRIORITY_SET = /* @__PURE__ */ new Set([
|
|
11545
11642
|
"low",
|
|
@@ -11708,6 +11805,9 @@ var formatOutput = (format, value) => {
|
|
|
11708
11805
|
return JSON.stringify(value);
|
|
11709
11806
|
};
|
|
11710
11807
|
|
|
11808
|
+
// src/cli/normalize-raw-cli-argv.ts
|
|
11809
|
+
var normalizeRawCliArgv = (argv) => argv.map((token) => token === "--no-github" ? "--skip-network" : token);
|
|
11810
|
+
|
|
11711
11811
|
// src/cli/prune-unchanged-work-item-update-payload.ts
|
|
11712
11812
|
var branchListsEqual = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
|
|
11713
11813
|
var pruneEpicOrStoryUpdatePayloadAgainstRow = (cur, draft) => {
|
|
@@ -11763,6 +11863,20 @@ var pruneTicketUpdatePayloadAgainstRow = (cur, draft) => {
|
|
|
11763
11863
|
out["labels"] = draft["labels"];
|
|
11764
11864
|
}
|
|
11765
11865
|
}
|
|
11866
|
+
if (draft["dependsOn"] !== void 0) {
|
|
11867
|
+
if (draft["dependsOn"] === null) {
|
|
11868
|
+
if (!ticketDependsOnListsEqual(cur.dependsOn, [])) {
|
|
11869
|
+
out["dependsOn"] = null;
|
|
11870
|
+
}
|
|
11871
|
+
} else {
|
|
11872
|
+
const parsed = parseTicketDependsOnFromPayloadValue(draft["dependsOn"]);
|
|
11873
|
+
if (parsed !== void 0) {
|
|
11874
|
+
if (!ticketDependsOnListsEqual(cur.dependsOn, parsed)) {
|
|
11875
|
+
out["dependsOn"] = parsed;
|
|
11876
|
+
}
|
|
11877
|
+
}
|
|
11878
|
+
}
|
|
11879
|
+
}
|
|
11766
11880
|
if (draft["priority"] !== void 0) {
|
|
11767
11881
|
const curP = cur.priority ?? null;
|
|
11768
11882
|
const nextP = draft["priority"] === null ? null : draft["priority"];
|
|
@@ -11929,6 +12043,20 @@ var workItemUpdateAspects = (kind, payload) => {
|
|
|
11929
12043
|
}
|
|
11930
12044
|
}
|
|
11931
12045
|
}
|
|
12046
|
+
if (kind === "ticket" && payload["dependsOn"] !== void 0) {
|
|
12047
|
+
if (payload["dependsOn"] === null) {
|
|
12048
|
+
aspects.push("cleared ticket dependencies");
|
|
12049
|
+
} else {
|
|
12050
|
+
const d = normalizeBranchList(payload["dependsOn"]);
|
|
12051
|
+
if (d.length > 0 && d.length <= MAX_BRANCH_NAMES_TO_LIST) {
|
|
12052
|
+
aspects.push(`set ticket dependencies to (${d.join(", ")})`);
|
|
12053
|
+
} else if (d.length > MAX_BRANCH_NAMES_TO_LIST) {
|
|
12054
|
+
aspects.push("updated ticket dependencies");
|
|
12055
|
+
} else {
|
|
12056
|
+
aspects.push("cleared ticket dependencies");
|
|
12057
|
+
}
|
|
12058
|
+
}
|
|
12059
|
+
}
|
|
11932
12060
|
if (kind === "ticket" && payload["priority"] !== void 0) {
|
|
11933
12061
|
if (payload["priority"] === null) {
|
|
11934
12062
|
aspects.push("cleared priority");
|
|
@@ -12035,6 +12163,7 @@ var buildAuditLinkMetadata = (evt, githubRepo) => {
|
|
|
12035
12163
|
if (p["status"] !== void 0) meta["status"] = String(p["status"]);
|
|
12036
12164
|
if (p["assignee"] !== void 0) meta["assignee"] = p["assignee"];
|
|
12037
12165
|
if (p["labels"] !== void 0) meta["labels"] = p["labels"];
|
|
12166
|
+
if (p["dependsOn"] !== void 0) meta["dependsOn"] = p["dependsOn"];
|
|
12038
12167
|
if (p["priority"] !== void 0) meta["priority"] = p["priority"];
|
|
12039
12168
|
if (p["size"] !== void 0) meta["size"] = p["size"];
|
|
12040
12169
|
if (p["estimate"] !== void 0) meta["estimate"] = p["estimate"];
|
|
@@ -12102,6 +12231,14 @@ var formatEpicStoryTicketCreated = (evt, entity) => {
|
|
|
12102
12231
|
parts.push("with labels");
|
|
12103
12232
|
}
|
|
12104
12233
|
}
|
|
12234
|
+
if (p["dependsOn"] !== void 0) {
|
|
12235
|
+
const d = normalizeBranchList(p["dependsOn"]);
|
|
12236
|
+
if (d.length > 0 && d.length <= MAX_BRANCH_NAMES_TO_LIST) {
|
|
12237
|
+
parts.push(`depending on (${d.join(", ")})`);
|
|
12238
|
+
} else if (d.length > MAX_BRANCH_NAMES_TO_LIST) {
|
|
12239
|
+
parts.push("with ticket dependencies");
|
|
12240
|
+
}
|
|
12241
|
+
}
|
|
12105
12242
|
if (p["priority"] !== void 0) {
|
|
12106
12243
|
parts.push(`with priority ${quoteStatus(String(p["priority"]))}`);
|
|
12107
12244
|
}
|
|
@@ -12195,6 +12332,18 @@ var formatAuditHumanSentence = (evt) => {
|
|
|
12195
12332
|
bits.push("updated labels");
|
|
12196
12333
|
}
|
|
12197
12334
|
}
|
|
12335
|
+
if (p["dependsOn"] !== void 0) {
|
|
12336
|
+
if (p["dependsOn"] === null) {
|
|
12337
|
+
bits.push("cleared ticket dependencies");
|
|
12338
|
+
} else {
|
|
12339
|
+
const d = normalizeBranchList(p["dependsOn"]);
|
|
12340
|
+
if (d.length > 0 && d.length <= MAX_BRANCH_NAMES_TO_LIST) {
|
|
12341
|
+
bits.push(`dependencies: ${d.join(", ")}`);
|
|
12342
|
+
} else {
|
|
12343
|
+
bits.push("updated ticket dependencies");
|
|
12344
|
+
}
|
|
12345
|
+
}
|
|
12346
|
+
}
|
|
12198
12347
|
if (p["priority"] !== void 0) {
|
|
12199
12348
|
bits.push("updated priority");
|
|
12200
12349
|
}
|
|
@@ -12474,6 +12623,13 @@ var ticketMatchesTicketListQuery = (ticket, projection, query) => {
|
|
|
12474
12623
|
return false;
|
|
12475
12624
|
}
|
|
12476
12625
|
}
|
|
12626
|
+
const dependsOnIncludesId = query.dependsOnIncludesId;
|
|
12627
|
+
if (dependsOnIncludesId !== void 0) {
|
|
12628
|
+
const deps = ticket.dependsOn ?? [];
|
|
12629
|
+
if (!deps.includes(dependsOnIncludesId)) {
|
|
12630
|
+
return false;
|
|
12631
|
+
}
|
|
12632
|
+
}
|
|
12477
12633
|
return true;
|
|
12478
12634
|
};
|
|
12479
12635
|
|
|
@@ -12626,6 +12782,7 @@ var sortTicketRecordsForList = (tickets, field, dir) => [...tickets].sort((x, y)
|
|
|
12626
12782
|
// src/cli/list-projection-summaries.ts
|
|
12627
12783
|
var listActiveEpicSummaries = (projection) => [...projection.epics.values()].filter((e) => !e.deleted).map((e) => ({
|
|
12628
12784
|
id: e.id,
|
|
12785
|
+
number: e.number,
|
|
12629
12786
|
title: e.title,
|
|
12630
12787
|
status: e.status,
|
|
12631
12788
|
createdAt: e.createdAt,
|
|
@@ -12637,6 +12794,7 @@ var listActiveStorySummaries = (projection, options) => [...projection.stories.v
|
|
|
12637
12794
|
(s) => !s.deleted && (options?.epicId === void 0 || s.epicId === options.epicId)
|
|
12638
12795
|
).map((s) => ({
|
|
12639
12796
|
id: s.id,
|
|
12797
|
+
number: s.number,
|
|
12640
12798
|
title: s.title,
|
|
12641
12799
|
epicId: s.epicId,
|
|
12642
12800
|
status: s.status,
|
|
@@ -12669,6 +12827,7 @@ var listActiveTicketSummaries = (projection, options) => {
|
|
|
12669
12827
|
const last = recent !== void 0 && recent.length > 0 ? recent[recent.length - 1] : void 0;
|
|
12670
12828
|
return {
|
|
12671
12829
|
id: t.id,
|
|
12830
|
+
number: t.number,
|
|
12672
12831
|
title: t.title,
|
|
12673
12832
|
status: t.status,
|
|
12674
12833
|
storyId: t.storyId,
|
|
@@ -12679,6 +12838,7 @@ var listActiveTicketSummaries = (projection, options) => {
|
|
|
12679
12838
|
...t.estimate !== void 0 ? { estimate: t.estimate } : {},
|
|
12680
12839
|
...t.startWorkAt !== void 0 ? { startWorkAt: t.startWorkAt } : {},
|
|
12681
12840
|
...t.targetFinishAt !== void 0 ? { targetFinishAt: t.targetFinishAt } : {},
|
|
12841
|
+
...t.dependsOn !== void 0 && t.dependsOn.length > 0 ? { dependsOn: t.dependsOn } : {},
|
|
12682
12842
|
...t.linkedBranches.length > 0 ? { linkedBranches: t.linkedBranches } : {},
|
|
12683
12843
|
...last !== void 0 ? {
|
|
12684
12844
|
lastPrActivity: {
|
|
@@ -12775,6 +12935,17 @@ var inboundTicketPlanningPayloadFromFenceMeta = (meta) => {
|
|
|
12775
12935
|
}
|
|
12776
12936
|
}
|
|
12777
12937
|
}
|
|
12938
|
+
if (Object.prototype.hasOwnProperty.call(meta, "depends_on")) {
|
|
12939
|
+
const v = meta["depends_on"];
|
|
12940
|
+
if (v === null) {
|
|
12941
|
+
out["dependsOn"] = null;
|
|
12942
|
+
} else {
|
|
12943
|
+
const parsed = parseTicketDependsOnFromFenceValue(v);
|
|
12944
|
+
if (parsed !== void 0) {
|
|
12945
|
+
out["dependsOn"] = parsed;
|
|
12946
|
+
}
|
|
12947
|
+
}
|
|
12948
|
+
}
|
|
12778
12949
|
return out;
|
|
12779
12950
|
};
|
|
12780
12951
|
var buildGithubIssueBody = (params) => {
|
|
@@ -12800,6 +12971,9 @@ var buildGithubIssueBody = (params) => {
|
|
|
12800
12971
|
if (p.targetFinishAt !== void 0) {
|
|
12801
12972
|
meta.target_finish_at = p.targetFinishAt;
|
|
12802
12973
|
}
|
|
12974
|
+
if (p.dependsOn !== void 0 && p.dependsOn.length > 0) {
|
|
12975
|
+
meta.depends_on = p.dependsOn;
|
|
12976
|
+
}
|
|
12803
12977
|
}
|
|
12804
12978
|
return `${params.description.trim()}
|
|
12805
12979
|
|
|
@@ -12825,6 +12999,9 @@ var ticketPlanningForGithubIssueBody = (ticket) => {
|
|
|
12825
12999
|
if (ticket.targetFinishAt !== void 0) {
|
|
12826
13000
|
out.targetFinishAt = ticket.targetFinishAt;
|
|
12827
13001
|
}
|
|
13002
|
+
if (ticket.dependsOn !== void 0 && ticket.dependsOn.length > 0) {
|
|
13003
|
+
out.dependsOn = ticket.dependsOn;
|
|
13004
|
+
}
|
|
12828
13005
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
12829
13006
|
};
|
|
12830
13007
|
|
|
@@ -13019,750 +13196,302 @@ var partitionGithubIssuesForImport = (params) => {
|
|
|
13019
13196
|
}
|
|
13020
13197
|
return { candidates, skipped };
|
|
13021
13198
|
};
|
|
13022
|
-
var mergeTicketImportCreatePayload = (ticketId, base, storyId) => {
|
|
13199
|
+
var mergeTicketImportCreatePayload = (ticketId, base, storyId, number) => {
|
|
13023
13200
|
const storyTrimmed = storyId !== void 0 && storyId !== "" ? storyId.trim() : void 0;
|
|
13024
13201
|
const storyPayload = storyTrimmed !== void 0 && storyTrimmed !== "" ? { storyId: storyTrimmed } : {};
|
|
13025
|
-
return { id: ticketId, ...base, ...storyPayload };
|
|
13202
|
+
return { id: ticketId, number, ...base, ...storyPayload };
|
|
13026
13203
|
};
|
|
13027
13204
|
|
|
13028
|
-
// src/
|
|
13029
|
-
var
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
13035
|
-
|
|
13036
|
-
|
|
13037
|
-
|
|
13038
|
-
|
|
13039
|
-
var
|
|
13040
|
-
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
const
|
|
13048
|
-
const
|
|
13049
|
-
const
|
|
13050
|
-
const
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
|
|
13054
|
-
const
|
|
13055
|
-
|
|
13056
|
-
|
|
13205
|
+
// src/lib/github-pr-activity.ts
|
|
13206
|
+
var GITHUB_PR_ACTIVITY_RECENT_CAP = 20;
|
|
13207
|
+
var githubPrActivityKindSchema = external_exports.enum([
|
|
13208
|
+
"opened",
|
|
13209
|
+
"updated",
|
|
13210
|
+
"commented",
|
|
13211
|
+
"reviewed",
|
|
13212
|
+
"merged",
|
|
13213
|
+
"closed",
|
|
13214
|
+
"ready_for_review"
|
|
13215
|
+
]);
|
|
13216
|
+
var githubPrReviewStateSchema = external_exports.enum([
|
|
13217
|
+
"approved",
|
|
13218
|
+
"changes_requested",
|
|
13219
|
+
"commented"
|
|
13220
|
+
]);
|
|
13221
|
+
var buildPrOpenSourceId = (ticketId, prNumber) => `hyper-pm:pr-open:${ticketId}:${prNumber}`;
|
|
13222
|
+
var parseGithubPrActivityPayload = (payload) => {
|
|
13223
|
+
const ticketId = payload["ticketId"];
|
|
13224
|
+
const prRaw = payload["prNumber"];
|
|
13225
|
+
const kindRaw = payload["kind"];
|
|
13226
|
+
const occurredAt = payload["occurredAt"];
|
|
13227
|
+
const sourceId = payload["sourceId"];
|
|
13228
|
+
if (typeof ticketId !== "string" || typeof occurredAt !== "string" || typeof sourceId !== "string") {
|
|
13229
|
+
return void 0;
|
|
13230
|
+
}
|
|
13231
|
+
const prNumber = typeof prRaw === "number" ? prRaw : Number(prRaw);
|
|
13232
|
+
if (!Number.isFinite(prNumber)) return void 0;
|
|
13233
|
+
const kindParsed = githubPrActivityKindSchema.safeParse(kindRaw);
|
|
13234
|
+
if (!kindParsed.success) return void 0;
|
|
13235
|
+
const out = {
|
|
13236
|
+
prNumber,
|
|
13237
|
+
kind: kindParsed.data,
|
|
13238
|
+
occurredAt,
|
|
13239
|
+
sourceId
|
|
13240
|
+
};
|
|
13241
|
+
const url = payload["url"];
|
|
13242
|
+
if (typeof url === "string" && url.length > 0) {
|
|
13243
|
+
out.url = url;
|
|
13244
|
+
}
|
|
13245
|
+
const rs = githubPrReviewStateSchema.safeParse(payload["reviewState"]);
|
|
13246
|
+
if (rs.success) {
|
|
13247
|
+
out.reviewState = rs.data;
|
|
13057
13248
|
}
|
|
13058
13249
|
return out;
|
|
13059
13250
|
};
|
|
13060
13251
|
|
|
13061
|
-
// src/
|
|
13062
|
-
var
|
|
13063
|
-
|
|
13064
|
-
|
|
13065
|
-
|
|
13066
|
-
|
|
13067
|
-
|
|
13068
|
-
|
|
13252
|
+
// src/storage/projection.ts
|
|
13253
|
+
var readOptionalPositiveIntegerFromPayload = (payload, key) => {
|
|
13254
|
+
if (!Object.prototype.hasOwnProperty.call(payload, key)) return void 0;
|
|
13255
|
+
const raw = payload[key];
|
|
13256
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) return void 0;
|
|
13257
|
+
if (!Number.isInteger(raw)) return void 0;
|
|
13258
|
+
if (raw < 1 || raw > Number.MAX_SAFE_INTEGER) return void 0;
|
|
13259
|
+
return raw;
|
|
13069
13260
|
};
|
|
13070
|
-
|
|
13071
|
-
|
|
13072
|
-
|
|
13073
|
-
|
|
13074
|
-
for (let i = 0; i < lines.length; i++) {
|
|
13075
|
-
const line = lines[i]?.trim() ?? "";
|
|
13076
|
-
if (!line) continue;
|
|
13077
|
-
let json;
|
|
13078
|
-
try {
|
|
13079
|
-
json = JSON.parse(line);
|
|
13080
|
-
} catch (e) {
|
|
13081
|
-
issues.push({
|
|
13082
|
-
kind: "invalid-json",
|
|
13083
|
-
line: i + 1,
|
|
13084
|
-
message: e instanceof Error ? e.message : "parse error"
|
|
13085
|
-
});
|
|
13086
|
-
return issues;
|
|
13087
|
-
}
|
|
13088
|
-
const parsed = eventLineSchema.safeParse(json);
|
|
13089
|
-
if (!parsed.success) {
|
|
13090
|
-
issues.push({
|
|
13091
|
-
kind: "invalid-event",
|
|
13092
|
-
line: i + 1,
|
|
13093
|
-
message: parsed.error.message
|
|
13094
|
-
});
|
|
13095
|
-
return issues;
|
|
13096
|
-
}
|
|
13261
|
+
var maxNumberAmongRows = (rows) => {
|
|
13262
|
+
let m = 0;
|
|
13263
|
+
for (const r of rows) {
|
|
13264
|
+
if (r.number > m) m = r.number;
|
|
13097
13265
|
}
|
|
13098
|
-
return
|
|
13266
|
+
return m;
|
|
13099
13267
|
};
|
|
13100
|
-
|
|
13101
|
-
|
|
13102
|
-
var
|
|
13103
|
-
|
|
13104
|
-
|
|
13105
|
-
|
|
13106
|
-
|
|
13107
|
-
|
|
13108
|
-
|
|
13268
|
+
var maxEpicNumberInProjection = (projection) => maxNumberAmongRows(projection.epics.values());
|
|
13269
|
+
var maxStoryNumberInProjection = (projection) => maxNumberAmongRows(projection.stories.values());
|
|
13270
|
+
var maxTicketNumberInProjection = (projection) => maxNumberAmongRows(projection.tickets.values());
|
|
13271
|
+
var nextEpicNumberForCreate = (projection) => maxEpicNumberInProjection(projection) + 1;
|
|
13272
|
+
var nextStoryNumberForCreate = (projection) => maxStoryNumberInProjection(projection) + 1;
|
|
13273
|
+
var nextTicketNumberForCreate = (projection) => maxTicketNumberInProjection(projection) + 1;
|
|
13274
|
+
var resolveWorkItemCreateNumber = (projection, kind, payload) => {
|
|
13275
|
+
const explicit = readOptionalPositiveIntegerFromPayload(payload, "number");
|
|
13276
|
+
if (explicit !== void 0) {
|
|
13277
|
+
return explicit;
|
|
13278
|
+
}
|
|
13279
|
+
return (kind === "epic" ? maxEpicNumberInProjection(projection) : kind === "story" ? maxStoryNumberInProjection(projection) : maxTicketNumberInProjection(projection)) + 1;
|
|
13109
13280
|
};
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
|
|
13113
|
-
|
|
13114
|
-
|
|
13115
|
-
|
|
13281
|
+
var emptyProjection = () => ({
|
|
13282
|
+
epics: /* @__PURE__ */ new Map(),
|
|
13283
|
+
stories: /* @__PURE__ */ new Map(),
|
|
13284
|
+
tickets: /* @__PURE__ */ new Map()
|
|
13285
|
+
});
|
|
13286
|
+
var parsePrRefs = (body) => {
|
|
13287
|
+
const out = /* @__PURE__ */ new Set();
|
|
13288
|
+
const re = /\b(?:Closes|Refs|Fixes)\s+#(\d+)\b/gi;
|
|
13289
|
+
let m = re.exec(body);
|
|
13290
|
+
while (m !== null) {
|
|
13291
|
+
out.add(Number(m[1]));
|
|
13292
|
+
m = re.exec(body);
|
|
13293
|
+
}
|
|
13294
|
+
return [...out];
|
|
13116
13295
|
};
|
|
13117
|
-
var
|
|
13118
|
-
|
|
13119
|
-
|
|
13120
|
-
|
|
13121
|
-
|
|
13122
|
-
return
|
|
13296
|
+
var applyTicketAssigneeFromPayload = (row, payload) => {
|
|
13297
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "assignee")) return;
|
|
13298
|
+
const v = payload["assignee"];
|
|
13299
|
+
if (v === null) {
|
|
13300
|
+
delete row.assignee;
|
|
13301
|
+
return;
|
|
13123
13302
|
}
|
|
13303
|
+
if (typeof v !== "string") return;
|
|
13304
|
+
const n = normalizeGithubLogin(v);
|
|
13305
|
+
if (n === "") delete row.assignee;
|
|
13306
|
+
else row.assignee = n;
|
|
13124
13307
|
};
|
|
13125
|
-
var
|
|
13126
|
-
|
|
13127
|
-
|
|
13128
|
-
for (const block of blocks) {
|
|
13129
|
-
const lines = block.split("\n");
|
|
13130
|
-
let worktreePath;
|
|
13131
|
-
let branch;
|
|
13132
|
-
for (const line of lines) {
|
|
13133
|
-
if (line.startsWith("worktree ")) {
|
|
13134
|
-
worktreePath = line.slice("worktree ".length);
|
|
13135
|
-
} else if (line.startsWith("branch ")) {
|
|
13136
|
-
branch = line.slice("branch ".length);
|
|
13137
|
-
}
|
|
13138
|
-
}
|
|
13139
|
-
if (worktreePath !== void 0 && branch === wantRef) {
|
|
13140
|
-
return worktreePath;
|
|
13141
|
-
}
|
|
13308
|
+
var storyIdFromTicketCreatedPayload = (payload) => {
|
|
13309
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "storyId")) {
|
|
13310
|
+
return null;
|
|
13142
13311
|
}
|
|
13143
|
-
|
|
13312
|
+
const v = payload["storyId"];
|
|
13313
|
+
if (v === null) return null;
|
|
13314
|
+
if (typeof v !== "string") return null;
|
|
13315
|
+
const t = v.trim();
|
|
13316
|
+
return t === "" ? null : t;
|
|
13144
13317
|
};
|
|
13145
|
-
var
|
|
13146
|
-
|
|
13147
|
-
const
|
|
13148
|
-
|
|
13149
|
-
|
|
13150
|
-
|
|
13151
|
-
]);
|
|
13152
|
-
const listedPath = parseDataBranchWorktreeFromPorcelain(
|
|
13153
|
-
listOut,
|
|
13154
|
-
opts.dataBranch
|
|
13155
|
-
);
|
|
13156
|
-
if (listedPath !== void 0 && await pathExists(listedPath)) {
|
|
13157
|
-
const dispose2 = async () => {
|
|
13158
|
-
return;
|
|
13159
|
-
};
|
|
13160
|
-
return { worktreePath: listedPath, dispose: dispose2 };
|
|
13161
|
-
}
|
|
13162
|
-
const worktreePath = (0, import_node_path3.join)(
|
|
13163
|
-
opts.tmpBase,
|
|
13164
|
-
`hyper-pm-worktree-${ulid().toLowerCase()}`
|
|
13165
|
-
);
|
|
13166
|
-
await ensureDir(opts.tmpBase);
|
|
13167
|
-
try {
|
|
13168
|
-
await opts.runGit(opts.repoRoot, [
|
|
13169
|
-
"worktree",
|
|
13170
|
-
"add",
|
|
13171
|
-
worktreePath,
|
|
13172
|
-
opts.dataBranch
|
|
13173
|
-
]);
|
|
13174
|
-
} catch (err) {
|
|
13175
|
-
await (0, import_promises3.rm)(worktreePath, { recursive: true, force: true }).catch(() => {
|
|
13176
|
-
});
|
|
13177
|
-
throw err;
|
|
13178
|
-
}
|
|
13179
|
-
const dispose = async () => {
|
|
13180
|
-
if (opts.keepWorktree) {
|
|
13181
|
-
return;
|
|
13182
|
-
}
|
|
13183
|
-
await opts.runGit(opts.repoRoot, ["worktree", "remove", "--force", worktreePath]).catch(() => {
|
|
13184
|
-
});
|
|
13185
|
-
await (0, import_promises3.rm)(worktreePath, { recursive: true, force: true }).catch(() => {
|
|
13186
|
-
});
|
|
13187
|
-
};
|
|
13188
|
-
return { worktreePath, dispose };
|
|
13189
|
-
};
|
|
13190
|
-
|
|
13191
|
-
// src/git/find-git-root.ts
|
|
13192
|
-
var findGitRoot = async (cwd, deps) => {
|
|
13193
|
-
const { stdout } = await deps.runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
13194
|
-
return stdout;
|
|
13195
|
-
};
|
|
13196
|
-
|
|
13197
|
-
// src/git/init-orphan-data-branch.ts
|
|
13198
|
-
var import_promises4 = require("node:fs/promises");
|
|
13199
|
-
var import_node_path4 = require("node:path");
|
|
13200
|
-
var initOrphanDataBranchInWorktree = async (opts) => {
|
|
13201
|
-
const { stdout: tip } = await opts.runGit(opts.repoRoot, [
|
|
13202
|
-
"rev-parse",
|
|
13203
|
-
"HEAD"
|
|
13204
|
-
]);
|
|
13205
|
-
const tipCommit = tip.trim();
|
|
13206
|
-
await opts.runGit(opts.repoRoot, [
|
|
13207
|
-
"worktree",
|
|
13208
|
-
"add",
|
|
13209
|
-
opts.worktreePath,
|
|
13210
|
-
tipCommit
|
|
13211
|
-
]);
|
|
13212
|
-
await opts.runGit(opts.worktreePath, [
|
|
13213
|
-
"checkout",
|
|
13214
|
-
"--orphan",
|
|
13215
|
-
opts.dataBranch
|
|
13216
|
-
]);
|
|
13217
|
-
await opts.runGit(opts.worktreePath, ["rm", "-rf", "."]).catch(() => {
|
|
13218
|
-
});
|
|
13219
|
-
const marker = (0, import_node_path4.join)(opts.worktreePath, "README.hyper-pm.md");
|
|
13220
|
-
await (0, import_promises4.writeFile)(
|
|
13221
|
-
marker,
|
|
13222
|
-
"# hyper-pm data branch\n\nAppend-only events live under `events/`.\n",
|
|
13223
|
-
"utf8"
|
|
13224
|
-
);
|
|
13225
|
-
await opts.runGit(opts.worktreePath, ["add", "."]);
|
|
13226
|
-
await opts.runGit(opts.worktreePath, [
|
|
13227
|
-
"commit",
|
|
13228
|
-
"-m",
|
|
13229
|
-
"init hyper-pm data branch"
|
|
13230
|
-
]);
|
|
13231
|
-
};
|
|
13232
|
-
|
|
13233
|
-
// src/git/pick-unique-local-branch-name.ts
|
|
13234
|
-
var DEFAULT_MAX_SUFFIX = 1e3;
|
|
13235
|
-
var localBranchRefExists = async (repoRoot, name, git) => {
|
|
13236
|
-
try {
|
|
13237
|
-
await git(repoRoot, ["show-ref", "--verify", `refs/heads/${name}`]);
|
|
13238
|
-
return true;
|
|
13239
|
-
} catch {
|
|
13240
|
-
return false;
|
|
13318
|
+
var applyTicketStoryIdFromPayload = (row, payload) => {
|
|
13319
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "storyId")) return;
|
|
13320
|
+
const v = payload["storyId"];
|
|
13321
|
+
if (v === null) {
|
|
13322
|
+
row.storyId = null;
|
|
13323
|
+
return;
|
|
13241
13324
|
}
|
|
13325
|
+
if (typeof v !== "string") return;
|
|
13326
|
+
const t = v.trim();
|
|
13327
|
+
row.storyId = t === "" ? null : t;
|
|
13242
13328
|
};
|
|
13243
|
-
var
|
|
13244
|
-
|
|
13245
|
-
|
|
13246
|
-
for (let n = 1; n <= max; n += 1) {
|
|
13247
|
-
const raw = n === 1 ? preferredBase : `${preferredBase}-${n}`;
|
|
13248
|
-
const norm = normalizeTicketBranchName(raw);
|
|
13249
|
-
if (norm === void 0) {
|
|
13250
|
-
continue;
|
|
13251
|
-
}
|
|
13252
|
-
if (!await localBranchRefExists(repoRoot, norm, git)) {
|
|
13253
|
-
return { branch: norm, preferred: preferredBase };
|
|
13254
|
-
}
|
|
13329
|
+
var linkedBranchesFromTicketCreatedPayload = (payload) => {
|
|
13330
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "branches")) {
|
|
13331
|
+
return [];
|
|
13255
13332
|
}
|
|
13256
|
-
|
|
13257
|
-
`Could not allocate a free local branch name from ${JSON.stringify(preferredBase)} (tried suffixes up to -${String(max)})`
|
|
13258
|
-
);
|
|
13333
|
+
return normalizeTicketBranchListFromPayloadValue(payload["branches"]);
|
|
13259
13334
|
};
|
|
13260
|
-
|
|
13261
|
-
|
|
13262
|
-
|
|
13263
|
-
|
|
13264
|
-
|
|
13265
|
-
} catch {
|
|
13266
|
-
throw new Error(`Invalid or ambiguous --from ref: ${ref}`);
|
|
13267
|
-
}
|
|
13335
|
+
var applyTicketBranchesFromUpdatePayload = (row, payload) => {
|
|
13336
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "branches")) return;
|
|
13337
|
+
const v = payload["branches"];
|
|
13338
|
+
if (!Array.isArray(v)) return;
|
|
13339
|
+
row.linkedBranches = normalizeTicketBranchListFromPayloadValue(v);
|
|
13268
13340
|
};
|
|
13269
|
-
var
|
|
13270
|
-
|
|
13271
|
-
|
|
13272
|
-
|
|
13273
|
-
|
|
13274
|
-
if (target !== "") {
|
|
13275
|
-
await git(repoRoot, ["rev-parse", "-q", "--verify", target]);
|
|
13276
|
-
return target;
|
|
13341
|
+
var applyTicketPlanningFieldsFromCreatePayload = (row, payload) => {
|
|
13342
|
+
if (Object.prototype.hasOwnProperty.call(payload, "labels")) {
|
|
13343
|
+
const v = ticketLabelsFromPayloadValue(payload["labels"]);
|
|
13344
|
+
if (v !== void 0 && v.length > 0) {
|
|
13345
|
+
row.labels = v;
|
|
13277
13346
|
}
|
|
13278
|
-
} catch {
|
|
13279
13347
|
}
|
|
13280
|
-
|
|
13281
|
-
|
|
13282
|
-
|
|
13283
|
-
return head;
|
|
13284
|
-
} catch {
|
|
13285
|
-
}
|
|
13348
|
+
const pr = readTicketPriorityPatch(payload);
|
|
13349
|
+
if (typeof pr === "string") {
|
|
13350
|
+
row.priority = pr;
|
|
13286
13351
|
}
|
|
13287
|
-
|
|
13288
|
-
|
|
13289
|
-
|
|
13290
|
-
} catch {
|
|
13352
|
+
const sz = readTicketSizePatch(payload);
|
|
13353
|
+
if (typeof sz === "string") {
|
|
13354
|
+
row.size = sz;
|
|
13291
13355
|
}
|
|
13292
|
-
|
|
13293
|
-
|
|
13294
|
-
|
|
13295
|
-
};
|
|
13296
|
-
|
|
13297
|
-
// src/git/list-repo-commit-authors.ts
|
|
13298
|
-
var listRepoCommitAuthors = async (repoRoot, git) => {
|
|
13299
|
-
try {
|
|
13300
|
-
const { stdout } = await git(repoRoot, [
|
|
13301
|
-
"-c",
|
|
13302
|
-
"log.showSignature=false",
|
|
13303
|
-
"log",
|
|
13304
|
-
"--all",
|
|
13305
|
-
"--format=%an%x1f%ae"
|
|
13306
|
-
]);
|
|
13307
|
-
if (stdout === "") return [];
|
|
13308
|
-
const lines = stdout.split("\n").filter((l) => l.length > 0);
|
|
13309
|
-
const seenEmail = /* @__PURE__ */ new Set();
|
|
13310
|
-
const out = [];
|
|
13311
|
-
for (const line of lines) {
|
|
13312
|
-
const sep2 = line.indexOf("");
|
|
13313
|
-
if (sep2 <= 0) continue;
|
|
13314
|
-
const name = line.slice(0, sep2).trim();
|
|
13315
|
-
const email = line.slice(sep2 + 1).trim();
|
|
13316
|
-
if (email === "") continue;
|
|
13317
|
-
const key = email.toLowerCase();
|
|
13318
|
-
if (seenEmail.has(key)) continue;
|
|
13319
|
-
seenEmail.add(key);
|
|
13320
|
-
const loginGuess = guessGithubLoginFromContact(name, email);
|
|
13321
|
-
out.push(
|
|
13322
|
-
loginGuess !== void 0 ? { name, email, loginGuess } : { name, email }
|
|
13323
|
-
);
|
|
13324
|
-
}
|
|
13325
|
-
return out;
|
|
13326
|
-
} catch {
|
|
13327
|
-
return [];
|
|
13356
|
+
const est = readTicketEstimatePatch(payload);
|
|
13357
|
+
if (typeof est === "number") {
|
|
13358
|
+
row.estimate = est;
|
|
13328
13359
|
}
|
|
13329
|
-
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
var parseGithubOwnerRepoFromRemoteUrl = (rawUrl) => {
|
|
13333
|
-
const trimmed = rawUrl.trim();
|
|
13334
|
-
if (!trimmed) {
|
|
13335
|
-
return void 0;
|
|
13360
|
+
const sw = readTicketIsoInstantPatch(payload, "startWorkAt");
|
|
13361
|
+
if (typeof sw === "string") {
|
|
13362
|
+
row.startWorkAt = sw;
|
|
13336
13363
|
}
|
|
13337
|
-
const
|
|
13338
|
-
if (
|
|
13339
|
-
|
|
13364
|
+
const tf = readTicketIsoInstantPatch(payload, "targetFinishAt");
|
|
13365
|
+
if (typeof tf === "string") {
|
|
13366
|
+
row.targetFinishAt = tf;
|
|
13340
13367
|
}
|
|
13341
|
-
|
|
13342
|
-
const
|
|
13343
|
-
|
|
13344
|
-
|
|
13345
|
-
if (host !== "github.com" && host !== "www.github.com") {
|
|
13346
|
-
return void 0;
|
|
13368
|
+
if (Object.prototype.hasOwnProperty.call(payload, "dependsOn")) {
|
|
13369
|
+
const v = parseTicketDependsOnFromPayloadValue(payload["dependsOn"]);
|
|
13370
|
+
if (v !== void 0 && v.length > 0) {
|
|
13371
|
+
row.dependsOn = v;
|
|
13347
13372
|
}
|
|
13348
|
-
return slugFromGithubPath(u.pathname);
|
|
13349
|
-
} catch {
|
|
13350
|
-
return void 0;
|
|
13351
13373
|
}
|
|
13352
13374
|
};
|
|
13353
|
-
var
|
|
13354
|
-
|
|
13355
|
-
|
|
13356
|
-
|
|
13375
|
+
var applyTicketPlanningFieldsFromUpdatePayload = (row, payload) => {
|
|
13376
|
+
if (Object.prototype.hasOwnProperty.call(payload, "labels")) {
|
|
13377
|
+
if (payload["labels"] === null) {
|
|
13378
|
+
delete row.labels;
|
|
13379
|
+
} else {
|
|
13380
|
+
const v = ticketLabelsFromPayloadValue(payload["labels"]);
|
|
13381
|
+
if (v !== void 0) {
|
|
13382
|
+
if (v.length === 0) {
|
|
13383
|
+
delete row.labels;
|
|
13384
|
+
} else {
|
|
13385
|
+
row.labels = v;
|
|
13386
|
+
}
|
|
13387
|
+
}
|
|
13388
|
+
}
|
|
13357
13389
|
}
|
|
13358
|
-
const
|
|
13359
|
-
|
|
13360
|
-
|
|
13361
|
-
|
|
13390
|
+
const pr = readTicketPriorityPatch(payload);
|
|
13391
|
+
if (pr === null) {
|
|
13392
|
+
delete row.priority;
|
|
13393
|
+
} else if (typeof pr === "string") {
|
|
13394
|
+
row.priority = pr;
|
|
13362
13395
|
}
|
|
13363
|
-
|
|
13364
|
-
|
|
13365
|
-
|
|
13366
|
-
|
|
13367
|
-
|
|
13368
|
-
try {
|
|
13369
|
-
const { stdout } = await params.runGit(params.repoRoot, [
|
|
13370
|
-
"remote",
|
|
13371
|
-
"get-url",
|
|
13372
|
-
params.remote
|
|
13373
|
-
]);
|
|
13374
|
-
return parseGithubOwnerRepoFromRemoteUrl(stdout);
|
|
13375
|
-
} catch {
|
|
13376
|
-
return void 0;
|
|
13396
|
+
const sz = readTicketSizePatch(payload);
|
|
13397
|
+
if (sz === null) {
|
|
13398
|
+
delete row.size;
|
|
13399
|
+
} else if (typeof sz === "string") {
|
|
13400
|
+
row.size = sz;
|
|
13377
13401
|
}
|
|
13378
|
-
|
|
13379
|
-
|
|
13380
|
-
|
|
13381
|
-
|
|
13382
|
-
|
|
13383
|
-
var tryReadGitConfigUserName = async (cwd, runGit2) => {
|
|
13384
|
-
try {
|
|
13385
|
-
const { stdout } = await runGit2(cwd, ["config", "--get", "user.name"]);
|
|
13386
|
-
return stdout.trim();
|
|
13387
|
-
} catch {
|
|
13388
|
-
return "";
|
|
13402
|
+
const est = readTicketEstimatePatch(payload);
|
|
13403
|
+
if (est === null) {
|
|
13404
|
+
delete row.estimate;
|
|
13405
|
+
} else if (typeof est === "number") {
|
|
13406
|
+
row.estimate = est;
|
|
13389
13407
|
}
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
} catch {
|
|
13396
|
-
return "";
|
|
13408
|
+
const sw = readTicketIsoInstantPatch(payload, "startWorkAt");
|
|
13409
|
+
if (sw === null) {
|
|
13410
|
+
delete row.startWorkAt;
|
|
13411
|
+
} else if (typeof sw === "string") {
|
|
13412
|
+
row.startWorkAt = sw;
|
|
13397
13413
|
}
|
|
13398
|
-
|
|
13399
|
-
|
|
13400
|
-
|
|
13401
|
-
|
|
13402
|
-
|
|
13403
|
-
|
|
13404
|
-
|
|
13405
|
-
|
|
13406
|
-
|
|
13407
|
-
|
|
13408
|
-
|
|
13409
|
-
|
|
13410
|
-
|
|
13411
|
-
|
|
13412
|
-
|
|
13414
|
+
const tf = readTicketIsoInstantPatch(payload, "targetFinishAt");
|
|
13415
|
+
if (tf === null) {
|
|
13416
|
+
delete row.targetFinishAt;
|
|
13417
|
+
} else if (typeof tf === "string") {
|
|
13418
|
+
row.targetFinishAt = tf;
|
|
13419
|
+
}
|
|
13420
|
+
if (Object.prototype.hasOwnProperty.call(payload, "dependsOn")) {
|
|
13421
|
+
if (payload["dependsOn"] === null) {
|
|
13422
|
+
delete row.dependsOn;
|
|
13423
|
+
} else {
|
|
13424
|
+
const v = parseTicketDependsOnFromPayloadValue(payload["dependsOn"]);
|
|
13425
|
+
if (v !== void 0) {
|
|
13426
|
+
if (v.length === 0) {
|
|
13427
|
+
delete row.dependsOn;
|
|
13428
|
+
} else {
|
|
13429
|
+
row.dependsOn = v;
|
|
13430
|
+
}
|
|
13431
|
+
}
|
|
13432
|
+
}
|
|
13413
13433
|
}
|
|
13414
|
-
const collapsed = raw.replace(/\s+/g, " ");
|
|
13415
|
-
const suffix = collapsed.length > maxActorSuffixLen ? `${collapsed.slice(0, maxActorSuffixLen - 1)}\u2026` : collapsed;
|
|
13416
|
-
return `${base} (${suffix})`;
|
|
13417
|
-
};
|
|
13418
|
-
var commitDataWorktreeIfNeeded = async (worktreePath, message, runGit2, opts) => {
|
|
13419
|
-
const { stdout } = await runGit2(worktreePath, ["status", "--porcelain"]);
|
|
13420
|
-
if (!stdout.trim()) return;
|
|
13421
|
-
await runGit2(worktreePath, ["add", "."]);
|
|
13422
|
-
const authorEnv = opts?.authorEnv ?? env;
|
|
13423
|
-
const { name, email } = await resolveEffectiveGitAuthorForDataCommit(
|
|
13424
|
-
worktreePath,
|
|
13425
|
-
runGit2,
|
|
13426
|
-
authorEnv
|
|
13427
|
-
);
|
|
13428
|
-
await runGit2(worktreePath, [
|
|
13429
|
-
"-c",
|
|
13430
|
-
`user.name=${name}`,
|
|
13431
|
-
"-c",
|
|
13432
|
-
`user.email=${email}`,
|
|
13433
|
-
"commit",
|
|
13434
|
-
"-m",
|
|
13435
|
-
message
|
|
13436
|
-
]);
|
|
13437
13434
|
};
|
|
13438
|
-
|
|
13439
|
-
|
|
13440
|
-
|
|
13441
|
-
|
|
13442
|
-
|
|
13443
|
-
return line.length > 0 ? line : "git error";
|
|
13435
|
+
var applyCreatedAudit = (row, evt) => {
|
|
13436
|
+
row.createdAt = evt.ts;
|
|
13437
|
+
row.createdBy = evt.actor;
|
|
13438
|
+
row.updatedAt = evt.ts;
|
|
13439
|
+
row.updatedBy = evt.actor;
|
|
13444
13440
|
};
|
|
13445
|
-
var
|
|
13446
|
-
|
|
13447
|
-
|
|
13448
|
-
} catch (e) {
|
|
13449
|
-
return {
|
|
13450
|
-
status: "skipped_no_remote",
|
|
13451
|
-
detail: firstLineFromUnknown(e)
|
|
13452
|
-
};
|
|
13453
|
-
}
|
|
13454
|
-
try {
|
|
13455
|
-
await runGit2(worktreePath, ["push", "-u", remote, branch]);
|
|
13456
|
-
return { status: "pushed" };
|
|
13457
|
-
} catch (e) {
|
|
13458
|
-
return {
|
|
13459
|
-
status: "failed",
|
|
13460
|
-
detail: firstLineFromUnknown(e)
|
|
13461
|
-
};
|
|
13462
|
-
}
|
|
13441
|
+
var applyLastUpdate = (row, evt) => {
|
|
13442
|
+
row.updatedAt = evt.ts;
|
|
13443
|
+
row.updatedBy = evt.actor;
|
|
13463
13444
|
};
|
|
13464
|
-
|
|
13465
|
-
|
|
13466
|
-
|
|
13467
|
-
|
|
13468
|
-
|
|
13469
|
-
|
|
13470
|
-
var nextEventRelPath = (now, deps) => {
|
|
13471
|
-
const y = String(now.getUTCFullYear());
|
|
13472
|
-
const m = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
13473
|
-
const nextId = deps?.nextId ?? (() => ulid().toLowerCase());
|
|
13474
|
-
const part = `part-${nextId()}`;
|
|
13475
|
-
return `events/${y}/${m}/${part}.jsonl`;
|
|
13445
|
+
var applyStatusIfChanged = (row, evt, nextStatus) => {
|
|
13446
|
+
if (row.status === nextStatus) return false;
|
|
13447
|
+
row.status = nextStatus;
|
|
13448
|
+
row.statusChangedAt = evt.ts;
|
|
13449
|
+
row.statusChangedBy = evt.actor;
|
|
13450
|
+
return true;
|
|
13476
13451
|
};
|
|
13477
|
-
|
|
13478
|
-
|
|
13479
|
-
|
|
13480
|
-
|
|
13481
|
-
|
|
13452
|
+
var appendTicketCommentFromEvent = (ticket, evt) => {
|
|
13453
|
+
const list = ticket.comments ?? (ticket.comments = []);
|
|
13454
|
+
list.push({
|
|
13455
|
+
id: evt.id,
|
|
13456
|
+
body: String(evt.payload["body"] ?? ""),
|
|
13457
|
+
createdAt: evt.ts,
|
|
13458
|
+
createdBy: evt.actor
|
|
13482
13459
|
});
|
|
13483
|
-
|
|
13484
|
-
await (0, import_promises5.mkdir)((0, import_node_path5.dirname)(abs), { recursive: true });
|
|
13485
|
-
await (0, import_promises5.appendFile)(abs, `${JSON.stringify(event)}
|
|
13486
|
-
`, "utf8");
|
|
13487
|
-
return rel;
|
|
13460
|
+
applyLastUpdate(ticket, evt);
|
|
13488
13461
|
};
|
|
13489
|
-
|
|
13490
|
-
|
|
13491
|
-
|
|
13492
|
-
|
|
13493
|
-
|
|
13494
|
-
|
|
13495
|
-
|
|
13496
|
-
|
|
13497
|
-
|
|
13498
|
-
if (e.isDirectory()) {
|
|
13499
|
-
out.push(...await listJsonlFiles(abs, root));
|
|
13500
|
-
} else if (e.isFile() && e.name.endsWith(".jsonl")) {
|
|
13501
|
-
out.push((0, import_node_path6.relative)(root, abs).split("\\").join("/"));
|
|
13502
|
-
}
|
|
13462
|
+
var resolveInboundTicketStatusFromPayload = (ticket, payload) => {
|
|
13463
|
+
const explicit = parseWorkItemStatus(payload["status"]);
|
|
13464
|
+
if (explicit !== void 0) return explicit;
|
|
13465
|
+
const legacySt = payload["state"];
|
|
13466
|
+
if (legacySt === "open" || legacySt === "closed") {
|
|
13467
|
+
return resolveTicketInboundStatus({
|
|
13468
|
+
issueState: legacySt,
|
|
13469
|
+
currentStatus: ticket.status
|
|
13470
|
+
});
|
|
13503
13471
|
}
|
|
13504
|
-
return
|
|
13472
|
+
return void 0;
|
|
13505
13473
|
};
|
|
13506
|
-
var
|
|
13507
|
-
|
|
13508
|
-
|
|
13509
|
-
|
|
13510
|
-
|
|
13511
|
-
|
|
13512
|
-
|
|
13513
|
-
|
|
13514
|
-
|
|
13515
|
-
|
|
13516
|
-
|
|
13517
|
-
|
|
13518
|
-
|
|
13519
|
-
|
|
13520
|
-
|
|
13521
|
-
|
|
13522
|
-
|
|
13523
|
-
};
|
|
13524
|
-
|
|
13525
|
-
|
|
13526
|
-
|
|
13527
|
-
var githubPrActivityKindSchema = external_exports.enum([
|
|
13528
|
-
"opened",
|
|
13529
|
-
"updated",
|
|
13530
|
-
"commented",
|
|
13531
|
-
"reviewed",
|
|
13532
|
-
"merged",
|
|
13533
|
-
"closed",
|
|
13534
|
-
"ready_for_review"
|
|
13535
|
-
]);
|
|
13536
|
-
var githubPrReviewStateSchema = external_exports.enum([
|
|
13537
|
-
"approved",
|
|
13538
|
-
"changes_requested",
|
|
13539
|
-
"commented"
|
|
13540
|
-
]);
|
|
13541
|
-
var buildPrOpenSourceId = (ticketId, prNumber) => `hyper-pm:pr-open:${ticketId}:${prNumber}`;
|
|
13542
|
-
var parseGithubPrActivityPayload = (payload) => {
|
|
13543
|
-
const ticketId = payload["ticketId"];
|
|
13544
|
-
const prRaw = payload["prNumber"];
|
|
13545
|
-
const kindRaw = payload["kind"];
|
|
13546
|
-
const occurredAt = payload["occurredAt"];
|
|
13547
|
-
const sourceId = payload["sourceId"];
|
|
13548
|
-
if (typeof ticketId !== "string" || typeof occurredAt !== "string" || typeof sourceId !== "string") {
|
|
13549
|
-
return void 0;
|
|
13550
|
-
}
|
|
13551
|
-
const prNumber = typeof prRaw === "number" ? prRaw : Number(prRaw);
|
|
13552
|
-
if (!Number.isFinite(prNumber)) return void 0;
|
|
13553
|
-
const kindParsed = githubPrActivityKindSchema.safeParse(kindRaw);
|
|
13554
|
-
if (!kindParsed.success) return void 0;
|
|
13555
|
-
const out = {
|
|
13556
|
-
prNumber,
|
|
13557
|
-
kind: kindParsed.data,
|
|
13558
|
-
occurredAt,
|
|
13559
|
-
sourceId
|
|
13560
|
-
};
|
|
13561
|
-
const url = payload["url"];
|
|
13562
|
-
if (typeof url === "string" && url.length > 0) {
|
|
13563
|
-
out.url = url;
|
|
13564
|
-
}
|
|
13565
|
-
const rs = githubPrReviewStateSchema.safeParse(payload["reviewState"]);
|
|
13566
|
-
if (rs.success) {
|
|
13567
|
-
out.reviewState = rs.data;
|
|
13568
|
-
}
|
|
13569
|
-
return out;
|
|
13570
|
-
};
|
|
13571
|
-
|
|
13572
|
-
// src/storage/projection.ts
|
|
13573
|
-
var emptyProjection = () => ({
|
|
13574
|
-
epics: /* @__PURE__ */ new Map(),
|
|
13575
|
-
stories: /* @__PURE__ */ new Map(),
|
|
13576
|
-
tickets: /* @__PURE__ */ new Map()
|
|
13577
|
-
});
|
|
13578
|
-
var parsePrRefs = (body) => {
|
|
13579
|
-
const out = /* @__PURE__ */ new Set();
|
|
13580
|
-
const re = /\b(?:Closes|Refs|Fixes)\s+#(\d+)\b/gi;
|
|
13581
|
-
let m = re.exec(body);
|
|
13582
|
-
while (m !== null) {
|
|
13583
|
-
out.add(Number(m[1]));
|
|
13584
|
-
m = re.exec(body);
|
|
13585
|
-
}
|
|
13586
|
-
return [...out];
|
|
13587
|
-
};
|
|
13588
|
-
var applyTicketAssigneeFromPayload = (row, payload) => {
|
|
13589
|
-
if (!Object.prototype.hasOwnProperty.call(payload, "assignee")) return;
|
|
13590
|
-
const v = payload["assignee"];
|
|
13591
|
-
if (v === null) {
|
|
13592
|
-
delete row.assignee;
|
|
13593
|
-
return;
|
|
13594
|
-
}
|
|
13595
|
-
if (typeof v !== "string") return;
|
|
13596
|
-
const n = normalizeGithubLogin(v);
|
|
13597
|
-
if (n === "") delete row.assignee;
|
|
13598
|
-
else row.assignee = n;
|
|
13599
|
-
};
|
|
13600
|
-
var storyIdFromTicketCreatedPayload = (payload) => {
|
|
13601
|
-
if (!Object.prototype.hasOwnProperty.call(payload, "storyId")) {
|
|
13602
|
-
return null;
|
|
13603
|
-
}
|
|
13604
|
-
const v = payload["storyId"];
|
|
13605
|
-
if (v === null) return null;
|
|
13606
|
-
if (typeof v !== "string") return null;
|
|
13607
|
-
const t = v.trim();
|
|
13608
|
-
return t === "" ? null : t;
|
|
13609
|
-
};
|
|
13610
|
-
var applyTicketStoryIdFromPayload = (row, payload) => {
|
|
13611
|
-
if (!Object.prototype.hasOwnProperty.call(payload, "storyId")) return;
|
|
13612
|
-
const v = payload["storyId"];
|
|
13613
|
-
if (v === null) {
|
|
13614
|
-
row.storyId = null;
|
|
13615
|
-
return;
|
|
13616
|
-
}
|
|
13617
|
-
if (typeof v !== "string") return;
|
|
13618
|
-
const t = v.trim();
|
|
13619
|
-
row.storyId = t === "" ? null : t;
|
|
13620
|
-
};
|
|
13621
|
-
var linkedBranchesFromTicketCreatedPayload = (payload) => {
|
|
13622
|
-
if (!Object.prototype.hasOwnProperty.call(payload, "branches")) {
|
|
13623
|
-
return [];
|
|
13624
|
-
}
|
|
13625
|
-
return normalizeTicketBranchListFromPayloadValue(payload["branches"]);
|
|
13626
|
-
};
|
|
13627
|
-
var applyTicketBranchesFromUpdatePayload = (row, payload) => {
|
|
13628
|
-
if (!Object.prototype.hasOwnProperty.call(payload, "branches")) return;
|
|
13629
|
-
const v = payload["branches"];
|
|
13630
|
-
if (!Array.isArray(v)) return;
|
|
13631
|
-
row.linkedBranches = normalizeTicketBranchListFromPayloadValue(v);
|
|
13632
|
-
};
|
|
13633
|
-
var applyTicketPlanningFieldsFromCreatePayload = (row, payload) => {
|
|
13634
|
-
if (Object.prototype.hasOwnProperty.call(payload, "labels")) {
|
|
13635
|
-
const v = ticketLabelsFromPayloadValue(payload["labels"]);
|
|
13636
|
-
if (v !== void 0 && v.length > 0) {
|
|
13637
|
-
row.labels = v;
|
|
13638
|
-
}
|
|
13639
|
-
}
|
|
13640
|
-
const pr = readTicketPriorityPatch(payload);
|
|
13641
|
-
if (typeof pr === "string") {
|
|
13642
|
-
row.priority = pr;
|
|
13643
|
-
}
|
|
13644
|
-
const sz = readTicketSizePatch(payload);
|
|
13645
|
-
if (typeof sz === "string") {
|
|
13646
|
-
row.size = sz;
|
|
13647
|
-
}
|
|
13648
|
-
const est = readTicketEstimatePatch(payload);
|
|
13649
|
-
if (typeof est === "number") {
|
|
13650
|
-
row.estimate = est;
|
|
13651
|
-
}
|
|
13652
|
-
const sw = readTicketIsoInstantPatch(payload, "startWorkAt");
|
|
13653
|
-
if (typeof sw === "string") {
|
|
13654
|
-
row.startWorkAt = sw;
|
|
13655
|
-
}
|
|
13656
|
-
const tf = readTicketIsoInstantPatch(payload, "targetFinishAt");
|
|
13657
|
-
if (typeof tf === "string") {
|
|
13658
|
-
row.targetFinishAt = tf;
|
|
13659
|
-
}
|
|
13660
|
-
};
|
|
13661
|
-
var applyTicketPlanningFieldsFromUpdatePayload = (row, payload) => {
|
|
13662
|
-
if (Object.prototype.hasOwnProperty.call(payload, "labels")) {
|
|
13663
|
-
if (payload["labels"] === null) {
|
|
13664
|
-
delete row.labels;
|
|
13665
|
-
} else {
|
|
13666
|
-
const v = ticketLabelsFromPayloadValue(payload["labels"]);
|
|
13667
|
-
if (v !== void 0) {
|
|
13668
|
-
if (v.length === 0) {
|
|
13669
|
-
delete row.labels;
|
|
13670
|
-
} else {
|
|
13671
|
-
row.labels = v;
|
|
13672
|
-
}
|
|
13673
|
-
}
|
|
13674
|
-
}
|
|
13675
|
-
}
|
|
13676
|
-
const pr = readTicketPriorityPatch(payload);
|
|
13677
|
-
if (pr === null) {
|
|
13678
|
-
delete row.priority;
|
|
13679
|
-
} else if (typeof pr === "string") {
|
|
13680
|
-
row.priority = pr;
|
|
13681
|
-
}
|
|
13682
|
-
const sz = readTicketSizePatch(payload);
|
|
13683
|
-
if (sz === null) {
|
|
13684
|
-
delete row.size;
|
|
13685
|
-
} else if (typeof sz === "string") {
|
|
13686
|
-
row.size = sz;
|
|
13687
|
-
}
|
|
13688
|
-
const est = readTicketEstimatePatch(payload);
|
|
13689
|
-
if (est === null) {
|
|
13690
|
-
delete row.estimate;
|
|
13691
|
-
} else if (typeof est === "number") {
|
|
13692
|
-
row.estimate = est;
|
|
13693
|
-
}
|
|
13694
|
-
const sw = readTicketIsoInstantPatch(payload, "startWorkAt");
|
|
13695
|
-
if (sw === null) {
|
|
13696
|
-
delete row.startWorkAt;
|
|
13697
|
-
} else if (typeof sw === "string") {
|
|
13698
|
-
row.startWorkAt = sw;
|
|
13699
|
-
}
|
|
13700
|
-
const tf = readTicketIsoInstantPatch(payload, "targetFinishAt");
|
|
13701
|
-
if (tf === null) {
|
|
13702
|
-
delete row.targetFinishAt;
|
|
13703
|
-
} else if (typeof tf === "string") {
|
|
13704
|
-
row.targetFinishAt = tf;
|
|
13705
|
-
}
|
|
13706
|
-
};
|
|
13707
|
-
var applyCreatedAudit = (row, evt) => {
|
|
13708
|
-
row.createdAt = evt.ts;
|
|
13709
|
-
row.createdBy = evt.actor;
|
|
13710
|
-
row.updatedAt = evt.ts;
|
|
13711
|
-
row.updatedBy = evt.actor;
|
|
13712
|
-
};
|
|
13713
|
-
var applyLastUpdate = (row, evt) => {
|
|
13714
|
-
row.updatedAt = evt.ts;
|
|
13715
|
-
row.updatedBy = evt.actor;
|
|
13716
|
-
};
|
|
13717
|
-
var applyStatusIfChanged = (row, evt, nextStatus) => {
|
|
13718
|
-
if (row.status === nextStatus) return false;
|
|
13719
|
-
row.status = nextStatus;
|
|
13720
|
-
row.statusChangedAt = evt.ts;
|
|
13721
|
-
row.statusChangedBy = evt.actor;
|
|
13722
|
-
return true;
|
|
13723
|
-
};
|
|
13724
|
-
var appendTicketCommentFromEvent = (ticket, evt) => {
|
|
13725
|
-
const list = ticket.comments ?? (ticket.comments = []);
|
|
13726
|
-
list.push({
|
|
13727
|
-
id: evt.id,
|
|
13728
|
-
body: String(evt.payload["body"] ?? ""),
|
|
13729
|
-
createdAt: evt.ts,
|
|
13730
|
-
createdBy: evt.actor
|
|
13731
|
-
});
|
|
13732
|
-
applyLastUpdate(ticket, evt);
|
|
13733
|
-
};
|
|
13734
|
-
var resolveInboundTicketStatusFromPayload = (ticket, payload) => {
|
|
13735
|
-
const explicit = parseWorkItemStatus(payload["status"]);
|
|
13736
|
-
if (explicit !== void 0) return explicit;
|
|
13737
|
-
const legacySt = payload["state"];
|
|
13738
|
-
if (legacySt === "open" || legacySt === "closed") {
|
|
13739
|
-
return resolveTicketInboundStatus({
|
|
13740
|
-
issueState: legacySt,
|
|
13741
|
-
currentStatus: ticket.status
|
|
13742
|
-
});
|
|
13743
|
-
}
|
|
13744
|
-
return void 0;
|
|
13745
|
-
};
|
|
13746
|
-
var applyEvent = (projection, evt) => {
|
|
13747
|
-
switch (evt.type) {
|
|
13748
|
-
case "EpicCreated": {
|
|
13749
|
-
const id = String(evt.payload["id"]);
|
|
13750
|
-
const status = resolveStatusForNewEpicStoryPayload(evt.payload);
|
|
13751
|
-
const row = {
|
|
13752
|
-
id,
|
|
13753
|
-
title: String(evt.payload["title"] ?? ""),
|
|
13754
|
-
body: String(evt.payload["body"] ?? ""),
|
|
13755
|
-
status,
|
|
13756
|
-
statusChangedAt: evt.ts,
|
|
13757
|
-
statusChangedBy: evt.actor,
|
|
13758
|
-
createdAt: "",
|
|
13759
|
-
createdBy: "",
|
|
13760
|
-
updatedAt: "",
|
|
13761
|
-
updatedBy: ""
|
|
13762
|
-
};
|
|
13763
|
-
applyCreatedAudit(row, evt);
|
|
13764
|
-
projection.epics.set(id, row);
|
|
13765
|
-
break;
|
|
13474
|
+
var applyEvent = (projection, evt) => {
|
|
13475
|
+
switch (evt.type) {
|
|
13476
|
+
case "EpicCreated": {
|
|
13477
|
+
const id = String(evt.payload["id"]);
|
|
13478
|
+
const status = resolveStatusForNewEpicStoryPayload(evt.payload);
|
|
13479
|
+
const row = {
|
|
13480
|
+
id,
|
|
13481
|
+
number: resolveWorkItemCreateNumber(projection, "epic", evt.payload),
|
|
13482
|
+
title: String(evt.payload["title"] ?? ""),
|
|
13483
|
+
body: String(evt.payload["body"] ?? ""),
|
|
13484
|
+
status,
|
|
13485
|
+
statusChangedAt: evt.ts,
|
|
13486
|
+
statusChangedBy: evt.actor,
|
|
13487
|
+
createdAt: "",
|
|
13488
|
+
createdBy: "",
|
|
13489
|
+
updatedAt: "",
|
|
13490
|
+
updatedBy: ""
|
|
13491
|
+
};
|
|
13492
|
+
applyCreatedAudit(row, evt);
|
|
13493
|
+
projection.epics.set(id, row);
|
|
13494
|
+
break;
|
|
13766
13495
|
}
|
|
13767
13496
|
case "EpicUpdated": {
|
|
13768
13497
|
const id = String(evt.payload["id"]);
|
|
@@ -13795,6 +13524,7 @@ var applyEvent = (projection, evt) => {
|
|
|
13795
13524
|
const status = resolveStatusForNewEpicStoryPayload(evt.payload);
|
|
13796
13525
|
const row = {
|
|
13797
13526
|
id,
|
|
13527
|
+
number: resolveWorkItemCreateNumber(projection, "story", evt.payload),
|
|
13798
13528
|
epicId: String(evt.payload["epicId"]),
|
|
13799
13529
|
title: String(evt.payload["title"] ?? ""),
|
|
13800
13530
|
body: String(evt.payload["body"] ?? ""),
|
|
@@ -13842,6 +13572,7 @@ var applyEvent = (projection, evt) => {
|
|
|
13842
13572
|
const status = resolveStatusForNewTicketPayload(evt.payload);
|
|
13843
13573
|
const row = {
|
|
13844
13574
|
id,
|
|
13575
|
+
number: resolveWorkItemCreateNumber(projection, "ticket", evt.payload),
|
|
13845
13576
|
storyId: storyIdFromTicketCreatedPayload(evt.payload),
|
|
13846
13577
|
title: String(evt.payload["title"] ?? ""),
|
|
13847
13578
|
body,
|
|
@@ -13954,28 +13685,733 @@ var applyEvent = (projection, evt) => {
|
|
|
13954
13685
|
}
|
|
13955
13686
|
break;
|
|
13956
13687
|
}
|
|
13957
|
-
default:
|
|
13958
|
-
break;
|
|
13688
|
+
default:
|
|
13689
|
+
break;
|
|
13690
|
+
}
|
|
13691
|
+
};
|
|
13692
|
+
var replayEvents = (lines) => {
|
|
13693
|
+
const proj = emptyProjection();
|
|
13694
|
+
const events = [];
|
|
13695
|
+
for (const line of lines) {
|
|
13696
|
+
const trimmed = line.trim();
|
|
13697
|
+
if (!trimmed) continue;
|
|
13698
|
+
const json = JSON.parse(trimmed);
|
|
13699
|
+
events.push(eventLineSchema.parse(json));
|
|
13700
|
+
}
|
|
13701
|
+
events.sort((a, b) => {
|
|
13702
|
+
const t = a.ts.localeCompare(b.ts);
|
|
13703
|
+
if (t !== 0) return t;
|
|
13704
|
+
return a.id.localeCompare(b.id);
|
|
13705
|
+
});
|
|
13706
|
+
for (const e of events) {
|
|
13707
|
+
applyEvent(proj, e);
|
|
13708
|
+
}
|
|
13709
|
+
return proj;
|
|
13710
|
+
};
|
|
13711
|
+
|
|
13712
|
+
// src/cli/resolve-projection-work-item-id.ts
|
|
13713
|
+
var isDigitOnlyWorkItemRef = (raw) => /^\d+$/.test(raw.trim());
|
|
13714
|
+
var resolveEpicId = (projection, raw) => {
|
|
13715
|
+
const t = raw.trim();
|
|
13716
|
+
if (t === "") return void 0;
|
|
13717
|
+
if (projection.epics.has(t)) return t;
|
|
13718
|
+
if (!isDigitOnlyWorkItemRef(t)) return void 0;
|
|
13719
|
+
const n = Number(t);
|
|
13720
|
+
const hits = [...projection.epics.values()].filter((e) => e.number === n);
|
|
13721
|
+
if (hits.length !== 1) return void 0;
|
|
13722
|
+
return hits[0].id;
|
|
13723
|
+
};
|
|
13724
|
+
var resolveStoryId = (projection, raw) => {
|
|
13725
|
+
const t = raw.trim();
|
|
13726
|
+
if (t === "") return void 0;
|
|
13727
|
+
if (projection.stories.has(t)) return t;
|
|
13728
|
+
if (!isDigitOnlyWorkItemRef(t)) return void 0;
|
|
13729
|
+
const n = Number(t);
|
|
13730
|
+
const hits = [...projection.stories.values()].filter((s) => s.number === n);
|
|
13731
|
+
if (hits.length !== 1) return void 0;
|
|
13732
|
+
return hits[0].id;
|
|
13733
|
+
};
|
|
13734
|
+
var resolveTicketId = (projection, raw) => {
|
|
13735
|
+
const t = raw.trim();
|
|
13736
|
+
if (t === "") return void 0;
|
|
13737
|
+
if (projection.tickets.has(t)) return t;
|
|
13738
|
+
if (!isDigitOnlyWorkItemRef(t)) return void 0;
|
|
13739
|
+
const n = Number(t);
|
|
13740
|
+
const hits = [...projection.tickets.values()].filter((x) => x.number === n);
|
|
13741
|
+
if (hits.length !== 1) return void 0;
|
|
13742
|
+
return hits[0].id;
|
|
13743
|
+
};
|
|
13744
|
+
var resolveTicketDependsOnTokensToIds = (projection, tokens) => {
|
|
13745
|
+
const mapped = tokens.map((raw) => {
|
|
13746
|
+
const t = raw.trim();
|
|
13747
|
+
if (t === "") return "";
|
|
13748
|
+
if (projection.tickets.has(t)) return t;
|
|
13749
|
+
if (!isDigitOnlyWorkItemRef(t)) return t;
|
|
13750
|
+
const hit = resolveTicketId(projection, t);
|
|
13751
|
+
return hit ?? t;
|
|
13752
|
+
});
|
|
13753
|
+
return normalizeTicketDependsOnIds(mapped);
|
|
13754
|
+
};
|
|
13755
|
+
var assertCreatePayloadUsesExpectedHeadNumber = (projection, kind, payload) => {
|
|
13756
|
+
const n = readOptionalPositiveIntegerFromPayload(payload, "number");
|
|
13757
|
+
const expected = kind === "epic" ? nextEpicNumberForCreate(projection) : kind === "story" ? nextStoryNumberForCreate(projection) : nextTicketNumberForCreate(projection);
|
|
13758
|
+
if (n !== expected) {
|
|
13759
|
+
const got = n === void 0 ? "missing" : String(n);
|
|
13760
|
+
throw new Error(
|
|
13761
|
+
`Invalid ${kind} number for this data branch: expected ${String(expected)}, got ${got}.`
|
|
13762
|
+
);
|
|
13763
|
+
}
|
|
13764
|
+
};
|
|
13765
|
+
|
|
13766
|
+
// src/config/hyper-pm-config.ts
|
|
13767
|
+
var hyperPmConfigSchema = external_exports.object({
|
|
13768
|
+
schema: external_exports.literal(1),
|
|
13769
|
+
dataBranch: external_exports.string().min(1),
|
|
13770
|
+
remote: external_exports.string().min(1).default("origin"),
|
|
13771
|
+
sync: external_exports.enum(["off", "outbound", "full"]).default("outbound"),
|
|
13772
|
+
githubRepo: external_exports.string().optional(),
|
|
13773
|
+
issueMapping: external_exports.enum(["ticket", "story", "epic"]).default("ticket")
|
|
13774
|
+
});
|
|
13775
|
+
|
|
13776
|
+
// src/config/load-config.ts
|
|
13777
|
+
var import_promises = require("node:fs/promises");
|
|
13778
|
+
var import_node_path = require("node:path");
|
|
13779
|
+
var CONFIG_DIR = ".hyper-pm";
|
|
13780
|
+
var CONFIG_FILE = "config.json";
|
|
13781
|
+
var getHyperPmConfigPath = (repoRoot) => {
|
|
13782
|
+
return (0, import_node_path.join)(repoRoot, CONFIG_DIR, CONFIG_FILE);
|
|
13783
|
+
};
|
|
13784
|
+
var loadHyperPmConfig = async (repoRoot, overrides = {}) => {
|
|
13785
|
+
const raw = await (0, import_promises.readFile)(getHyperPmConfigPath(repoRoot), "utf8");
|
|
13786
|
+
const parsed = JSON.parse(raw);
|
|
13787
|
+
const base = hyperPmConfigSchema.parse(parsed);
|
|
13788
|
+
const merged = { ...base, ...stripUndefined(overrides) };
|
|
13789
|
+
return hyperPmConfigSchema.parse(merged);
|
|
13790
|
+
};
|
|
13791
|
+
var stripUndefined = (obj) => {
|
|
13792
|
+
const out = {};
|
|
13793
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
13794
|
+
if (v !== void 0) out[k] = v;
|
|
13795
|
+
}
|
|
13796
|
+
return out;
|
|
13797
|
+
};
|
|
13798
|
+
|
|
13799
|
+
// src/config/save-config.ts
|
|
13800
|
+
var import_promises2 = require("node:fs/promises");
|
|
13801
|
+
var import_node_path2 = require("node:path");
|
|
13802
|
+
var saveHyperPmConfig = async (repoRoot, config) => {
|
|
13803
|
+
const target = getHyperPmConfigPath(repoRoot);
|
|
13804
|
+
await (0, import_promises2.mkdir)((0, import_node_path2.dirname)(target), { recursive: true });
|
|
13805
|
+
await (0, import_promises2.writeFile)(target, `${JSON.stringify(config, null, 2)}
|
|
13806
|
+
`, "utf8");
|
|
13807
|
+
};
|
|
13808
|
+
|
|
13809
|
+
// src/doctor/run-doctor.ts
|
|
13810
|
+
var runDoctorOnLines = (lines) => {
|
|
13811
|
+
const issues = [];
|
|
13812
|
+
for (let i = 0; i < lines.length; i++) {
|
|
13813
|
+
const line = lines[i]?.trim() ?? "";
|
|
13814
|
+
if (!line) continue;
|
|
13815
|
+
let json;
|
|
13816
|
+
try {
|
|
13817
|
+
json = JSON.parse(line);
|
|
13818
|
+
} catch (e) {
|
|
13819
|
+
issues.push({
|
|
13820
|
+
kind: "invalid-json",
|
|
13821
|
+
line: i + 1,
|
|
13822
|
+
message: e instanceof Error ? e.message : "parse error"
|
|
13823
|
+
});
|
|
13824
|
+
return issues;
|
|
13825
|
+
}
|
|
13826
|
+
const parsed = eventLineSchema.safeParse(json);
|
|
13827
|
+
if (!parsed.success) {
|
|
13828
|
+
issues.push({
|
|
13829
|
+
kind: "invalid-event",
|
|
13830
|
+
line: i + 1,
|
|
13831
|
+
message: parsed.error.message
|
|
13832
|
+
});
|
|
13833
|
+
return issues;
|
|
13834
|
+
}
|
|
13835
|
+
}
|
|
13836
|
+
return issues;
|
|
13837
|
+
};
|
|
13838
|
+
|
|
13839
|
+
// src/git/create-and-checkout-branch.ts
|
|
13840
|
+
var createAndCheckoutBranch = async (opts) => {
|
|
13841
|
+
await opts.runGit(opts.repoRoot, [
|
|
13842
|
+
"switch",
|
|
13843
|
+
"-c",
|
|
13844
|
+
opts.branchName,
|
|
13845
|
+
opts.startPoint
|
|
13846
|
+
]);
|
|
13847
|
+
};
|
|
13848
|
+
|
|
13849
|
+
// src/git/data-worktree-session.ts
|
|
13850
|
+
var import_promises3 = require("node:fs/promises");
|
|
13851
|
+
var import_node_path3 = require("node:path");
|
|
13852
|
+
var ensureDir = async (path) => {
|
|
13853
|
+
await (0, import_promises3.mkdir)(path, { recursive: true });
|
|
13854
|
+
};
|
|
13855
|
+
var defaultPathExists = async (path) => {
|
|
13856
|
+
try {
|
|
13857
|
+
await (0, import_promises3.access)(path);
|
|
13858
|
+
return true;
|
|
13859
|
+
} catch {
|
|
13860
|
+
return false;
|
|
13861
|
+
}
|
|
13862
|
+
};
|
|
13863
|
+
var parseDataBranchWorktreeFromPorcelain = (stdout, dataBranch) => {
|
|
13864
|
+
const wantRef = `refs/heads/${dataBranch}`;
|
|
13865
|
+
const blocks = stdout.split(/\n\n+/).map((b) => b.trim()).filter(Boolean);
|
|
13866
|
+
for (const block of blocks) {
|
|
13867
|
+
const lines = block.split("\n");
|
|
13868
|
+
let worktreePath;
|
|
13869
|
+
let branch;
|
|
13870
|
+
for (const line of lines) {
|
|
13871
|
+
if (line.startsWith("worktree ")) {
|
|
13872
|
+
worktreePath = line.slice("worktree ".length);
|
|
13873
|
+
} else if (line.startsWith("branch ")) {
|
|
13874
|
+
branch = line.slice("branch ".length);
|
|
13875
|
+
}
|
|
13876
|
+
}
|
|
13877
|
+
if (worktreePath !== void 0 && branch === wantRef) {
|
|
13878
|
+
return worktreePath;
|
|
13879
|
+
}
|
|
13880
|
+
}
|
|
13881
|
+
return void 0;
|
|
13882
|
+
};
|
|
13883
|
+
var openDataBranchWorktree = async (opts) => {
|
|
13884
|
+
const pathExists = opts.pathExists ?? defaultPathExists;
|
|
13885
|
+
const { stdout: listOut } = await opts.runGit(opts.repoRoot, [
|
|
13886
|
+
"worktree",
|
|
13887
|
+
"list",
|
|
13888
|
+
"--porcelain"
|
|
13889
|
+
]);
|
|
13890
|
+
const listedPath = parseDataBranchWorktreeFromPorcelain(
|
|
13891
|
+
listOut,
|
|
13892
|
+
opts.dataBranch
|
|
13893
|
+
);
|
|
13894
|
+
if (listedPath !== void 0 && await pathExists(listedPath)) {
|
|
13895
|
+
const dispose2 = async () => {
|
|
13896
|
+
return;
|
|
13897
|
+
};
|
|
13898
|
+
return { worktreePath: listedPath, dispose: dispose2 };
|
|
13899
|
+
}
|
|
13900
|
+
const worktreePath = (0, import_node_path3.join)(
|
|
13901
|
+
opts.tmpBase,
|
|
13902
|
+
`hyper-pm-worktree-${ulid().toLowerCase()}`
|
|
13903
|
+
);
|
|
13904
|
+
await ensureDir(opts.tmpBase);
|
|
13905
|
+
try {
|
|
13906
|
+
await opts.runGit(opts.repoRoot, [
|
|
13907
|
+
"worktree",
|
|
13908
|
+
"add",
|
|
13909
|
+
worktreePath,
|
|
13910
|
+
opts.dataBranch
|
|
13911
|
+
]);
|
|
13912
|
+
} catch (err) {
|
|
13913
|
+
await (0, import_promises3.rm)(worktreePath, { recursive: true, force: true }).catch(() => {
|
|
13914
|
+
});
|
|
13915
|
+
throw err;
|
|
13916
|
+
}
|
|
13917
|
+
const dispose = async () => {
|
|
13918
|
+
if (opts.keepWorktree) {
|
|
13919
|
+
return;
|
|
13920
|
+
}
|
|
13921
|
+
await opts.runGit(opts.repoRoot, ["worktree", "remove", "--force", worktreePath]).catch(() => {
|
|
13922
|
+
});
|
|
13923
|
+
await (0, import_promises3.rm)(worktreePath, { recursive: true, force: true }).catch(() => {
|
|
13924
|
+
});
|
|
13925
|
+
};
|
|
13926
|
+
return { worktreePath, dispose };
|
|
13927
|
+
};
|
|
13928
|
+
|
|
13929
|
+
// src/git/find-git-root.ts
|
|
13930
|
+
var findGitRoot = async (cwd, deps) => {
|
|
13931
|
+
const { stdout } = await deps.runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
13932
|
+
return stdout;
|
|
13933
|
+
};
|
|
13934
|
+
|
|
13935
|
+
// src/git/init-orphan-data-branch.ts
|
|
13936
|
+
var import_promises4 = require("node:fs/promises");
|
|
13937
|
+
var import_node_path4 = require("node:path");
|
|
13938
|
+
var initOrphanDataBranchInWorktree = async (opts) => {
|
|
13939
|
+
const { stdout: tip } = await opts.runGit(opts.repoRoot, [
|
|
13940
|
+
"rev-parse",
|
|
13941
|
+
"HEAD"
|
|
13942
|
+
]);
|
|
13943
|
+
const tipCommit = tip.trim();
|
|
13944
|
+
await opts.runGit(opts.repoRoot, [
|
|
13945
|
+
"worktree",
|
|
13946
|
+
"add",
|
|
13947
|
+
opts.worktreePath,
|
|
13948
|
+
tipCommit
|
|
13949
|
+
]);
|
|
13950
|
+
await opts.runGit(opts.worktreePath, [
|
|
13951
|
+
"checkout",
|
|
13952
|
+
"--orphan",
|
|
13953
|
+
opts.dataBranch
|
|
13954
|
+
]);
|
|
13955
|
+
await opts.runGit(opts.worktreePath, ["rm", "-rf", "."]).catch(() => {
|
|
13956
|
+
});
|
|
13957
|
+
const marker = (0, import_node_path4.join)(opts.worktreePath, "README.hyper-pm.md");
|
|
13958
|
+
await (0, import_promises4.writeFile)(
|
|
13959
|
+
marker,
|
|
13960
|
+
"# hyper-pm data branch\n\nAppend-only events live under `events/`.\n",
|
|
13961
|
+
"utf8"
|
|
13962
|
+
);
|
|
13963
|
+
await opts.runGit(opts.worktreePath, ["add", "."]);
|
|
13964
|
+
await opts.runGit(opts.worktreePath, [
|
|
13965
|
+
"commit",
|
|
13966
|
+
"-m",
|
|
13967
|
+
"init hyper-pm data branch"
|
|
13968
|
+
]);
|
|
13969
|
+
};
|
|
13970
|
+
|
|
13971
|
+
// src/git/pick-unique-local-branch-name.ts
|
|
13972
|
+
var DEFAULT_MAX_SUFFIX = 1e3;
|
|
13973
|
+
var localBranchRefExists = async (repoRoot, name, git) => {
|
|
13974
|
+
try {
|
|
13975
|
+
await git(repoRoot, ["show-ref", "--verify", `refs/heads/${name}`]);
|
|
13976
|
+
return true;
|
|
13977
|
+
} catch {
|
|
13978
|
+
return false;
|
|
13979
|
+
}
|
|
13980
|
+
};
|
|
13981
|
+
var pickUniqueLocalBranchName = async (opts) => {
|
|
13982
|
+
const max = opts.maxSuffix ?? DEFAULT_MAX_SUFFIX;
|
|
13983
|
+
const { preferredBase, repoRoot, runGit: git } = opts;
|
|
13984
|
+
for (let n = 1; n <= max; n += 1) {
|
|
13985
|
+
const raw = n === 1 ? preferredBase : `${preferredBase}-${n}`;
|
|
13986
|
+
const norm = normalizeTicketBranchName(raw);
|
|
13987
|
+
if (norm === void 0) {
|
|
13988
|
+
continue;
|
|
13989
|
+
}
|
|
13990
|
+
if (!await localBranchRefExists(repoRoot, norm, git)) {
|
|
13991
|
+
return { branch: norm, preferred: preferredBase };
|
|
13992
|
+
}
|
|
13993
|
+
}
|
|
13994
|
+
throw new Error(
|
|
13995
|
+
`Could not allocate a free local branch name from ${JSON.stringify(preferredBase)} (tried suffixes up to -${String(max)})`
|
|
13996
|
+
);
|
|
13997
|
+
};
|
|
13998
|
+
|
|
13999
|
+
// src/git/resolve-integration-start-point.ts
|
|
14000
|
+
var assertGitRefResolvable = async (repoRoot, ref, git) => {
|
|
14001
|
+
try {
|
|
14002
|
+
await git(repoRoot, ["rev-parse", "-q", "--verify", ref]);
|
|
14003
|
+
} catch {
|
|
14004
|
+
throw new Error(`Invalid or ambiguous --from ref: ${ref}`);
|
|
14005
|
+
}
|
|
14006
|
+
};
|
|
14007
|
+
var resolveIntegrationStartPoint = async (repoRoot, remote, git) => {
|
|
14008
|
+
const symRef = `refs/remotes/${remote}/HEAD`;
|
|
14009
|
+
try {
|
|
14010
|
+
const { stdout } = await git(repoRoot, ["symbolic-ref", "-q", symRef]);
|
|
14011
|
+
const target = stdout.trim();
|
|
14012
|
+
if (target !== "") {
|
|
14013
|
+
await git(repoRoot, ["rev-parse", "-q", "--verify", target]);
|
|
14014
|
+
return target;
|
|
14015
|
+
}
|
|
14016
|
+
} catch {
|
|
14017
|
+
}
|
|
14018
|
+
for (const head of ["refs/heads/main", "refs/heads/master"]) {
|
|
14019
|
+
try {
|
|
14020
|
+
await git(repoRoot, ["rev-parse", "-q", "--verify", head]);
|
|
14021
|
+
return head;
|
|
14022
|
+
} catch {
|
|
14023
|
+
}
|
|
14024
|
+
}
|
|
14025
|
+
try {
|
|
14026
|
+
await git(repoRoot, ["rev-parse", "-q", "--verify", "HEAD"]);
|
|
14027
|
+
return "HEAD";
|
|
14028
|
+
} catch {
|
|
14029
|
+
}
|
|
14030
|
+
throw new Error(
|
|
14031
|
+
"Could not resolve a default branch to branch from; pass --from <ref> (e.g. main or origin/main)."
|
|
14032
|
+
);
|
|
14033
|
+
};
|
|
14034
|
+
|
|
14035
|
+
// src/git/list-repo-commit-authors.ts
|
|
14036
|
+
var listRepoCommitAuthors = async (repoRoot, git) => {
|
|
14037
|
+
try {
|
|
14038
|
+
const { stdout } = await git(repoRoot, [
|
|
14039
|
+
"-c",
|
|
14040
|
+
"log.showSignature=false",
|
|
14041
|
+
"log",
|
|
14042
|
+
"--all",
|
|
14043
|
+
"--format=%an%x1f%ae"
|
|
14044
|
+
]);
|
|
14045
|
+
if (stdout === "") return [];
|
|
14046
|
+
const lines = stdout.split("\n").filter((l) => l.length > 0);
|
|
14047
|
+
const seenEmail = /* @__PURE__ */ new Set();
|
|
14048
|
+
const out = [];
|
|
14049
|
+
for (const line of lines) {
|
|
14050
|
+
const sep2 = line.indexOf("");
|
|
14051
|
+
if (sep2 <= 0) continue;
|
|
14052
|
+
const name = line.slice(0, sep2).trim();
|
|
14053
|
+
const email = line.slice(sep2 + 1).trim();
|
|
14054
|
+
if (email === "") continue;
|
|
14055
|
+
const key = email.toLowerCase();
|
|
14056
|
+
if (seenEmail.has(key)) continue;
|
|
14057
|
+
seenEmail.add(key);
|
|
14058
|
+
const loginGuess = guessGithubLoginFromContact(name, email);
|
|
14059
|
+
out.push(
|
|
14060
|
+
loginGuess !== void 0 ? { name, email, loginGuess } : { name, email }
|
|
14061
|
+
);
|
|
14062
|
+
}
|
|
14063
|
+
return out;
|
|
14064
|
+
} catch {
|
|
14065
|
+
return [];
|
|
14066
|
+
}
|
|
14067
|
+
};
|
|
14068
|
+
|
|
14069
|
+
// src/git/parse-github-owner-repo-from-remote-url.ts
|
|
14070
|
+
var parseGithubOwnerRepoFromRemoteUrl = (rawUrl) => {
|
|
14071
|
+
const trimmed = rawUrl.trim();
|
|
14072
|
+
if (!trimmed) {
|
|
14073
|
+
return void 0;
|
|
14074
|
+
}
|
|
14075
|
+
const scpMatch = /^git@github\.com:(?<path>.+)$/i.exec(trimmed);
|
|
14076
|
+
if (scpMatch?.groups?.path) {
|
|
14077
|
+
return slugFromGithubPath(scpMatch.groups.path);
|
|
14078
|
+
}
|
|
14079
|
+
try {
|
|
14080
|
+
const withScheme = trimmed.includes("://") ? trimmed : `https://${trimmed}`;
|
|
14081
|
+
const u = new URL(withScheme);
|
|
14082
|
+
const host = u.hostname.toLowerCase();
|
|
14083
|
+
if (host !== "github.com" && host !== "www.github.com") {
|
|
14084
|
+
return void 0;
|
|
14085
|
+
}
|
|
14086
|
+
return slugFromGithubPath(u.pathname);
|
|
14087
|
+
} catch {
|
|
14088
|
+
return void 0;
|
|
14089
|
+
}
|
|
14090
|
+
};
|
|
14091
|
+
var slugFromGithubPath = (path) => {
|
|
14092
|
+
const segments = path.replace(/^\/+/, "").split("/").filter((s) => s.length > 0).map((s) => s.replace(/\.git$/i, ""));
|
|
14093
|
+
if (segments.length < 2) {
|
|
14094
|
+
return void 0;
|
|
14095
|
+
}
|
|
14096
|
+
const owner = segments[0];
|
|
14097
|
+
const repo = segments[1];
|
|
14098
|
+
if (!owner || !repo) {
|
|
14099
|
+
return void 0;
|
|
14100
|
+
}
|
|
14101
|
+
return `${owner}/${repo}`;
|
|
14102
|
+
};
|
|
14103
|
+
|
|
14104
|
+
// src/git/try-read-github-owner-repo-slug-from-git.ts
|
|
14105
|
+
var tryReadGithubOwnerRepoSlugFromGit = async (params) => {
|
|
14106
|
+
try {
|
|
14107
|
+
const { stdout } = await params.runGit(params.repoRoot, [
|
|
14108
|
+
"remote",
|
|
14109
|
+
"get-url",
|
|
14110
|
+
params.remote
|
|
14111
|
+
]);
|
|
14112
|
+
return parseGithubOwnerRepoFromRemoteUrl(stdout);
|
|
14113
|
+
} catch {
|
|
14114
|
+
return void 0;
|
|
14115
|
+
}
|
|
14116
|
+
};
|
|
14117
|
+
|
|
14118
|
+
// src/git/resolve-effective-git-author-for-data-commit.ts
|
|
14119
|
+
var defaultDataCommitName = "hyper-pm";
|
|
14120
|
+
var defaultDataCommitEmail = "hyper-pm@users.noreply.github.com";
|
|
14121
|
+
var tryReadGitConfigUserName = async (cwd, runGit2) => {
|
|
14122
|
+
try {
|
|
14123
|
+
const { stdout } = await runGit2(cwd, ["config", "--get", "user.name"]);
|
|
14124
|
+
return stdout.trim();
|
|
14125
|
+
} catch {
|
|
14126
|
+
return "";
|
|
14127
|
+
}
|
|
14128
|
+
};
|
|
14129
|
+
var tryReadGitConfigUserEmail = async (cwd, runGit2) => {
|
|
14130
|
+
try {
|
|
14131
|
+
const { stdout } = await runGit2(cwd, ["config", "--get", "user.email"]);
|
|
14132
|
+
return stdout.trim();
|
|
14133
|
+
} catch {
|
|
14134
|
+
return "";
|
|
14135
|
+
}
|
|
14136
|
+
};
|
|
14137
|
+
var resolveEffectiveGitAuthorForDataCommit = async (cwd, runGit2, authorEnv) => {
|
|
14138
|
+
const fromGitName = await tryReadGitConfigUserName(cwd, runGit2);
|
|
14139
|
+
const fromGitEmail = await tryReadGitConfigUserEmail(cwd, runGit2);
|
|
14140
|
+
const name = fromGitName || authorEnv.HYPER_PM_GIT_USER_NAME?.trim() || authorEnv.GIT_AUTHOR_NAME?.trim() || defaultDataCommitName;
|
|
14141
|
+
const email = fromGitEmail || authorEnv.HYPER_PM_GIT_USER_EMAIL?.trim() || authorEnv.GIT_AUTHOR_EMAIL?.trim() || defaultDataCommitEmail;
|
|
14142
|
+
return { name, email };
|
|
14143
|
+
};
|
|
14144
|
+
|
|
14145
|
+
// src/run/commit-data.ts
|
|
14146
|
+
var maxActorSuffixLen = 60;
|
|
14147
|
+
var formatDataBranchCommitMessage = (base, actorSuffix) => {
|
|
14148
|
+
const raw = actorSuffix?.trim();
|
|
14149
|
+
if (!raw) {
|
|
14150
|
+
return base;
|
|
14151
|
+
}
|
|
14152
|
+
const collapsed = raw.replace(/\s+/g, " ");
|
|
14153
|
+
const suffix = collapsed.length > maxActorSuffixLen ? `${collapsed.slice(0, maxActorSuffixLen - 1)}\u2026` : collapsed;
|
|
14154
|
+
return `${base} (${suffix})`;
|
|
14155
|
+
};
|
|
14156
|
+
var commitDataWorktreeIfNeeded = async (worktreePath, message, runGit2, opts) => {
|
|
14157
|
+
const { stdout } = await runGit2(worktreePath, ["status", "--porcelain"]);
|
|
14158
|
+
if (!stdout.trim()) return;
|
|
14159
|
+
await runGit2(worktreePath, ["add", "."]);
|
|
14160
|
+
const authorEnv = opts?.authorEnv ?? env;
|
|
14161
|
+
const { name, email } = await resolveEffectiveGitAuthorForDataCommit(
|
|
14162
|
+
worktreePath,
|
|
14163
|
+
runGit2,
|
|
14164
|
+
authorEnv
|
|
14165
|
+
);
|
|
14166
|
+
await runGit2(worktreePath, [
|
|
14167
|
+
"-c",
|
|
14168
|
+
`user.name=${name}`,
|
|
14169
|
+
"-c",
|
|
14170
|
+
`user.email=${email}`,
|
|
14171
|
+
"commit",
|
|
14172
|
+
"-m",
|
|
14173
|
+
message
|
|
14174
|
+
]);
|
|
14175
|
+
};
|
|
14176
|
+
|
|
14177
|
+
// src/run/push-data-branch.ts
|
|
14178
|
+
var firstLineFromUnknown = (err) => {
|
|
14179
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
14180
|
+
const line = raw.trim().split("\n")[0]?.trim() ?? raw.trim();
|
|
14181
|
+
return line.length > 0 ? line : "git error";
|
|
14182
|
+
};
|
|
14183
|
+
var tryPushDataBranchToRemote = async (worktreePath, remote, branch, runGit2) => {
|
|
14184
|
+
try {
|
|
14185
|
+
await runGit2(worktreePath, ["remote", "get-url", remote]);
|
|
14186
|
+
} catch (e) {
|
|
14187
|
+
return {
|
|
14188
|
+
status: "skipped_no_remote",
|
|
14189
|
+
detail: firstLineFromUnknown(e)
|
|
14190
|
+
};
|
|
14191
|
+
}
|
|
14192
|
+
try {
|
|
14193
|
+
await runGit2(worktreePath, ["push", "-u", remote, branch]);
|
|
14194
|
+
return { status: "pushed" };
|
|
14195
|
+
} catch (e) {
|
|
14196
|
+
return {
|
|
14197
|
+
status: "failed",
|
|
14198
|
+
detail: firstLineFromUnknown(e)
|
|
14199
|
+
};
|
|
14200
|
+
}
|
|
14201
|
+
};
|
|
14202
|
+
|
|
14203
|
+
// src/run/sync-remote-data-branch.ts
|
|
14204
|
+
var SyncRemoteDataBranchMergeError = class extends Error {
|
|
14205
|
+
/** @param message - Human-readable reason (stderr excerpt or generic text). */
|
|
14206
|
+
constructor(message) {
|
|
14207
|
+
super(message);
|
|
14208
|
+
this.name = "SyncRemoteDataBranchMergeError";
|
|
14209
|
+
}
|
|
14210
|
+
};
|
|
14211
|
+
var remoteTrackingRef = (remote, dataBranch) => `refs/remotes/${remote}/${dataBranch}`;
|
|
14212
|
+
var mergeRefSpecifier = (remote, dataBranch) => `${remote}/${dataBranch}`;
|
|
14213
|
+
var isLikelyNonFastForwardPushFailure = (detail) => {
|
|
14214
|
+
if (detail === void 0) return false;
|
|
14215
|
+
const d = detail.toLowerCase();
|
|
14216
|
+
return d.includes("non-fast-forward") || d.includes("failed to push");
|
|
14217
|
+
};
|
|
14218
|
+
var classifyMergeOutput = (combined) => {
|
|
14219
|
+
const c = combined.toLowerCase();
|
|
14220
|
+
if (c.includes("already up to date")) {
|
|
14221
|
+
return "up_to_date";
|
|
14222
|
+
}
|
|
14223
|
+
if (c.includes("fast-forward")) {
|
|
14224
|
+
return "fast_forward";
|
|
14225
|
+
}
|
|
14226
|
+
return "merge_commit";
|
|
14227
|
+
};
|
|
14228
|
+
var refExists = async (cwd, ref, runGit2) => {
|
|
14229
|
+
try {
|
|
14230
|
+
await runGit2(cwd, ["show-ref", "--verify", "--quiet", ref]);
|
|
14231
|
+
return true;
|
|
14232
|
+
} catch {
|
|
14233
|
+
return false;
|
|
14234
|
+
}
|
|
14235
|
+
};
|
|
14236
|
+
var fetchRemoteDataBranch = async (worktreePath, remote, dataBranch, runGit2) => {
|
|
14237
|
+
try {
|
|
14238
|
+
await runGit2(worktreePath, ["remote", "get-url", remote]);
|
|
14239
|
+
} catch {
|
|
14240
|
+
return "skipped_no_remote";
|
|
14241
|
+
}
|
|
14242
|
+
try {
|
|
14243
|
+
await runGit2(worktreePath, ["fetch", remote, dataBranch]);
|
|
14244
|
+
return "ok";
|
|
14245
|
+
} catch (e) {
|
|
14246
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
14247
|
+
if (/couldn't find remote ref|could not find remote ref/i.test(msg)) {
|
|
14248
|
+
return "remote_branch_absent";
|
|
14249
|
+
}
|
|
14250
|
+
throw e;
|
|
14251
|
+
}
|
|
14252
|
+
};
|
|
14253
|
+
var mergeRemoteTrackingIntoHead = async (worktreePath, remote, dataBranch, runGit2, authorEnv = env) => {
|
|
14254
|
+
const spec = mergeRefSpecifier(remote, dataBranch);
|
|
14255
|
+
const { name, email } = await resolveEffectiveGitAuthorForDataCommit(
|
|
14256
|
+
worktreePath,
|
|
14257
|
+
runGit2,
|
|
14258
|
+
authorEnv
|
|
14259
|
+
);
|
|
14260
|
+
try {
|
|
14261
|
+
const { stdout, stderr } = await runGit2(worktreePath, [
|
|
14262
|
+
"-c",
|
|
14263
|
+
`user.name=${name}`,
|
|
14264
|
+
"-c",
|
|
14265
|
+
`user.email=${email}`,
|
|
14266
|
+
"merge",
|
|
14267
|
+
"--no-edit",
|
|
14268
|
+
spec
|
|
14269
|
+
]);
|
|
14270
|
+
return classifyMergeOutput(`${stdout}
|
|
14271
|
+
${stderr}`);
|
|
14272
|
+
} catch (e) {
|
|
14273
|
+
await runGit2(worktreePath, ["merge", "--abort"]).catch(() => {
|
|
14274
|
+
});
|
|
14275
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
14276
|
+
throw new SyncRemoteDataBranchMergeError(
|
|
14277
|
+
msg.toLowerCase().includes("conflict") ? "Merge conflict while syncing hyper-pm data branch; merge aborted. Resolve manually on the data branch if needed." : `git merge failed: ${msg.trim().split("\n")[0] ?? msg}`
|
|
14278
|
+
);
|
|
14279
|
+
}
|
|
14280
|
+
};
|
|
14281
|
+
var runRemoteDataBranchGitSync = async (worktreePath, remote, dataBranch, runGit2, skipPush, deps = {}) => {
|
|
14282
|
+
const tryPushFn = deps.tryPush ?? tryPushDataBranchToRemote;
|
|
14283
|
+
const maxPushAttempts = deps.maxPushAttempts ?? 3;
|
|
14284
|
+
const authorEnv = deps.authorEnv ?? env;
|
|
14285
|
+
let dataBranchFetch = await fetchRemoteDataBranch(worktreePath, remote, dataBranch, runGit2);
|
|
14286
|
+
let dataBranchMerge = dataBranchFetch === "skipped_no_remote" ? "skipped_no_remote" : dataBranchFetch === "remote_branch_absent" ? "skipped_missing_remote_branch" : "up_to_date";
|
|
14287
|
+
if (dataBranchFetch === "ok") {
|
|
14288
|
+
const tracking = remoteTrackingRef(remote, dataBranch);
|
|
14289
|
+
const exists = await refExists(worktreePath, tracking, runGit2);
|
|
14290
|
+
if (!exists) {
|
|
14291
|
+
dataBranchMerge = "skipped_missing_remote_branch";
|
|
14292
|
+
} else {
|
|
14293
|
+
dataBranchMerge = await mergeRemoteTrackingIntoHead(
|
|
14294
|
+
worktreePath,
|
|
14295
|
+
remote,
|
|
14296
|
+
dataBranch,
|
|
14297
|
+
runGit2,
|
|
14298
|
+
authorEnv
|
|
14299
|
+
);
|
|
14300
|
+
}
|
|
14301
|
+
}
|
|
14302
|
+
if (skipPush) {
|
|
14303
|
+
return {
|
|
14304
|
+
dataBranchFetch,
|
|
14305
|
+
dataBranchMerge,
|
|
14306
|
+
dataBranchPush: "skipped_cli",
|
|
14307
|
+
dataBranchPushDetail: "skip-push",
|
|
14308
|
+
pushAttempts: 0
|
|
14309
|
+
};
|
|
14310
|
+
}
|
|
14311
|
+
let pushAttempts = 0;
|
|
14312
|
+
let lastPush = { status: "skipped_no_remote" };
|
|
14313
|
+
const refetchAndMerge = async () => {
|
|
14314
|
+
dataBranchFetch = await fetchRemoteDataBranch(
|
|
14315
|
+
worktreePath,
|
|
14316
|
+
remote,
|
|
14317
|
+
dataBranch,
|
|
14318
|
+
runGit2
|
|
14319
|
+
);
|
|
14320
|
+
if (dataBranchFetch !== "ok") {
|
|
14321
|
+
return;
|
|
14322
|
+
}
|
|
14323
|
+
const tracking = remoteTrackingRef(remote, dataBranch);
|
|
14324
|
+
const exists = await refExists(worktreePath, tracking, runGit2);
|
|
14325
|
+
if (!exists) {
|
|
14326
|
+
return;
|
|
14327
|
+
}
|
|
14328
|
+
dataBranchMerge = await mergeRemoteTrackingIntoHead(
|
|
14329
|
+
worktreePath,
|
|
14330
|
+
remote,
|
|
14331
|
+
dataBranch,
|
|
14332
|
+
runGit2,
|
|
14333
|
+
authorEnv
|
|
14334
|
+
);
|
|
14335
|
+
};
|
|
14336
|
+
for (let attempt = 1; attempt <= maxPushAttempts; attempt += 1) {
|
|
14337
|
+
pushAttempts = attempt;
|
|
14338
|
+
lastPush = await tryPushFn(worktreePath, remote, dataBranch, runGit2);
|
|
14339
|
+
if (lastPush.status === "pushed" || lastPush.status === "skipped_no_remote") {
|
|
14340
|
+
break;
|
|
14341
|
+
}
|
|
14342
|
+
if (lastPush.status === "failed" && isLikelyNonFastForwardPushFailure(lastPush.detail) && attempt < maxPushAttempts) {
|
|
14343
|
+
await refetchAndMerge();
|
|
14344
|
+
continue;
|
|
14345
|
+
}
|
|
14346
|
+
break;
|
|
14347
|
+
}
|
|
14348
|
+
return {
|
|
14349
|
+
dataBranchFetch,
|
|
14350
|
+
dataBranchMerge,
|
|
14351
|
+
dataBranchPush: lastPush.status,
|
|
14352
|
+
...lastPush.detail !== void 0 ? { dataBranchPushDetail: lastPush.detail } : {},
|
|
14353
|
+
pushAttempts
|
|
14354
|
+
};
|
|
14355
|
+
};
|
|
14356
|
+
|
|
14357
|
+
// src/storage/append-event.ts
|
|
14358
|
+
var import_promises5 = require("node:fs/promises");
|
|
14359
|
+
var import_node_path5 = require("node:path");
|
|
14360
|
+
|
|
14361
|
+
// src/storage/event-path.ts
|
|
14362
|
+
var nextEventRelPath = (now, deps) => {
|
|
14363
|
+
const y = String(now.getUTCFullYear());
|
|
14364
|
+
const m = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
14365
|
+
const nextId = deps?.nextId ?? (() => ulid().toLowerCase());
|
|
14366
|
+
const part = `part-${nextId()}`;
|
|
14367
|
+
return `events/${y}/${m}/${part}.jsonl`;
|
|
14368
|
+
};
|
|
14369
|
+
|
|
14370
|
+
// src/storage/append-event.ts
|
|
14371
|
+
var appendEventLine = async (dataRoot, event, clock, opts) => {
|
|
14372
|
+
const rel = nextEventRelPath(clock.now(), {
|
|
14373
|
+
nextId: opts?.nextEventId
|
|
14374
|
+
});
|
|
14375
|
+
const abs = `${dataRoot.replace(/\/$/, "")}/${rel}`;
|
|
14376
|
+
await (0, import_promises5.mkdir)((0, import_node_path5.dirname)(abs), { recursive: true });
|
|
14377
|
+
await (0, import_promises5.appendFile)(abs, `${JSON.stringify(event)}
|
|
14378
|
+
`, "utf8");
|
|
14379
|
+
return rel;
|
|
14380
|
+
};
|
|
14381
|
+
|
|
14382
|
+
// src/storage/read-event-lines.ts
|
|
14383
|
+
var import_promises6 = require("node:fs/promises");
|
|
14384
|
+
var import_node_path6 = require("node:path");
|
|
14385
|
+
var listJsonlFiles = async (dir, root) => {
|
|
14386
|
+
const out = [];
|
|
14387
|
+
const entries = await (0, import_promises6.readdir)(dir, { withFileTypes: true });
|
|
14388
|
+
for (const e of entries) {
|
|
14389
|
+
const abs = (0, import_node_path6.join)(dir, e.name);
|
|
14390
|
+
if (e.isDirectory()) {
|
|
14391
|
+
out.push(...await listJsonlFiles(abs, root));
|
|
14392
|
+
} else if (e.isFile() && e.name.endsWith(".jsonl")) {
|
|
14393
|
+
out.push((0, import_node_path6.relative)(root, abs).split("\\").join("/"));
|
|
14394
|
+
}
|
|
13959
14395
|
}
|
|
14396
|
+
return out;
|
|
13960
14397
|
};
|
|
13961
|
-
var
|
|
13962
|
-
const
|
|
13963
|
-
|
|
13964
|
-
|
|
13965
|
-
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
events.push(eventLineSchema.parse(json));
|
|
14398
|
+
var readAllEventLines = async (dataRoot) => {
|
|
14399
|
+
const eventsRoot = (0, import_node_path6.join)(dataRoot, "events");
|
|
14400
|
+
let files = [];
|
|
14401
|
+
try {
|
|
14402
|
+
files = await listJsonlFiles(eventsRoot, dataRoot);
|
|
14403
|
+
} catch {
|
|
14404
|
+
return [];
|
|
13969
14405
|
}
|
|
13970
|
-
|
|
13971
|
-
|
|
13972
|
-
|
|
13973
|
-
|
|
13974
|
-
|
|
13975
|
-
|
|
13976
|
-
|
|
14406
|
+
files.sort((a, b) => a.localeCompare(b));
|
|
14407
|
+
const lines = [];
|
|
14408
|
+
for (const rel of files) {
|
|
14409
|
+
const content = await (0, import_promises6.readFile)((0, import_node_path6.join)(dataRoot, rel), "utf8");
|
|
14410
|
+
for (const line of content.split("\n")) {
|
|
14411
|
+
if (line.trim()) lines.push(line);
|
|
14412
|
+
}
|
|
13977
14413
|
}
|
|
13978
|
-
return
|
|
14414
|
+
return lines;
|
|
13979
14415
|
};
|
|
13980
14416
|
|
|
13981
14417
|
// src/sync/collect-github-pr-activity-source-ids.ts
|
|
@@ -14329,6 +14765,19 @@ var runGithubInboundSync = async (params) => {
|
|
|
14329
14765
|
const planningSource = inboundTicketPlanningPayloadFromFenceMeta(meta);
|
|
14330
14766
|
const planningPayload = {};
|
|
14331
14767
|
for (const [k, v] of Object.entries(planningSource)) {
|
|
14768
|
+
if (k === "dependsOn") {
|
|
14769
|
+
if (v === null) {
|
|
14770
|
+
if (!ticketDependsOnListsEqual(ticket.dependsOn, [])) {
|
|
14771
|
+
planningPayload["dependsOn"] = null;
|
|
14772
|
+
}
|
|
14773
|
+
} else if (Array.isArray(v)) {
|
|
14774
|
+
const parsed = parseTicketDependsOnFromPayloadValue(v);
|
|
14775
|
+
if (parsed !== void 0 && !ticketDependsOnListsEqual(ticket.dependsOn, parsed)) {
|
|
14776
|
+
planningPayload["dependsOn"] = parsed;
|
|
14777
|
+
}
|
|
14778
|
+
}
|
|
14779
|
+
continue;
|
|
14780
|
+
}
|
|
14332
14781
|
const tk = k;
|
|
14333
14782
|
const cur = ticket[tk];
|
|
14334
14783
|
if (v === null) {
|
|
@@ -14522,14 +14971,15 @@ var parseCliWorkItemStatusList = (raws, deps) => {
|
|
|
14522
14971
|
}
|
|
14523
14972
|
return out;
|
|
14524
14973
|
};
|
|
14525
|
-
var buildTicketListQueryFromReadListOpts = (o, deps) => {
|
|
14974
|
+
var buildTicketListQueryFromReadListOpts = (projection, o, deps) => {
|
|
14526
14975
|
const query = {};
|
|
14527
14976
|
const statusTokens = normalizeCliStringList(o.status);
|
|
14528
14977
|
if (statusTokens.length > 0) {
|
|
14529
14978
|
query.statuses = parseCliWorkItemStatusList(statusTokens, deps);
|
|
14530
14979
|
}
|
|
14531
14980
|
if (o.epic !== void 0 && o.epic !== "") {
|
|
14532
|
-
|
|
14981
|
+
const trimmed = o.epic.trim();
|
|
14982
|
+
query.epicId = resolveEpicId(projection, trimmed) ?? trimmed;
|
|
14533
14983
|
}
|
|
14534
14984
|
const createdAfterMs = parseCliOptionalIsoMillis(
|
|
14535
14985
|
o.createdAfter,
|
|
@@ -14666,6 +15116,10 @@ var buildTicketListQueryFromReadListOpts = (o, deps) => {
|
|
|
14666
15116
|
if (targetFinishBeforeMs !== void 0) {
|
|
14667
15117
|
query.targetFinishBeforeMs = targetFinishBeforeMs;
|
|
14668
15118
|
}
|
|
15119
|
+
if (o.dependsOn !== void 0 && o.dependsOn.trim() !== "") {
|
|
15120
|
+
const trimmed = o.dependsOn.trim();
|
|
15121
|
+
query.dependsOnIncludesId = resolveTicketId(projection, trimmed) ?? trimmed;
|
|
15122
|
+
}
|
|
14669
15123
|
return Object.keys(query).length > 0 ? query : void 0;
|
|
14670
15124
|
};
|
|
14671
15125
|
var runCli = async (argv, deps = {
|
|
@@ -14741,19 +15195,19 @@ var runCli = async (argv, deps = {
|
|
|
14741
15195
|
const g = readGlobals(this);
|
|
14742
15196
|
const o = this.opts();
|
|
14743
15197
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
15198
|
+
const lines = await readAllEventLines(root);
|
|
15199
|
+
const proj = replayEvents(lines);
|
|
14744
15200
|
const id = o.id ?? ulid();
|
|
14745
15201
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
14746
|
-
const
|
|
14747
|
-
|
|
14748
|
-
|
|
14749
|
-
|
|
14750
|
-
|
|
14751
|
-
|
|
14752
|
-
|
|
14753
|
-
|
|
14754
|
-
|
|
14755
|
-
actor
|
|
14756
|
-
);
|
|
15202
|
+
const payload = {
|
|
15203
|
+
id,
|
|
15204
|
+
number: nextEpicNumberForCreate(proj),
|
|
15205
|
+
title: o.title,
|
|
15206
|
+
body: o.body,
|
|
15207
|
+
...status !== void 0 ? { status } : {}
|
|
15208
|
+
};
|
|
15209
|
+
assertCreatePayloadUsesExpectedHeadNumber(proj, "epic", payload);
|
|
15210
|
+
const evt = makeEvent("EpicCreated", payload, deps.clock, actor);
|
|
14757
15211
|
await appendEventLine(root, evt, deps.clock);
|
|
14758
15212
|
return evt.payload;
|
|
14759
15213
|
});
|
|
@@ -14769,18 +15223,19 @@ var runCli = async (argv, deps = {
|
|
|
14769
15223
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
14770
15224
|
const lines = await readAllEventLines(root);
|
|
14771
15225
|
const proj = replayEvents(lines);
|
|
14772
|
-
const
|
|
15226
|
+
const epicId = resolveEpicId(proj, o.id) ?? o.id.trim();
|
|
15227
|
+
const cur = proj.epics.get(epicId);
|
|
14773
15228
|
if (!cur || cur.deleted) {
|
|
14774
15229
|
throw new Error(`Epic not found: ${o.id}`);
|
|
14775
15230
|
}
|
|
14776
15231
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
14777
|
-
const draft = { id:
|
|
15232
|
+
const draft = { id: epicId };
|
|
14778
15233
|
if (o.title !== void 0) draft["title"] = o.title;
|
|
14779
15234
|
if (o.body !== void 0) draft["body"] = o.body;
|
|
14780
15235
|
if (status !== void 0) draft["status"] = status;
|
|
14781
15236
|
const payload = pruneEpicOrStoryUpdatePayloadAgainstRow(cur, draft);
|
|
14782
15237
|
if (isNoOpUpdatePayload(payload)) {
|
|
14783
|
-
return { id:
|
|
15238
|
+
return { id: epicId, noChanges: true };
|
|
14784
15239
|
}
|
|
14785
15240
|
const evt = makeEvent("EpicUpdated", payload, deps.clock, actor);
|
|
14786
15241
|
await appendEventLine(root, evt, deps.clock);
|
|
@@ -14791,9 +15246,12 @@ var runCli = async (argv, deps = {
|
|
|
14791
15246
|
const g = readGlobals(this);
|
|
14792
15247
|
const o = this.opts();
|
|
14793
15248
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
14794
|
-
const
|
|
15249
|
+
const lines = await readAllEventLines(root);
|
|
15250
|
+
const proj = replayEvents(lines);
|
|
15251
|
+
const epicId = resolveEpicId(proj, o.id) ?? o.id.trim();
|
|
15252
|
+
const evt = makeEvent("EpicDeleted", { id: epicId }, deps.clock, actor);
|
|
14795
15253
|
await appendEventLine(root, evt, deps.clock);
|
|
14796
|
-
return { id:
|
|
15254
|
+
return { id: epicId, deleted: true };
|
|
14797
15255
|
});
|
|
14798
15256
|
});
|
|
14799
15257
|
const story = program2.command("story");
|
|
@@ -14806,24 +15264,23 @@ var runCli = async (argv, deps = {
|
|
|
14806
15264
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
14807
15265
|
const lines = await readAllEventLines(root);
|
|
14808
15266
|
const proj = replayEvents(lines);
|
|
14809
|
-
const
|
|
15267
|
+
const epicId = resolveEpicId(proj, o.epic) ?? o.epic.trim();
|
|
15268
|
+
const epic2 = proj.epics.get(epicId);
|
|
14810
15269
|
if (!epic2 || epic2.deleted) {
|
|
14811
15270
|
throw new Error(`Epic not found: ${o.epic}`);
|
|
14812
15271
|
}
|
|
14813
15272
|
const id = o.id ?? ulid();
|
|
14814
15273
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
14815
|
-
const
|
|
14816
|
-
|
|
14817
|
-
|
|
14818
|
-
|
|
14819
|
-
|
|
14820
|
-
|
|
14821
|
-
|
|
14822
|
-
|
|
14823
|
-
|
|
14824
|
-
|
|
14825
|
-
actor
|
|
14826
|
-
);
|
|
15274
|
+
const payload = {
|
|
15275
|
+
id,
|
|
15276
|
+
number: nextStoryNumberForCreate(proj),
|
|
15277
|
+
epicId,
|
|
15278
|
+
title: o.title,
|
|
15279
|
+
body: o.body,
|
|
15280
|
+
...status !== void 0 ? { status } : {}
|
|
15281
|
+
};
|
|
15282
|
+
assertCreatePayloadUsesExpectedHeadNumber(proj, "story", payload);
|
|
15283
|
+
const evt = makeEvent("StoryCreated", payload, deps.clock, actor);
|
|
14827
15284
|
await appendEventLine(root, evt, deps.clock);
|
|
14828
15285
|
return evt.payload;
|
|
14829
15286
|
});
|
|
@@ -14842,18 +15299,19 @@ var runCli = async (argv, deps = {
|
|
|
14842
15299
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
14843
15300
|
const lines = await readAllEventLines(root);
|
|
14844
15301
|
const proj = replayEvents(lines);
|
|
14845
|
-
const
|
|
15302
|
+
const storyId = resolveStoryId(proj, o.id) ?? o.id.trim();
|
|
15303
|
+
const cur = proj.stories.get(storyId);
|
|
14846
15304
|
if (!cur || cur.deleted) {
|
|
14847
15305
|
throw new Error(`Story not found: ${o.id}`);
|
|
14848
15306
|
}
|
|
14849
15307
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
14850
|
-
const draft = { id:
|
|
15308
|
+
const draft = { id: storyId };
|
|
14851
15309
|
if (o.title !== void 0) draft["title"] = o.title;
|
|
14852
15310
|
if (o.body !== void 0) draft["body"] = o.body;
|
|
14853
15311
|
if (status !== void 0) draft["status"] = status;
|
|
14854
15312
|
const payload = pruneEpicOrStoryUpdatePayloadAgainstRow(cur, draft);
|
|
14855
15313
|
if (isNoOpUpdatePayload(payload)) {
|
|
14856
|
-
return { id:
|
|
15314
|
+
return { id: storyId, noChanges: true };
|
|
14857
15315
|
}
|
|
14858
15316
|
const evt = makeEvent("StoryUpdated", payload, deps.clock, actor);
|
|
14859
15317
|
await appendEventLine(root, evt, deps.clock);
|
|
@@ -14864,9 +15322,17 @@ var runCli = async (argv, deps = {
|
|
|
14864
15322
|
const g = readGlobals(this);
|
|
14865
15323
|
const o = this.opts();
|
|
14866
15324
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
14867
|
-
const
|
|
15325
|
+
const lines = await readAllEventLines(root);
|
|
15326
|
+
const proj = replayEvents(lines);
|
|
15327
|
+
const storyId = resolveStoryId(proj, o.id) ?? o.id.trim();
|
|
15328
|
+
const evt = makeEvent(
|
|
15329
|
+
"StoryDeleted",
|
|
15330
|
+
{ id: storyId },
|
|
15331
|
+
deps.clock,
|
|
15332
|
+
actor
|
|
15333
|
+
);
|
|
14868
15334
|
await appendEventLine(root, evt, deps.clock);
|
|
14869
|
-
return { id:
|
|
15335
|
+
return { id: storyId, deleted: true };
|
|
14870
15336
|
});
|
|
14871
15337
|
});
|
|
14872
15338
|
const ticket = program2.command("ticket");
|
|
@@ -14889,6 +15355,11 @@ var runCli = async (argv, deps = {
|
|
|
14889
15355
|
"planning label (repeatable)",
|
|
14890
15356
|
(value, previous) => [...previous, value],
|
|
14891
15357
|
[]
|
|
15358
|
+
).option(
|
|
15359
|
+
"--depends-on <id>",
|
|
15360
|
+
"prerequisite ticket id (repeatable)",
|
|
15361
|
+
(value, previous) => [...previous, value],
|
|
15362
|
+
[]
|
|
14892
15363
|
).option("--priority <p>", "low|medium|high|urgent").option("--size <s>", "xs|s|m|l|xl").option("--estimate <n>", "non-negative estimate (e.g. story points)").option("--start-at <iso>", "planned start work at (ISO-8601)").option("--target-finish-at <iso>", "planned target finish at (ISO-8601)").option("--ai-draft", "draft body via AI (explicit)", false).action(async function() {
|
|
14893
15364
|
const g = readGlobals(this);
|
|
14894
15365
|
const o = this.opts();
|
|
@@ -14951,6 +15422,7 @@ var runCli = async (argv, deps = {
|
|
|
14951
15422
|
"--target-finish-at",
|
|
14952
15423
|
deps
|
|
14953
15424
|
);
|
|
15425
|
+
const dependsOnTokensCreate = normalizeCliStringList(o.dependsOn);
|
|
14954
15426
|
const planningPayload = {
|
|
14955
15427
|
...labelsPayloadCreate,
|
|
14956
15428
|
...priorityParsed !== void 0 ? { priority: priorityParsed } : {},
|
|
@@ -14964,31 +15436,53 @@ var runCli = async (argv, deps = {
|
|
|
14964
15436
|
const proj = replayEvents(lines);
|
|
14965
15437
|
const storyRaw = o.story;
|
|
14966
15438
|
const storyTrimmed = storyRaw !== void 0 && storyRaw !== "" ? storyRaw.trim() : void 0;
|
|
15439
|
+
const storyIdResolved = storyTrimmed !== void 0 ? resolveStoryId(proj, storyTrimmed) ?? storyTrimmed : void 0;
|
|
14967
15440
|
if (storyTrimmed !== void 0) {
|
|
14968
|
-
const storyRow = proj.stories.get(
|
|
15441
|
+
const storyRow = proj.stories.get(storyIdResolved ?? "");
|
|
14969
15442
|
if (!storyRow || storyRow.deleted) {
|
|
14970
15443
|
throw new Error(`Story not found: ${storyTrimmed}`);
|
|
14971
15444
|
}
|
|
14972
15445
|
}
|
|
14973
15446
|
const id = o.id ?? ulid();
|
|
15447
|
+
const dependsOnNormCreate = resolveTicketDependsOnTokensToIds(
|
|
15448
|
+
proj,
|
|
15449
|
+
dependsOnTokensCreate
|
|
15450
|
+
);
|
|
14974
15451
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
14975
15452
|
const assigneeCreate = o.assignee !== void 0 ? { assignee: normalizeGithubLogin(o.assignee) } : {};
|
|
14976
|
-
const storyPayload =
|
|
15453
|
+
const storyPayload = storyIdResolved !== void 0 ? { storyId: storyIdResolved } : {};
|
|
14977
15454
|
const branchTokens = normalizeCliStringList(o.branch);
|
|
14978
15455
|
const branchesNorm = normalizeTicketBranchListFromStrings(branchTokens);
|
|
14979
15456
|
const branchesPayload = branchesNorm.length > 0 ? { branches: branchesNorm } : {};
|
|
15457
|
+
const dependsOnErr = validateTicketDependsOnForWrite({
|
|
15458
|
+
projection: proj,
|
|
15459
|
+
fromTicketId: id,
|
|
15460
|
+
nextDependsOn: dependsOnNormCreate
|
|
15461
|
+
});
|
|
15462
|
+
if (dependsOnErr !== void 0) {
|
|
15463
|
+
throw new Error(dependsOnErr);
|
|
15464
|
+
}
|
|
15465
|
+
const dependsOnPayloadCreate = dependsOnNormCreate.length > 0 ? { dependsOn: dependsOnNormCreate } : {};
|
|
15466
|
+
const createPayload = {
|
|
15467
|
+
id,
|
|
15468
|
+
number: nextTicketNumberForCreate(proj),
|
|
15469
|
+
...storyPayload,
|
|
15470
|
+
title: o.title,
|
|
15471
|
+
body,
|
|
15472
|
+
...status !== void 0 ? { status } : {},
|
|
15473
|
+
...assigneeCreate,
|
|
15474
|
+
...branchesPayload,
|
|
15475
|
+
...dependsOnPayloadCreate,
|
|
15476
|
+
...planningPayload
|
|
15477
|
+
};
|
|
15478
|
+
assertCreatePayloadUsesExpectedHeadNumber(
|
|
15479
|
+
proj,
|
|
15480
|
+
"ticket",
|
|
15481
|
+
createPayload
|
|
15482
|
+
);
|
|
14980
15483
|
const evt = makeEvent(
|
|
14981
15484
|
"TicketCreated",
|
|
14982
|
-
|
|
14983
|
-
id,
|
|
14984
|
-
...storyPayload,
|
|
14985
|
-
title: o.title,
|
|
14986
|
-
body,
|
|
14987
|
-
...status !== void 0 ? { status } : {},
|
|
14988
|
-
...assigneeCreate,
|
|
14989
|
-
...branchesPayload,
|
|
14990
|
-
...planningPayload
|
|
14991
|
-
},
|
|
15485
|
+
createPayload,
|
|
14992
15486
|
deps.clock,
|
|
14993
15487
|
actor
|
|
14994
15488
|
);
|
|
@@ -15044,6 +15538,9 @@ var runCli = async (argv, deps = {
|
|
|
15044
15538
|
).option(
|
|
15045
15539
|
"--branch <name>",
|
|
15046
15540
|
"when listing (no --id): only tickets linked to this branch (normalized exact match)"
|
|
15541
|
+
).option(
|
|
15542
|
+
"--depends-on <id>",
|
|
15543
|
+
"when listing (no --id): only tickets that list this ticket id in dependsOn"
|
|
15047
15544
|
).option(
|
|
15048
15545
|
"--priority <p>",
|
|
15049
15546
|
"when listing (no --id): OR-set of priorities (repeat flag); low|medium|high|urgent",
|
|
@@ -15123,7 +15620,17 @@ var runCli = async (argv, deps = {
|
|
|
15123
15620
|
"--clear-labels",
|
|
15124
15621
|
"remove all planning labels from the ticket",
|
|
15125
15622
|
false
|
|
15126
|
-
).option(
|
|
15623
|
+
).option(
|
|
15624
|
+
"--add-depends-on <id>",
|
|
15625
|
+
"add a prerequisite ticket id (repeatable)",
|
|
15626
|
+
(value, previous) => [...previous, value],
|
|
15627
|
+
[]
|
|
15628
|
+
).option(
|
|
15629
|
+
"--remove-depends-on <id>",
|
|
15630
|
+
"remove a prerequisite ticket id (repeatable)",
|
|
15631
|
+
(value, previous) => [...previous, value],
|
|
15632
|
+
[]
|
|
15633
|
+
).option("--clear-depends-on", "remove all ticket dependencies", false).option("--priority <p>", "low|medium|high|urgent").option("--clear-priority", "remove priority", false).option("--size <s>", "xs|s|m|l|xl").option("--clear-size", "remove size", false).option("--estimate <n>", "non-negative estimate (e.g. story points)").option("--clear-estimate", "remove estimate", false).option("--start-at <iso>", "planned start work at (ISO-8601)").option("--clear-start-at", "remove start work date", false).option("--target-finish-at <iso>", "planned target finish at (ISO-8601)").option("--clear-target-finish-at", "remove target finish date", false).option("--ai-improve", "expand description via AI (explicit)", false).action(async function() {
|
|
15127
15634
|
const g = readGlobals(this);
|
|
15128
15635
|
const o = this.opts();
|
|
15129
15636
|
let body = o.body;
|
|
@@ -15175,6 +15682,14 @@ ${body}`
|
|
|
15175
15682
|
);
|
|
15176
15683
|
deps.exit(ExitCode.UserError);
|
|
15177
15684
|
}
|
|
15685
|
+
const addDependsOnTokens = normalizeCliStringList(o.addDependsOn);
|
|
15686
|
+
const removeDependsOnTokens = normalizeCliStringList(o.removeDependsOn);
|
|
15687
|
+
if (o.clearDependsOn === true && (addDependsOnTokens.length > 0 || removeDependsOnTokens.length > 0)) {
|
|
15688
|
+
deps.error(
|
|
15689
|
+
"Cannot use --clear-depends-on with --add-depends-on or --remove-depends-on"
|
|
15690
|
+
);
|
|
15691
|
+
deps.exit(ExitCode.UserError);
|
|
15692
|
+
}
|
|
15178
15693
|
const mutual = (clear, set, clearName, setName) => {
|
|
15179
15694
|
if (clear === true && set !== void 0 && set !== "") {
|
|
15180
15695
|
deps.error(`Cannot use ${clearName} and ${setName} together`);
|
|
@@ -15239,25 +15754,27 @@ ${body}`
|
|
|
15239
15754
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
15240
15755
|
const lines = await readAllEventLines(root);
|
|
15241
15756
|
const proj = replayEvents(lines);
|
|
15757
|
+
const ticketId = resolveTicketId(proj, o.id) ?? o.id.trim();
|
|
15758
|
+
const storyIdResolved = storyTrimmed !== void 0 ? resolveStoryId(proj, storyTrimmed) ?? storyTrimmed : void 0;
|
|
15242
15759
|
if (storyTrimmed !== void 0) {
|
|
15243
|
-
const storyRow = proj.stories.get(
|
|
15760
|
+
const storyRow = proj.stories.get(storyIdResolved ?? "");
|
|
15244
15761
|
if (!storyRow || storyRow.deleted) {
|
|
15245
15762
|
throw new Error(`Story not found: ${storyTrimmed}`);
|
|
15246
15763
|
}
|
|
15247
15764
|
}
|
|
15248
|
-
const curTicket = proj.tickets.get(
|
|
15765
|
+
const curTicket = proj.tickets.get(ticketId);
|
|
15249
15766
|
if (curTicket === void 0 || curTicket.deleted) {
|
|
15250
15767
|
throw new Error(`Ticket not found: ${o.id}`);
|
|
15251
15768
|
}
|
|
15252
15769
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
15253
|
-
const payload = { id:
|
|
15770
|
+
const payload = { id: ticketId };
|
|
15254
15771
|
if (o.title !== void 0) payload["title"] = o.title;
|
|
15255
15772
|
if (body !== void 0) payload["body"] = body;
|
|
15256
15773
|
if (status !== void 0) payload["status"] = status;
|
|
15257
15774
|
if (o.unlinkStory) {
|
|
15258
15775
|
payload["storyId"] = null;
|
|
15259
15776
|
} else if (storyTrimmed !== void 0) {
|
|
15260
|
-
payload["storyId"] =
|
|
15777
|
+
payload["storyId"] = storyIdResolved;
|
|
15261
15778
|
}
|
|
15262
15779
|
if (o.unassign) {
|
|
15263
15780
|
payload["assignee"] = null;
|
|
@@ -15307,6 +15824,35 @@ ${body}`
|
|
|
15307
15824
|
payload["labels"] = nextLabels;
|
|
15308
15825
|
}
|
|
15309
15826
|
}
|
|
15827
|
+
const wantsDependsOnChange = o.clearDependsOn === true || addDependsOnTokens.length > 0 || removeDependsOnTokens.length > 0;
|
|
15828
|
+
if (wantsDependsOnChange) {
|
|
15829
|
+
let nextDepends;
|
|
15830
|
+
if (o.clearDependsOn === true) {
|
|
15831
|
+
nextDepends = [];
|
|
15832
|
+
} else {
|
|
15833
|
+
const removeDepSet = new Set(
|
|
15834
|
+
resolveTicketDependsOnTokensToIds(proj, removeDependsOnTokens)
|
|
15835
|
+
);
|
|
15836
|
+
nextDepends = normalizeTicketDependsOnIds(
|
|
15837
|
+
(curTicket.dependsOn ?? []).filter((d) => !removeDepSet.has(d))
|
|
15838
|
+
);
|
|
15839
|
+
nextDepends = normalizeTicketDependsOnIds([
|
|
15840
|
+
...nextDepends,
|
|
15841
|
+
...resolveTicketDependsOnTokensToIds(proj, addDependsOnTokens)
|
|
15842
|
+
]);
|
|
15843
|
+
}
|
|
15844
|
+
const depErr = validateTicketDependsOnForWrite({
|
|
15845
|
+
projection: proj,
|
|
15846
|
+
fromTicketId: ticketId,
|
|
15847
|
+
nextDependsOn: nextDepends
|
|
15848
|
+
});
|
|
15849
|
+
if (depErr !== void 0) {
|
|
15850
|
+
throw new Error(depErr);
|
|
15851
|
+
}
|
|
15852
|
+
if (!ticketDependsOnListsEqual(curTicket.dependsOn, nextDepends)) {
|
|
15853
|
+
payload["dependsOn"] = nextDepends;
|
|
15854
|
+
}
|
|
15855
|
+
}
|
|
15310
15856
|
if (priorityUpdate !== void 0) {
|
|
15311
15857
|
payload["priority"] = priorityUpdate;
|
|
15312
15858
|
}
|
|
@@ -15327,7 +15873,7 @@ ${body}`
|
|
|
15327
15873
|
payload
|
|
15328
15874
|
);
|
|
15329
15875
|
if (isNoOpUpdatePayload(prunedPayload)) {
|
|
15330
|
-
return { id:
|
|
15876
|
+
return { id: ticketId, noChanges: true };
|
|
15331
15877
|
}
|
|
15332
15878
|
const evt = makeEvent(
|
|
15333
15879
|
"TicketUpdated",
|
|
@@ -15364,25 +15910,25 @@ ${body}`
|
|
|
15364
15910
|
runGit
|
|
15365
15911
|
);
|
|
15366
15912
|
}
|
|
15367
|
-
const preferred = normalizeTicketBranchName(
|
|
15368
|
-
o.branch ?? `hyper-pm/${o.id}`
|
|
15369
|
-
);
|
|
15370
|
-
if (preferred === void 0) {
|
|
15371
|
-
deps.error(
|
|
15372
|
-
"Invalid --branch or default branch name for this ticket id"
|
|
15373
|
-
);
|
|
15374
|
-
deps.exit(ExitCode.UserError);
|
|
15375
|
-
}
|
|
15376
15913
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
15377
15914
|
const lines = await readAllEventLines(root);
|
|
15378
15915
|
const proj = replayEvents(lines);
|
|
15379
|
-
const
|
|
15916
|
+
const ticketId = resolveTicketId(proj, o.id) ?? o.id.trim();
|
|
15917
|
+
const preferred = normalizeTicketBranchName(
|
|
15918
|
+
o.branch ?? `hyper-pm/${ticketId}`
|
|
15919
|
+
);
|
|
15920
|
+
if (preferred === void 0) {
|
|
15921
|
+
throw new Error(
|
|
15922
|
+
"Invalid --branch or default branch name for this ticket id"
|
|
15923
|
+
);
|
|
15924
|
+
}
|
|
15925
|
+
const curRow = proj.tickets.get(ticketId);
|
|
15380
15926
|
if (curRow === void 0 || curRow.deleted) {
|
|
15381
15927
|
throw new Error(`Ticket not found: ${o.id}`);
|
|
15382
15928
|
}
|
|
15383
15929
|
if (curRow.status === "done" || curRow.status === "cancelled") {
|
|
15384
15930
|
throw new Error(
|
|
15385
|
-
`Ticket ${
|
|
15931
|
+
`Ticket ${ticketId} is ${curRow.status}; change status before starting work`
|
|
15386
15932
|
);
|
|
15387
15933
|
}
|
|
15388
15934
|
const { branch: chosenBranch } = await pickUniqueLocalBranchName({
|
|
@@ -15403,7 +15949,7 @@ ${body}`
|
|
|
15403
15949
|
}
|
|
15404
15950
|
next = normalizeTicketBranchListFromStrings(next);
|
|
15405
15951
|
const payload = {
|
|
15406
|
-
id:
|
|
15952
|
+
id: ticketId,
|
|
15407
15953
|
status: "in_progress"
|
|
15408
15954
|
};
|
|
15409
15955
|
if (!ticketBranchListsEqual(next, curRow.linkedBranches)) {
|
|
@@ -15412,7 +15958,7 @@ ${body}`
|
|
|
15412
15958
|
const evt = makeEvent("TicketUpdated", payload, deps.clock, actor);
|
|
15413
15959
|
await appendEventLine(root, evt, deps.clock);
|
|
15414
15960
|
const result = {
|
|
15415
|
-
id:
|
|
15961
|
+
id: ticketId,
|
|
15416
15962
|
status: "in_progress",
|
|
15417
15963
|
branch: chosenBranch,
|
|
15418
15964
|
branches: next
|
|
@@ -15434,27 +15980,36 @@ ${body}`
|
|
|
15434
15980
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
15435
15981
|
const lines = await readAllEventLines(root);
|
|
15436
15982
|
const proj = replayEvents(lines);
|
|
15437
|
-
const
|
|
15983
|
+
const ticketId = resolveTicketId(proj, o.id) ?? o.id.trim();
|
|
15984
|
+
const row = proj.tickets.get(ticketId);
|
|
15438
15985
|
if (!row || row.deleted) {
|
|
15439
15986
|
throw new Error(`Ticket not found: ${o.id}`);
|
|
15440
15987
|
}
|
|
15441
15988
|
const evt = makeEvent(
|
|
15442
15989
|
"TicketCommentAdded",
|
|
15443
|
-
{ ticketId
|
|
15990
|
+
{ ticketId, body: trimmed },
|
|
15444
15991
|
deps.clock,
|
|
15445
15992
|
actor
|
|
15446
15993
|
);
|
|
15447
15994
|
await appendEventLine(root, evt, deps.clock);
|
|
15448
|
-
return { commentId: evt.id, ticketId
|
|
15995
|
+
return { commentId: evt.id, ticketId, body: trimmed };
|
|
15449
15996
|
});
|
|
15450
15997
|
});
|
|
15451
15998
|
ticket.command("delete").requiredOption("--id <id>").action(async function() {
|
|
15452
15999
|
const g = readGlobals(this);
|
|
15453
16000
|
const o = this.opts();
|
|
15454
16001
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
15455
|
-
const
|
|
16002
|
+
const lines = await readAllEventLines(root);
|
|
16003
|
+
const proj = replayEvents(lines);
|
|
16004
|
+
const ticketId = resolveTicketId(proj, o.id) ?? o.id.trim();
|
|
16005
|
+
const evt = makeEvent(
|
|
16006
|
+
"TicketDeleted",
|
|
16007
|
+
{ id: ticketId },
|
|
16008
|
+
deps.clock,
|
|
16009
|
+
actor
|
|
16010
|
+
);
|
|
15456
16011
|
await appendEventLine(root, evt, deps.clock);
|
|
15457
|
-
return { id:
|
|
16012
|
+
return { id: ticketId, deleted: true };
|
|
15458
16013
|
});
|
|
15459
16014
|
});
|
|
15460
16015
|
ticket.command("import-github").description(
|
|
@@ -15525,7 +16080,8 @@ ${body}`
|
|
|
15525
16080
|
const storyRaw = o.story;
|
|
15526
16081
|
const storyTrimmed = storyRaw !== void 0 && storyRaw !== "" ? storyRaw.trim() : void 0;
|
|
15527
16082
|
if (storyTrimmed !== void 0 && storyTrimmed !== "") {
|
|
15528
|
-
const
|
|
16083
|
+
const storyIdForImport = resolveStoryId(proj, storyTrimmed) ?? storyTrimmed;
|
|
16084
|
+
const storyRow = proj.stories.get(storyIdForImport);
|
|
15529
16085
|
if (!storyRow || storyRow.deleted) {
|
|
15530
16086
|
deps.error(`Story not found: ${storyTrimmed}`);
|
|
15531
16087
|
deps.exit(ExitCode.UserError);
|
|
@@ -15568,12 +16124,22 @@ ${body}`
|
|
|
15568
16124
|
);
|
|
15569
16125
|
} else {
|
|
15570
16126
|
const imported = [];
|
|
16127
|
+
let importProj = proj;
|
|
16128
|
+
let importLines = lines;
|
|
16129
|
+
const storyIdForPayload = storyTrimmed !== void 0 && storyTrimmed !== "" ? resolveStoryId(importProj, storyTrimmed) ?? storyTrimmed : void 0;
|
|
15571
16130
|
for (const c of candidates) {
|
|
15572
16131
|
const ticketId = ulid();
|
|
16132
|
+
const nextNum = nextTicketNumberForCreate(importProj);
|
|
15573
16133
|
const createPayload = mergeTicketImportCreatePayload(
|
|
15574
16134
|
ticketId,
|
|
15575
16135
|
c.ticketCreatedPayloadBase,
|
|
15576
|
-
|
|
16136
|
+
storyIdForPayload,
|
|
16137
|
+
nextNum
|
|
16138
|
+
);
|
|
16139
|
+
assertCreatePayloadUsesExpectedHeadNumber(
|
|
16140
|
+
importProj,
|
|
16141
|
+
"ticket",
|
|
16142
|
+
createPayload
|
|
15577
16143
|
);
|
|
15578
16144
|
const createdEvt = makeEvent(
|
|
15579
16145
|
"TicketCreated",
|
|
@@ -15594,6 +16160,8 @@ ${body}`
|
|
|
15594
16160
|
);
|
|
15595
16161
|
await appendEventLine(session.worktreePath, linkEvt, deps.clock);
|
|
15596
16162
|
imported.push({ ticketId, issueNumber: c.issueNumber });
|
|
16163
|
+
importLines = await readAllEventLines(session.worktreePath);
|
|
16164
|
+
importProj = replayEvents(importLines);
|
|
15597
16165
|
}
|
|
15598
16166
|
await commitDataWorktreeIfNeeded(
|
|
15599
16167
|
session.worktreePath,
|
|
@@ -15620,7 +16188,19 @@ ${body}`
|
|
|
15620
16188
|
}
|
|
15621
16189
|
deps.exit(ExitCode.Success);
|
|
15622
16190
|
});
|
|
15623
|
-
program2.command("sync").description("
|
|
16191
|
+
program2.command("sync").description("Sync data branch over git; optional GitHub Issues sync").option(
|
|
16192
|
+
"--skip-network",
|
|
16193
|
+
"skip all sync network operations (git fetch/merge/push and GitHub); legacy: --no-github",
|
|
16194
|
+
false
|
|
16195
|
+
).option(
|
|
16196
|
+
"--with-github",
|
|
16197
|
+
"also run GitHub Issues sync (requires GITHUB_TOKEN or gh; needs sync not off in config)",
|
|
16198
|
+
false
|
|
16199
|
+
).option(
|
|
16200
|
+
"--git-data",
|
|
16201
|
+
"legacy no-op: default sync already updates the data branch via git only",
|
|
16202
|
+
false
|
|
16203
|
+
).option(
|
|
15624
16204
|
"--skip-push",
|
|
15625
16205
|
"after a successful sync, do not push the data branch to the remote",
|
|
15626
16206
|
false
|
|
@@ -15629,115 +16209,171 @@ ${body}`
|
|
|
15629
16209
|
const o = this.opts();
|
|
15630
16210
|
const repoRoot = await resolveRepoRoot(g.repo);
|
|
15631
16211
|
const cfg = await loadMergedConfig(repoRoot, g);
|
|
15632
|
-
if (
|
|
16212
|
+
if (o.withGithub && o.skipNetwork) {
|
|
16213
|
+
deps.error("Cannot use --with-github together with --skip-network");
|
|
16214
|
+
deps.exit(ExitCode.UserError);
|
|
16215
|
+
}
|
|
16216
|
+
if (o.skipNetwork) {
|
|
15633
16217
|
deps.log(formatOutput(g.format, { ok: true, skipped: true }));
|
|
15634
16218
|
deps.exit(ExitCode.Success);
|
|
15635
16219
|
}
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
|
|
15650
|
-
|
|
15651
|
-
|
|
15652
|
-
|
|
15653
|
-
|
|
15654
|
-
|
|
15655
|
-
const projection = await loadProjectionFromDataRoot(
|
|
15656
|
-
session.worktreePath
|
|
15657
|
-
);
|
|
15658
|
-
const gitDerivedSlug = await tryReadGithubOwnerRepoSlugFromGit({
|
|
16220
|
+
if (o.withGithub) {
|
|
16221
|
+
if (cfg.sync === "off") {
|
|
16222
|
+
deps.error(
|
|
16223
|
+
"GitHub Issues sync is off in config (sync: off). Omit --with-github to sync the data branch via git only, or set sync to outbound/full."
|
|
16224
|
+
);
|
|
16225
|
+
deps.exit(ExitCode.UserError);
|
|
16226
|
+
}
|
|
16227
|
+
const githubToken = await resolveGithubTokenForSync({
|
|
16228
|
+
envToken: env.GITHUB_TOKEN,
|
|
16229
|
+
cwd: repoRoot
|
|
16230
|
+
});
|
|
16231
|
+
if (!githubToken) {
|
|
16232
|
+
deps.error(
|
|
16233
|
+
"GitHub auth required for sync --with-github: set GITHUB_TOKEN or run `gh auth login`"
|
|
16234
|
+
);
|
|
16235
|
+
deps.exit(ExitCode.EnvironmentAuth);
|
|
16236
|
+
}
|
|
16237
|
+
const tmpBase = g.tempDir ?? env.TMPDIR ?? (0, import_node_os2.tmpdir)();
|
|
16238
|
+
const session = await openDataBranchWorktree({
|
|
15659
16239
|
repoRoot,
|
|
15660
|
-
|
|
16240
|
+
dataBranch: cfg.dataBranch,
|
|
16241
|
+
tmpBase,
|
|
16242
|
+
keepWorktree: g.keepWorktree,
|
|
15661
16243
|
runGit
|
|
15662
16244
|
});
|
|
15663
|
-
|
|
15664
|
-
|
|
15665
|
-
|
|
15666
|
-
|
|
15667
|
-
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
|
|
15671
|
-
|
|
15672
|
-
owner,
|
|
15673
|
-
|
|
15674
|
-
|
|
15675
|
-
|
|
15676
|
-
|
|
15677
|
-
|
|
15678
|
-
|
|
15679
|
-
|
|
15680
|
-
config: cfg,
|
|
15681
|
-
deps: depsGh
|
|
15682
|
-
});
|
|
15683
|
-
await runGithubInboundSync({
|
|
15684
|
-
dataRoot: session.worktreePath,
|
|
15685
|
-
projection,
|
|
15686
|
-
config: cfg,
|
|
15687
|
-
deps: depsGh
|
|
15688
|
-
});
|
|
15689
|
-
const projectionAfterInbound = await loadProjectionFromDataRoot(
|
|
15690
|
-
session.worktreePath
|
|
15691
|
-
);
|
|
15692
|
-
await runGithubPrActivitySync({
|
|
15693
|
-
projection: projectionAfterInbound,
|
|
15694
|
-
config: cfg,
|
|
15695
|
-
deps: defaultGithubPrActivitySyncDeps({
|
|
15696
|
-
dataRoot: session.worktreePath,
|
|
15697
|
-
clock: deps.clock,
|
|
16245
|
+
try {
|
|
16246
|
+
const projection = await loadProjectionFromDataRoot(
|
|
16247
|
+
session.worktreePath
|
|
16248
|
+
);
|
|
16249
|
+
const gitDerivedSlug = await tryReadGithubOwnerRepoSlugFromGit({
|
|
16250
|
+
repoRoot,
|
|
16251
|
+
remote: cfg.remote,
|
|
16252
|
+
runGit
|
|
16253
|
+
});
|
|
16254
|
+
const { owner, repo: repo2 } = resolveGithubRepo(
|
|
16255
|
+
cfg,
|
|
16256
|
+
env.GITHUB_REPO,
|
|
16257
|
+
gitDerivedSlug
|
|
16258
|
+
);
|
|
16259
|
+
const octokit = new Octokit2({ auth: githubToken });
|
|
16260
|
+
const outboundActor = await resolveGithubTokenActor(octokit);
|
|
16261
|
+
const depsGh = {
|
|
15698
16262
|
octokit,
|
|
15699
16263
|
owner,
|
|
15700
16264
|
repo: repo2,
|
|
15701
|
-
|
|
15702
|
-
|
|
15703
|
-
|
|
15704
|
-
|
|
15705
|
-
|
|
15706
|
-
|
|
15707
|
-
|
|
15708
|
-
|
|
15709
|
-
|
|
15710
|
-
|
|
15711
|
-
|
|
15712
|
-
|
|
15713
|
-
|
|
15714
|
-
|
|
15715
|
-
|
|
16265
|
+
clock: deps.clock,
|
|
16266
|
+
outboundActor
|
|
16267
|
+
};
|
|
16268
|
+
await runGithubOutboundSync({
|
|
16269
|
+
dataRoot: session.worktreePath,
|
|
16270
|
+
projection,
|
|
16271
|
+
config: cfg,
|
|
16272
|
+
deps: depsGh
|
|
16273
|
+
});
|
|
16274
|
+
await runGithubInboundSync({
|
|
16275
|
+
dataRoot: session.worktreePath,
|
|
16276
|
+
projection,
|
|
16277
|
+
config: cfg,
|
|
16278
|
+
deps: depsGh
|
|
16279
|
+
});
|
|
16280
|
+
const projectionAfterInbound = await loadProjectionFromDataRoot(
|
|
16281
|
+
session.worktreePath
|
|
16282
|
+
);
|
|
16283
|
+
await runGithubPrActivitySync({
|
|
16284
|
+
projection: projectionAfterInbound,
|
|
16285
|
+
config: cfg,
|
|
16286
|
+
deps: defaultGithubPrActivitySyncDeps({
|
|
16287
|
+
dataRoot: session.worktreePath,
|
|
16288
|
+
clock: deps.clock,
|
|
16289
|
+
octokit,
|
|
16290
|
+
owner,
|
|
16291
|
+
repo: repo2,
|
|
16292
|
+
actor: outboundActor
|
|
16293
|
+
})
|
|
16294
|
+
});
|
|
16295
|
+
await commitDataWorktreeIfNeeded(
|
|
15716
16296
|
session.worktreePath,
|
|
15717
|
-
|
|
15718
|
-
cfg.dataBranch,
|
|
16297
|
+
formatDataBranchCommitMessage("hyper-pm: sync", outboundActor),
|
|
15719
16298
|
runGit
|
|
15720
16299
|
);
|
|
15721
|
-
dataBranchPush
|
|
15722
|
-
dataBranchPushDetail
|
|
15723
|
-
if (
|
|
15724
|
-
|
|
15725
|
-
|
|
16300
|
+
let dataBranchPush;
|
|
16301
|
+
let dataBranchPushDetail;
|
|
16302
|
+
if (o.skipPush) {
|
|
16303
|
+
dataBranchPush = "skipped_cli";
|
|
16304
|
+
dataBranchPushDetail = "skip-push";
|
|
16305
|
+
} else {
|
|
16306
|
+
const pushResult = await tryPushDataBranchToRemote(
|
|
16307
|
+
session.worktreePath,
|
|
16308
|
+
cfg.remote,
|
|
16309
|
+
cfg.dataBranch,
|
|
16310
|
+
runGit
|
|
15726
16311
|
);
|
|
16312
|
+
dataBranchPush = pushResult.status;
|
|
16313
|
+
dataBranchPushDetail = pushResult.detail;
|
|
16314
|
+
if (pushResult.status === "failed" && pushResult.detail) {
|
|
16315
|
+
deps.error(
|
|
16316
|
+
`hyper-pm: data branch not pushed (${cfg.remote}/${cfg.dataBranch}): ${pushResult.detail}`
|
|
16317
|
+
);
|
|
16318
|
+
}
|
|
15727
16319
|
}
|
|
16320
|
+
deps.log(
|
|
16321
|
+
formatOutput(g.format, {
|
|
16322
|
+
ok: true,
|
|
16323
|
+
githubSync: true,
|
|
16324
|
+
dataBranchPush,
|
|
16325
|
+
...dataBranchPushDetail !== void 0 ? { dataBranchPushDetail } : {}
|
|
16326
|
+
})
|
|
16327
|
+
);
|
|
16328
|
+
} catch (e) {
|
|
16329
|
+
deps.error(e instanceof Error ? e.message : String(e));
|
|
16330
|
+
deps.exit(ExitCode.ExternalApi);
|
|
16331
|
+
} finally {
|
|
16332
|
+
await session.dispose();
|
|
16333
|
+
}
|
|
16334
|
+
deps.exit(ExitCode.Success);
|
|
16335
|
+
}
|
|
16336
|
+
const tmpBaseGit = g.tempDir ?? env.TMPDIR ?? (0, import_node_os2.tmpdir)();
|
|
16337
|
+
const sessionGit = await openDataBranchWorktree({
|
|
16338
|
+
repoRoot,
|
|
16339
|
+
dataBranch: cfg.dataBranch,
|
|
16340
|
+
tmpBase: tmpBaseGit,
|
|
16341
|
+
keepWorktree: g.keepWorktree,
|
|
16342
|
+
runGit
|
|
16343
|
+
});
|
|
16344
|
+
try {
|
|
16345
|
+
let syncResult;
|
|
16346
|
+
try {
|
|
16347
|
+
syncResult = await runRemoteDataBranchGitSync(
|
|
16348
|
+
sessionGit.worktreePath,
|
|
16349
|
+
cfg.remote,
|
|
16350
|
+
cfg.dataBranch,
|
|
16351
|
+
runGit,
|
|
16352
|
+
Boolean(o.skipPush)
|
|
16353
|
+
);
|
|
16354
|
+
} catch (e) {
|
|
16355
|
+
if (e instanceof SyncRemoteDataBranchMergeError) {
|
|
16356
|
+
deps.error(e.message);
|
|
16357
|
+
deps.exit(ExitCode.UserError);
|
|
16358
|
+
}
|
|
16359
|
+
deps.error(e instanceof Error ? e.message : String(e));
|
|
16360
|
+
deps.exit(ExitCode.UserError);
|
|
16361
|
+
}
|
|
16362
|
+
if (syncResult.dataBranchPush === "failed" && syncResult.dataBranchPushDetail !== void 0) {
|
|
16363
|
+
deps.error(
|
|
16364
|
+
`hyper-pm: data branch not pushed (${cfg.remote}/${cfg.dataBranch}): ${syncResult.dataBranchPushDetail}`
|
|
16365
|
+
);
|
|
15728
16366
|
}
|
|
15729
16367
|
deps.log(
|
|
15730
16368
|
formatOutput(g.format, {
|
|
15731
16369
|
ok: true,
|
|
15732
|
-
|
|
15733
|
-
...
|
|
16370
|
+
gitDataOnly: true,
|
|
16371
|
+
...o.gitData ? { legacyGitDataFlag: true } : {},
|
|
16372
|
+
...syncResult
|
|
15734
16373
|
})
|
|
15735
16374
|
);
|
|
15736
|
-
} catch (e) {
|
|
15737
|
-
deps.error(e instanceof Error ? e.message : String(e));
|
|
15738
|
-
deps.exit(ExitCode.ExternalApi);
|
|
15739
16375
|
} finally {
|
|
15740
|
-
await
|
|
16376
|
+
await sessionGit.dispose();
|
|
15741
16377
|
}
|
|
15742
16378
|
deps.exit(ExitCode.Success);
|
|
15743
16379
|
});
|
|
@@ -15903,7 +16539,7 @@ ${body}`
|
|
|
15903
16539
|
await session.dispose();
|
|
15904
16540
|
}
|
|
15905
16541
|
});
|
|
15906
|
-
await program2.parseAsync(argv, { from: "node" });
|
|
16542
|
+
await program2.parseAsync(normalizeRawCliArgv(argv), { from: "node" });
|
|
15907
16543
|
};
|
|
15908
16544
|
var makeEvent = (type, payload, clock, actor) => ({
|
|
15909
16545
|
schema: 1,
|
|
@@ -15993,7 +16629,8 @@ var readEpic = async (g, id, deps) => {
|
|
|
15993
16629
|
formatOutput(g.format, { items: listActiveEpicSummaries(proj) })
|
|
15994
16630
|
);
|
|
15995
16631
|
} else {
|
|
15996
|
-
const
|
|
16632
|
+
const epicId = resolveEpicId(proj, id) ?? id.trim();
|
|
16633
|
+
const row = proj.epics.get(epicId);
|
|
15997
16634
|
if (!row || row.deleted) {
|
|
15998
16635
|
deps.error("Epic not found");
|
|
15999
16636
|
exitCode = ExitCode.UserError;
|
|
@@ -16028,7 +16665,8 @@ var readStory = async (g, opts, deps) => {
|
|
|
16028
16665
|
const proj = replayEvents(lines);
|
|
16029
16666
|
const { id, epicId } = opts;
|
|
16030
16667
|
if (id === void 0 || id === "") {
|
|
16031
|
-
const
|
|
16668
|
+
const epicFilterRaw = epicId !== void 0 && epicId !== "" ? epicId : void 0;
|
|
16669
|
+
const epicFilter = epicFilterRaw !== void 0 ? resolveEpicId(proj, epicFilterRaw) ?? epicFilterRaw.trim() : void 0;
|
|
16032
16670
|
if (epicFilter !== void 0) {
|
|
16033
16671
|
const epicRow = proj.epics.get(epicFilter);
|
|
16034
16672
|
if (!epicRow || epicRow.deleted) {
|
|
@@ -16049,7 +16687,8 @@ var readStory = async (g, opts, deps) => {
|
|
|
16049
16687
|
);
|
|
16050
16688
|
}
|
|
16051
16689
|
} else {
|
|
16052
|
-
const
|
|
16690
|
+
const storyId = resolveStoryId(proj, id) ?? id.trim();
|
|
16691
|
+
const row = proj.stories.get(storyId);
|
|
16053
16692
|
if (!row || row.deleted) {
|
|
16054
16693
|
deps.error("Story not found");
|
|
16055
16694
|
exitCode = ExitCode.UserError;
|
|
@@ -16093,8 +16732,8 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16093
16732
|
} = opts;
|
|
16094
16733
|
if (id === void 0 || id === "") {
|
|
16095
16734
|
const listWithoutStory = withoutStoryRaw === true;
|
|
16096
|
-
const storyFilter = storyIdRaw !== void 0 && storyIdRaw !== "" ? storyIdRaw : void 0;
|
|
16097
|
-
const epicFilter = epicIdRaw !== void 0 && epicIdRaw !== "" ? epicIdRaw : void 0;
|
|
16735
|
+
const storyFilter = storyIdRaw !== void 0 && storyIdRaw !== "" ? resolveStoryId(proj, storyIdRaw) ?? storyIdRaw.trim() : void 0;
|
|
16736
|
+
const epicFilter = epicIdRaw !== void 0 && epicIdRaw !== "" ? resolveEpicId(proj, epicIdRaw) ?? epicIdRaw.trim() : void 0;
|
|
16098
16737
|
const sortBy = tryParseTicketListSortField(sortByOpt);
|
|
16099
16738
|
const sortDir = tryParseTicketListSortDir(sortDirOpt);
|
|
16100
16739
|
if (sortBy === void 0) {
|
|
@@ -16133,6 +16772,7 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16133
16772
|
exitCode = ExitCode.UserError;
|
|
16134
16773
|
} else {
|
|
16135
16774
|
const listQuery = buildTicketListQueryFromReadListOpts(
|
|
16775
|
+
proj,
|
|
16136
16776
|
{ epic: epicFilter, ...listFlagRest },
|
|
16137
16777
|
deps
|
|
16138
16778
|
);
|
|
@@ -16153,6 +16793,7 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16153
16793
|
exitCode = ExitCode.UserError;
|
|
16154
16794
|
} else {
|
|
16155
16795
|
const listQuery = buildTicketListQueryFromReadListOpts(
|
|
16796
|
+
proj,
|
|
16156
16797
|
listFlagRest,
|
|
16157
16798
|
deps
|
|
16158
16799
|
);
|
|
@@ -16169,6 +16810,7 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16169
16810
|
}
|
|
16170
16811
|
} else if (listWithoutStory) {
|
|
16171
16812
|
const listQuery = buildTicketListQueryFromReadListOpts(
|
|
16813
|
+
proj,
|
|
16172
16814
|
{ withoutStory: true, ...listFlagRest },
|
|
16173
16815
|
deps
|
|
16174
16816
|
);
|
|
@@ -16183,6 +16825,7 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16183
16825
|
);
|
|
16184
16826
|
} else {
|
|
16185
16827
|
const listQuery = buildTicketListQueryFromReadListOpts(
|
|
16828
|
+
proj,
|
|
16186
16829
|
{ epic: epicFilter, ...listFlagRest },
|
|
16187
16830
|
deps
|
|
16188
16831
|
);
|
|
@@ -16197,7 +16840,8 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16197
16840
|
);
|
|
16198
16841
|
}
|
|
16199
16842
|
} else {
|
|
16200
|
-
const
|
|
16843
|
+
const ticketId = resolveTicketId(proj, id) ?? id.trim();
|
|
16844
|
+
const row = proj.tickets.get(ticketId);
|
|
16201
16845
|
if (!row || row.deleted) {
|
|
16202
16846
|
deps.error("Ticket not found");
|
|
16203
16847
|
exitCode = ExitCode.UserError;
|