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/main.cjs
CHANGED
|
@@ -11530,6 +11530,103 @@ var normalizeTicketBranchListFromPayloadValue = (value) => {
|
|
|
11530
11530
|
return normalizeTicketBranchListFromStrings(strings);
|
|
11531
11531
|
};
|
|
11532
11532
|
|
|
11533
|
+
// src/lib/ticket-depends-on.ts
|
|
11534
|
+
var normalizeTicketDependsOnIds = (ids) => {
|
|
11535
|
+
const out = [];
|
|
11536
|
+
const seen = /* @__PURE__ */ new Set();
|
|
11537
|
+
for (const raw of ids) {
|
|
11538
|
+
const t = raw.trim();
|
|
11539
|
+
if (t === "") continue;
|
|
11540
|
+
if (seen.has(t)) continue;
|
|
11541
|
+
seen.add(t);
|
|
11542
|
+
out.push(t);
|
|
11543
|
+
}
|
|
11544
|
+
return out;
|
|
11545
|
+
};
|
|
11546
|
+
var ticketDependsOnListsEqual = (a, b) => {
|
|
11547
|
+
const an = normalizeTicketDependsOnIds(a ?? []);
|
|
11548
|
+
const bn = normalizeTicketDependsOnIds(b ?? []);
|
|
11549
|
+
if (an.length !== bn.length) return false;
|
|
11550
|
+
return an.every((x, i) => x === bn[i]);
|
|
11551
|
+
};
|
|
11552
|
+
var parseTicketDependsOnFromPayloadValue = (value) => {
|
|
11553
|
+
if (!Array.isArray(value)) return void 0;
|
|
11554
|
+
const strings = [];
|
|
11555
|
+
for (const x of value) {
|
|
11556
|
+
if (typeof x !== "string") return void 0;
|
|
11557
|
+
strings.push(x);
|
|
11558
|
+
}
|
|
11559
|
+
return normalizeTicketDependsOnIds(strings);
|
|
11560
|
+
};
|
|
11561
|
+
var parseTicketDependsOnFromFenceValue = (value) => {
|
|
11562
|
+
if (!Array.isArray(value)) return void 0;
|
|
11563
|
+
const strings = [];
|
|
11564
|
+
for (const x of value) {
|
|
11565
|
+
if (typeof x === "string") strings.push(x);
|
|
11566
|
+
}
|
|
11567
|
+
return normalizeTicketDependsOnIds(strings);
|
|
11568
|
+
};
|
|
11569
|
+
var wouldTicketDependsOnCreateCycle = (params) => {
|
|
11570
|
+
const { fromTicketId, nextDependsOn, successorsFor } = params;
|
|
11571
|
+
const dfsFromPrerequisite = (start) => {
|
|
11572
|
+
const stack = [start];
|
|
11573
|
+
const visited = /* @__PURE__ */ new Set();
|
|
11574
|
+
while (stack.length > 0) {
|
|
11575
|
+
const node = stack.pop();
|
|
11576
|
+
if (node === fromTicketId) return true;
|
|
11577
|
+
if (visited.has(node)) continue;
|
|
11578
|
+
visited.add(node);
|
|
11579
|
+
const next = successorsFor(node) ?? [];
|
|
11580
|
+
for (let i = next.length - 1; i >= 0; i -= 1) {
|
|
11581
|
+
stack.push(next[i]);
|
|
11582
|
+
}
|
|
11583
|
+
}
|
|
11584
|
+
return false;
|
|
11585
|
+
};
|
|
11586
|
+
for (const p of nextDependsOn) {
|
|
11587
|
+
if (dfsFromPrerequisite(p)) return true;
|
|
11588
|
+
}
|
|
11589
|
+
return false;
|
|
11590
|
+
};
|
|
11591
|
+
var ticketDependsOnSuccessorsForProjection = (projection, fromTicketId, nextDependsOn) => {
|
|
11592
|
+
return (ticketId) => {
|
|
11593
|
+
if (ticketId === fromTicketId) {
|
|
11594
|
+
return nextDependsOn.length > 0 ? nextDependsOn : void 0;
|
|
11595
|
+
}
|
|
11596
|
+
const row = projection.tickets.get(ticketId);
|
|
11597
|
+
if (!row || row.deleted) return void 0;
|
|
11598
|
+
return row.dependsOn;
|
|
11599
|
+
};
|
|
11600
|
+
};
|
|
11601
|
+
var validateTicketDependsOnForWrite = (params) => {
|
|
11602
|
+
const { projection, fromTicketId, nextDependsOn } = params;
|
|
11603
|
+
for (const id of nextDependsOn) {
|
|
11604
|
+
if (id === fromTicketId) {
|
|
11605
|
+
return `Ticket cannot depend on itself (${id})`;
|
|
11606
|
+
}
|
|
11607
|
+
const row = projection.tickets.get(id);
|
|
11608
|
+
if (row === void 0) {
|
|
11609
|
+
return `Dependency ticket not found: ${id}`;
|
|
11610
|
+
}
|
|
11611
|
+
if (row.deleted) {
|
|
11612
|
+
return `Dependency ticket deleted: ${id}`;
|
|
11613
|
+
}
|
|
11614
|
+
}
|
|
11615
|
+
const successorsFor = ticketDependsOnSuccessorsForProjection(
|
|
11616
|
+
projection,
|
|
11617
|
+
fromTicketId,
|
|
11618
|
+
nextDependsOn
|
|
11619
|
+
);
|
|
11620
|
+
if (wouldTicketDependsOnCreateCycle({
|
|
11621
|
+
fromTicketId,
|
|
11622
|
+
nextDependsOn,
|
|
11623
|
+
successorsFor
|
|
11624
|
+
})) {
|
|
11625
|
+
return "Ticket dependencies would create a cycle";
|
|
11626
|
+
}
|
|
11627
|
+
return void 0;
|
|
11628
|
+
};
|
|
11629
|
+
|
|
11533
11630
|
// src/lib/ticket-planning-fields.ts
|
|
11534
11631
|
var PRIORITY_SET = /* @__PURE__ */ new Set([
|
|
11535
11632
|
"low",
|
|
@@ -11698,6 +11795,9 @@ var formatOutput = (format, value) => {
|
|
|
11698
11795
|
return JSON.stringify(value);
|
|
11699
11796
|
};
|
|
11700
11797
|
|
|
11798
|
+
// src/cli/normalize-raw-cli-argv.ts
|
|
11799
|
+
var normalizeRawCliArgv = (argv) => argv.map((token) => token === "--no-github" ? "--skip-network" : token);
|
|
11800
|
+
|
|
11701
11801
|
// src/cli/prune-unchanged-work-item-update-payload.ts
|
|
11702
11802
|
var branchListsEqual = (a, b) => a.length === b.length && a.every((x, i) => x === b[i]);
|
|
11703
11803
|
var pruneEpicOrStoryUpdatePayloadAgainstRow = (cur, draft) => {
|
|
@@ -11753,6 +11853,20 @@ var pruneTicketUpdatePayloadAgainstRow = (cur, draft) => {
|
|
|
11753
11853
|
out["labels"] = draft["labels"];
|
|
11754
11854
|
}
|
|
11755
11855
|
}
|
|
11856
|
+
if (draft["dependsOn"] !== void 0) {
|
|
11857
|
+
if (draft["dependsOn"] === null) {
|
|
11858
|
+
if (!ticketDependsOnListsEqual(cur.dependsOn, [])) {
|
|
11859
|
+
out["dependsOn"] = null;
|
|
11860
|
+
}
|
|
11861
|
+
} else {
|
|
11862
|
+
const parsed = parseTicketDependsOnFromPayloadValue(draft["dependsOn"]);
|
|
11863
|
+
if (parsed !== void 0) {
|
|
11864
|
+
if (!ticketDependsOnListsEqual(cur.dependsOn, parsed)) {
|
|
11865
|
+
out["dependsOn"] = parsed;
|
|
11866
|
+
}
|
|
11867
|
+
}
|
|
11868
|
+
}
|
|
11869
|
+
}
|
|
11756
11870
|
if (draft["priority"] !== void 0) {
|
|
11757
11871
|
const curP = cur.priority ?? null;
|
|
11758
11872
|
const nextP = draft["priority"] === null ? null : draft["priority"];
|
|
@@ -11919,6 +12033,20 @@ var workItemUpdateAspects = (kind, payload) => {
|
|
|
11919
12033
|
}
|
|
11920
12034
|
}
|
|
11921
12035
|
}
|
|
12036
|
+
if (kind === "ticket" && payload["dependsOn"] !== void 0) {
|
|
12037
|
+
if (payload["dependsOn"] === null) {
|
|
12038
|
+
aspects.push("cleared ticket dependencies");
|
|
12039
|
+
} else {
|
|
12040
|
+
const d = normalizeBranchList(payload["dependsOn"]);
|
|
12041
|
+
if (d.length > 0 && d.length <= MAX_BRANCH_NAMES_TO_LIST) {
|
|
12042
|
+
aspects.push(`set ticket dependencies to (${d.join(", ")})`);
|
|
12043
|
+
} else if (d.length > MAX_BRANCH_NAMES_TO_LIST) {
|
|
12044
|
+
aspects.push("updated ticket dependencies");
|
|
12045
|
+
} else {
|
|
12046
|
+
aspects.push("cleared ticket dependencies");
|
|
12047
|
+
}
|
|
12048
|
+
}
|
|
12049
|
+
}
|
|
11922
12050
|
if (kind === "ticket" && payload["priority"] !== void 0) {
|
|
11923
12051
|
if (payload["priority"] === null) {
|
|
11924
12052
|
aspects.push("cleared priority");
|
|
@@ -12025,6 +12153,7 @@ var buildAuditLinkMetadata = (evt, githubRepo) => {
|
|
|
12025
12153
|
if (p["status"] !== void 0) meta["status"] = String(p["status"]);
|
|
12026
12154
|
if (p["assignee"] !== void 0) meta["assignee"] = p["assignee"];
|
|
12027
12155
|
if (p["labels"] !== void 0) meta["labels"] = p["labels"];
|
|
12156
|
+
if (p["dependsOn"] !== void 0) meta["dependsOn"] = p["dependsOn"];
|
|
12028
12157
|
if (p["priority"] !== void 0) meta["priority"] = p["priority"];
|
|
12029
12158
|
if (p["size"] !== void 0) meta["size"] = p["size"];
|
|
12030
12159
|
if (p["estimate"] !== void 0) meta["estimate"] = p["estimate"];
|
|
@@ -12092,6 +12221,14 @@ var formatEpicStoryTicketCreated = (evt, entity) => {
|
|
|
12092
12221
|
parts.push("with labels");
|
|
12093
12222
|
}
|
|
12094
12223
|
}
|
|
12224
|
+
if (p["dependsOn"] !== void 0) {
|
|
12225
|
+
const d = normalizeBranchList(p["dependsOn"]);
|
|
12226
|
+
if (d.length > 0 && d.length <= MAX_BRANCH_NAMES_TO_LIST) {
|
|
12227
|
+
parts.push(`depending on (${d.join(", ")})`);
|
|
12228
|
+
} else if (d.length > MAX_BRANCH_NAMES_TO_LIST) {
|
|
12229
|
+
parts.push("with ticket dependencies");
|
|
12230
|
+
}
|
|
12231
|
+
}
|
|
12095
12232
|
if (p["priority"] !== void 0) {
|
|
12096
12233
|
parts.push(`with priority ${quoteStatus(String(p["priority"]))}`);
|
|
12097
12234
|
}
|
|
@@ -12185,6 +12322,18 @@ var formatAuditHumanSentence = (evt) => {
|
|
|
12185
12322
|
bits.push("updated labels");
|
|
12186
12323
|
}
|
|
12187
12324
|
}
|
|
12325
|
+
if (p["dependsOn"] !== void 0) {
|
|
12326
|
+
if (p["dependsOn"] === null) {
|
|
12327
|
+
bits.push("cleared ticket dependencies");
|
|
12328
|
+
} else {
|
|
12329
|
+
const d = normalizeBranchList(p["dependsOn"]);
|
|
12330
|
+
if (d.length > 0 && d.length <= MAX_BRANCH_NAMES_TO_LIST) {
|
|
12331
|
+
bits.push(`dependencies: ${d.join(", ")}`);
|
|
12332
|
+
} else {
|
|
12333
|
+
bits.push("updated ticket dependencies");
|
|
12334
|
+
}
|
|
12335
|
+
}
|
|
12336
|
+
}
|
|
12188
12337
|
if (p["priority"] !== void 0) {
|
|
12189
12338
|
bits.push("updated priority");
|
|
12190
12339
|
}
|
|
@@ -12464,6 +12613,13 @@ var ticketMatchesTicketListQuery = (ticket, projection, query) => {
|
|
|
12464
12613
|
return false;
|
|
12465
12614
|
}
|
|
12466
12615
|
}
|
|
12616
|
+
const dependsOnIncludesId = query.dependsOnIncludesId;
|
|
12617
|
+
if (dependsOnIncludesId !== void 0) {
|
|
12618
|
+
const deps = ticket.dependsOn ?? [];
|
|
12619
|
+
if (!deps.includes(dependsOnIncludesId)) {
|
|
12620
|
+
return false;
|
|
12621
|
+
}
|
|
12622
|
+
}
|
|
12467
12623
|
return true;
|
|
12468
12624
|
};
|
|
12469
12625
|
|
|
@@ -12616,6 +12772,7 @@ var sortTicketRecordsForList = (tickets, field, dir) => [...tickets].sort((x, y)
|
|
|
12616
12772
|
// src/cli/list-projection-summaries.ts
|
|
12617
12773
|
var listActiveEpicSummaries = (projection) => [...projection.epics.values()].filter((e) => !e.deleted).map((e) => ({
|
|
12618
12774
|
id: e.id,
|
|
12775
|
+
number: e.number,
|
|
12619
12776
|
title: e.title,
|
|
12620
12777
|
status: e.status,
|
|
12621
12778
|
createdAt: e.createdAt,
|
|
@@ -12627,6 +12784,7 @@ var listActiveStorySummaries = (projection, options) => [...projection.stories.v
|
|
|
12627
12784
|
(s) => !s.deleted && (options?.epicId === void 0 || s.epicId === options.epicId)
|
|
12628
12785
|
).map((s) => ({
|
|
12629
12786
|
id: s.id,
|
|
12787
|
+
number: s.number,
|
|
12630
12788
|
title: s.title,
|
|
12631
12789
|
epicId: s.epicId,
|
|
12632
12790
|
status: s.status,
|
|
@@ -12659,6 +12817,7 @@ var listActiveTicketSummaries = (projection, options) => {
|
|
|
12659
12817
|
const last = recent !== void 0 && recent.length > 0 ? recent[recent.length - 1] : void 0;
|
|
12660
12818
|
return {
|
|
12661
12819
|
id: t.id,
|
|
12820
|
+
number: t.number,
|
|
12662
12821
|
title: t.title,
|
|
12663
12822
|
status: t.status,
|
|
12664
12823
|
storyId: t.storyId,
|
|
@@ -12669,6 +12828,7 @@ var listActiveTicketSummaries = (projection, options) => {
|
|
|
12669
12828
|
...t.estimate !== void 0 ? { estimate: t.estimate } : {},
|
|
12670
12829
|
...t.startWorkAt !== void 0 ? { startWorkAt: t.startWorkAt } : {},
|
|
12671
12830
|
...t.targetFinishAt !== void 0 ? { targetFinishAt: t.targetFinishAt } : {},
|
|
12831
|
+
...t.dependsOn !== void 0 && t.dependsOn.length > 0 ? { dependsOn: t.dependsOn } : {},
|
|
12672
12832
|
...t.linkedBranches.length > 0 ? { linkedBranches: t.linkedBranches } : {},
|
|
12673
12833
|
...last !== void 0 ? {
|
|
12674
12834
|
lastPrActivity: {
|
|
@@ -12765,6 +12925,17 @@ var inboundTicketPlanningPayloadFromFenceMeta = (meta) => {
|
|
|
12765
12925
|
}
|
|
12766
12926
|
}
|
|
12767
12927
|
}
|
|
12928
|
+
if (Object.prototype.hasOwnProperty.call(meta, "depends_on")) {
|
|
12929
|
+
const v = meta["depends_on"];
|
|
12930
|
+
if (v === null) {
|
|
12931
|
+
out["dependsOn"] = null;
|
|
12932
|
+
} else {
|
|
12933
|
+
const parsed = parseTicketDependsOnFromFenceValue(v);
|
|
12934
|
+
if (parsed !== void 0) {
|
|
12935
|
+
out["dependsOn"] = parsed;
|
|
12936
|
+
}
|
|
12937
|
+
}
|
|
12938
|
+
}
|
|
12768
12939
|
return out;
|
|
12769
12940
|
};
|
|
12770
12941
|
var buildGithubIssueBody = (params) => {
|
|
@@ -12790,6 +12961,9 @@ var buildGithubIssueBody = (params) => {
|
|
|
12790
12961
|
if (p.targetFinishAt !== void 0) {
|
|
12791
12962
|
meta.target_finish_at = p.targetFinishAt;
|
|
12792
12963
|
}
|
|
12964
|
+
if (p.dependsOn !== void 0 && p.dependsOn.length > 0) {
|
|
12965
|
+
meta.depends_on = p.dependsOn;
|
|
12966
|
+
}
|
|
12793
12967
|
}
|
|
12794
12968
|
return `${params.description.trim()}
|
|
12795
12969
|
|
|
@@ -12815,6 +12989,9 @@ var ticketPlanningForGithubIssueBody = (ticket) => {
|
|
|
12815
12989
|
if (ticket.targetFinishAt !== void 0) {
|
|
12816
12990
|
out.targetFinishAt = ticket.targetFinishAt;
|
|
12817
12991
|
}
|
|
12992
|
+
if (ticket.dependsOn !== void 0 && ticket.dependsOn.length > 0) {
|
|
12993
|
+
out.dependsOn = ticket.dependsOn;
|
|
12994
|
+
}
|
|
12818
12995
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
12819
12996
|
};
|
|
12820
12997
|
|
|
@@ -13009,750 +13186,302 @@ var partitionGithubIssuesForImport = (params) => {
|
|
|
13009
13186
|
}
|
|
13010
13187
|
return { candidates, skipped };
|
|
13011
13188
|
};
|
|
13012
|
-
var mergeTicketImportCreatePayload = (ticketId, base, storyId) => {
|
|
13189
|
+
var mergeTicketImportCreatePayload = (ticketId, base, storyId, number) => {
|
|
13013
13190
|
const storyTrimmed = storyId !== void 0 && storyId !== "" ? storyId.trim() : void 0;
|
|
13014
13191
|
const storyPayload = storyTrimmed !== void 0 && storyTrimmed !== "" ? { storyId: storyTrimmed } : {};
|
|
13015
|
-
return { id: ticketId, ...base, ...storyPayload };
|
|
13192
|
+
return { id: ticketId, number, ...base, ...storyPayload };
|
|
13016
13193
|
};
|
|
13017
13194
|
|
|
13018
|
-
// src/
|
|
13019
|
-
var
|
|
13020
|
-
|
|
13021
|
-
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13025
|
-
|
|
13026
|
-
|
|
13027
|
-
|
|
13028
|
-
|
|
13029
|
-
var
|
|
13030
|
-
|
|
13031
|
-
|
|
13032
|
-
|
|
13033
|
-
|
|
13034
|
-
|
|
13035
|
-
|
|
13036
|
-
|
|
13037
|
-
const
|
|
13038
|
-
const
|
|
13039
|
-
const
|
|
13040
|
-
const
|
|
13041
|
-
|
|
13042
|
-
|
|
13043
|
-
|
|
13044
|
-
const
|
|
13045
|
-
|
|
13046
|
-
|
|
13195
|
+
// src/lib/github-pr-activity.ts
|
|
13196
|
+
var GITHUB_PR_ACTIVITY_RECENT_CAP = 20;
|
|
13197
|
+
var githubPrActivityKindSchema = external_exports.enum([
|
|
13198
|
+
"opened",
|
|
13199
|
+
"updated",
|
|
13200
|
+
"commented",
|
|
13201
|
+
"reviewed",
|
|
13202
|
+
"merged",
|
|
13203
|
+
"closed",
|
|
13204
|
+
"ready_for_review"
|
|
13205
|
+
]);
|
|
13206
|
+
var githubPrReviewStateSchema = external_exports.enum([
|
|
13207
|
+
"approved",
|
|
13208
|
+
"changes_requested",
|
|
13209
|
+
"commented"
|
|
13210
|
+
]);
|
|
13211
|
+
var buildPrOpenSourceId = (ticketId, prNumber) => `hyper-pm:pr-open:${ticketId}:${prNumber}`;
|
|
13212
|
+
var parseGithubPrActivityPayload = (payload) => {
|
|
13213
|
+
const ticketId = payload["ticketId"];
|
|
13214
|
+
const prRaw = payload["prNumber"];
|
|
13215
|
+
const kindRaw = payload["kind"];
|
|
13216
|
+
const occurredAt = payload["occurredAt"];
|
|
13217
|
+
const sourceId = payload["sourceId"];
|
|
13218
|
+
if (typeof ticketId !== "string" || typeof occurredAt !== "string" || typeof sourceId !== "string") {
|
|
13219
|
+
return void 0;
|
|
13220
|
+
}
|
|
13221
|
+
const prNumber = typeof prRaw === "number" ? prRaw : Number(prRaw);
|
|
13222
|
+
if (!Number.isFinite(prNumber)) return void 0;
|
|
13223
|
+
const kindParsed = githubPrActivityKindSchema.safeParse(kindRaw);
|
|
13224
|
+
if (!kindParsed.success) return void 0;
|
|
13225
|
+
const out = {
|
|
13226
|
+
prNumber,
|
|
13227
|
+
kind: kindParsed.data,
|
|
13228
|
+
occurredAt,
|
|
13229
|
+
sourceId
|
|
13230
|
+
};
|
|
13231
|
+
const url = payload["url"];
|
|
13232
|
+
if (typeof url === "string" && url.length > 0) {
|
|
13233
|
+
out.url = url;
|
|
13234
|
+
}
|
|
13235
|
+
const rs = githubPrReviewStateSchema.safeParse(payload["reviewState"]);
|
|
13236
|
+
if (rs.success) {
|
|
13237
|
+
out.reviewState = rs.data;
|
|
13047
13238
|
}
|
|
13048
13239
|
return out;
|
|
13049
13240
|
};
|
|
13050
13241
|
|
|
13051
|
-
// src/
|
|
13052
|
-
var
|
|
13053
|
-
|
|
13054
|
-
|
|
13055
|
-
|
|
13056
|
-
|
|
13057
|
-
|
|
13058
|
-
|
|
13242
|
+
// src/storage/projection.ts
|
|
13243
|
+
var readOptionalPositiveIntegerFromPayload = (payload, key) => {
|
|
13244
|
+
if (!Object.prototype.hasOwnProperty.call(payload, key)) return void 0;
|
|
13245
|
+
const raw = payload[key];
|
|
13246
|
+
if (typeof raw !== "number" || !Number.isFinite(raw)) return void 0;
|
|
13247
|
+
if (!Number.isInteger(raw)) return void 0;
|
|
13248
|
+
if (raw < 1 || raw > Number.MAX_SAFE_INTEGER) return void 0;
|
|
13249
|
+
return raw;
|
|
13059
13250
|
};
|
|
13060
|
-
|
|
13061
|
-
|
|
13062
|
-
|
|
13063
|
-
|
|
13064
|
-
for (let i = 0; i < lines.length; i++) {
|
|
13065
|
-
const line = lines[i]?.trim() ?? "";
|
|
13066
|
-
if (!line) continue;
|
|
13067
|
-
let json;
|
|
13068
|
-
try {
|
|
13069
|
-
json = JSON.parse(line);
|
|
13070
|
-
} catch (e) {
|
|
13071
|
-
issues.push({
|
|
13072
|
-
kind: "invalid-json",
|
|
13073
|
-
line: i + 1,
|
|
13074
|
-
message: e instanceof Error ? e.message : "parse error"
|
|
13075
|
-
});
|
|
13076
|
-
return issues;
|
|
13077
|
-
}
|
|
13078
|
-
const parsed = eventLineSchema.safeParse(json);
|
|
13079
|
-
if (!parsed.success) {
|
|
13080
|
-
issues.push({
|
|
13081
|
-
kind: "invalid-event",
|
|
13082
|
-
line: i + 1,
|
|
13083
|
-
message: parsed.error.message
|
|
13084
|
-
});
|
|
13085
|
-
return issues;
|
|
13086
|
-
}
|
|
13251
|
+
var maxNumberAmongRows = (rows) => {
|
|
13252
|
+
let m = 0;
|
|
13253
|
+
for (const r of rows) {
|
|
13254
|
+
if (r.number > m) m = r.number;
|
|
13087
13255
|
}
|
|
13088
|
-
return
|
|
13256
|
+
return m;
|
|
13089
13257
|
};
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
var
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
|
|
13096
|
-
|
|
13097
|
-
|
|
13098
|
-
|
|
13258
|
+
var maxEpicNumberInProjection = (projection) => maxNumberAmongRows(projection.epics.values());
|
|
13259
|
+
var maxStoryNumberInProjection = (projection) => maxNumberAmongRows(projection.stories.values());
|
|
13260
|
+
var maxTicketNumberInProjection = (projection) => maxNumberAmongRows(projection.tickets.values());
|
|
13261
|
+
var nextEpicNumberForCreate = (projection) => maxEpicNumberInProjection(projection) + 1;
|
|
13262
|
+
var nextStoryNumberForCreate = (projection) => maxStoryNumberInProjection(projection) + 1;
|
|
13263
|
+
var nextTicketNumberForCreate = (projection) => maxTicketNumberInProjection(projection) + 1;
|
|
13264
|
+
var resolveWorkItemCreateNumber = (projection, kind, payload) => {
|
|
13265
|
+
const explicit = readOptionalPositiveIntegerFromPayload(payload, "number");
|
|
13266
|
+
if (explicit !== void 0) {
|
|
13267
|
+
return explicit;
|
|
13268
|
+
}
|
|
13269
|
+
return (kind === "epic" ? maxEpicNumberInProjection(projection) : kind === "story" ? maxStoryNumberInProjection(projection) : maxTicketNumberInProjection(projection)) + 1;
|
|
13099
13270
|
};
|
|
13100
|
-
|
|
13101
|
-
|
|
13102
|
-
|
|
13103
|
-
|
|
13104
|
-
|
|
13105
|
-
|
|
13271
|
+
var emptyProjection = () => ({
|
|
13272
|
+
epics: /* @__PURE__ */ new Map(),
|
|
13273
|
+
stories: /* @__PURE__ */ new Map(),
|
|
13274
|
+
tickets: /* @__PURE__ */ new Map()
|
|
13275
|
+
});
|
|
13276
|
+
var parsePrRefs = (body) => {
|
|
13277
|
+
const out = /* @__PURE__ */ new Set();
|
|
13278
|
+
const re = /\b(?:Closes|Refs|Fixes)\s+#(\d+)\b/gi;
|
|
13279
|
+
let m = re.exec(body);
|
|
13280
|
+
while (m !== null) {
|
|
13281
|
+
out.add(Number(m[1]));
|
|
13282
|
+
m = re.exec(body);
|
|
13283
|
+
}
|
|
13284
|
+
return [...out];
|
|
13106
13285
|
};
|
|
13107
|
-
var
|
|
13108
|
-
|
|
13109
|
-
|
|
13110
|
-
|
|
13111
|
-
|
|
13112
|
-
return
|
|
13286
|
+
var applyTicketAssigneeFromPayload = (row, payload) => {
|
|
13287
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "assignee")) return;
|
|
13288
|
+
const v = payload["assignee"];
|
|
13289
|
+
if (v === null) {
|
|
13290
|
+
delete row.assignee;
|
|
13291
|
+
return;
|
|
13113
13292
|
}
|
|
13293
|
+
if (typeof v !== "string") return;
|
|
13294
|
+
const n = normalizeGithubLogin(v);
|
|
13295
|
+
if (n === "") delete row.assignee;
|
|
13296
|
+
else row.assignee = n;
|
|
13114
13297
|
};
|
|
13115
|
-
var
|
|
13116
|
-
|
|
13117
|
-
|
|
13118
|
-
for (const block of blocks) {
|
|
13119
|
-
const lines = block.split("\n");
|
|
13120
|
-
let worktreePath;
|
|
13121
|
-
let branch;
|
|
13122
|
-
for (const line of lines) {
|
|
13123
|
-
if (line.startsWith("worktree ")) {
|
|
13124
|
-
worktreePath = line.slice("worktree ".length);
|
|
13125
|
-
} else if (line.startsWith("branch ")) {
|
|
13126
|
-
branch = line.slice("branch ".length);
|
|
13127
|
-
}
|
|
13128
|
-
}
|
|
13129
|
-
if (worktreePath !== void 0 && branch === wantRef) {
|
|
13130
|
-
return worktreePath;
|
|
13131
|
-
}
|
|
13298
|
+
var storyIdFromTicketCreatedPayload = (payload) => {
|
|
13299
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "storyId")) {
|
|
13300
|
+
return null;
|
|
13132
13301
|
}
|
|
13133
|
-
|
|
13302
|
+
const v = payload["storyId"];
|
|
13303
|
+
if (v === null) return null;
|
|
13304
|
+
if (typeof v !== "string") return null;
|
|
13305
|
+
const t = v.trim();
|
|
13306
|
+
return t === "" ? null : t;
|
|
13134
13307
|
};
|
|
13135
|
-
var
|
|
13136
|
-
|
|
13137
|
-
const
|
|
13138
|
-
|
|
13139
|
-
|
|
13140
|
-
|
|
13141
|
-
]);
|
|
13142
|
-
const listedPath = parseDataBranchWorktreeFromPorcelain(
|
|
13143
|
-
listOut,
|
|
13144
|
-
opts.dataBranch
|
|
13145
|
-
);
|
|
13146
|
-
if (listedPath !== void 0 && await pathExists(listedPath)) {
|
|
13147
|
-
const dispose2 = async () => {
|
|
13148
|
-
return;
|
|
13149
|
-
};
|
|
13150
|
-
return { worktreePath: listedPath, dispose: dispose2 };
|
|
13151
|
-
}
|
|
13152
|
-
const worktreePath = (0, import_node_path3.join)(
|
|
13153
|
-
opts.tmpBase,
|
|
13154
|
-
`hyper-pm-worktree-${ulid().toLowerCase()}`
|
|
13155
|
-
);
|
|
13156
|
-
await ensureDir(opts.tmpBase);
|
|
13157
|
-
try {
|
|
13158
|
-
await opts.runGit(opts.repoRoot, [
|
|
13159
|
-
"worktree",
|
|
13160
|
-
"add",
|
|
13161
|
-
worktreePath,
|
|
13162
|
-
opts.dataBranch
|
|
13163
|
-
]);
|
|
13164
|
-
} catch (err) {
|
|
13165
|
-
await (0, import_promises3.rm)(worktreePath, { recursive: true, force: true }).catch(() => {
|
|
13166
|
-
});
|
|
13167
|
-
throw err;
|
|
13168
|
-
}
|
|
13169
|
-
const dispose = async () => {
|
|
13170
|
-
if (opts.keepWorktree) {
|
|
13171
|
-
return;
|
|
13172
|
-
}
|
|
13173
|
-
await opts.runGit(opts.repoRoot, ["worktree", "remove", "--force", worktreePath]).catch(() => {
|
|
13174
|
-
});
|
|
13175
|
-
await (0, import_promises3.rm)(worktreePath, { recursive: true, force: true }).catch(() => {
|
|
13176
|
-
});
|
|
13177
|
-
};
|
|
13178
|
-
return { worktreePath, dispose };
|
|
13179
|
-
};
|
|
13180
|
-
|
|
13181
|
-
// src/git/find-git-root.ts
|
|
13182
|
-
var findGitRoot = async (cwd, deps) => {
|
|
13183
|
-
const { stdout } = await deps.runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
13184
|
-
return stdout;
|
|
13185
|
-
};
|
|
13186
|
-
|
|
13187
|
-
// src/git/init-orphan-data-branch.ts
|
|
13188
|
-
var import_promises4 = require("node:fs/promises");
|
|
13189
|
-
var import_node_path4 = require("node:path");
|
|
13190
|
-
var initOrphanDataBranchInWorktree = async (opts) => {
|
|
13191
|
-
const { stdout: tip } = await opts.runGit(opts.repoRoot, [
|
|
13192
|
-
"rev-parse",
|
|
13193
|
-
"HEAD"
|
|
13194
|
-
]);
|
|
13195
|
-
const tipCommit = tip.trim();
|
|
13196
|
-
await opts.runGit(opts.repoRoot, [
|
|
13197
|
-
"worktree",
|
|
13198
|
-
"add",
|
|
13199
|
-
opts.worktreePath,
|
|
13200
|
-
tipCommit
|
|
13201
|
-
]);
|
|
13202
|
-
await opts.runGit(opts.worktreePath, [
|
|
13203
|
-
"checkout",
|
|
13204
|
-
"--orphan",
|
|
13205
|
-
opts.dataBranch
|
|
13206
|
-
]);
|
|
13207
|
-
await opts.runGit(opts.worktreePath, ["rm", "-rf", "."]).catch(() => {
|
|
13208
|
-
});
|
|
13209
|
-
const marker = (0, import_node_path4.join)(opts.worktreePath, "README.hyper-pm.md");
|
|
13210
|
-
await (0, import_promises4.writeFile)(
|
|
13211
|
-
marker,
|
|
13212
|
-
"# hyper-pm data branch\n\nAppend-only events live under `events/`.\n",
|
|
13213
|
-
"utf8"
|
|
13214
|
-
);
|
|
13215
|
-
await opts.runGit(opts.worktreePath, ["add", "."]);
|
|
13216
|
-
await opts.runGit(opts.worktreePath, [
|
|
13217
|
-
"commit",
|
|
13218
|
-
"-m",
|
|
13219
|
-
"init hyper-pm data branch"
|
|
13220
|
-
]);
|
|
13221
|
-
};
|
|
13222
|
-
|
|
13223
|
-
// src/git/pick-unique-local-branch-name.ts
|
|
13224
|
-
var DEFAULT_MAX_SUFFIX = 1e3;
|
|
13225
|
-
var localBranchRefExists = async (repoRoot, name, git) => {
|
|
13226
|
-
try {
|
|
13227
|
-
await git(repoRoot, ["show-ref", "--verify", `refs/heads/${name}`]);
|
|
13228
|
-
return true;
|
|
13229
|
-
} catch {
|
|
13230
|
-
return false;
|
|
13308
|
+
var applyTicketStoryIdFromPayload = (row, payload) => {
|
|
13309
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "storyId")) return;
|
|
13310
|
+
const v = payload["storyId"];
|
|
13311
|
+
if (v === null) {
|
|
13312
|
+
row.storyId = null;
|
|
13313
|
+
return;
|
|
13231
13314
|
}
|
|
13315
|
+
if (typeof v !== "string") return;
|
|
13316
|
+
const t = v.trim();
|
|
13317
|
+
row.storyId = t === "" ? null : t;
|
|
13232
13318
|
};
|
|
13233
|
-
var
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
for (let n = 1; n <= max; n += 1) {
|
|
13237
|
-
const raw = n === 1 ? preferredBase : `${preferredBase}-${n}`;
|
|
13238
|
-
const norm = normalizeTicketBranchName(raw);
|
|
13239
|
-
if (norm === void 0) {
|
|
13240
|
-
continue;
|
|
13241
|
-
}
|
|
13242
|
-
if (!await localBranchRefExists(repoRoot, norm, git)) {
|
|
13243
|
-
return { branch: norm, preferred: preferredBase };
|
|
13244
|
-
}
|
|
13319
|
+
var linkedBranchesFromTicketCreatedPayload = (payload) => {
|
|
13320
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "branches")) {
|
|
13321
|
+
return [];
|
|
13245
13322
|
}
|
|
13246
|
-
|
|
13247
|
-
`Could not allocate a free local branch name from ${JSON.stringify(preferredBase)} (tried suffixes up to -${String(max)})`
|
|
13248
|
-
);
|
|
13323
|
+
return normalizeTicketBranchListFromPayloadValue(payload["branches"]);
|
|
13249
13324
|
};
|
|
13250
|
-
|
|
13251
|
-
|
|
13252
|
-
|
|
13253
|
-
|
|
13254
|
-
|
|
13255
|
-
} catch {
|
|
13256
|
-
throw new Error(`Invalid or ambiguous --from ref: ${ref}`);
|
|
13257
|
-
}
|
|
13325
|
+
var applyTicketBranchesFromUpdatePayload = (row, payload) => {
|
|
13326
|
+
if (!Object.prototype.hasOwnProperty.call(payload, "branches")) return;
|
|
13327
|
+
const v = payload["branches"];
|
|
13328
|
+
if (!Array.isArray(v)) return;
|
|
13329
|
+
row.linkedBranches = normalizeTicketBranchListFromPayloadValue(v);
|
|
13258
13330
|
};
|
|
13259
|
-
var
|
|
13260
|
-
|
|
13261
|
-
|
|
13262
|
-
|
|
13263
|
-
|
|
13264
|
-
if (target !== "") {
|
|
13265
|
-
await git(repoRoot, ["rev-parse", "-q", "--verify", target]);
|
|
13266
|
-
return target;
|
|
13331
|
+
var applyTicketPlanningFieldsFromCreatePayload = (row, payload) => {
|
|
13332
|
+
if (Object.prototype.hasOwnProperty.call(payload, "labels")) {
|
|
13333
|
+
const v = ticketLabelsFromPayloadValue(payload["labels"]);
|
|
13334
|
+
if (v !== void 0 && v.length > 0) {
|
|
13335
|
+
row.labels = v;
|
|
13267
13336
|
}
|
|
13268
|
-
} catch {
|
|
13269
13337
|
}
|
|
13270
|
-
|
|
13271
|
-
|
|
13272
|
-
|
|
13273
|
-
return head;
|
|
13274
|
-
} catch {
|
|
13275
|
-
}
|
|
13338
|
+
const pr = readTicketPriorityPatch(payload);
|
|
13339
|
+
if (typeof pr === "string") {
|
|
13340
|
+
row.priority = pr;
|
|
13276
13341
|
}
|
|
13277
|
-
|
|
13278
|
-
|
|
13279
|
-
|
|
13280
|
-
} catch {
|
|
13342
|
+
const sz = readTicketSizePatch(payload);
|
|
13343
|
+
if (typeof sz === "string") {
|
|
13344
|
+
row.size = sz;
|
|
13281
13345
|
}
|
|
13282
|
-
|
|
13283
|
-
|
|
13284
|
-
|
|
13285
|
-
};
|
|
13286
|
-
|
|
13287
|
-
// src/git/list-repo-commit-authors.ts
|
|
13288
|
-
var listRepoCommitAuthors = async (repoRoot, git) => {
|
|
13289
|
-
try {
|
|
13290
|
-
const { stdout } = await git(repoRoot, [
|
|
13291
|
-
"-c",
|
|
13292
|
-
"log.showSignature=false",
|
|
13293
|
-
"log",
|
|
13294
|
-
"--all",
|
|
13295
|
-
"--format=%an%x1f%ae"
|
|
13296
|
-
]);
|
|
13297
|
-
if (stdout === "") return [];
|
|
13298
|
-
const lines = stdout.split("\n").filter((l) => l.length > 0);
|
|
13299
|
-
const seenEmail = /* @__PURE__ */ new Set();
|
|
13300
|
-
const out = [];
|
|
13301
|
-
for (const line of lines) {
|
|
13302
|
-
const sep2 = line.indexOf("");
|
|
13303
|
-
if (sep2 <= 0) continue;
|
|
13304
|
-
const name = line.slice(0, sep2).trim();
|
|
13305
|
-
const email = line.slice(sep2 + 1).trim();
|
|
13306
|
-
if (email === "") continue;
|
|
13307
|
-
const key = email.toLowerCase();
|
|
13308
|
-
if (seenEmail.has(key)) continue;
|
|
13309
|
-
seenEmail.add(key);
|
|
13310
|
-
const loginGuess = guessGithubLoginFromContact(name, email);
|
|
13311
|
-
out.push(
|
|
13312
|
-
loginGuess !== void 0 ? { name, email, loginGuess } : { name, email }
|
|
13313
|
-
);
|
|
13314
|
-
}
|
|
13315
|
-
return out;
|
|
13316
|
-
} catch {
|
|
13317
|
-
return [];
|
|
13346
|
+
const est = readTicketEstimatePatch(payload);
|
|
13347
|
+
if (typeof est === "number") {
|
|
13348
|
+
row.estimate = est;
|
|
13318
13349
|
}
|
|
13319
|
-
|
|
13320
|
-
|
|
13321
|
-
|
|
13322
|
-
var parseGithubOwnerRepoFromRemoteUrl = (rawUrl) => {
|
|
13323
|
-
const trimmed = rawUrl.trim();
|
|
13324
|
-
if (!trimmed) {
|
|
13325
|
-
return void 0;
|
|
13350
|
+
const sw = readTicketIsoInstantPatch(payload, "startWorkAt");
|
|
13351
|
+
if (typeof sw === "string") {
|
|
13352
|
+
row.startWorkAt = sw;
|
|
13326
13353
|
}
|
|
13327
|
-
const
|
|
13328
|
-
if (
|
|
13329
|
-
|
|
13354
|
+
const tf = readTicketIsoInstantPatch(payload, "targetFinishAt");
|
|
13355
|
+
if (typeof tf === "string") {
|
|
13356
|
+
row.targetFinishAt = tf;
|
|
13330
13357
|
}
|
|
13331
|
-
|
|
13332
|
-
const
|
|
13333
|
-
|
|
13334
|
-
|
|
13335
|
-
if (host !== "github.com" && host !== "www.github.com") {
|
|
13336
|
-
return void 0;
|
|
13358
|
+
if (Object.prototype.hasOwnProperty.call(payload, "dependsOn")) {
|
|
13359
|
+
const v = parseTicketDependsOnFromPayloadValue(payload["dependsOn"]);
|
|
13360
|
+
if (v !== void 0 && v.length > 0) {
|
|
13361
|
+
row.dependsOn = v;
|
|
13337
13362
|
}
|
|
13338
|
-
return slugFromGithubPath(u.pathname);
|
|
13339
|
-
} catch {
|
|
13340
|
-
return void 0;
|
|
13341
13363
|
}
|
|
13342
13364
|
};
|
|
13343
|
-
var
|
|
13344
|
-
|
|
13345
|
-
|
|
13346
|
-
|
|
13365
|
+
var applyTicketPlanningFieldsFromUpdatePayload = (row, payload) => {
|
|
13366
|
+
if (Object.prototype.hasOwnProperty.call(payload, "labels")) {
|
|
13367
|
+
if (payload["labels"] === null) {
|
|
13368
|
+
delete row.labels;
|
|
13369
|
+
} else {
|
|
13370
|
+
const v = ticketLabelsFromPayloadValue(payload["labels"]);
|
|
13371
|
+
if (v !== void 0) {
|
|
13372
|
+
if (v.length === 0) {
|
|
13373
|
+
delete row.labels;
|
|
13374
|
+
} else {
|
|
13375
|
+
row.labels = v;
|
|
13376
|
+
}
|
|
13377
|
+
}
|
|
13378
|
+
}
|
|
13347
13379
|
}
|
|
13348
|
-
const
|
|
13349
|
-
|
|
13350
|
-
|
|
13351
|
-
|
|
13380
|
+
const pr = readTicketPriorityPatch(payload);
|
|
13381
|
+
if (pr === null) {
|
|
13382
|
+
delete row.priority;
|
|
13383
|
+
} else if (typeof pr === "string") {
|
|
13384
|
+
row.priority = pr;
|
|
13352
13385
|
}
|
|
13353
|
-
|
|
13354
|
-
|
|
13355
|
-
|
|
13356
|
-
|
|
13357
|
-
|
|
13358
|
-
try {
|
|
13359
|
-
const { stdout } = await params.runGit(params.repoRoot, [
|
|
13360
|
-
"remote",
|
|
13361
|
-
"get-url",
|
|
13362
|
-
params.remote
|
|
13363
|
-
]);
|
|
13364
|
-
return parseGithubOwnerRepoFromRemoteUrl(stdout);
|
|
13365
|
-
} catch {
|
|
13366
|
-
return void 0;
|
|
13386
|
+
const sz = readTicketSizePatch(payload);
|
|
13387
|
+
if (sz === null) {
|
|
13388
|
+
delete row.size;
|
|
13389
|
+
} else if (typeof sz === "string") {
|
|
13390
|
+
row.size = sz;
|
|
13367
13391
|
}
|
|
13368
|
-
|
|
13369
|
-
|
|
13370
|
-
|
|
13371
|
-
|
|
13372
|
-
|
|
13373
|
-
var tryReadGitConfigUserName = async (cwd, runGit2) => {
|
|
13374
|
-
try {
|
|
13375
|
-
const { stdout } = await runGit2(cwd, ["config", "--get", "user.name"]);
|
|
13376
|
-
return stdout.trim();
|
|
13377
|
-
} catch {
|
|
13378
|
-
return "";
|
|
13392
|
+
const est = readTicketEstimatePatch(payload);
|
|
13393
|
+
if (est === null) {
|
|
13394
|
+
delete row.estimate;
|
|
13395
|
+
} else if (typeof est === "number") {
|
|
13396
|
+
row.estimate = est;
|
|
13379
13397
|
}
|
|
13380
|
-
|
|
13381
|
-
|
|
13382
|
-
|
|
13383
|
-
|
|
13384
|
-
|
|
13385
|
-
} catch {
|
|
13386
|
-
return "";
|
|
13398
|
+
const sw = readTicketIsoInstantPatch(payload, "startWorkAt");
|
|
13399
|
+
if (sw === null) {
|
|
13400
|
+
delete row.startWorkAt;
|
|
13401
|
+
} else if (typeof sw === "string") {
|
|
13402
|
+
row.startWorkAt = sw;
|
|
13387
13403
|
}
|
|
13388
|
-
|
|
13389
|
-
|
|
13390
|
-
|
|
13391
|
-
|
|
13392
|
-
|
|
13393
|
-
|
|
13394
|
-
|
|
13395
|
-
|
|
13396
|
-
|
|
13397
|
-
|
|
13398
|
-
|
|
13399
|
-
|
|
13400
|
-
|
|
13401
|
-
|
|
13402
|
-
|
|
13404
|
+
const tf = readTicketIsoInstantPatch(payload, "targetFinishAt");
|
|
13405
|
+
if (tf === null) {
|
|
13406
|
+
delete row.targetFinishAt;
|
|
13407
|
+
} else if (typeof tf === "string") {
|
|
13408
|
+
row.targetFinishAt = tf;
|
|
13409
|
+
}
|
|
13410
|
+
if (Object.prototype.hasOwnProperty.call(payload, "dependsOn")) {
|
|
13411
|
+
if (payload["dependsOn"] === null) {
|
|
13412
|
+
delete row.dependsOn;
|
|
13413
|
+
} else {
|
|
13414
|
+
const v = parseTicketDependsOnFromPayloadValue(payload["dependsOn"]);
|
|
13415
|
+
if (v !== void 0) {
|
|
13416
|
+
if (v.length === 0) {
|
|
13417
|
+
delete row.dependsOn;
|
|
13418
|
+
} else {
|
|
13419
|
+
row.dependsOn = v;
|
|
13420
|
+
}
|
|
13421
|
+
}
|
|
13422
|
+
}
|
|
13403
13423
|
}
|
|
13404
|
-
const collapsed = raw.replace(/\s+/g, " ");
|
|
13405
|
-
const suffix = collapsed.length > maxActorSuffixLen ? `${collapsed.slice(0, maxActorSuffixLen - 1)}\u2026` : collapsed;
|
|
13406
|
-
return `${base} (${suffix})`;
|
|
13407
|
-
};
|
|
13408
|
-
var commitDataWorktreeIfNeeded = async (worktreePath, message, runGit2, opts) => {
|
|
13409
|
-
const { stdout } = await runGit2(worktreePath, ["status", "--porcelain"]);
|
|
13410
|
-
if (!stdout.trim()) return;
|
|
13411
|
-
await runGit2(worktreePath, ["add", "."]);
|
|
13412
|
-
const authorEnv = opts?.authorEnv ?? env;
|
|
13413
|
-
const { name, email } = await resolveEffectiveGitAuthorForDataCommit(
|
|
13414
|
-
worktreePath,
|
|
13415
|
-
runGit2,
|
|
13416
|
-
authorEnv
|
|
13417
|
-
);
|
|
13418
|
-
await runGit2(worktreePath, [
|
|
13419
|
-
"-c",
|
|
13420
|
-
`user.name=${name}`,
|
|
13421
|
-
"-c",
|
|
13422
|
-
`user.email=${email}`,
|
|
13423
|
-
"commit",
|
|
13424
|
-
"-m",
|
|
13425
|
-
message
|
|
13426
|
-
]);
|
|
13427
13424
|
};
|
|
13428
|
-
|
|
13429
|
-
|
|
13430
|
-
|
|
13431
|
-
|
|
13432
|
-
|
|
13433
|
-
return line.length > 0 ? line : "git error";
|
|
13425
|
+
var applyCreatedAudit = (row, evt) => {
|
|
13426
|
+
row.createdAt = evt.ts;
|
|
13427
|
+
row.createdBy = evt.actor;
|
|
13428
|
+
row.updatedAt = evt.ts;
|
|
13429
|
+
row.updatedBy = evt.actor;
|
|
13434
13430
|
};
|
|
13435
|
-
var
|
|
13436
|
-
|
|
13437
|
-
|
|
13438
|
-
} catch (e) {
|
|
13439
|
-
return {
|
|
13440
|
-
status: "skipped_no_remote",
|
|
13441
|
-
detail: firstLineFromUnknown(e)
|
|
13442
|
-
};
|
|
13443
|
-
}
|
|
13444
|
-
try {
|
|
13445
|
-
await runGit2(worktreePath, ["push", "-u", remote, branch]);
|
|
13446
|
-
return { status: "pushed" };
|
|
13447
|
-
} catch (e) {
|
|
13448
|
-
return {
|
|
13449
|
-
status: "failed",
|
|
13450
|
-
detail: firstLineFromUnknown(e)
|
|
13451
|
-
};
|
|
13452
|
-
}
|
|
13431
|
+
var applyLastUpdate = (row, evt) => {
|
|
13432
|
+
row.updatedAt = evt.ts;
|
|
13433
|
+
row.updatedBy = evt.actor;
|
|
13453
13434
|
};
|
|
13454
|
-
|
|
13455
|
-
|
|
13456
|
-
|
|
13457
|
-
|
|
13458
|
-
|
|
13459
|
-
|
|
13460
|
-
var nextEventRelPath = (now, deps) => {
|
|
13461
|
-
const y = String(now.getUTCFullYear());
|
|
13462
|
-
const m = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
13463
|
-
const nextId = deps?.nextId ?? (() => ulid().toLowerCase());
|
|
13464
|
-
const part = `part-${nextId()}`;
|
|
13465
|
-
return `events/${y}/${m}/${part}.jsonl`;
|
|
13435
|
+
var applyStatusIfChanged = (row, evt, nextStatus) => {
|
|
13436
|
+
if (row.status === nextStatus) return false;
|
|
13437
|
+
row.status = nextStatus;
|
|
13438
|
+
row.statusChangedAt = evt.ts;
|
|
13439
|
+
row.statusChangedBy = evt.actor;
|
|
13440
|
+
return true;
|
|
13466
13441
|
};
|
|
13467
|
-
|
|
13468
|
-
|
|
13469
|
-
|
|
13470
|
-
|
|
13471
|
-
|
|
13442
|
+
var appendTicketCommentFromEvent = (ticket, evt) => {
|
|
13443
|
+
const list = ticket.comments ?? (ticket.comments = []);
|
|
13444
|
+
list.push({
|
|
13445
|
+
id: evt.id,
|
|
13446
|
+
body: String(evt.payload["body"] ?? ""),
|
|
13447
|
+
createdAt: evt.ts,
|
|
13448
|
+
createdBy: evt.actor
|
|
13472
13449
|
});
|
|
13473
|
-
|
|
13474
|
-
await (0, import_promises5.mkdir)((0, import_node_path5.dirname)(abs), { recursive: true });
|
|
13475
|
-
await (0, import_promises5.appendFile)(abs, `${JSON.stringify(event)}
|
|
13476
|
-
`, "utf8");
|
|
13477
|
-
return rel;
|
|
13450
|
+
applyLastUpdate(ticket, evt);
|
|
13478
13451
|
};
|
|
13479
|
-
|
|
13480
|
-
|
|
13481
|
-
|
|
13482
|
-
|
|
13483
|
-
|
|
13484
|
-
|
|
13485
|
-
|
|
13486
|
-
|
|
13487
|
-
|
|
13488
|
-
if (e.isDirectory()) {
|
|
13489
|
-
out.push(...await listJsonlFiles(abs, root));
|
|
13490
|
-
} else if (e.isFile() && e.name.endsWith(".jsonl")) {
|
|
13491
|
-
out.push((0, import_node_path6.relative)(root, abs).split("\\").join("/"));
|
|
13492
|
-
}
|
|
13452
|
+
var resolveInboundTicketStatusFromPayload = (ticket, payload) => {
|
|
13453
|
+
const explicit = parseWorkItemStatus(payload["status"]);
|
|
13454
|
+
if (explicit !== void 0) return explicit;
|
|
13455
|
+
const legacySt = payload["state"];
|
|
13456
|
+
if (legacySt === "open" || legacySt === "closed") {
|
|
13457
|
+
return resolveTicketInboundStatus({
|
|
13458
|
+
issueState: legacySt,
|
|
13459
|
+
currentStatus: ticket.status
|
|
13460
|
+
});
|
|
13493
13461
|
}
|
|
13494
|
-
return
|
|
13462
|
+
return void 0;
|
|
13495
13463
|
};
|
|
13496
|
-
var
|
|
13497
|
-
|
|
13498
|
-
|
|
13499
|
-
|
|
13500
|
-
|
|
13501
|
-
|
|
13502
|
-
|
|
13503
|
-
|
|
13504
|
-
|
|
13505
|
-
|
|
13506
|
-
|
|
13507
|
-
|
|
13508
|
-
|
|
13509
|
-
|
|
13510
|
-
|
|
13511
|
-
|
|
13512
|
-
|
|
13513
|
-
};
|
|
13514
|
-
|
|
13515
|
-
|
|
13516
|
-
|
|
13517
|
-
var githubPrActivityKindSchema = external_exports.enum([
|
|
13518
|
-
"opened",
|
|
13519
|
-
"updated",
|
|
13520
|
-
"commented",
|
|
13521
|
-
"reviewed",
|
|
13522
|
-
"merged",
|
|
13523
|
-
"closed",
|
|
13524
|
-
"ready_for_review"
|
|
13525
|
-
]);
|
|
13526
|
-
var githubPrReviewStateSchema = external_exports.enum([
|
|
13527
|
-
"approved",
|
|
13528
|
-
"changes_requested",
|
|
13529
|
-
"commented"
|
|
13530
|
-
]);
|
|
13531
|
-
var buildPrOpenSourceId = (ticketId, prNumber) => `hyper-pm:pr-open:${ticketId}:${prNumber}`;
|
|
13532
|
-
var parseGithubPrActivityPayload = (payload) => {
|
|
13533
|
-
const ticketId = payload["ticketId"];
|
|
13534
|
-
const prRaw = payload["prNumber"];
|
|
13535
|
-
const kindRaw = payload["kind"];
|
|
13536
|
-
const occurredAt = payload["occurredAt"];
|
|
13537
|
-
const sourceId = payload["sourceId"];
|
|
13538
|
-
if (typeof ticketId !== "string" || typeof occurredAt !== "string" || typeof sourceId !== "string") {
|
|
13539
|
-
return void 0;
|
|
13540
|
-
}
|
|
13541
|
-
const prNumber = typeof prRaw === "number" ? prRaw : Number(prRaw);
|
|
13542
|
-
if (!Number.isFinite(prNumber)) return void 0;
|
|
13543
|
-
const kindParsed = githubPrActivityKindSchema.safeParse(kindRaw);
|
|
13544
|
-
if (!kindParsed.success) return void 0;
|
|
13545
|
-
const out = {
|
|
13546
|
-
prNumber,
|
|
13547
|
-
kind: kindParsed.data,
|
|
13548
|
-
occurredAt,
|
|
13549
|
-
sourceId
|
|
13550
|
-
};
|
|
13551
|
-
const url = payload["url"];
|
|
13552
|
-
if (typeof url === "string" && url.length > 0) {
|
|
13553
|
-
out.url = url;
|
|
13554
|
-
}
|
|
13555
|
-
const rs = githubPrReviewStateSchema.safeParse(payload["reviewState"]);
|
|
13556
|
-
if (rs.success) {
|
|
13557
|
-
out.reviewState = rs.data;
|
|
13558
|
-
}
|
|
13559
|
-
return out;
|
|
13560
|
-
};
|
|
13561
|
-
|
|
13562
|
-
// src/storage/projection.ts
|
|
13563
|
-
var emptyProjection = () => ({
|
|
13564
|
-
epics: /* @__PURE__ */ new Map(),
|
|
13565
|
-
stories: /* @__PURE__ */ new Map(),
|
|
13566
|
-
tickets: /* @__PURE__ */ new Map()
|
|
13567
|
-
});
|
|
13568
|
-
var parsePrRefs = (body) => {
|
|
13569
|
-
const out = /* @__PURE__ */ new Set();
|
|
13570
|
-
const re = /\b(?:Closes|Refs|Fixes)\s+#(\d+)\b/gi;
|
|
13571
|
-
let m = re.exec(body);
|
|
13572
|
-
while (m !== null) {
|
|
13573
|
-
out.add(Number(m[1]));
|
|
13574
|
-
m = re.exec(body);
|
|
13575
|
-
}
|
|
13576
|
-
return [...out];
|
|
13577
|
-
};
|
|
13578
|
-
var applyTicketAssigneeFromPayload = (row, payload) => {
|
|
13579
|
-
if (!Object.prototype.hasOwnProperty.call(payload, "assignee")) return;
|
|
13580
|
-
const v = payload["assignee"];
|
|
13581
|
-
if (v === null) {
|
|
13582
|
-
delete row.assignee;
|
|
13583
|
-
return;
|
|
13584
|
-
}
|
|
13585
|
-
if (typeof v !== "string") return;
|
|
13586
|
-
const n = normalizeGithubLogin(v);
|
|
13587
|
-
if (n === "") delete row.assignee;
|
|
13588
|
-
else row.assignee = n;
|
|
13589
|
-
};
|
|
13590
|
-
var storyIdFromTicketCreatedPayload = (payload) => {
|
|
13591
|
-
if (!Object.prototype.hasOwnProperty.call(payload, "storyId")) {
|
|
13592
|
-
return null;
|
|
13593
|
-
}
|
|
13594
|
-
const v = payload["storyId"];
|
|
13595
|
-
if (v === null) return null;
|
|
13596
|
-
if (typeof v !== "string") return null;
|
|
13597
|
-
const t = v.trim();
|
|
13598
|
-
return t === "" ? null : t;
|
|
13599
|
-
};
|
|
13600
|
-
var applyTicketStoryIdFromPayload = (row, payload) => {
|
|
13601
|
-
if (!Object.prototype.hasOwnProperty.call(payload, "storyId")) return;
|
|
13602
|
-
const v = payload["storyId"];
|
|
13603
|
-
if (v === null) {
|
|
13604
|
-
row.storyId = null;
|
|
13605
|
-
return;
|
|
13606
|
-
}
|
|
13607
|
-
if (typeof v !== "string") return;
|
|
13608
|
-
const t = v.trim();
|
|
13609
|
-
row.storyId = t === "" ? null : t;
|
|
13610
|
-
};
|
|
13611
|
-
var linkedBranchesFromTicketCreatedPayload = (payload) => {
|
|
13612
|
-
if (!Object.prototype.hasOwnProperty.call(payload, "branches")) {
|
|
13613
|
-
return [];
|
|
13614
|
-
}
|
|
13615
|
-
return normalizeTicketBranchListFromPayloadValue(payload["branches"]);
|
|
13616
|
-
};
|
|
13617
|
-
var applyTicketBranchesFromUpdatePayload = (row, payload) => {
|
|
13618
|
-
if (!Object.prototype.hasOwnProperty.call(payload, "branches")) return;
|
|
13619
|
-
const v = payload["branches"];
|
|
13620
|
-
if (!Array.isArray(v)) return;
|
|
13621
|
-
row.linkedBranches = normalizeTicketBranchListFromPayloadValue(v);
|
|
13622
|
-
};
|
|
13623
|
-
var applyTicketPlanningFieldsFromCreatePayload = (row, payload) => {
|
|
13624
|
-
if (Object.prototype.hasOwnProperty.call(payload, "labels")) {
|
|
13625
|
-
const v = ticketLabelsFromPayloadValue(payload["labels"]);
|
|
13626
|
-
if (v !== void 0 && v.length > 0) {
|
|
13627
|
-
row.labels = v;
|
|
13628
|
-
}
|
|
13629
|
-
}
|
|
13630
|
-
const pr = readTicketPriorityPatch(payload);
|
|
13631
|
-
if (typeof pr === "string") {
|
|
13632
|
-
row.priority = pr;
|
|
13633
|
-
}
|
|
13634
|
-
const sz = readTicketSizePatch(payload);
|
|
13635
|
-
if (typeof sz === "string") {
|
|
13636
|
-
row.size = sz;
|
|
13637
|
-
}
|
|
13638
|
-
const est = readTicketEstimatePatch(payload);
|
|
13639
|
-
if (typeof est === "number") {
|
|
13640
|
-
row.estimate = est;
|
|
13641
|
-
}
|
|
13642
|
-
const sw = readTicketIsoInstantPatch(payload, "startWorkAt");
|
|
13643
|
-
if (typeof sw === "string") {
|
|
13644
|
-
row.startWorkAt = sw;
|
|
13645
|
-
}
|
|
13646
|
-
const tf = readTicketIsoInstantPatch(payload, "targetFinishAt");
|
|
13647
|
-
if (typeof tf === "string") {
|
|
13648
|
-
row.targetFinishAt = tf;
|
|
13649
|
-
}
|
|
13650
|
-
};
|
|
13651
|
-
var applyTicketPlanningFieldsFromUpdatePayload = (row, payload) => {
|
|
13652
|
-
if (Object.prototype.hasOwnProperty.call(payload, "labels")) {
|
|
13653
|
-
if (payload["labels"] === null) {
|
|
13654
|
-
delete row.labels;
|
|
13655
|
-
} else {
|
|
13656
|
-
const v = ticketLabelsFromPayloadValue(payload["labels"]);
|
|
13657
|
-
if (v !== void 0) {
|
|
13658
|
-
if (v.length === 0) {
|
|
13659
|
-
delete row.labels;
|
|
13660
|
-
} else {
|
|
13661
|
-
row.labels = v;
|
|
13662
|
-
}
|
|
13663
|
-
}
|
|
13664
|
-
}
|
|
13665
|
-
}
|
|
13666
|
-
const pr = readTicketPriorityPatch(payload);
|
|
13667
|
-
if (pr === null) {
|
|
13668
|
-
delete row.priority;
|
|
13669
|
-
} else if (typeof pr === "string") {
|
|
13670
|
-
row.priority = pr;
|
|
13671
|
-
}
|
|
13672
|
-
const sz = readTicketSizePatch(payload);
|
|
13673
|
-
if (sz === null) {
|
|
13674
|
-
delete row.size;
|
|
13675
|
-
} else if (typeof sz === "string") {
|
|
13676
|
-
row.size = sz;
|
|
13677
|
-
}
|
|
13678
|
-
const est = readTicketEstimatePatch(payload);
|
|
13679
|
-
if (est === null) {
|
|
13680
|
-
delete row.estimate;
|
|
13681
|
-
} else if (typeof est === "number") {
|
|
13682
|
-
row.estimate = est;
|
|
13683
|
-
}
|
|
13684
|
-
const sw = readTicketIsoInstantPatch(payload, "startWorkAt");
|
|
13685
|
-
if (sw === null) {
|
|
13686
|
-
delete row.startWorkAt;
|
|
13687
|
-
} else if (typeof sw === "string") {
|
|
13688
|
-
row.startWorkAt = sw;
|
|
13689
|
-
}
|
|
13690
|
-
const tf = readTicketIsoInstantPatch(payload, "targetFinishAt");
|
|
13691
|
-
if (tf === null) {
|
|
13692
|
-
delete row.targetFinishAt;
|
|
13693
|
-
} else if (typeof tf === "string") {
|
|
13694
|
-
row.targetFinishAt = tf;
|
|
13695
|
-
}
|
|
13696
|
-
};
|
|
13697
|
-
var applyCreatedAudit = (row, evt) => {
|
|
13698
|
-
row.createdAt = evt.ts;
|
|
13699
|
-
row.createdBy = evt.actor;
|
|
13700
|
-
row.updatedAt = evt.ts;
|
|
13701
|
-
row.updatedBy = evt.actor;
|
|
13702
|
-
};
|
|
13703
|
-
var applyLastUpdate = (row, evt) => {
|
|
13704
|
-
row.updatedAt = evt.ts;
|
|
13705
|
-
row.updatedBy = evt.actor;
|
|
13706
|
-
};
|
|
13707
|
-
var applyStatusIfChanged = (row, evt, nextStatus) => {
|
|
13708
|
-
if (row.status === nextStatus) return false;
|
|
13709
|
-
row.status = nextStatus;
|
|
13710
|
-
row.statusChangedAt = evt.ts;
|
|
13711
|
-
row.statusChangedBy = evt.actor;
|
|
13712
|
-
return true;
|
|
13713
|
-
};
|
|
13714
|
-
var appendTicketCommentFromEvent = (ticket, evt) => {
|
|
13715
|
-
const list = ticket.comments ?? (ticket.comments = []);
|
|
13716
|
-
list.push({
|
|
13717
|
-
id: evt.id,
|
|
13718
|
-
body: String(evt.payload["body"] ?? ""),
|
|
13719
|
-
createdAt: evt.ts,
|
|
13720
|
-
createdBy: evt.actor
|
|
13721
|
-
});
|
|
13722
|
-
applyLastUpdate(ticket, evt);
|
|
13723
|
-
};
|
|
13724
|
-
var resolveInboundTicketStatusFromPayload = (ticket, payload) => {
|
|
13725
|
-
const explicit = parseWorkItemStatus(payload["status"]);
|
|
13726
|
-
if (explicit !== void 0) return explicit;
|
|
13727
|
-
const legacySt = payload["state"];
|
|
13728
|
-
if (legacySt === "open" || legacySt === "closed") {
|
|
13729
|
-
return resolveTicketInboundStatus({
|
|
13730
|
-
issueState: legacySt,
|
|
13731
|
-
currentStatus: ticket.status
|
|
13732
|
-
});
|
|
13733
|
-
}
|
|
13734
|
-
return void 0;
|
|
13735
|
-
};
|
|
13736
|
-
var applyEvent = (projection, evt) => {
|
|
13737
|
-
switch (evt.type) {
|
|
13738
|
-
case "EpicCreated": {
|
|
13739
|
-
const id = String(evt.payload["id"]);
|
|
13740
|
-
const status = resolveStatusForNewEpicStoryPayload(evt.payload);
|
|
13741
|
-
const row = {
|
|
13742
|
-
id,
|
|
13743
|
-
title: String(evt.payload["title"] ?? ""),
|
|
13744
|
-
body: String(evt.payload["body"] ?? ""),
|
|
13745
|
-
status,
|
|
13746
|
-
statusChangedAt: evt.ts,
|
|
13747
|
-
statusChangedBy: evt.actor,
|
|
13748
|
-
createdAt: "",
|
|
13749
|
-
createdBy: "",
|
|
13750
|
-
updatedAt: "",
|
|
13751
|
-
updatedBy: ""
|
|
13752
|
-
};
|
|
13753
|
-
applyCreatedAudit(row, evt);
|
|
13754
|
-
projection.epics.set(id, row);
|
|
13755
|
-
break;
|
|
13464
|
+
var applyEvent = (projection, evt) => {
|
|
13465
|
+
switch (evt.type) {
|
|
13466
|
+
case "EpicCreated": {
|
|
13467
|
+
const id = String(evt.payload["id"]);
|
|
13468
|
+
const status = resolveStatusForNewEpicStoryPayload(evt.payload);
|
|
13469
|
+
const row = {
|
|
13470
|
+
id,
|
|
13471
|
+
number: resolveWorkItemCreateNumber(projection, "epic", evt.payload),
|
|
13472
|
+
title: String(evt.payload["title"] ?? ""),
|
|
13473
|
+
body: String(evt.payload["body"] ?? ""),
|
|
13474
|
+
status,
|
|
13475
|
+
statusChangedAt: evt.ts,
|
|
13476
|
+
statusChangedBy: evt.actor,
|
|
13477
|
+
createdAt: "",
|
|
13478
|
+
createdBy: "",
|
|
13479
|
+
updatedAt: "",
|
|
13480
|
+
updatedBy: ""
|
|
13481
|
+
};
|
|
13482
|
+
applyCreatedAudit(row, evt);
|
|
13483
|
+
projection.epics.set(id, row);
|
|
13484
|
+
break;
|
|
13756
13485
|
}
|
|
13757
13486
|
case "EpicUpdated": {
|
|
13758
13487
|
const id = String(evt.payload["id"]);
|
|
@@ -13785,6 +13514,7 @@ var applyEvent = (projection, evt) => {
|
|
|
13785
13514
|
const status = resolveStatusForNewEpicStoryPayload(evt.payload);
|
|
13786
13515
|
const row = {
|
|
13787
13516
|
id,
|
|
13517
|
+
number: resolveWorkItemCreateNumber(projection, "story", evt.payload),
|
|
13788
13518
|
epicId: String(evt.payload["epicId"]),
|
|
13789
13519
|
title: String(evt.payload["title"] ?? ""),
|
|
13790
13520
|
body: String(evt.payload["body"] ?? ""),
|
|
@@ -13832,6 +13562,7 @@ var applyEvent = (projection, evt) => {
|
|
|
13832
13562
|
const status = resolveStatusForNewTicketPayload(evt.payload);
|
|
13833
13563
|
const row = {
|
|
13834
13564
|
id,
|
|
13565
|
+
number: resolveWorkItemCreateNumber(projection, "ticket", evt.payload),
|
|
13835
13566
|
storyId: storyIdFromTicketCreatedPayload(evt.payload),
|
|
13836
13567
|
title: String(evt.payload["title"] ?? ""),
|
|
13837
13568
|
body,
|
|
@@ -13944,28 +13675,733 @@ var applyEvent = (projection, evt) => {
|
|
|
13944
13675
|
}
|
|
13945
13676
|
break;
|
|
13946
13677
|
}
|
|
13947
|
-
default:
|
|
13948
|
-
break;
|
|
13678
|
+
default:
|
|
13679
|
+
break;
|
|
13680
|
+
}
|
|
13681
|
+
};
|
|
13682
|
+
var replayEvents = (lines) => {
|
|
13683
|
+
const proj = emptyProjection();
|
|
13684
|
+
const events = [];
|
|
13685
|
+
for (const line of lines) {
|
|
13686
|
+
const trimmed = line.trim();
|
|
13687
|
+
if (!trimmed) continue;
|
|
13688
|
+
const json = JSON.parse(trimmed);
|
|
13689
|
+
events.push(eventLineSchema.parse(json));
|
|
13690
|
+
}
|
|
13691
|
+
events.sort((a, b) => {
|
|
13692
|
+
const t = a.ts.localeCompare(b.ts);
|
|
13693
|
+
if (t !== 0) return t;
|
|
13694
|
+
return a.id.localeCompare(b.id);
|
|
13695
|
+
});
|
|
13696
|
+
for (const e of events) {
|
|
13697
|
+
applyEvent(proj, e);
|
|
13698
|
+
}
|
|
13699
|
+
return proj;
|
|
13700
|
+
};
|
|
13701
|
+
|
|
13702
|
+
// src/cli/resolve-projection-work-item-id.ts
|
|
13703
|
+
var isDigitOnlyWorkItemRef = (raw) => /^\d+$/.test(raw.trim());
|
|
13704
|
+
var resolveEpicId = (projection, raw) => {
|
|
13705
|
+
const t = raw.trim();
|
|
13706
|
+
if (t === "") return void 0;
|
|
13707
|
+
if (projection.epics.has(t)) return t;
|
|
13708
|
+
if (!isDigitOnlyWorkItemRef(t)) return void 0;
|
|
13709
|
+
const n = Number(t);
|
|
13710
|
+
const hits = [...projection.epics.values()].filter((e) => e.number === n);
|
|
13711
|
+
if (hits.length !== 1) return void 0;
|
|
13712
|
+
return hits[0].id;
|
|
13713
|
+
};
|
|
13714
|
+
var resolveStoryId = (projection, raw) => {
|
|
13715
|
+
const t = raw.trim();
|
|
13716
|
+
if (t === "") return void 0;
|
|
13717
|
+
if (projection.stories.has(t)) return t;
|
|
13718
|
+
if (!isDigitOnlyWorkItemRef(t)) return void 0;
|
|
13719
|
+
const n = Number(t);
|
|
13720
|
+
const hits = [...projection.stories.values()].filter((s) => s.number === n);
|
|
13721
|
+
if (hits.length !== 1) return void 0;
|
|
13722
|
+
return hits[0].id;
|
|
13723
|
+
};
|
|
13724
|
+
var resolveTicketId = (projection, raw) => {
|
|
13725
|
+
const t = raw.trim();
|
|
13726
|
+
if (t === "") return void 0;
|
|
13727
|
+
if (projection.tickets.has(t)) return t;
|
|
13728
|
+
if (!isDigitOnlyWorkItemRef(t)) return void 0;
|
|
13729
|
+
const n = Number(t);
|
|
13730
|
+
const hits = [...projection.tickets.values()].filter((x) => x.number === n);
|
|
13731
|
+
if (hits.length !== 1) return void 0;
|
|
13732
|
+
return hits[0].id;
|
|
13733
|
+
};
|
|
13734
|
+
var resolveTicketDependsOnTokensToIds = (projection, tokens) => {
|
|
13735
|
+
const mapped = tokens.map((raw) => {
|
|
13736
|
+
const t = raw.trim();
|
|
13737
|
+
if (t === "") return "";
|
|
13738
|
+
if (projection.tickets.has(t)) return t;
|
|
13739
|
+
if (!isDigitOnlyWorkItemRef(t)) return t;
|
|
13740
|
+
const hit = resolveTicketId(projection, t);
|
|
13741
|
+
return hit ?? t;
|
|
13742
|
+
});
|
|
13743
|
+
return normalizeTicketDependsOnIds(mapped);
|
|
13744
|
+
};
|
|
13745
|
+
var assertCreatePayloadUsesExpectedHeadNumber = (projection, kind, payload) => {
|
|
13746
|
+
const n = readOptionalPositiveIntegerFromPayload(payload, "number");
|
|
13747
|
+
const expected = kind === "epic" ? nextEpicNumberForCreate(projection) : kind === "story" ? nextStoryNumberForCreate(projection) : nextTicketNumberForCreate(projection);
|
|
13748
|
+
if (n !== expected) {
|
|
13749
|
+
const got = n === void 0 ? "missing" : String(n);
|
|
13750
|
+
throw new Error(
|
|
13751
|
+
`Invalid ${kind} number for this data branch: expected ${String(expected)}, got ${got}.`
|
|
13752
|
+
);
|
|
13753
|
+
}
|
|
13754
|
+
};
|
|
13755
|
+
|
|
13756
|
+
// src/config/hyper-pm-config.ts
|
|
13757
|
+
var hyperPmConfigSchema = external_exports.object({
|
|
13758
|
+
schema: external_exports.literal(1),
|
|
13759
|
+
dataBranch: external_exports.string().min(1),
|
|
13760
|
+
remote: external_exports.string().min(1).default("origin"),
|
|
13761
|
+
sync: external_exports.enum(["off", "outbound", "full"]).default("outbound"),
|
|
13762
|
+
githubRepo: external_exports.string().optional(),
|
|
13763
|
+
issueMapping: external_exports.enum(["ticket", "story", "epic"]).default("ticket")
|
|
13764
|
+
});
|
|
13765
|
+
|
|
13766
|
+
// src/config/load-config.ts
|
|
13767
|
+
var import_promises = require("node:fs/promises");
|
|
13768
|
+
var import_node_path = require("node:path");
|
|
13769
|
+
var CONFIG_DIR = ".hyper-pm";
|
|
13770
|
+
var CONFIG_FILE = "config.json";
|
|
13771
|
+
var getHyperPmConfigPath = (repoRoot) => {
|
|
13772
|
+
return (0, import_node_path.join)(repoRoot, CONFIG_DIR, CONFIG_FILE);
|
|
13773
|
+
};
|
|
13774
|
+
var loadHyperPmConfig = async (repoRoot, overrides = {}) => {
|
|
13775
|
+
const raw = await (0, import_promises.readFile)(getHyperPmConfigPath(repoRoot), "utf8");
|
|
13776
|
+
const parsed = JSON.parse(raw);
|
|
13777
|
+
const base = hyperPmConfigSchema.parse(parsed);
|
|
13778
|
+
const merged = { ...base, ...stripUndefined(overrides) };
|
|
13779
|
+
return hyperPmConfigSchema.parse(merged);
|
|
13780
|
+
};
|
|
13781
|
+
var stripUndefined = (obj) => {
|
|
13782
|
+
const out = {};
|
|
13783
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
13784
|
+
if (v !== void 0) out[k] = v;
|
|
13785
|
+
}
|
|
13786
|
+
return out;
|
|
13787
|
+
};
|
|
13788
|
+
|
|
13789
|
+
// src/config/save-config.ts
|
|
13790
|
+
var import_promises2 = require("node:fs/promises");
|
|
13791
|
+
var import_node_path2 = require("node:path");
|
|
13792
|
+
var saveHyperPmConfig = async (repoRoot, config) => {
|
|
13793
|
+
const target = getHyperPmConfigPath(repoRoot);
|
|
13794
|
+
await (0, import_promises2.mkdir)((0, import_node_path2.dirname)(target), { recursive: true });
|
|
13795
|
+
await (0, import_promises2.writeFile)(target, `${JSON.stringify(config, null, 2)}
|
|
13796
|
+
`, "utf8");
|
|
13797
|
+
};
|
|
13798
|
+
|
|
13799
|
+
// src/doctor/run-doctor.ts
|
|
13800
|
+
var runDoctorOnLines = (lines) => {
|
|
13801
|
+
const issues = [];
|
|
13802
|
+
for (let i = 0; i < lines.length; i++) {
|
|
13803
|
+
const line = lines[i]?.trim() ?? "";
|
|
13804
|
+
if (!line) continue;
|
|
13805
|
+
let json;
|
|
13806
|
+
try {
|
|
13807
|
+
json = JSON.parse(line);
|
|
13808
|
+
} catch (e) {
|
|
13809
|
+
issues.push({
|
|
13810
|
+
kind: "invalid-json",
|
|
13811
|
+
line: i + 1,
|
|
13812
|
+
message: e instanceof Error ? e.message : "parse error"
|
|
13813
|
+
});
|
|
13814
|
+
return issues;
|
|
13815
|
+
}
|
|
13816
|
+
const parsed = eventLineSchema.safeParse(json);
|
|
13817
|
+
if (!parsed.success) {
|
|
13818
|
+
issues.push({
|
|
13819
|
+
kind: "invalid-event",
|
|
13820
|
+
line: i + 1,
|
|
13821
|
+
message: parsed.error.message
|
|
13822
|
+
});
|
|
13823
|
+
return issues;
|
|
13824
|
+
}
|
|
13825
|
+
}
|
|
13826
|
+
return issues;
|
|
13827
|
+
};
|
|
13828
|
+
|
|
13829
|
+
// src/git/create-and-checkout-branch.ts
|
|
13830
|
+
var createAndCheckoutBranch = async (opts) => {
|
|
13831
|
+
await opts.runGit(opts.repoRoot, [
|
|
13832
|
+
"switch",
|
|
13833
|
+
"-c",
|
|
13834
|
+
opts.branchName,
|
|
13835
|
+
opts.startPoint
|
|
13836
|
+
]);
|
|
13837
|
+
};
|
|
13838
|
+
|
|
13839
|
+
// src/git/data-worktree-session.ts
|
|
13840
|
+
var import_promises3 = require("node:fs/promises");
|
|
13841
|
+
var import_node_path3 = require("node:path");
|
|
13842
|
+
var ensureDir = async (path) => {
|
|
13843
|
+
await (0, import_promises3.mkdir)(path, { recursive: true });
|
|
13844
|
+
};
|
|
13845
|
+
var defaultPathExists = async (path) => {
|
|
13846
|
+
try {
|
|
13847
|
+
await (0, import_promises3.access)(path);
|
|
13848
|
+
return true;
|
|
13849
|
+
} catch {
|
|
13850
|
+
return false;
|
|
13851
|
+
}
|
|
13852
|
+
};
|
|
13853
|
+
var parseDataBranchWorktreeFromPorcelain = (stdout, dataBranch) => {
|
|
13854
|
+
const wantRef = `refs/heads/${dataBranch}`;
|
|
13855
|
+
const blocks = stdout.split(/\n\n+/).map((b) => b.trim()).filter(Boolean);
|
|
13856
|
+
for (const block of blocks) {
|
|
13857
|
+
const lines = block.split("\n");
|
|
13858
|
+
let worktreePath;
|
|
13859
|
+
let branch;
|
|
13860
|
+
for (const line of lines) {
|
|
13861
|
+
if (line.startsWith("worktree ")) {
|
|
13862
|
+
worktreePath = line.slice("worktree ".length);
|
|
13863
|
+
} else if (line.startsWith("branch ")) {
|
|
13864
|
+
branch = line.slice("branch ".length);
|
|
13865
|
+
}
|
|
13866
|
+
}
|
|
13867
|
+
if (worktreePath !== void 0 && branch === wantRef) {
|
|
13868
|
+
return worktreePath;
|
|
13869
|
+
}
|
|
13870
|
+
}
|
|
13871
|
+
return void 0;
|
|
13872
|
+
};
|
|
13873
|
+
var openDataBranchWorktree = async (opts) => {
|
|
13874
|
+
const pathExists = opts.pathExists ?? defaultPathExists;
|
|
13875
|
+
const { stdout: listOut } = await opts.runGit(opts.repoRoot, [
|
|
13876
|
+
"worktree",
|
|
13877
|
+
"list",
|
|
13878
|
+
"--porcelain"
|
|
13879
|
+
]);
|
|
13880
|
+
const listedPath = parseDataBranchWorktreeFromPorcelain(
|
|
13881
|
+
listOut,
|
|
13882
|
+
opts.dataBranch
|
|
13883
|
+
);
|
|
13884
|
+
if (listedPath !== void 0 && await pathExists(listedPath)) {
|
|
13885
|
+
const dispose2 = async () => {
|
|
13886
|
+
return;
|
|
13887
|
+
};
|
|
13888
|
+
return { worktreePath: listedPath, dispose: dispose2 };
|
|
13889
|
+
}
|
|
13890
|
+
const worktreePath = (0, import_node_path3.join)(
|
|
13891
|
+
opts.tmpBase,
|
|
13892
|
+
`hyper-pm-worktree-${ulid().toLowerCase()}`
|
|
13893
|
+
);
|
|
13894
|
+
await ensureDir(opts.tmpBase);
|
|
13895
|
+
try {
|
|
13896
|
+
await opts.runGit(opts.repoRoot, [
|
|
13897
|
+
"worktree",
|
|
13898
|
+
"add",
|
|
13899
|
+
worktreePath,
|
|
13900
|
+
opts.dataBranch
|
|
13901
|
+
]);
|
|
13902
|
+
} catch (err) {
|
|
13903
|
+
await (0, import_promises3.rm)(worktreePath, { recursive: true, force: true }).catch(() => {
|
|
13904
|
+
});
|
|
13905
|
+
throw err;
|
|
13906
|
+
}
|
|
13907
|
+
const dispose = async () => {
|
|
13908
|
+
if (opts.keepWorktree) {
|
|
13909
|
+
return;
|
|
13910
|
+
}
|
|
13911
|
+
await opts.runGit(opts.repoRoot, ["worktree", "remove", "--force", worktreePath]).catch(() => {
|
|
13912
|
+
});
|
|
13913
|
+
await (0, import_promises3.rm)(worktreePath, { recursive: true, force: true }).catch(() => {
|
|
13914
|
+
});
|
|
13915
|
+
};
|
|
13916
|
+
return { worktreePath, dispose };
|
|
13917
|
+
};
|
|
13918
|
+
|
|
13919
|
+
// src/git/find-git-root.ts
|
|
13920
|
+
var findGitRoot = async (cwd, deps) => {
|
|
13921
|
+
const { stdout } = await deps.runGit(cwd, ["rev-parse", "--show-toplevel"]);
|
|
13922
|
+
return stdout;
|
|
13923
|
+
};
|
|
13924
|
+
|
|
13925
|
+
// src/git/init-orphan-data-branch.ts
|
|
13926
|
+
var import_promises4 = require("node:fs/promises");
|
|
13927
|
+
var import_node_path4 = require("node:path");
|
|
13928
|
+
var initOrphanDataBranchInWorktree = async (opts) => {
|
|
13929
|
+
const { stdout: tip } = await opts.runGit(opts.repoRoot, [
|
|
13930
|
+
"rev-parse",
|
|
13931
|
+
"HEAD"
|
|
13932
|
+
]);
|
|
13933
|
+
const tipCommit = tip.trim();
|
|
13934
|
+
await opts.runGit(opts.repoRoot, [
|
|
13935
|
+
"worktree",
|
|
13936
|
+
"add",
|
|
13937
|
+
opts.worktreePath,
|
|
13938
|
+
tipCommit
|
|
13939
|
+
]);
|
|
13940
|
+
await opts.runGit(opts.worktreePath, [
|
|
13941
|
+
"checkout",
|
|
13942
|
+
"--orphan",
|
|
13943
|
+
opts.dataBranch
|
|
13944
|
+
]);
|
|
13945
|
+
await opts.runGit(opts.worktreePath, ["rm", "-rf", "."]).catch(() => {
|
|
13946
|
+
});
|
|
13947
|
+
const marker = (0, import_node_path4.join)(opts.worktreePath, "README.hyper-pm.md");
|
|
13948
|
+
await (0, import_promises4.writeFile)(
|
|
13949
|
+
marker,
|
|
13950
|
+
"# hyper-pm data branch\n\nAppend-only events live under `events/`.\n",
|
|
13951
|
+
"utf8"
|
|
13952
|
+
);
|
|
13953
|
+
await opts.runGit(opts.worktreePath, ["add", "."]);
|
|
13954
|
+
await opts.runGit(opts.worktreePath, [
|
|
13955
|
+
"commit",
|
|
13956
|
+
"-m",
|
|
13957
|
+
"init hyper-pm data branch"
|
|
13958
|
+
]);
|
|
13959
|
+
};
|
|
13960
|
+
|
|
13961
|
+
// src/git/pick-unique-local-branch-name.ts
|
|
13962
|
+
var DEFAULT_MAX_SUFFIX = 1e3;
|
|
13963
|
+
var localBranchRefExists = async (repoRoot, name, git) => {
|
|
13964
|
+
try {
|
|
13965
|
+
await git(repoRoot, ["show-ref", "--verify", `refs/heads/${name}`]);
|
|
13966
|
+
return true;
|
|
13967
|
+
} catch {
|
|
13968
|
+
return false;
|
|
13969
|
+
}
|
|
13970
|
+
};
|
|
13971
|
+
var pickUniqueLocalBranchName = async (opts) => {
|
|
13972
|
+
const max = opts.maxSuffix ?? DEFAULT_MAX_SUFFIX;
|
|
13973
|
+
const { preferredBase, repoRoot, runGit: git } = opts;
|
|
13974
|
+
for (let n = 1; n <= max; n += 1) {
|
|
13975
|
+
const raw = n === 1 ? preferredBase : `${preferredBase}-${n}`;
|
|
13976
|
+
const norm = normalizeTicketBranchName(raw);
|
|
13977
|
+
if (norm === void 0) {
|
|
13978
|
+
continue;
|
|
13979
|
+
}
|
|
13980
|
+
if (!await localBranchRefExists(repoRoot, norm, git)) {
|
|
13981
|
+
return { branch: norm, preferred: preferredBase };
|
|
13982
|
+
}
|
|
13983
|
+
}
|
|
13984
|
+
throw new Error(
|
|
13985
|
+
`Could not allocate a free local branch name from ${JSON.stringify(preferredBase)} (tried suffixes up to -${String(max)})`
|
|
13986
|
+
);
|
|
13987
|
+
};
|
|
13988
|
+
|
|
13989
|
+
// src/git/resolve-integration-start-point.ts
|
|
13990
|
+
var assertGitRefResolvable = async (repoRoot, ref, git) => {
|
|
13991
|
+
try {
|
|
13992
|
+
await git(repoRoot, ["rev-parse", "-q", "--verify", ref]);
|
|
13993
|
+
} catch {
|
|
13994
|
+
throw new Error(`Invalid or ambiguous --from ref: ${ref}`);
|
|
13995
|
+
}
|
|
13996
|
+
};
|
|
13997
|
+
var resolveIntegrationStartPoint = async (repoRoot, remote, git) => {
|
|
13998
|
+
const symRef = `refs/remotes/${remote}/HEAD`;
|
|
13999
|
+
try {
|
|
14000
|
+
const { stdout } = await git(repoRoot, ["symbolic-ref", "-q", symRef]);
|
|
14001
|
+
const target = stdout.trim();
|
|
14002
|
+
if (target !== "") {
|
|
14003
|
+
await git(repoRoot, ["rev-parse", "-q", "--verify", target]);
|
|
14004
|
+
return target;
|
|
14005
|
+
}
|
|
14006
|
+
} catch {
|
|
14007
|
+
}
|
|
14008
|
+
for (const head of ["refs/heads/main", "refs/heads/master"]) {
|
|
14009
|
+
try {
|
|
14010
|
+
await git(repoRoot, ["rev-parse", "-q", "--verify", head]);
|
|
14011
|
+
return head;
|
|
14012
|
+
} catch {
|
|
14013
|
+
}
|
|
14014
|
+
}
|
|
14015
|
+
try {
|
|
14016
|
+
await git(repoRoot, ["rev-parse", "-q", "--verify", "HEAD"]);
|
|
14017
|
+
return "HEAD";
|
|
14018
|
+
} catch {
|
|
14019
|
+
}
|
|
14020
|
+
throw new Error(
|
|
14021
|
+
"Could not resolve a default branch to branch from; pass --from <ref> (e.g. main or origin/main)."
|
|
14022
|
+
);
|
|
14023
|
+
};
|
|
14024
|
+
|
|
14025
|
+
// src/git/list-repo-commit-authors.ts
|
|
14026
|
+
var listRepoCommitAuthors = async (repoRoot, git) => {
|
|
14027
|
+
try {
|
|
14028
|
+
const { stdout } = await git(repoRoot, [
|
|
14029
|
+
"-c",
|
|
14030
|
+
"log.showSignature=false",
|
|
14031
|
+
"log",
|
|
14032
|
+
"--all",
|
|
14033
|
+
"--format=%an%x1f%ae"
|
|
14034
|
+
]);
|
|
14035
|
+
if (stdout === "") return [];
|
|
14036
|
+
const lines = stdout.split("\n").filter((l) => l.length > 0);
|
|
14037
|
+
const seenEmail = /* @__PURE__ */ new Set();
|
|
14038
|
+
const out = [];
|
|
14039
|
+
for (const line of lines) {
|
|
14040
|
+
const sep2 = line.indexOf("");
|
|
14041
|
+
if (sep2 <= 0) continue;
|
|
14042
|
+
const name = line.slice(0, sep2).trim();
|
|
14043
|
+
const email = line.slice(sep2 + 1).trim();
|
|
14044
|
+
if (email === "") continue;
|
|
14045
|
+
const key = email.toLowerCase();
|
|
14046
|
+
if (seenEmail.has(key)) continue;
|
|
14047
|
+
seenEmail.add(key);
|
|
14048
|
+
const loginGuess = guessGithubLoginFromContact(name, email);
|
|
14049
|
+
out.push(
|
|
14050
|
+
loginGuess !== void 0 ? { name, email, loginGuess } : { name, email }
|
|
14051
|
+
);
|
|
14052
|
+
}
|
|
14053
|
+
return out;
|
|
14054
|
+
} catch {
|
|
14055
|
+
return [];
|
|
14056
|
+
}
|
|
14057
|
+
};
|
|
14058
|
+
|
|
14059
|
+
// src/git/parse-github-owner-repo-from-remote-url.ts
|
|
14060
|
+
var parseGithubOwnerRepoFromRemoteUrl = (rawUrl) => {
|
|
14061
|
+
const trimmed = rawUrl.trim();
|
|
14062
|
+
if (!trimmed) {
|
|
14063
|
+
return void 0;
|
|
14064
|
+
}
|
|
14065
|
+
const scpMatch = /^git@github\.com:(?<path>.+)$/i.exec(trimmed);
|
|
14066
|
+
if (scpMatch?.groups?.path) {
|
|
14067
|
+
return slugFromGithubPath(scpMatch.groups.path);
|
|
14068
|
+
}
|
|
14069
|
+
try {
|
|
14070
|
+
const withScheme = trimmed.includes("://") ? trimmed : `https://${trimmed}`;
|
|
14071
|
+
const u = new URL(withScheme);
|
|
14072
|
+
const host = u.hostname.toLowerCase();
|
|
14073
|
+
if (host !== "github.com" && host !== "www.github.com") {
|
|
14074
|
+
return void 0;
|
|
14075
|
+
}
|
|
14076
|
+
return slugFromGithubPath(u.pathname);
|
|
14077
|
+
} catch {
|
|
14078
|
+
return void 0;
|
|
14079
|
+
}
|
|
14080
|
+
};
|
|
14081
|
+
var slugFromGithubPath = (path) => {
|
|
14082
|
+
const segments = path.replace(/^\/+/, "").split("/").filter((s) => s.length > 0).map((s) => s.replace(/\.git$/i, ""));
|
|
14083
|
+
if (segments.length < 2) {
|
|
14084
|
+
return void 0;
|
|
14085
|
+
}
|
|
14086
|
+
const owner = segments[0];
|
|
14087
|
+
const repo = segments[1];
|
|
14088
|
+
if (!owner || !repo) {
|
|
14089
|
+
return void 0;
|
|
14090
|
+
}
|
|
14091
|
+
return `${owner}/${repo}`;
|
|
14092
|
+
};
|
|
14093
|
+
|
|
14094
|
+
// src/git/try-read-github-owner-repo-slug-from-git.ts
|
|
14095
|
+
var tryReadGithubOwnerRepoSlugFromGit = async (params) => {
|
|
14096
|
+
try {
|
|
14097
|
+
const { stdout } = await params.runGit(params.repoRoot, [
|
|
14098
|
+
"remote",
|
|
14099
|
+
"get-url",
|
|
14100
|
+
params.remote
|
|
14101
|
+
]);
|
|
14102
|
+
return parseGithubOwnerRepoFromRemoteUrl(stdout);
|
|
14103
|
+
} catch {
|
|
14104
|
+
return void 0;
|
|
14105
|
+
}
|
|
14106
|
+
};
|
|
14107
|
+
|
|
14108
|
+
// src/git/resolve-effective-git-author-for-data-commit.ts
|
|
14109
|
+
var defaultDataCommitName = "hyper-pm";
|
|
14110
|
+
var defaultDataCommitEmail = "hyper-pm@users.noreply.github.com";
|
|
14111
|
+
var tryReadGitConfigUserName = async (cwd, runGit2) => {
|
|
14112
|
+
try {
|
|
14113
|
+
const { stdout } = await runGit2(cwd, ["config", "--get", "user.name"]);
|
|
14114
|
+
return stdout.trim();
|
|
14115
|
+
} catch {
|
|
14116
|
+
return "";
|
|
14117
|
+
}
|
|
14118
|
+
};
|
|
14119
|
+
var tryReadGitConfigUserEmail = async (cwd, runGit2) => {
|
|
14120
|
+
try {
|
|
14121
|
+
const { stdout } = await runGit2(cwd, ["config", "--get", "user.email"]);
|
|
14122
|
+
return stdout.trim();
|
|
14123
|
+
} catch {
|
|
14124
|
+
return "";
|
|
14125
|
+
}
|
|
14126
|
+
};
|
|
14127
|
+
var resolveEffectiveGitAuthorForDataCommit = async (cwd, runGit2, authorEnv) => {
|
|
14128
|
+
const fromGitName = await tryReadGitConfigUserName(cwd, runGit2);
|
|
14129
|
+
const fromGitEmail = await tryReadGitConfigUserEmail(cwd, runGit2);
|
|
14130
|
+
const name = fromGitName || authorEnv.HYPER_PM_GIT_USER_NAME?.trim() || authorEnv.GIT_AUTHOR_NAME?.trim() || defaultDataCommitName;
|
|
14131
|
+
const email = fromGitEmail || authorEnv.HYPER_PM_GIT_USER_EMAIL?.trim() || authorEnv.GIT_AUTHOR_EMAIL?.trim() || defaultDataCommitEmail;
|
|
14132
|
+
return { name, email };
|
|
14133
|
+
};
|
|
14134
|
+
|
|
14135
|
+
// src/run/commit-data.ts
|
|
14136
|
+
var maxActorSuffixLen = 60;
|
|
14137
|
+
var formatDataBranchCommitMessage = (base, actorSuffix) => {
|
|
14138
|
+
const raw = actorSuffix?.trim();
|
|
14139
|
+
if (!raw) {
|
|
14140
|
+
return base;
|
|
14141
|
+
}
|
|
14142
|
+
const collapsed = raw.replace(/\s+/g, " ");
|
|
14143
|
+
const suffix = collapsed.length > maxActorSuffixLen ? `${collapsed.slice(0, maxActorSuffixLen - 1)}\u2026` : collapsed;
|
|
14144
|
+
return `${base} (${suffix})`;
|
|
14145
|
+
};
|
|
14146
|
+
var commitDataWorktreeIfNeeded = async (worktreePath, message, runGit2, opts) => {
|
|
14147
|
+
const { stdout } = await runGit2(worktreePath, ["status", "--porcelain"]);
|
|
14148
|
+
if (!stdout.trim()) return;
|
|
14149
|
+
await runGit2(worktreePath, ["add", "."]);
|
|
14150
|
+
const authorEnv = opts?.authorEnv ?? env;
|
|
14151
|
+
const { name, email } = await resolveEffectiveGitAuthorForDataCommit(
|
|
14152
|
+
worktreePath,
|
|
14153
|
+
runGit2,
|
|
14154
|
+
authorEnv
|
|
14155
|
+
);
|
|
14156
|
+
await runGit2(worktreePath, [
|
|
14157
|
+
"-c",
|
|
14158
|
+
`user.name=${name}`,
|
|
14159
|
+
"-c",
|
|
14160
|
+
`user.email=${email}`,
|
|
14161
|
+
"commit",
|
|
14162
|
+
"-m",
|
|
14163
|
+
message
|
|
14164
|
+
]);
|
|
14165
|
+
};
|
|
14166
|
+
|
|
14167
|
+
// src/run/push-data-branch.ts
|
|
14168
|
+
var firstLineFromUnknown = (err) => {
|
|
14169
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
14170
|
+
const line = raw.trim().split("\n")[0]?.trim() ?? raw.trim();
|
|
14171
|
+
return line.length > 0 ? line : "git error";
|
|
14172
|
+
};
|
|
14173
|
+
var tryPushDataBranchToRemote = async (worktreePath, remote, branch, runGit2) => {
|
|
14174
|
+
try {
|
|
14175
|
+
await runGit2(worktreePath, ["remote", "get-url", remote]);
|
|
14176
|
+
} catch (e) {
|
|
14177
|
+
return {
|
|
14178
|
+
status: "skipped_no_remote",
|
|
14179
|
+
detail: firstLineFromUnknown(e)
|
|
14180
|
+
};
|
|
14181
|
+
}
|
|
14182
|
+
try {
|
|
14183
|
+
await runGit2(worktreePath, ["push", "-u", remote, branch]);
|
|
14184
|
+
return { status: "pushed" };
|
|
14185
|
+
} catch (e) {
|
|
14186
|
+
return {
|
|
14187
|
+
status: "failed",
|
|
14188
|
+
detail: firstLineFromUnknown(e)
|
|
14189
|
+
};
|
|
14190
|
+
}
|
|
14191
|
+
};
|
|
14192
|
+
|
|
14193
|
+
// src/run/sync-remote-data-branch.ts
|
|
14194
|
+
var SyncRemoteDataBranchMergeError = class extends Error {
|
|
14195
|
+
/** @param message - Human-readable reason (stderr excerpt or generic text). */
|
|
14196
|
+
constructor(message) {
|
|
14197
|
+
super(message);
|
|
14198
|
+
this.name = "SyncRemoteDataBranchMergeError";
|
|
14199
|
+
}
|
|
14200
|
+
};
|
|
14201
|
+
var remoteTrackingRef = (remote, dataBranch) => `refs/remotes/${remote}/${dataBranch}`;
|
|
14202
|
+
var mergeRefSpecifier = (remote, dataBranch) => `${remote}/${dataBranch}`;
|
|
14203
|
+
var isLikelyNonFastForwardPushFailure = (detail) => {
|
|
14204
|
+
if (detail === void 0) return false;
|
|
14205
|
+
const d = detail.toLowerCase();
|
|
14206
|
+
return d.includes("non-fast-forward") || d.includes("failed to push");
|
|
14207
|
+
};
|
|
14208
|
+
var classifyMergeOutput = (combined) => {
|
|
14209
|
+
const c = combined.toLowerCase();
|
|
14210
|
+
if (c.includes("already up to date")) {
|
|
14211
|
+
return "up_to_date";
|
|
14212
|
+
}
|
|
14213
|
+
if (c.includes("fast-forward")) {
|
|
14214
|
+
return "fast_forward";
|
|
14215
|
+
}
|
|
14216
|
+
return "merge_commit";
|
|
14217
|
+
};
|
|
14218
|
+
var refExists = async (cwd, ref, runGit2) => {
|
|
14219
|
+
try {
|
|
14220
|
+
await runGit2(cwd, ["show-ref", "--verify", "--quiet", ref]);
|
|
14221
|
+
return true;
|
|
14222
|
+
} catch {
|
|
14223
|
+
return false;
|
|
14224
|
+
}
|
|
14225
|
+
};
|
|
14226
|
+
var fetchRemoteDataBranch = async (worktreePath, remote, dataBranch, runGit2) => {
|
|
14227
|
+
try {
|
|
14228
|
+
await runGit2(worktreePath, ["remote", "get-url", remote]);
|
|
14229
|
+
} catch {
|
|
14230
|
+
return "skipped_no_remote";
|
|
14231
|
+
}
|
|
14232
|
+
try {
|
|
14233
|
+
await runGit2(worktreePath, ["fetch", remote, dataBranch]);
|
|
14234
|
+
return "ok";
|
|
14235
|
+
} catch (e) {
|
|
14236
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
14237
|
+
if (/couldn't find remote ref|could not find remote ref/i.test(msg)) {
|
|
14238
|
+
return "remote_branch_absent";
|
|
14239
|
+
}
|
|
14240
|
+
throw e;
|
|
14241
|
+
}
|
|
14242
|
+
};
|
|
14243
|
+
var mergeRemoteTrackingIntoHead = async (worktreePath, remote, dataBranch, runGit2, authorEnv = env) => {
|
|
14244
|
+
const spec = mergeRefSpecifier(remote, dataBranch);
|
|
14245
|
+
const { name, email } = await resolveEffectiveGitAuthorForDataCommit(
|
|
14246
|
+
worktreePath,
|
|
14247
|
+
runGit2,
|
|
14248
|
+
authorEnv
|
|
14249
|
+
);
|
|
14250
|
+
try {
|
|
14251
|
+
const { stdout, stderr } = await runGit2(worktreePath, [
|
|
14252
|
+
"-c",
|
|
14253
|
+
`user.name=${name}`,
|
|
14254
|
+
"-c",
|
|
14255
|
+
`user.email=${email}`,
|
|
14256
|
+
"merge",
|
|
14257
|
+
"--no-edit",
|
|
14258
|
+
spec
|
|
14259
|
+
]);
|
|
14260
|
+
return classifyMergeOutput(`${stdout}
|
|
14261
|
+
${stderr}`);
|
|
14262
|
+
} catch (e) {
|
|
14263
|
+
await runGit2(worktreePath, ["merge", "--abort"]).catch(() => {
|
|
14264
|
+
});
|
|
14265
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
14266
|
+
throw new SyncRemoteDataBranchMergeError(
|
|
14267
|
+
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}`
|
|
14268
|
+
);
|
|
14269
|
+
}
|
|
14270
|
+
};
|
|
14271
|
+
var runRemoteDataBranchGitSync = async (worktreePath, remote, dataBranch, runGit2, skipPush, deps = {}) => {
|
|
14272
|
+
const tryPushFn = deps.tryPush ?? tryPushDataBranchToRemote;
|
|
14273
|
+
const maxPushAttempts = deps.maxPushAttempts ?? 3;
|
|
14274
|
+
const authorEnv = deps.authorEnv ?? env;
|
|
14275
|
+
let dataBranchFetch = await fetchRemoteDataBranch(worktreePath, remote, dataBranch, runGit2);
|
|
14276
|
+
let dataBranchMerge = dataBranchFetch === "skipped_no_remote" ? "skipped_no_remote" : dataBranchFetch === "remote_branch_absent" ? "skipped_missing_remote_branch" : "up_to_date";
|
|
14277
|
+
if (dataBranchFetch === "ok") {
|
|
14278
|
+
const tracking = remoteTrackingRef(remote, dataBranch);
|
|
14279
|
+
const exists = await refExists(worktreePath, tracking, runGit2);
|
|
14280
|
+
if (!exists) {
|
|
14281
|
+
dataBranchMerge = "skipped_missing_remote_branch";
|
|
14282
|
+
} else {
|
|
14283
|
+
dataBranchMerge = await mergeRemoteTrackingIntoHead(
|
|
14284
|
+
worktreePath,
|
|
14285
|
+
remote,
|
|
14286
|
+
dataBranch,
|
|
14287
|
+
runGit2,
|
|
14288
|
+
authorEnv
|
|
14289
|
+
);
|
|
14290
|
+
}
|
|
14291
|
+
}
|
|
14292
|
+
if (skipPush) {
|
|
14293
|
+
return {
|
|
14294
|
+
dataBranchFetch,
|
|
14295
|
+
dataBranchMerge,
|
|
14296
|
+
dataBranchPush: "skipped_cli",
|
|
14297
|
+
dataBranchPushDetail: "skip-push",
|
|
14298
|
+
pushAttempts: 0
|
|
14299
|
+
};
|
|
14300
|
+
}
|
|
14301
|
+
let pushAttempts = 0;
|
|
14302
|
+
let lastPush = { status: "skipped_no_remote" };
|
|
14303
|
+
const refetchAndMerge = async () => {
|
|
14304
|
+
dataBranchFetch = await fetchRemoteDataBranch(
|
|
14305
|
+
worktreePath,
|
|
14306
|
+
remote,
|
|
14307
|
+
dataBranch,
|
|
14308
|
+
runGit2
|
|
14309
|
+
);
|
|
14310
|
+
if (dataBranchFetch !== "ok") {
|
|
14311
|
+
return;
|
|
14312
|
+
}
|
|
14313
|
+
const tracking = remoteTrackingRef(remote, dataBranch);
|
|
14314
|
+
const exists = await refExists(worktreePath, tracking, runGit2);
|
|
14315
|
+
if (!exists) {
|
|
14316
|
+
return;
|
|
14317
|
+
}
|
|
14318
|
+
dataBranchMerge = await mergeRemoteTrackingIntoHead(
|
|
14319
|
+
worktreePath,
|
|
14320
|
+
remote,
|
|
14321
|
+
dataBranch,
|
|
14322
|
+
runGit2,
|
|
14323
|
+
authorEnv
|
|
14324
|
+
);
|
|
14325
|
+
};
|
|
14326
|
+
for (let attempt = 1; attempt <= maxPushAttempts; attempt += 1) {
|
|
14327
|
+
pushAttempts = attempt;
|
|
14328
|
+
lastPush = await tryPushFn(worktreePath, remote, dataBranch, runGit2);
|
|
14329
|
+
if (lastPush.status === "pushed" || lastPush.status === "skipped_no_remote") {
|
|
14330
|
+
break;
|
|
14331
|
+
}
|
|
14332
|
+
if (lastPush.status === "failed" && isLikelyNonFastForwardPushFailure(lastPush.detail) && attempt < maxPushAttempts) {
|
|
14333
|
+
await refetchAndMerge();
|
|
14334
|
+
continue;
|
|
14335
|
+
}
|
|
14336
|
+
break;
|
|
14337
|
+
}
|
|
14338
|
+
return {
|
|
14339
|
+
dataBranchFetch,
|
|
14340
|
+
dataBranchMerge,
|
|
14341
|
+
dataBranchPush: lastPush.status,
|
|
14342
|
+
...lastPush.detail !== void 0 ? { dataBranchPushDetail: lastPush.detail } : {},
|
|
14343
|
+
pushAttempts
|
|
14344
|
+
};
|
|
14345
|
+
};
|
|
14346
|
+
|
|
14347
|
+
// src/storage/append-event.ts
|
|
14348
|
+
var import_promises5 = require("node:fs/promises");
|
|
14349
|
+
var import_node_path5 = require("node:path");
|
|
14350
|
+
|
|
14351
|
+
// src/storage/event-path.ts
|
|
14352
|
+
var nextEventRelPath = (now, deps) => {
|
|
14353
|
+
const y = String(now.getUTCFullYear());
|
|
14354
|
+
const m = String(now.getUTCMonth() + 1).padStart(2, "0");
|
|
14355
|
+
const nextId = deps?.nextId ?? (() => ulid().toLowerCase());
|
|
14356
|
+
const part = `part-${nextId()}`;
|
|
14357
|
+
return `events/${y}/${m}/${part}.jsonl`;
|
|
14358
|
+
};
|
|
14359
|
+
|
|
14360
|
+
// src/storage/append-event.ts
|
|
14361
|
+
var appendEventLine = async (dataRoot, event, clock, opts) => {
|
|
14362
|
+
const rel = nextEventRelPath(clock.now(), {
|
|
14363
|
+
nextId: opts?.nextEventId
|
|
14364
|
+
});
|
|
14365
|
+
const abs = `${dataRoot.replace(/\/$/, "")}/${rel}`;
|
|
14366
|
+
await (0, import_promises5.mkdir)((0, import_node_path5.dirname)(abs), { recursive: true });
|
|
14367
|
+
await (0, import_promises5.appendFile)(abs, `${JSON.stringify(event)}
|
|
14368
|
+
`, "utf8");
|
|
14369
|
+
return rel;
|
|
14370
|
+
};
|
|
14371
|
+
|
|
14372
|
+
// src/storage/read-event-lines.ts
|
|
14373
|
+
var import_promises6 = require("node:fs/promises");
|
|
14374
|
+
var import_node_path6 = require("node:path");
|
|
14375
|
+
var listJsonlFiles = async (dir, root) => {
|
|
14376
|
+
const out = [];
|
|
14377
|
+
const entries = await (0, import_promises6.readdir)(dir, { withFileTypes: true });
|
|
14378
|
+
for (const e of entries) {
|
|
14379
|
+
const abs = (0, import_node_path6.join)(dir, e.name);
|
|
14380
|
+
if (e.isDirectory()) {
|
|
14381
|
+
out.push(...await listJsonlFiles(abs, root));
|
|
14382
|
+
} else if (e.isFile() && e.name.endsWith(".jsonl")) {
|
|
14383
|
+
out.push((0, import_node_path6.relative)(root, abs).split("\\").join("/"));
|
|
14384
|
+
}
|
|
13949
14385
|
}
|
|
14386
|
+
return out;
|
|
13950
14387
|
};
|
|
13951
|
-
var
|
|
13952
|
-
const
|
|
13953
|
-
|
|
13954
|
-
|
|
13955
|
-
|
|
13956
|
-
|
|
13957
|
-
|
|
13958
|
-
events.push(eventLineSchema.parse(json));
|
|
14388
|
+
var readAllEventLines = async (dataRoot) => {
|
|
14389
|
+
const eventsRoot = (0, import_node_path6.join)(dataRoot, "events");
|
|
14390
|
+
let files = [];
|
|
14391
|
+
try {
|
|
14392
|
+
files = await listJsonlFiles(eventsRoot, dataRoot);
|
|
14393
|
+
} catch {
|
|
14394
|
+
return [];
|
|
13959
14395
|
}
|
|
13960
|
-
|
|
13961
|
-
|
|
13962
|
-
|
|
13963
|
-
|
|
13964
|
-
|
|
13965
|
-
|
|
13966
|
-
|
|
14396
|
+
files.sort((a, b) => a.localeCompare(b));
|
|
14397
|
+
const lines = [];
|
|
14398
|
+
for (const rel of files) {
|
|
14399
|
+
const content = await (0, import_promises6.readFile)((0, import_node_path6.join)(dataRoot, rel), "utf8");
|
|
14400
|
+
for (const line of content.split("\n")) {
|
|
14401
|
+
if (line.trim()) lines.push(line);
|
|
14402
|
+
}
|
|
13967
14403
|
}
|
|
13968
|
-
return
|
|
14404
|
+
return lines;
|
|
13969
14405
|
};
|
|
13970
14406
|
|
|
13971
14407
|
// src/sync/collect-github-pr-activity-source-ids.ts
|
|
@@ -14319,6 +14755,19 @@ var runGithubInboundSync = async (params) => {
|
|
|
14319
14755
|
const planningSource = inboundTicketPlanningPayloadFromFenceMeta(meta);
|
|
14320
14756
|
const planningPayload = {};
|
|
14321
14757
|
for (const [k, v] of Object.entries(planningSource)) {
|
|
14758
|
+
if (k === "dependsOn") {
|
|
14759
|
+
if (v === null) {
|
|
14760
|
+
if (!ticketDependsOnListsEqual(ticket.dependsOn, [])) {
|
|
14761
|
+
planningPayload["dependsOn"] = null;
|
|
14762
|
+
}
|
|
14763
|
+
} else if (Array.isArray(v)) {
|
|
14764
|
+
const parsed = parseTicketDependsOnFromPayloadValue(v);
|
|
14765
|
+
if (parsed !== void 0 && !ticketDependsOnListsEqual(ticket.dependsOn, parsed)) {
|
|
14766
|
+
planningPayload["dependsOn"] = parsed;
|
|
14767
|
+
}
|
|
14768
|
+
}
|
|
14769
|
+
continue;
|
|
14770
|
+
}
|
|
14322
14771
|
const tk = k;
|
|
14323
14772
|
const cur = ticket[tk];
|
|
14324
14773
|
if (v === null) {
|
|
@@ -14512,14 +14961,15 @@ var parseCliWorkItemStatusList = (raws, deps) => {
|
|
|
14512
14961
|
}
|
|
14513
14962
|
return out;
|
|
14514
14963
|
};
|
|
14515
|
-
var buildTicketListQueryFromReadListOpts = (o, deps) => {
|
|
14964
|
+
var buildTicketListQueryFromReadListOpts = (projection, o, deps) => {
|
|
14516
14965
|
const query = {};
|
|
14517
14966
|
const statusTokens = normalizeCliStringList(o.status);
|
|
14518
14967
|
if (statusTokens.length > 0) {
|
|
14519
14968
|
query.statuses = parseCliWorkItemStatusList(statusTokens, deps);
|
|
14520
14969
|
}
|
|
14521
14970
|
if (o.epic !== void 0 && o.epic !== "") {
|
|
14522
|
-
|
|
14971
|
+
const trimmed = o.epic.trim();
|
|
14972
|
+
query.epicId = resolveEpicId(projection, trimmed) ?? trimmed;
|
|
14523
14973
|
}
|
|
14524
14974
|
const createdAfterMs = parseCliOptionalIsoMillis(
|
|
14525
14975
|
o.createdAfter,
|
|
@@ -14656,6 +15106,10 @@ var buildTicketListQueryFromReadListOpts = (o, deps) => {
|
|
|
14656
15106
|
if (targetFinishBeforeMs !== void 0) {
|
|
14657
15107
|
query.targetFinishBeforeMs = targetFinishBeforeMs;
|
|
14658
15108
|
}
|
|
15109
|
+
if (o.dependsOn !== void 0 && o.dependsOn.trim() !== "") {
|
|
15110
|
+
const trimmed = o.dependsOn.trim();
|
|
15111
|
+
query.dependsOnIncludesId = resolveTicketId(projection, trimmed) ?? trimmed;
|
|
15112
|
+
}
|
|
14659
15113
|
return Object.keys(query).length > 0 ? query : void 0;
|
|
14660
15114
|
};
|
|
14661
15115
|
var runCli = async (argv, deps = {
|
|
@@ -14731,19 +15185,19 @@ var runCli = async (argv, deps = {
|
|
|
14731
15185
|
const g = readGlobals(this);
|
|
14732
15186
|
const o = this.opts();
|
|
14733
15187
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
15188
|
+
const lines = await readAllEventLines(root);
|
|
15189
|
+
const proj = replayEvents(lines);
|
|
14734
15190
|
const id = o.id ?? ulid();
|
|
14735
15191
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
14736
|
-
const
|
|
14737
|
-
|
|
14738
|
-
|
|
14739
|
-
|
|
14740
|
-
|
|
14741
|
-
|
|
14742
|
-
|
|
14743
|
-
|
|
14744
|
-
|
|
14745
|
-
actor
|
|
14746
|
-
);
|
|
15192
|
+
const payload = {
|
|
15193
|
+
id,
|
|
15194
|
+
number: nextEpicNumberForCreate(proj),
|
|
15195
|
+
title: o.title,
|
|
15196
|
+
body: o.body,
|
|
15197
|
+
...status !== void 0 ? { status } : {}
|
|
15198
|
+
};
|
|
15199
|
+
assertCreatePayloadUsesExpectedHeadNumber(proj, "epic", payload);
|
|
15200
|
+
const evt = makeEvent("EpicCreated", payload, deps.clock, actor);
|
|
14747
15201
|
await appendEventLine(root, evt, deps.clock);
|
|
14748
15202
|
return evt.payload;
|
|
14749
15203
|
});
|
|
@@ -14759,18 +15213,19 @@ var runCli = async (argv, deps = {
|
|
|
14759
15213
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
14760
15214
|
const lines = await readAllEventLines(root);
|
|
14761
15215
|
const proj = replayEvents(lines);
|
|
14762
|
-
const
|
|
15216
|
+
const epicId = resolveEpicId(proj, o.id) ?? o.id.trim();
|
|
15217
|
+
const cur = proj.epics.get(epicId);
|
|
14763
15218
|
if (!cur || cur.deleted) {
|
|
14764
15219
|
throw new Error(`Epic not found: ${o.id}`);
|
|
14765
15220
|
}
|
|
14766
15221
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
14767
|
-
const draft = { id:
|
|
15222
|
+
const draft = { id: epicId };
|
|
14768
15223
|
if (o.title !== void 0) draft["title"] = o.title;
|
|
14769
15224
|
if (o.body !== void 0) draft["body"] = o.body;
|
|
14770
15225
|
if (status !== void 0) draft["status"] = status;
|
|
14771
15226
|
const payload = pruneEpicOrStoryUpdatePayloadAgainstRow(cur, draft);
|
|
14772
15227
|
if (isNoOpUpdatePayload(payload)) {
|
|
14773
|
-
return { id:
|
|
15228
|
+
return { id: epicId, noChanges: true };
|
|
14774
15229
|
}
|
|
14775
15230
|
const evt = makeEvent("EpicUpdated", payload, deps.clock, actor);
|
|
14776
15231
|
await appendEventLine(root, evt, deps.clock);
|
|
@@ -14781,9 +15236,12 @@ var runCli = async (argv, deps = {
|
|
|
14781
15236
|
const g = readGlobals(this);
|
|
14782
15237
|
const o = this.opts();
|
|
14783
15238
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
14784
|
-
const
|
|
15239
|
+
const lines = await readAllEventLines(root);
|
|
15240
|
+
const proj = replayEvents(lines);
|
|
15241
|
+
const epicId = resolveEpicId(proj, o.id) ?? o.id.trim();
|
|
15242
|
+
const evt = makeEvent("EpicDeleted", { id: epicId }, deps.clock, actor);
|
|
14785
15243
|
await appendEventLine(root, evt, deps.clock);
|
|
14786
|
-
return { id:
|
|
15244
|
+
return { id: epicId, deleted: true };
|
|
14787
15245
|
});
|
|
14788
15246
|
});
|
|
14789
15247
|
const story = program2.command("story");
|
|
@@ -14796,24 +15254,23 @@ var runCli = async (argv, deps = {
|
|
|
14796
15254
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
14797
15255
|
const lines = await readAllEventLines(root);
|
|
14798
15256
|
const proj = replayEvents(lines);
|
|
14799
|
-
const
|
|
15257
|
+
const epicId = resolveEpicId(proj, o.epic) ?? o.epic.trim();
|
|
15258
|
+
const epic2 = proj.epics.get(epicId);
|
|
14800
15259
|
if (!epic2 || epic2.deleted) {
|
|
14801
15260
|
throw new Error(`Epic not found: ${o.epic}`);
|
|
14802
15261
|
}
|
|
14803
15262
|
const id = o.id ?? ulid();
|
|
14804
15263
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
14805
|
-
const
|
|
14806
|
-
|
|
14807
|
-
|
|
14808
|
-
|
|
14809
|
-
|
|
14810
|
-
|
|
14811
|
-
|
|
14812
|
-
|
|
14813
|
-
|
|
14814
|
-
|
|
14815
|
-
actor
|
|
14816
|
-
);
|
|
15264
|
+
const payload = {
|
|
15265
|
+
id,
|
|
15266
|
+
number: nextStoryNumberForCreate(proj),
|
|
15267
|
+
epicId,
|
|
15268
|
+
title: o.title,
|
|
15269
|
+
body: o.body,
|
|
15270
|
+
...status !== void 0 ? { status } : {}
|
|
15271
|
+
};
|
|
15272
|
+
assertCreatePayloadUsesExpectedHeadNumber(proj, "story", payload);
|
|
15273
|
+
const evt = makeEvent("StoryCreated", payload, deps.clock, actor);
|
|
14817
15274
|
await appendEventLine(root, evt, deps.clock);
|
|
14818
15275
|
return evt.payload;
|
|
14819
15276
|
});
|
|
@@ -14832,18 +15289,19 @@ var runCli = async (argv, deps = {
|
|
|
14832
15289
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
14833
15290
|
const lines = await readAllEventLines(root);
|
|
14834
15291
|
const proj = replayEvents(lines);
|
|
14835
|
-
const
|
|
15292
|
+
const storyId = resolveStoryId(proj, o.id) ?? o.id.trim();
|
|
15293
|
+
const cur = proj.stories.get(storyId);
|
|
14836
15294
|
if (!cur || cur.deleted) {
|
|
14837
15295
|
throw new Error(`Story not found: ${o.id}`);
|
|
14838
15296
|
}
|
|
14839
15297
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
14840
|
-
const draft = { id:
|
|
15298
|
+
const draft = { id: storyId };
|
|
14841
15299
|
if (o.title !== void 0) draft["title"] = o.title;
|
|
14842
15300
|
if (o.body !== void 0) draft["body"] = o.body;
|
|
14843
15301
|
if (status !== void 0) draft["status"] = status;
|
|
14844
15302
|
const payload = pruneEpicOrStoryUpdatePayloadAgainstRow(cur, draft);
|
|
14845
15303
|
if (isNoOpUpdatePayload(payload)) {
|
|
14846
|
-
return { id:
|
|
15304
|
+
return { id: storyId, noChanges: true };
|
|
14847
15305
|
}
|
|
14848
15306
|
const evt = makeEvent("StoryUpdated", payload, deps.clock, actor);
|
|
14849
15307
|
await appendEventLine(root, evt, deps.clock);
|
|
@@ -14854,9 +15312,17 @@ var runCli = async (argv, deps = {
|
|
|
14854
15312
|
const g = readGlobals(this);
|
|
14855
15313
|
const o = this.opts();
|
|
14856
15314
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
14857
|
-
const
|
|
15315
|
+
const lines = await readAllEventLines(root);
|
|
15316
|
+
const proj = replayEvents(lines);
|
|
15317
|
+
const storyId = resolveStoryId(proj, o.id) ?? o.id.trim();
|
|
15318
|
+
const evt = makeEvent(
|
|
15319
|
+
"StoryDeleted",
|
|
15320
|
+
{ id: storyId },
|
|
15321
|
+
deps.clock,
|
|
15322
|
+
actor
|
|
15323
|
+
);
|
|
14858
15324
|
await appendEventLine(root, evt, deps.clock);
|
|
14859
|
-
return { id:
|
|
15325
|
+
return { id: storyId, deleted: true };
|
|
14860
15326
|
});
|
|
14861
15327
|
});
|
|
14862
15328
|
const ticket = program2.command("ticket");
|
|
@@ -14879,6 +15345,11 @@ var runCli = async (argv, deps = {
|
|
|
14879
15345
|
"planning label (repeatable)",
|
|
14880
15346
|
(value, previous) => [...previous, value],
|
|
14881
15347
|
[]
|
|
15348
|
+
).option(
|
|
15349
|
+
"--depends-on <id>",
|
|
15350
|
+
"prerequisite ticket id (repeatable)",
|
|
15351
|
+
(value, previous) => [...previous, value],
|
|
15352
|
+
[]
|
|
14882
15353
|
).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() {
|
|
14883
15354
|
const g = readGlobals(this);
|
|
14884
15355
|
const o = this.opts();
|
|
@@ -14941,6 +15412,7 @@ var runCli = async (argv, deps = {
|
|
|
14941
15412
|
"--target-finish-at",
|
|
14942
15413
|
deps
|
|
14943
15414
|
);
|
|
15415
|
+
const dependsOnTokensCreate = normalizeCliStringList(o.dependsOn);
|
|
14944
15416
|
const planningPayload = {
|
|
14945
15417
|
...labelsPayloadCreate,
|
|
14946
15418
|
...priorityParsed !== void 0 ? { priority: priorityParsed } : {},
|
|
@@ -14954,31 +15426,53 @@ var runCli = async (argv, deps = {
|
|
|
14954
15426
|
const proj = replayEvents(lines);
|
|
14955
15427
|
const storyRaw = o.story;
|
|
14956
15428
|
const storyTrimmed = storyRaw !== void 0 && storyRaw !== "" ? storyRaw.trim() : void 0;
|
|
15429
|
+
const storyIdResolved = storyTrimmed !== void 0 ? resolveStoryId(proj, storyTrimmed) ?? storyTrimmed : void 0;
|
|
14957
15430
|
if (storyTrimmed !== void 0) {
|
|
14958
|
-
const storyRow = proj.stories.get(
|
|
15431
|
+
const storyRow = proj.stories.get(storyIdResolved ?? "");
|
|
14959
15432
|
if (!storyRow || storyRow.deleted) {
|
|
14960
15433
|
throw new Error(`Story not found: ${storyTrimmed}`);
|
|
14961
15434
|
}
|
|
14962
15435
|
}
|
|
14963
15436
|
const id = o.id ?? ulid();
|
|
15437
|
+
const dependsOnNormCreate = resolveTicketDependsOnTokensToIds(
|
|
15438
|
+
proj,
|
|
15439
|
+
dependsOnTokensCreate
|
|
15440
|
+
);
|
|
14964
15441
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
14965
15442
|
const assigneeCreate = o.assignee !== void 0 ? { assignee: normalizeGithubLogin(o.assignee) } : {};
|
|
14966
|
-
const storyPayload =
|
|
15443
|
+
const storyPayload = storyIdResolved !== void 0 ? { storyId: storyIdResolved } : {};
|
|
14967
15444
|
const branchTokens = normalizeCliStringList(o.branch);
|
|
14968
15445
|
const branchesNorm = normalizeTicketBranchListFromStrings(branchTokens);
|
|
14969
15446
|
const branchesPayload = branchesNorm.length > 0 ? { branches: branchesNorm } : {};
|
|
15447
|
+
const dependsOnErr = validateTicketDependsOnForWrite({
|
|
15448
|
+
projection: proj,
|
|
15449
|
+
fromTicketId: id,
|
|
15450
|
+
nextDependsOn: dependsOnNormCreate
|
|
15451
|
+
});
|
|
15452
|
+
if (dependsOnErr !== void 0) {
|
|
15453
|
+
throw new Error(dependsOnErr);
|
|
15454
|
+
}
|
|
15455
|
+
const dependsOnPayloadCreate = dependsOnNormCreate.length > 0 ? { dependsOn: dependsOnNormCreate } : {};
|
|
15456
|
+
const createPayload = {
|
|
15457
|
+
id,
|
|
15458
|
+
number: nextTicketNumberForCreate(proj),
|
|
15459
|
+
...storyPayload,
|
|
15460
|
+
title: o.title,
|
|
15461
|
+
body,
|
|
15462
|
+
...status !== void 0 ? { status } : {},
|
|
15463
|
+
...assigneeCreate,
|
|
15464
|
+
...branchesPayload,
|
|
15465
|
+
...dependsOnPayloadCreate,
|
|
15466
|
+
...planningPayload
|
|
15467
|
+
};
|
|
15468
|
+
assertCreatePayloadUsesExpectedHeadNumber(
|
|
15469
|
+
proj,
|
|
15470
|
+
"ticket",
|
|
15471
|
+
createPayload
|
|
15472
|
+
);
|
|
14970
15473
|
const evt = makeEvent(
|
|
14971
15474
|
"TicketCreated",
|
|
14972
|
-
|
|
14973
|
-
id,
|
|
14974
|
-
...storyPayload,
|
|
14975
|
-
title: o.title,
|
|
14976
|
-
body,
|
|
14977
|
-
...status !== void 0 ? { status } : {},
|
|
14978
|
-
...assigneeCreate,
|
|
14979
|
-
...branchesPayload,
|
|
14980
|
-
...planningPayload
|
|
14981
|
-
},
|
|
15475
|
+
createPayload,
|
|
14982
15476
|
deps.clock,
|
|
14983
15477
|
actor
|
|
14984
15478
|
);
|
|
@@ -15034,6 +15528,9 @@ var runCli = async (argv, deps = {
|
|
|
15034
15528
|
).option(
|
|
15035
15529
|
"--branch <name>",
|
|
15036
15530
|
"when listing (no --id): only tickets linked to this branch (normalized exact match)"
|
|
15531
|
+
).option(
|
|
15532
|
+
"--depends-on <id>",
|
|
15533
|
+
"when listing (no --id): only tickets that list this ticket id in dependsOn"
|
|
15037
15534
|
).option(
|
|
15038
15535
|
"--priority <p>",
|
|
15039
15536
|
"when listing (no --id): OR-set of priorities (repeat flag); low|medium|high|urgent",
|
|
@@ -15113,7 +15610,17 @@ var runCli = async (argv, deps = {
|
|
|
15113
15610
|
"--clear-labels",
|
|
15114
15611
|
"remove all planning labels from the ticket",
|
|
15115
15612
|
false
|
|
15116
|
-
).option(
|
|
15613
|
+
).option(
|
|
15614
|
+
"--add-depends-on <id>",
|
|
15615
|
+
"add a prerequisite ticket id (repeatable)",
|
|
15616
|
+
(value, previous) => [...previous, value],
|
|
15617
|
+
[]
|
|
15618
|
+
).option(
|
|
15619
|
+
"--remove-depends-on <id>",
|
|
15620
|
+
"remove a prerequisite ticket id (repeatable)",
|
|
15621
|
+
(value, previous) => [...previous, value],
|
|
15622
|
+
[]
|
|
15623
|
+
).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() {
|
|
15117
15624
|
const g = readGlobals(this);
|
|
15118
15625
|
const o = this.opts();
|
|
15119
15626
|
let body = o.body;
|
|
@@ -15165,6 +15672,14 @@ ${body}`
|
|
|
15165
15672
|
);
|
|
15166
15673
|
deps.exit(ExitCode.UserError);
|
|
15167
15674
|
}
|
|
15675
|
+
const addDependsOnTokens = normalizeCliStringList(o.addDependsOn);
|
|
15676
|
+
const removeDependsOnTokens = normalizeCliStringList(o.removeDependsOn);
|
|
15677
|
+
if (o.clearDependsOn === true && (addDependsOnTokens.length > 0 || removeDependsOnTokens.length > 0)) {
|
|
15678
|
+
deps.error(
|
|
15679
|
+
"Cannot use --clear-depends-on with --add-depends-on or --remove-depends-on"
|
|
15680
|
+
);
|
|
15681
|
+
deps.exit(ExitCode.UserError);
|
|
15682
|
+
}
|
|
15168
15683
|
const mutual = (clear, set, clearName, setName) => {
|
|
15169
15684
|
if (clear === true && set !== void 0 && set !== "") {
|
|
15170
15685
|
deps.error(`Cannot use ${clearName} and ${setName} together`);
|
|
@@ -15229,25 +15744,27 @@ ${body}`
|
|
|
15229
15744
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
15230
15745
|
const lines = await readAllEventLines(root);
|
|
15231
15746
|
const proj = replayEvents(lines);
|
|
15747
|
+
const ticketId = resolveTicketId(proj, o.id) ?? o.id.trim();
|
|
15748
|
+
const storyIdResolved = storyTrimmed !== void 0 ? resolveStoryId(proj, storyTrimmed) ?? storyTrimmed : void 0;
|
|
15232
15749
|
if (storyTrimmed !== void 0) {
|
|
15233
|
-
const storyRow = proj.stories.get(
|
|
15750
|
+
const storyRow = proj.stories.get(storyIdResolved ?? "");
|
|
15234
15751
|
if (!storyRow || storyRow.deleted) {
|
|
15235
15752
|
throw new Error(`Story not found: ${storyTrimmed}`);
|
|
15236
15753
|
}
|
|
15237
15754
|
}
|
|
15238
|
-
const curTicket = proj.tickets.get(
|
|
15755
|
+
const curTicket = proj.tickets.get(ticketId);
|
|
15239
15756
|
if (curTicket === void 0 || curTicket.deleted) {
|
|
15240
15757
|
throw new Error(`Ticket not found: ${o.id}`);
|
|
15241
15758
|
}
|
|
15242
15759
|
const status = parseCliWorkItemStatus(o.status, deps);
|
|
15243
|
-
const payload = { id:
|
|
15760
|
+
const payload = { id: ticketId };
|
|
15244
15761
|
if (o.title !== void 0) payload["title"] = o.title;
|
|
15245
15762
|
if (body !== void 0) payload["body"] = body;
|
|
15246
15763
|
if (status !== void 0) payload["status"] = status;
|
|
15247
15764
|
if (o.unlinkStory) {
|
|
15248
15765
|
payload["storyId"] = null;
|
|
15249
15766
|
} else if (storyTrimmed !== void 0) {
|
|
15250
|
-
payload["storyId"] =
|
|
15767
|
+
payload["storyId"] = storyIdResolved;
|
|
15251
15768
|
}
|
|
15252
15769
|
if (o.unassign) {
|
|
15253
15770
|
payload["assignee"] = null;
|
|
@@ -15297,6 +15814,35 @@ ${body}`
|
|
|
15297
15814
|
payload["labels"] = nextLabels;
|
|
15298
15815
|
}
|
|
15299
15816
|
}
|
|
15817
|
+
const wantsDependsOnChange = o.clearDependsOn === true || addDependsOnTokens.length > 0 || removeDependsOnTokens.length > 0;
|
|
15818
|
+
if (wantsDependsOnChange) {
|
|
15819
|
+
let nextDepends;
|
|
15820
|
+
if (o.clearDependsOn === true) {
|
|
15821
|
+
nextDepends = [];
|
|
15822
|
+
} else {
|
|
15823
|
+
const removeDepSet = new Set(
|
|
15824
|
+
resolveTicketDependsOnTokensToIds(proj, removeDependsOnTokens)
|
|
15825
|
+
);
|
|
15826
|
+
nextDepends = normalizeTicketDependsOnIds(
|
|
15827
|
+
(curTicket.dependsOn ?? []).filter((d) => !removeDepSet.has(d))
|
|
15828
|
+
);
|
|
15829
|
+
nextDepends = normalizeTicketDependsOnIds([
|
|
15830
|
+
...nextDepends,
|
|
15831
|
+
...resolveTicketDependsOnTokensToIds(proj, addDependsOnTokens)
|
|
15832
|
+
]);
|
|
15833
|
+
}
|
|
15834
|
+
const depErr = validateTicketDependsOnForWrite({
|
|
15835
|
+
projection: proj,
|
|
15836
|
+
fromTicketId: ticketId,
|
|
15837
|
+
nextDependsOn: nextDepends
|
|
15838
|
+
});
|
|
15839
|
+
if (depErr !== void 0) {
|
|
15840
|
+
throw new Error(depErr);
|
|
15841
|
+
}
|
|
15842
|
+
if (!ticketDependsOnListsEqual(curTicket.dependsOn, nextDepends)) {
|
|
15843
|
+
payload["dependsOn"] = nextDepends;
|
|
15844
|
+
}
|
|
15845
|
+
}
|
|
15300
15846
|
if (priorityUpdate !== void 0) {
|
|
15301
15847
|
payload["priority"] = priorityUpdate;
|
|
15302
15848
|
}
|
|
@@ -15317,7 +15863,7 @@ ${body}`
|
|
|
15317
15863
|
payload
|
|
15318
15864
|
);
|
|
15319
15865
|
if (isNoOpUpdatePayload(prunedPayload)) {
|
|
15320
|
-
return { id:
|
|
15866
|
+
return { id: ticketId, noChanges: true };
|
|
15321
15867
|
}
|
|
15322
15868
|
const evt = makeEvent(
|
|
15323
15869
|
"TicketUpdated",
|
|
@@ -15354,25 +15900,25 @@ ${body}`
|
|
|
15354
15900
|
runGit
|
|
15355
15901
|
);
|
|
15356
15902
|
}
|
|
15357
|
-
const preferred = normalizeTicketBranchName(
|
|
15358
|
-
o.branch ?? `hyper-pm/${o.id}`
|
|
15359
|
-
);
|
|
15360
|
-
if (preferred === void 0) {
|
|
15361
|
-
deps.error(
|
|
15362
|
-
"Invalid --branch or default branch name for this ticket id"
|
|
15363
|
-
);
|
|
15364
|
-
deps.exit(ExitCode.UserError);
|
|
15365
|
-
}
|
|
15366
15903
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
15367
15904
|
const lines = await readAllEventLines(root);
|
|
15368
15905
|
const proj = replayEvents(lines);
|
|
15369
|
-
const
|
|
15906
|
+
const ticketId = resolveTicketId(proj, o.id) ?? o.id.trim();
|
|
15907
|
+
const preferred = normalizeTicketBranchName(
|
|
15908
|
+
o.branch ?? `hyper-pm/${ticketId}`
|
|
15909
|
+
);
|
|
15910
|
+
if (preferred === void 0) {
|
|
15911
|
+
throw new Error(
|
|
15912
|
+
"Invalid --branch or default branch name for this ticket id"
|
|
15913
|
+
);
|
|
15914
|
+
}
|
|
15915
|
+
const curRow = proj.tickets.get(ticketId);
|
|
15370
15916
|
if (curRow === void 0 || curRow.deleted) {
|
|
15371
15917
|
throw new Error(`Ticket not found: ${o.id}`);
|
|
15372
15918
|
}
|
|
15373
15919
|
if (curRow.status === "done" || curRow.status === "cancelled") {
|
|
15374
15920
|
throw new Error(
|
|
15375
|
-
`Ticket ${
|
|
15921
|
+
`Ticket ${ticketId} is ${curRow.status}; change status before starting work`
|
|
15376
15922
|
);
|
|
15377
15923
|
}
|
|
15378
15924
|
const { branch: chosenBranch } = await pickUniqueLocalBranchName({
|
|
@@ -15393,7 +15939,7 @@ ${body}`
|
|
|
15393
15939
|
}
|
|
15394
15940
|
next = normalizeTicketBranchListFromStrings(next);
|
|
15395
15941
|
const payload = {
|
|
15396
|
-
id:
|
|
15942
|
+
id: ticketId,
|
|
15397
15943
|
status: "in_progress"
|
|
15398
15944
|
};
|
|
15399
15945
|
if (!ticketBranchListsEqual(next, curRow.linkedBranches)) {
|
|
@@ -15402,7 +15948,7 @@ ${body}`
|
|
|
15402
15948
|
const evt = makeEvent("TicketUpdated", payload, deps.clock, actor);
|
|
15403
15949
|
await appendEventLine(root, evt, deps.clock);
|
|
15404
15950
|
const result = {
|
|
15405
|
-
id:
|
|
15951
|
+
id: ticketId,
|
|
15406
15952
|
status: "in_progress",
|
|
15407
15953
|
branch: chosenBranch,
|
|
15408
15954
|
branches: next
|
|
@@ -15424,27 +15970,36 @@ ${body}`
|
|
|
15424
15970
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
15425
15971
|
const lines = await readAllEventLines(root);
|
|
15426
15972
|
const proj = replayEvents(lines);
|
|
15427
|
-
const
|
|
15973
|
+
const ticketId = resolveTicketId(proj, o.id) ?? o.id.trim();
|
|
15974
|
+
const row = proj.tickets.get(ticketId);
|
|
15428
15975
|
if (!row || row.deleted) {
|
|
15429
15976
|
throw new Error(`Ticket not found: ${o.id}`);
|
|
15430
15977
|
}
|
|
15431
15978
|
const evt = makeEvent(
|
|
15432
15979
|
"TicketCommentAdded",
|
|
15433
|
-
{ ticketId
|
|
15980
|
+
{ ticketId, body: trimmed },
|
|
15434
15981
|
deps.clock,
|
|
15435
15982
|
actor
|
|
15436
15983
|
);
|
|
15437
15984
|
await appendEventLine(root, evt, deps.clock);
|
|
15438
|
-
return { commentId: evt.id, ticketId
|
|
15985
|
+
return { commentId: evt.id, ticketId, body: trimmed };
|
|
15439
15986
|
});
|
|
15440
15987
|
});
|
|
15441
15988
|
ticket.command("delete").requiredOption("--id <id>").action(async function() {
|
|
15442
15989
|
const g = readGlobals(this);
|
|
15443
15990
|
const o = this.opts();
|
|
15444
15991
|
await mutateDataBranch(g, deps, async (root, { actor }) => {
|
|
15445
|
-
const
|
|
15992
|
+
const lines = await readAllEventLines(root);
|
|
15993
|
+
const proj = replayEvents(lines);
|
|
15994
|
+
const ticketId = resolveTicketId(proj, o.id) ?? o.id.trim();
|
|
15995
|
+
const evt = makeEvent(
|
|
15996
|
+
"TicketDeleted",
|
|
15997
|
+
{ id: ticketId },
|
|
15998
|
+
deps.clock,
|
|
15999
|
+
actor
|
|
16000
|
+
);
|
|
15446
16001
|
await appendEventLine(root, evt, deps.clock);
|
|
15447
|
-
return { id:
|
|
16002
|
+
return { id: ticketId, deleted: true };
|
|
15448
16003
|
});
|
|
15449
16004
|
});
|
|
15450
16005
|
ticket.command("import-github").description(
|
|
@@ -15515,7 +16070,8 @@ ${body}`
|
|
|
15515
16070
|
const storyRaw = o.story;
|
|
15516
16071
|
const storyTrimmed = storyRaw !== void 0 && storyRaw !== "" ? storyRaw.trim() : void 0;
|
|
15517
16072
|
if (storyTrimmed !== void 0 && storyTrimmed !== "") {
|
|
15518
|
-
const
|
|
16073
|
+
const storyIdForImport = resolveStoryId(proj, storyTrimmed) ?? storyTrimmed;
|
|
16074
|
+
const storyRow = proj.stories.get(storyIdForImport);
|
|
15519
16075
|
if (!storyRow || storyRow.deleted) {
|
|
15520
16076
|
deps.error(`Story not found: ${storyTrimmed}`);
|
|
15521
16077
|
deps.exit(ExitCode.UserError);
|
|
@@ -15558,12 +16114,22 @@ ${body}`
|
|
|
15558
16114
|
);
|
|
15559
16115
|
} else {
|
|
15560
16116
|
const imported = [];
|
|
16117
|
+
let importProj = proj;
|
|
16118
|
+
let importLines = lines;
|
|
16119
|
+
const storyIdForPayload = storyTrimmed !== void 0 && storyTrimmed !== "" ? resolveStoryId(importProj, storyTrimmed) ?? storyTrimmed : void 0;
|
|
15561
16120
|
for (const c of candidates) {
|
|
15562
16121
|
const ticketId = ulid();
|
|
16122
|
+
const nextNum = nextTicketNumberForCreate(importProj);
|
|
15563
16123
|
const createPayload = mergeTicketImportCreatePayload(
|
|
15564
16124
|
ticketId,
|
|
15565
16125
|
c.ticketCreatedPayloadBase,
|
|
15566
|
-
|
|
16126
|
+
storyIdForPayload,
|
|
16127
|
+
nextNum
|
|
16128
|
+
);
|
|
16129
|
+
assertCreatePayloadUsesExpectedHeadNumber(
|
|
16130
|
+
importProj,
|
|
16131
|
+
"ticket",
|
|
16132
|
+
createPayload
|
|
15567
16133
|
);
|
|
15568
16134
|
const createdEvt = makeEvent(
|
|
15569
16135
|
"TicketCreated",
|
|
@@ -15584,6 +16150,8 @@ ${body}`
|
|
|
15584
16150
|
);
|
|
15585
16151
|
await appendEventLine(session.worktreePath, linkEvt, deps.clock);
|
|
15586
16152
|
imported.push({ ticketId, issueNumber: c.issueNumber });
|
|
16153
|
+
importLines = await readAllEventLines(session.worktreePath);
|
|
16154
|
+
importProj = replayEvents(importLines);
|
|
15587
16155
|
}
|
|
15588
16156
|
await commitDataWorktreeIfNeeded(
|
|
15589
16157
|
session.worktreePath,
|
|
@@ -15610,7 +16178,19 @@ ${body}`
|
|
|
15610
16178
|
}
|
|
15611
16179
|
deps.exit(ExitCode.Success);
|
|
15612
16180
|
});
|
|
15613
|
-
program2.command("sync").description("
|
|
16181
|
+
program2.command("sync").description("Sync data branch over git; optional GitHub Issues sync").option(
|
|
16182
|
+
"--skip-network",
|
|
16183
|
+
"skip all sync network operations (git fetch/merge/push and GitHub); legacy: --no-github",
|
|
16184
|
+
false
|
|
16185
|
+
).option(
|
|
16186
|
+
"--with-github",
|
|
16187
|
+
"also run GitHub Issues sync (requires GITHUB_TOKEN or gh; needs sync not off in config)",
|
|
16188
|
+
false
|
|
16189
|
+
).option(
|
|
16190
|
+
"--git-data",
|
|
16191
|
+
"legacy no-op: default sync already updates the data branch via git only",
|
|
16192
|
+
false
|
|
16193
|
+
).option(
|
|
15614
16194
|
"--skip-push",
|
|
15615
16195
|
"after a successful sync, do not push the data branch to the remote",
|
|
15616
16196
|
false
|
|
@@ -15619,115 +16199,171 @@ ${body}`
|
|
|
15619
16199
|
const o = this.opts();
|
|
15620
16200
|
const repoRoot = await resolveRepoRoot(g.repo);
|
|
15621
16201
|
const cfg = await loadMergedConfig(repoRoot, g);
|
|
15622
|
-
if (
|
|
16202
|
+
if (o.withGithub && o.skipNetwork) {
|
|
16203
|
+
deps.error("Cannot use --with-github together with --skip-network");
|
|
16204
|
+
deps.exit(ExitCode.UserError);
|
|
16205
|
+
}
|
|
16206
|
+
if (o.skipNetwork) {
|
|
15623
16207
|
deps.log(formatOutput(g.format, { ok: true, skipped: true }));
|
|
15624
16208
|
deps.exit(ExitCode.Success);
|
|
15625
16209
|
}
|
|
15626
|
-
|
|
15627
|
-
|
|
15628
|
-
|
|
15629
|
-
|
|
15630
|
-
|
|
15631
|
-
|
|
15632
|
-
|
|
15633
|
-
|
|
15634
|
-
|
|
15635
|
-
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
const projection = await loadProjectionFromDataRoot(
|
|
15646
|
-
session.worktreePath
|
|
15647
|
-
);
|
|
15648
|
-
const gitDerivedSlug = await tryReadGithubOwnerRepoSlugFromGit({
|
|
16210
|
+
if (o.withGithub) {
|
|
16211
|
+
if (cfg.sync === "off") {
|
|
16212
|
+
deps.error(
|
|
16213
|
+
"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."
|
|
16214
|
+
);
|
|
16215
|
+
deps.exit(ExitCode.UserError);
|
|
16216
|
+
}
|
|
16217
|
+
const githubToken = await resolveGithubTokenForSync({
|
|
16218
|
+
envToken: env.GITHUB_TOKEN,
|
|
16219
|
+
cwd: repoRoot
|
|
16220
|
+
});
|
|
16221
|
+
if (!githubToken) {
|
|
16222
|
+
deps.error(
|
|
16223
|
+
"GitHub auth required for sync --with-github: set GITHUB_TOKEN or run `gh auth login`"
|
|
16224
|
+
);
|
|
16225
|
+
deps.exit(ExitCode.EnvironmentAuth);
|
|
16226
|
+
}
|
|
16227
|
+
const tmpBase = g.tempDir ?? env.TMPDIR ?? (0, import_node_os2.tmpdir)();
|
|
16228
|
+
const session = await openDataBranchWorktree({
|
|
15649
16229
|
repoRoot,
|
|
15650
|
-
|
|
16230
|
+
dataBranch: cfg.dataBranch,
|
|
16231
|
+
tmpBase,
|
|
16232
|
+
keepWorktree: g.keepWorktree,
|
|
15651
16233
|
runGit
|
|
15652
16234
|
});
|
|
15653
|
-
|
|
15654
|
-
|
|
15655
|
-
|
|
15656
|
-
|
|
15657
|
-
|
|
15658
|
-
|
|
15659
|
-
|
|
15660
|
-
|
|
15661
|
-
|
|
15662
|
-
owner,
|
|
15663
|
-
|
|
15664
|
-
|
|
15665
|
-
|
|
15666
|
-
|
|
15667
|
-
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
config: cfg,
|
|
15671
|
-
deps: depsGh
|
|
15672
|
-
});
|
|
15673
|
-
await runGithubInboundSync({
|
|
15674
|
-
dataRoot: session.worktreePath,
|
|
15675
|
-
projection,
|
|
15676
|
-
config: cfg,
|
|
15677
|
-
deps: depsGh
|
|
15678
|
-
});
|
|
15679
|
-
const projectionAfterInbound = await loadProjectionFromDataRoot(
|
|
15680
|
-
session.worktreePath
|
|
15681
|
-
);
|
|
15682
|
-
await runGithubPrActivitySync({
|
|
15683
|
-
projection: projectionAfterInbound,
|
|
15684
|
-
config: cfg,
|
|
15685
|
-
deps: defaultGithubPrActivitySyncDeps({
|
|
15686
|
-
dataRoot: session.worktreePath,
|
|
15687
|
-
clock: deps.clock,
|
|
16235
|
+
try {
|
|
16236
|
+
const projection = await loadProjectionFromDataRoot(
|
|
16237
|
+
session.worktreePath
|
|
16238
|
+
);
|
|
16239
|
+
const gitDerivedSlug = await tryReadGithubOwnerRepoSlugFromGit({
|
|
16240
|
+
repoRoot,
|
|
16241
|
+
remote: cfg.remote,
|
|
16242
|
+
runGit
|
|
16243
|
+
});
|
|
16244
|
+
const { owner, repo: repo2 } = resolveGithubRepo(
|
|
16245
|
+
cfg,
|
|
16246
|
+
env.GITHUB_REPO,
|
|
16247
|
+
gitDerivedSlug
|
|
16248
|
+
);
|
|
16249
|
+
const octokit = new Octokit2({ auth: githubToken });
|
|
16250
|
+
const outboundActor = await resolveGithubTokenActor(octokit);
|
|
16251
|
+
const depsGh = {
|
|
15688
16252
|
octokit,
|
|
15689
16253
|
owner,
|
|
15690
16254
|
repo: repo2,
|
|
15691
|
-
|
|
15692
|
-
|
|
15693
|
-
|
|
15694
|
-
|
|
15695
|
-
|
|
15696
|
-
|
|
15697
|
-
|
|
15698
|
-
|
|
15699
|
-
|
|
15700
|
-
|
|
15701
|
-
|
|
15702
|
-
|
|
15703
|
-
|
|
15704
|
-
|
|
15705
|
-
|
|
16255
|
+
clock: deps.clock,
|
|
16256
|
+
outboundActor
|
|
16257
|
+
};
|
|
16258
|
+
await runGithubOutboundSync({
|
|
16259
|
+
dataRoot: session.worktreePath,
|
|
16260
|
+
projection,
|
|
16261
|
+
config: cfg,
|
|
16262
|
+
deps: depsGh
|
|
16263
|
+
});
|
|
16264
|
+
await runGithubInboundSync({
|
|
16265
|
+
dataRoot: session.worktreePath,
|
|
16266
|
+
projection,
|
|
16267
|
+
config: cfg,
|
|
16268
|
+
deps: depsGh
|
|
16269
|
+
});
|
|
16270
|
+
const projectionAfterInbound = await loadProjectionFromDataRoot(
|
|
16271
|
+
session.worktreePath
|
|
16272
|
+
);
|
|
16273
|
+
await runGithubPrActivitySync({
|
|
16274
|
+
projection: projectionAfterInbound,
|
|
16275
|
+
config: cfg,
|
|
16276
|
+
deps: defaultGithubPrActivitySyncDeps({
|
|
16277
|
+
dataRoot: session.worktreePath,
|
|
16278
|
+
clock: deps.clock,
|
|
16279
|
+
octokit,
|
|
16280
|
+
owner,
|
|
16281
|
+
repo: repo2,
|
|
16282
|
+
actor: outboundActor
|
|
16283
|
+
})
|
|
16284
|
+
});
|
|
16285
|
+
await commitDataWorktreeIfNeeded(
|
|
15706
16286
|
session.worktreePath,
|
|
15707
|
-
|
|
15708
|
-
cfg.dataBranch,
|
|
16287
|
+
formatDataBranchCommitMessage("hyper-pm: sync", outboundActor),
|
|
15709
16288
|
runGit
|
|
15710
16289
|
);
|
|
15711
|
-
dataBranchPush
|
|
15712
|
-
dataBranchPushDetail
|
|
15713
|
-
if (
|
|
15714
|
-
|
|
15715
|
-
|
|
16290
|
+
let dataBranchPush;
|
|
16291
|
+
let dataBranchPushDetail;
|
|
16292
|
+
if (o.skipPush) {
|
|
16293
|
+
dataBranchPush = "skipped_cli";
|
|
16294
|
+
dataBranchPushDetail = "skip-push";
|
|
16295
|
+
} else {
|
|
16296
|
+
const pushResult = await tryPushDataBranchToRemote(
|
|
16297
|
+
session.worktreePath,
|
|
16298
|
+
cfg.remote,
|
|
16299
|
+
cfg.dataBranch,
|
|
16300
|
+
runGit
|
|
15716
16301
|
);
|
|
16302
|
+
dataBranchPush = pushResult.status;
|
|
16303
|
+
dataBranchPushDetail = pushResult.detail;
|
|
16304
|
+
if (pushResult.status === "failed" && pushResult.detail) {
|
|
16305
|
+
deps.error(
|
|
16306
|
+
`hyper-pm: data branch not pushed (${cfg.remote}/${cfg.dataBranch}): ${pushResult.detail}`
|
|
16307
|
+
);
|
|
16308
|
+
}
|
|
15717
16309
|
}
|
|
16310
|
+
deps.log(
|
|
16311
|
+
formatOutput(g.format, {
|
|
16312
|
+
ok: true,
|
|
16313
|
+
githubSync: true,
|
|
16314
|
+
dataBranchPush,
|
|
16315
|
+
...dataBranchPushDetail !== void 0 ? { dataBranchPushDetail } : {}
|
|
16316
|
+
})
|
|
16317
|
+
);
|
|
16318
|
+
} catch (e) {
|
|
16319
|
+
deps.error(e instanceof Error ? e.message : String(e));
|
|
16320
|
+
deps.exit(ExitCode.ExternalApi);
|
|
16321
|
+
} finally {
|
|
16322
|
+
await session.dispose();
|
|
16323
|
+
}
|
|
16324
|
+
deps.exit(ExitCode.Success);
|
|
16325
|
+
}
|
|
16326
|
+
const tmpBaseGit = g.tempDir ?? env.TMPDIR ?? (0, import_node_os2.tmpdir)();
|
|
16327
|
+
const sessionGit = await openDataBranchWorktree({
|
|
16328
|
+
repoRoot,
|
|
16329
|
+
dataBranch: cfg.dataBranch,
|
|
16330
|
+
tmpBase: tmpBaseGit,
|
|
16331
|
+
keepWorktree: g.keepWorktree,
|
|
16332
|
+
runGit
|
|
16333
|
+
});
|
|
16334
|
+
try {
|
|
16335
|
+
let syncResult;
|
|
16336
|
+
try {
|
|
16337
|
+
syncResult = await runRemoteDataBranchGitSync(
|
|
16338
|
+
sessionGit.worktreePath,
|
|
16339
|
+
cfg.remote,
|
|
16340
|
+
cfg.dataBranch,
|
|
16341
|
+
runGit,
|
|
16342
|
+
Boolean(o.skipPush)
|
|
16343
|
+
);
|
|
16344
|
+
} catch (e) {
|
|
16345
|
+
if (e instanceof SyncRemoteDataBranchMergeError) {
|
|
16346
|
+
deps.error(e.message);
|
|
16347
|
+
deps.exit(ExitCode.UserError);
|
|
16348
|
+
}
|
|
16349
|
+
deps.error(e instanceof Error ? e.message : String(e));
|
|
16350
|
+
deps.exit(ExitCode.UserError);
|
|
16351
|
+
}
|
|
16352
|
+
if (syncResult.dataBranchPush === "failed" && syncResult.dataBranchPushDetail !== void 0) {
|
|
16353
|
+
deps.error(
|
|
16354
|
+
`hyper-pm: data branch not pushed (${cfg.remote}/${cfg.dataBranch}): ${syncResult.dataBranchPushDetail}`
|
|
16355
|
+
);
|
|
15718
16356
|
}
|
|
15719
16357
|
deps.log(
|
|
15720
16358
|
formatOutput(g.format, {
|
|
15721
16359
|
ok: true,
|
|
15722
|
-
|
|
15723
|
-
...
|
|
16360
|
+
gitDataOnly: true,
|
|
16361
|
+
...o.gitData ? { legacyGitDataFlag: true } : {},
|
|
16362
|
+
...syncResult
|
|
15724
16363
|
})
|
|
15725
16364
|
);
|
|
15726
|
-
} catch (e) {
|
|
15727
|
-
deps.error(e instanceof Error ? e.message : String(e));
|
|
15728
|
-
deps.exit(ExitCode.ExternalApi);
|
|
15729
16365
|
} finally {
|
|
15730
|
-
await
|
|
16366
|
+
await sessionGit.dispose();
|
|
15731
16367
|
}
|
|
15732
16368
|
deps.exit(ExitCode.Success);
|
|
15733
16369
|
});
|
|
@@ -15893,7 +16529,7 @@ ${body}`
|
|
|
15893
16529
|
await session.dispose();
|
|
15894
16530
|
}
|
|
15895
16531
|
});
|
|
15896
|
-
await program2.parseAsync(argv, { from: "node" });
|
|
16532
|
+
await program2.parseAsync(normalizeRawCliArgv(argv), { from: "node" });
|
|
15897
16533
|
};
|
|
15898
16534
|
var makeEvent = (type, payload, clock, actor) => ({
|
|
15899
16535
|
schema: 1,
|
|
@@ -15983,7 +16619,8 @@ var readEpic = async (g, id, deps) => {
|
|
|
15983
16619
|
formatOutput(g.format, { items: listActiveEpicSummaries(proj) })
|
|
15984
16620
|
);
|
|
15985
16621
|
} else {
|
|
15986
|
-
const
|
|
16622
|
+
const epicId = resolveEpicId(proj, id) ?? id.trim();
|
|
16623
|
+
const row = proj.epics.get(epicId);
|
|
15987
16624
|
if (!row || row.deleted) {
|
|
15988
16625
|
deps.error("Epic not found");
|
|
15989
16626
|
exitCode = ExitCode.UserError;
|
|
@@ -16018,7 +16655,8 @@ var readStory = async (g, opts, deps) => {
|
|
|
16018
16655
|
const proj = replayEvents(lines);
|
|
16019
16656
|
const { id, epicId } = opts;
|
|
16020
16657
|
if (id === void 0 || id === "") {
|
|
16021
|
-
const
|
|
16658
|
+
const epicFilterRaw = epicId !== void 0 && epicId !== "" ? epicId : void 0;
|
|
16659
|
+
const epicFilter = epicFilterRaw !== void 0 ? resolveEpicId(proj, epicFilterRaw) ?? epicFilterRaw.trim() : void 0;
|
|
16022
16660
|
if (epicFilter !== void 0) {
|
|
16023
16661
|
const epicRow = proj.epics.get(epicFilter);
|
|
16024
16662
|
if (!epicRow || epicRow.deleted) {
|
|
@@ -16039,7 +16677,8 @@ var readStory = async (g, opts, deps) => {
|
|
|
16039
16677
|
);
|
|
16040
16678
|
}
|
|
16041
16679
|
} else {
|
|
16042
|
-
const
|
|
16680
|
+
const storyId = resolveStoryId(proj, id) ?? id.trim();
|
|
16681
|
+
const row = proj.stories.get(storyId);
|
|
16043
16682
|
if (!row || row.deleted) {
|
|
16044
16683
|
deps.error("Story not found");
|
|
16045
16684
|
exitCode = ExitCode.UserError;
|
|
@@ -16083,8 +16722,8 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16083
16722
|
} = opts;
|
|
16084
16723
|
if (id === void 0 || id === "") {
|
|
16085
16724
|
const listWithoutStory = withoutStoryRaw === true;
|
|
16086
|
-
const storyFilter = storyIdRaw !== void 0 && storyIdRaw !== "" ? storyIdRaw : void 0;
|
|
16087
|
-
const epicFilter = epicIdRaw !== void 0 && epicIdRaw !== "" ? epicIdRaw : void 0;
|
|
16725
|
+
const storyFilter = storyIdRaw !== void 0 && storyIdRaw !== "" ? resolveStoryId(proj, storyIdRaw) ?? storyIdRaw.trim() : void 0;
|
|
16726
|
+
const epicFilter = epicIdRaw !== void 0 && epicIdRaw !== "" ? resolveEpicId(proj, epicIdRaw) ?? epicIdRaw.trim() : void 0;
|
|
16088
16727
|
const sortBy = tryParseTicketListSortField(sortByOpt);
|
|
16089
16728
|
const sortDir = tryParseTicketListSortDir(sortDirOpt);
|
|
16090
16729
|
if (sortBy === void 0) {
|
|
@@ -16123,6 +16762,7 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16123
16762
|
exitCode = ExitCode.UserError;
|
|
16124
16763
|
} else {
|
|
16125
16764
|
const listQuery = buildTicketListQueryFromReadListOpts(
|
|
16765
|
+
proj,
|
|
16126
16766
|
{ epic: epicFilter, ...listFlagRest },
|
|
16127
16767
|
deps
|
|
16128
16768
|
);
|
|
@@ -16143,6 +16783,7 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16143
16783
|
exitCode = ExitCode.UserError;
|
|
16144
16784
|
} else {
|
|
16145
16785
|
const listQuery = buildTicketListQueryFromReadListOpts(
|
|
16786
|
+
proj,
|
|
16146
16787
|
listFlagRest,
|
|
16147
16788
|
deps
|
|
16148
16789
|
);
|
|
@@ -16159,6 +16800,7 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16159
16800
|
}
|
|
16160
16801
|
} else if (listWithoutStory) {
|
|
16161
16802
|
const listQuery = buildTicketListQueryFromReadListOpts(
|
|
16803
|
+
proj,
|
|
16162
16804
|
{ withoutStory: true, ...listFlagRest },
|
|
16163
16805
|
deps
|
|
16164
16806
|
);
|
|
@@ -16173,6 +16815,7 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16173
16815
|
);
|
|
16174
16816
|
} else {
|
|
16175
16817
|
const listQuery = buildTicketListQueryFromReadListOpts(
|
|
16818
|
+
proj,
|
|
16176
16819
|
{ epic: epicFilter, ...listFlagRest },
|
|
16177
16820
|
deps
|
|
16178
16821
|
);
|
|
@@ -16187,7 +16830,8 @@ var readTicket = async (g, opts, deps) => {
|
|
|
16187
16830
|
);
|
|
16188
16831
|
}
|
|
16189
16832
|
} else {
|
|
16190
|
-
const
|
|
16833
|
+
const ticketId = resolveTicketId(proj, id) ?? id.trim();
|
|
16834
|
+
const row = proj.tickets.get(ticketId);
|
|
16191
16835
|
if (!row || row.deleted) {
|
|
16192
16836
|
deps.error("Ticket not found");
|
|
16193
16837
|
exitCode = ExitCode.UserError;
|