@markmdev/pebble 0.1.18 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js
CHANGED
|
@@ -294,6 +294,8 @@ function computeState(events) {
|
|
|
294
294
|
comments: [],
|
|
295
295
|
createdAt: event.timestamp,
|
|
296
296
|
updatedAt: event.timestamp,
|
|
297
|
+
statusChangedAt: event.timestamp,
|
|
298
|
+
// Initial status is 'open'
|
|
297
299
|
lastSource: event.source
|
|
298
300
|
};
|
|
299
301
|
issues.set(event.issueId, issue);
|
|
@@ -313,6 +315,9 @@ function computeState(events) {
|
|
|
313
315
|
issue.priority = updateEvent.data.priority;
|
|
314
316
|
}
|
|
315
317
|
if (updateEvent.data.status !== void 0) {
|
|
318
|
+
if (issue.status !== updateEvent.data.status) {
|
|
319
|
+
issue.statusChangedAt = event.timestamp;
|
|
320
|
+
}
|
|
316
321
|
issue.status = updateEvent.data.status;
|
|
317
322
|
}
|
|
318
323
|
if (updateEvent.data.description !== void 0) {
|
|
@@ -336,6 +341,7 @@ function computeState(events) {
|
|
|
336
341
|
const issue = issues.get(event.issueId);
|
|
337
342
|
if (issue) {
|
|
338
343
|
issue.status = "closed";
|
|
344
|
+
issue.statusChangedAt = event.timestamp;
|
|
339
345
|
issue.updatedAt = event.timestamp;
|
|
340
346
|
if (event.source) issue.lastSource = event.source;
|
|
341
347
|
}
|
|
@@ -345,6 +351,7 @@ function computeState(events) {
|
|
|
345
351
|
const issue = issues.get(event.issueId);
|
|
346
352
|
if (issue) {
|
|
347
353
|
issue.status = "open";
|
|
354
|
+
issue.statusChangedAt = event.timestamp;
|
|
348
355
|
issue.updatedAt = event.timestamp;
|
|
349
356
|
if (event.source) issue.lastSource = event.source;
|
|
350
357
|
}
|
|
@@ -707,7 +714,8 @@ function formatIssueDetailPretty(issue, ctx) {
|
|
|
707
714
|
lines.push("\u2500".repeat(60));
|
|
708
715
|
lines.push(`Type: ${formatType(issue.type)}`);
|
|
709
716
|
lines.push(`Priority: ${formatPriority(issue.priority)}`);
|
|
710
|
-
|
|
717
|
+
const statusTime = issue.statusChangedAt ? ` (${formatRelativeTime(issue.statusChangedAt)})` : "";
|
|
718
|
+
lines.push(`Status: ${formatStatus(issue.status)}${statusTime}`);
|
|
711
719
|
if (ctx.ancestry && ctx.ancestry.length > 0) {
|
|
712
720
|
const chain = [...ctx.ancestry].reverse().map((a) => a.id).join(" > ");
|
|
713
721
|
lines.push(`Ancestry: ${chain}`);
|
|
@@ -764,8 +772,8 @@ function formatIssueDetailPretty(issue, ctx) {
|
|
|
764
772
|
}
|
|
765
773
|
}
|
|
766
774
|
lines.push("");
|
|
767
|
-
lines.push(`Created: ${
|
|
768
|
-
lines.push(`Updated: ${
|
|
775
|
+
lines.push(`Created: ${formatRelativeTime(issue.createdAt)}`);
|
|
776
|
+
lines.push(`Updated: ${formatRelativeTime(issue.updatedAt)}`);
|
|
769
777
|
return lines.join("\n");
|
|
770
778
|
}
|
|
771
779
|
function outputIssueDetail(issue, ctx, pretty) {
|
|
@@ -859,11 +867,30 @@ function formatErrorPretty(error) {
|
|
|
859
867
|
const message = error instanceof Error ? error.message : error;
|
|
860
868
|
return `Error: ${message}`;
|
|
861
869
|
}
|
|
862
|
-
function outputMutationSuccess(id, pretty) {
|
|
870
|
+
function outputMutationSuccess(id, pretty, extra) {
|
|
863
871
|
if (pretty) {
|
|
864
|
-
|
|
872
|
+
const notes = [];
|
|
873
|
+
if (extra?.parentReopened) {
|
|
874
|
+
notes.push(`parent ${extra.parentReopened.id} reopened`);
|
|
875
|
+
}
|
|
876
|
+
if (extra?.blockersReopened?.length) {
|
|
877
|
+
const ids = extra.blockersReopened.map((b) => b.id).join(", ");
|
|
878
|
+
notes.push(`blocker${extra.blockersReopened.length > 1 ? "s" : ""} ${ids} reopened`);
|
|
879
|
+
}
|
|
880
|
+
if (notes.length > 0) {
|
|
881
|
+
console.log(`\u2713 ${id} (${notes.join(", ")})`);
|
|
882
|
+
} else {
|
|
883
|
+
console.log(`\u2713 ${id}`);
|
|
884
|
+
}
|
|
865
885
|
} else {
|
|
866
|
-
|
|
886
|
+
const result = { id, success: true };
|
|
887
|
+
if (extra?.parentReopened) {
|
|
888
|
+
result._parentReopened = extra.parentReopened;
|
|
889
|
+
}
|
|
890
|
+
if (extra?.blockersReopened?.length) {
|
|
891
|
+
result._blockersReopened = extra.blockersReopened;
|
|
892
|
+
}
|
|
893
|
+
console.log(JSON.stringify(result));
|
|
867
894
|
}
|
|
868
895
|
}
|
|
869
896
|
function outputIssueList(issues, pretty, limitInfo) {
|
|
@@ -900,6 +927,7 @@ function buildIssueTree(issues) {
|
|
|
900
927
|
priority: issue.priority,
|
|
901
928
|
status: issue.status,
|
|
902
929
|
createdAt: issue.createdAt,
|
|
930
|
+
statusChangedAt: issue.statusChangedAt,
|
|
903
931
|
childrenCount: children.length,
|
|
904
932
|
...children.length > 0 && { children }
|
|
905
933
|
};
|
|
@@ -925,7 +953,7 @@ function formatIssueTreePretty(nodes, sectionHeader) {
|
|
|
925
953
|
const connector = isRoot ? "" : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
926
954
|
const statusIcon = node.status === "closed" ? "\u2713" : node.status === "in_progress" ? "\u25B6" : node.status === "pending_verification" ? "\u23F3" : "\u25CB";
|
|
927
955
|
const statusText = STATUS_LABELS[node.status].toLowerCase();
|
|
928
|
-
const relativeTime = formatRelativeTime(node.createdAt);
|
|
956
|
+
const relativeTime = node.statusChangedAt ? formatRelativeTime(node.statusChangedAt) : formatRelativeTime(node.createdAt);
|
|
929
957
|
lines.push(`${prefix}${connector}${statusIcon} ${node.id}: ${node.title} [${node.type}] P${node.priority} ${statusText} ${relativeTime}`);
|
|
930
958
|
const children = node.children ?? [];
|
|
931
959
|
const childPrefix = isRoot ? "" : prefix + (isLast ? " " : "\u2502 ");
|
|
@@ -975,8 +1003,9 @@ function formatIssueListVerbose(issues, sectionHeader) {
|
|
|
975
1003
|
}
|
|
976
1004
|
for (const info of issues) {
|
|
977
1005
|
const { issue, blocking, children, verifications, blockers, ancestry } = info;
|
|
1006
|
+
const statusTime = issue.statusChangedAt ? formatRelativeTime(issue.statusChangedAt) : formatRelativeTime(issue.createdAt);
|
|
978
1007
|
lines.push(`${issue.id}: ${issue.title}`);
|
|
979
|
-
lines.push(` Type: ${formatType(issue.type)} | Priority: P${issue.priority} |
|
|
1008
|
+
lines.push(` Type: ${formatType(issue.type)} | Priority: P${issue.priority} | Status: ${formatStatus(issue.status)} (${statusTime})`);
|
|
980
1009
|
if (ancestry.length > 0) {
|
|
981
1010
|
const chain = [...ancestry].reverse().map((a) => a.title).join(" \u2192 ");
|
|
982
1011
|
lines.push(` Ancestry: ${chain}`);
|
|
@@ -1039,6 +1068,7 @@ function createCommand(program2) {
|
|
|
1039
1068
|
const pebbleDir = getOrCreatePebbleDir();
|
|
1040
1069
|
const config = getConfig(pebbleDir);
|
|
1041
1070
|
let parentId;
|
|
1071
|
+
let parentReopened;
|
|
1042
1072
|
if (options.parent) {
|
|
1043
1073
|
parentId = resolveId(options.parent);
|
|
1044
1074
|
const parent = getIssue(parentId);
|
|
@@ -1049,7 +1079,14 @@ function createCommand(program2) {
|
|
|
1049
1079
|
throw new Error(`Verification issues cannot be parents`);
|
|
1050
1080
|
}
|
|
1051
1081
|
if (parent.status === "closed") {
|
|
1052
|
-
|
|
1082
|
+
const reopenEvent = {
|
|
1083
|
+
type: "reopen",
|
|
1084
|
+
issueId: parentId,
|
|
1085
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1086
|
+
data: { reason: "Reopened to add child" }
|
|
1087
|
+
};
|
|
1088
|
+
appendEvent(reopenEvent, pebbleDir);
|
|
1089
|
+
parentReopened = { id: parentId, title: parent.title };
|
|
1053
1090
|
}
|
|
1054
1091
|
}
|
|
1055
1092
|
let verifiesId;
|
|
@@ -1061,6 +1098,7 @@ function createCommand(program2) {
|
|
|
1061
1098
|
}
|
|
1062
1099
|
}
|
|
1063
1100
|
const blockedByIds = [];
|
|
1101
|
+
const blockersReopened = [];
|
|
1064
1102
|
if (options.blockedBy) {
|
|
1065
1103
|
const ids = options.blockedBy.split(",").map((s) => s.trim()).filter(Boolean);
|
|
1066
1104
|
for (const rawId of ids) {
|
|
@@ -1070,7 +1108,14 @@ function createCommand(program2) {
|
|
|
1070
1108
|
throw new Error(`Blocker issue not found: ${rawId}`);
|
|
1071
1109
|
}
|
|
1072
1110
|
if (blocker.status === "closed") {
|
|
1073
|
-
|
|
1111
|
+
const reopenEvent = {
|
|
1112
|
+
type: "reopen",
|
|
1113
|
+
issueId: resolvedId,
|
|
1114
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1115
|
+
data: { reason: "Reopened to block new issue" }
|
|
1116
|
+
};
|
|
1117
|
+
appendEvent(reopenEvent, pebbleDir);
|
|
1118
|
+
blockersReopened.push({ id: resolvedId, title: blocker.title });
|
|
1074
1119
|
}
|
|
1075
1120
|
blockedByIds.push(resolvedId);
|
|
1076
1121
|
}
|
|
@@ -1123,7 +1168,8 @@ function createCommand(program2) {
|
|
|
1123
1168
|
};
|
|
1124
1169
|
appendEvent(depEvent, pebbleDir);
|
|
1125
1170
|
}
|
|
1126
|
-
|
|
1171
|
+
const extra = parentReopened || blockersReopened.length > 0 ? { parentReopened, blockersReopened: blockersReopened.length > 0 ? blockersReopened : void 0 } : void 0;
|
|
1172
|
+
outputMutationSuccess(id, pretty, extra);
|
|
1127
1173
|
} catch (error) {
|
|
1128
1174
|
outputError(error, pretty);
|
|
1129
1175
|
}
|
|
@@ -1166,6 +1212,7 @@ function updateCommand(program2) {
|
|
|
1166
1212
|
data.description = options.description;
|
|
1167
1213
|
hasChanges = true;
|
|
1168
1214
|
}
|
|
1215
|
+
let parentReopened;
|
|
1169
1216
|
if (options.parent !== void 0) {
|
|
1170
1217
|
if (options.parent.toLowerCase() === "null") {
|
|
1171
1218
|
data.parent = "";
|
|
@@ -1179,7 +1226,14 @@ function updateCommand(program2) {
|
|
|
1179
1226
|
throw new Error(`Verification issues cannot be parents`);
|
|
1180
1227
|
}
|
|
1181
1228
|
if (parentIssue.status === "closed") {
|
|
1182
|
-
|
|
1229
|
+
const reopenEvent = {
|
|
1230
|
+
type: "reopen",
|
|
1231
|
+
issueId: parentId,
|
|
1232
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1233
|
+
data: { reason: "Reopened to add child" }
|
|
1234
|
+
};
|
|
1235
|
+
appendEvent(reopenEvent, pebbleDir);
|
|
1236
|
+
parentReopened = { id: parentId, title: parentIssue.title };
|
|
1183
1237
|
}
|
|
1184
1238
|
data.parent = parentId;
|
|
1185
1239
|
}
|
|
@@ -1218,7 +1272,7 @@ function updateCommand(program2) {
|
|
|
1218
1272
|
if (allIds.length === 1) {
|
|
1219
1273
|
const result = results[0];
|
|
1220
1274
|
if (result.success) {
|
|
1221
|
-
outputMutationSuccess(result.id, pretty);
|
|
1275
|
+
outputMutationSuccess(result.id, pretty, parentReopened ? { parentReopened } : void 0);
|
|
1222
1276
|
} else {
|
|
1223
1277
|
throw new Error(result.error || "Unknown error");
|
|
1224
1278
|
}
|
|
@@ -2714,6 +2768,7 @@ data: ${message}
|
|
|
2714
2768
|
res.status(400).json({ error: "Priority must be 0-4" });
|
|
2715
2769
|
return;
|
|
2716
2770
|
}
|
|
2771
|
+
let parentReopened;
|
|
2717
2772
|
if (parent) {
|
|
2718
2773
|
const parentIssue = getIssue(parent);
|
|
2719
2774
|
if (!parentIssue) {
|
|
@@ -2725,8 +2780,14 @@ data: ${message}
|
|
|
2725
2780
|
return;
|
|
2726
2781
|
}
|
|
2727
2782
|
if (parentIssue.status === "closed") {
|
|
2728
|
-
|
|
2729
|
-
|
|
2783
|
+
const reopenEvent = {
|
|
2784
|
+
type: "reopen",
|
|
2785
|
+
issueId: parent,
|
|
2786
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2787
|
+
data: { reason: "Reopened to add child" }
|
|
2788
|
+
};
|
|
2789
|
+
appendEvent(reopenEvent, pebbleDir);
|
|
2790
|
+
parentReopened = { id: parent, title: parentIssue.title };
|
|
2730
2791
|
}
|
|
2731
2792
|
}
|
|
2732
2793
|
const issueId = generateId(config.prefix);
|
|
@@ -2745,7 +2806,8 @@ data: ${message}
|
|
|
2745
2806
|
};
|
|
2746
2807
|
appendEvent(event, pebbleDir);
|
|
2747
2808
|
const issue = getIssue(issueId);
|
|
2748
|
-
|
|
2809
|
+
const result = parentReopened ? { ...issue, _parentReopened: parentReopened } : issue;
|
|
2810
|
+
res.status(201).json(result);
|
|
2749
2811
|
} catch (error) {
|
|
2750
2812
|
res.status(500).json({ error: error.message });
|
|
2751
2813
|
}
|