@markmdev/pebble 0.1.24 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +51 -360
- package/dist/cli/index.js.map +1 -1
- package/dist/ui/assets/index-D_QGntQB.js +333 -0
- package/dist/ui/assets/index-PT9nHzvM.css +1 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-BLTrHFDr.js +0 -343
- package/dist/ui/assets/index-CSfeY9Yu.css +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -13,9 +13,9 @@ import { fileURLToPath as fileURLToPath2 } from "url";
|
|
|
13
13
|
import { dirname as dirname2, join as join3 } from "path";
|
|
14
14
|
|
|
15
15
|
// src/shared/types.ts
|
|
16
|
-
var ISSUE_TYPES = ["task", "bug", "epic"
|
|
16
|
+
var ISSUE_TYPES = ["task", "bug", "epic"];
|
|
17
17
|
var PRIORITIES = [0, 1, 2, 3, 4];
|
|
18
|
-
var STATUSES = ["open", "in_progress", "blocked", "
|
|
18
|
+
var STATUSES = ["open", "in_progress", "blocked", "closed"];
|
|
19
19
|
var EVENT_TYPES = ["create", "update", "close", "reopen", "comment", "delete", "restore"];
|
|
20
20
|
var PRIORITY_LABELS = {
|
|
21
21
|
0: "critical",
|
|
@@ -28,14 +28,12 @@ var STATUS_LABELS = {
|
|
|
28
28
|
open: "Open",
|
|
29
29
|
in_progress: "In Progress",
|
|
30
30
|
blocked: "Blocked",
|
|
31
|
-
pending_verification: "Pending Verification",
|
|
32
31
|
closed: "Closed"
|
|
33
32
|
};
|
|
34
33
|
var TYPE_LABELS = {
|
|
35
34
|
task: "Task",
|
|
36
35
|
bug: "Bug",
|
|
37
|
-
epic: "Epic"
|
|
38
|
-
verification: "Verification"
|
|
36
|
+
epic: "Epic"
|
|
39
37
|
};
|
|
40
38
|
|
|
41
39
|
// src/cli/lib/storage.ts
|
|
@@ -290,7 +288,6 @@ function computeState(events) {
|
|
|
290
288
|
parent: createEvent.data.parent,
|
|
291
289
|
blockedBy: [],
|
|
292
290
|
relatedTo: [],
|
|
293
|
-
verifies: createEvent.data.verifies,
|
|
294
291
|
comments: [],
|
|
295
292
|
createdAt: event.timestamp,
|
|
296
293
|
updatedAt: event.timestamp,
|
|
@@ -467,7 +464,7 @@ function getReady() {
|
|
|
467
464
|
if (issue.deleted) {
|
|
468
465
|
return false;
|
|
469
466
|
}
|
|
470
|
-
if (issue.status === "closed"
|
|
467
|
+
if (issue.status === "closed") {
|
|
471
468
|
return false;
|
|
472
469
|
}
|
|
473
470
|
for (const blockerId of issue.blockedBy) {
|
|
@@ -476,12 +473,6 @@ function getReady() {
|
|
|
476
473
|
return false;
|
|
477
474
|
}
|
|
478
475
|
}
|
|
479
|
-
if (issue.type === "verification" && issue.verifies) {
|
|
480
|
-
const target = state.get(issue.verifies);
|
|
481
|
-
if (!target || target.status !== "closed") {
|
|
482
|
-
return false;
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
476
|
return true;
|
|
486
477
|
});
|
|
487
478
|
}
|
|
@@ -573,11 +564,6 @@ function getChildren(epicId, includeDeleted = false) {
|
|
|
573
564
|
(issue) => issue.parent === epicId && (includeDeleted || !issue.deleted)
|
|
574
565
|
);
|
|
575
566
|
}
|
|
576
|
-
function getVerifications(issueId) {
|
|
577
|
-
const events = readEvents();
|
|
578
|
-
const state = computeState(events);
|
|
579
|
-
return Array.from(state.values()).filter((issue) => issue.verifies === issueId);
|
|
580
|
-
}
|
|
581
567
|
function hasOpenChildren(epicId) {
|
|
582
568
|
const children = getChildren(epicId);
|
|
583
569
|
return children.some((child) => child.status !== "closed");
|
|
@@ -588,28 +574,15 @@ function getNewlyUnblocked(closedIssueId) {
|
|
|
588
574
|
const result = [];
|
|
589
575
|
for (const issue of state.values()) {
|
|
590
576
|
if (issue.status === "closed") continue;
|
|
591
|
-
let isUnblockedByThis = false;
|
|
592
577
|
if (issue.blockedBy.includes(closedIssueId)) {
|
|
593
578
|
const allBlockersClosed = issue.blockedBy.every((blockerId) => {
|
|
594
579
|
const blocker = state.get(blockerId);
|
|
595
580
|
return blocker?.status === "closed";
|
|
596
581
|
});
|
|
597
582
|
if (allBlockersClosed) {
|
|
598
|
-
|
|
583
|
+
result.push(issue);
|
|
599
584
|
}
|
|
600
585
|
}
|
|
601
|
-
if (issue.type === "verification" && issue.verifies === closedIssueId) {
|
|
602
|
-
const allBlockersClosed = issue.blockedBy.every((blockerId) => {
|
|
603
|
-
const blocker = state.get(blockerId);
|
|
604
|
-
return blocker?.status === "closed";
|
|
605
|
-
});
|
|
606
|
-
if (allBlockersClosed) {
|
|
607
|
-
isUnblockedByThis = true;
|
|
608
|
-
}
|
|
609
|
-
}
|
|
610
|
-
if (isUnblockedByThis) {
|
|
611
|
-
result.push(issue);
|
|
612
|
-
}
|
|
613
586
|
}
|
|
614
587
|
return result;
|
|
615
588
|
}
|
|
@@ -798,10 +771,8 @@ function formatIssueDetailPretty(issue, ctx) {
|
|
|
798
771
|
}
|
|
799
772
|
if (ctx.children.length > 0) {
|
|
800
773
|
const closedChildren = ctx.children.filter((c) => c.status === "closed");
|
|
801
|
-
const pendingChildren = ctx.children.filter((c) => c.status === "pending_verification");
|
|
802
|
-
const pendingStr = pendingChildren.length > 0 ? ` (${pendingChildren.length} pending verification)` : "";
|
|
803
774
|
lines.push("");
|
|
804
|
-
lines.push(`Children (${closedChildren.length}/${ctx.children.length} done
|
|
775
|
+
lines.push(`Children (${closedChildren.length}/${ctx.children.length} done):`);
|
|
805
776
|
for (const child of ctx.children) {
|
|
806
777
|
const statusIcon = child.status === "closed" ? "\u2713" : child.status === "in_progress" ? "\u25B6" : "\u25CB";
|
|
807
778
|
lines.push(` ${statusIcon} ${child.id} - ${child.title} [${child.status}]`);
|
|
@@ -815,18 +786,6 @@ function formatIssueDetailPretty(issue, ctx) {
|
|
|
815
786
|
lines.push("");
|
|
816
787
|
lines.push(`Blocking: ${ctx.blocking.map((i) => i.id).join(", ")}`);
|
|
817
788
|
}
|
|
818
|
-
if (ctx.verifications.length > 0) {
|
|
819
|
-
const closedVerifications = ctx.verifications.filter((v) => v.status === "closed");
|
|
820
|
-
lines.push("");
|
|
821
|
-
lines.push(`Verifications (${closedVerifications.length}/${ctx.verifications.length} done):`);
|
|
822
|
-
for (const v of ctx.verifications) {
|
|
823
|
-
const statusIcon = v.status === "closed" ? "\u2713" : "\u25CB";
|
|
824
|
-
lines.push(` ${statusIcon} ${v.id} - ${v.title} [${v.status}]`);
|
|
825
|
-
}
|
|
826
|
-
} else if (issue.status === "pending_verification") {
|
|
827
|
-
lines.push("");
|
|
828
|
-
lines.push("Verifications: None found (status may be stale)");
|
|
829
|
-
}
|
|
830
789
|
if (ctx.related.length > 0) {
|
|
831
790
|
lines.push("");
|
|
832
791
|
lines.push(`Related: ${ctx.related.map((r) => r.id).join(", ")}`);
|
|
@@ -853,7 +812,6 @@ function outputIssueDetail(issue, ctx, pretty) {
|
|
|
853
812
|
...issue,
|
|
854
813
|
blocking: ctx.blocking.map((i) => i.id),
|
|
855
814
|
children: ctx.children.map((i) => ({ id: i.id, title: i.title, status: i.status })),
|
|
856
|
-
verifications: ctx.verifications.map((i) => ({ id: i.id, title: i.title, status: i.status })),
|
|
857
815
|
related: ctx.related.map((i) => i.id),
|
|
858
816
|
...ctx.ancestry && ctx.ancestry.length > 0 && { ancestry: ctx.ancestry }
|
|
859
817
|
};
|
|
@@ -1021,7 +979,7 @@ function formatIssueTreePretty(nodes, sectionHeader) {
|
|
|
1021
979
|
const totalCount = nodes.reduce((sum, node) => sum + countAll(node), 0);
|
|
1022
980
|
const formatNode = (node, prefix, isLast, isRoot) => {
|
|
1023
981
|
const connector = isRoot ? "" : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
1024
|
-
const statusIcon = node.status === "closed" ? "\u2713" : node.status === "in_progress" ? "\u25B6" :
|
|
982
|
+
const statusIcon = node.status === "closed" ? "\u2713" : node.status === "in_progress" ? "\u25B6" : "\u25CB";
|
|
1025
983
|
const statusText = STATUS_LABELS[node.status].toLowerCase();
|
|
1026
984
|
const relativeTime = node.statusChangedAt ? formatRelativeTime(node.statusChangedAt) : formatRelativeTime(node.createdAt);
|
|
1027
985
|
lines.push(`${prefix}${connector}${statusIcon} ${node.id}: ${node.title} [${node.type}] P${node.priority} ${statusText} ${relativeTime}`);
|
|
@@ -1072,7 +1030,7 @@ function formatIssueListVerbose(issues, sectionHeader) {
|
|
|
1072
1030
|
lines.push("");
|
|
1073
1031
|
}
|
|
1074
1032
|
for (const info of issues) {
|
|
1075
|
-
const { issue, blocking, children,
|
|
1033
|
+
const { issue, blocking, children, blockers, ancestry } = info;
|
|
1076
1034
|
const statusTime = issue.statusChangedAt ? formatRelativeTime(issue.statusChangedAt) : formatRelativeTime(issue.createdAt);
|
|
1077
1035
|
lines.push(`${issue.id}: ${issue.title}`);
|
|
1078
1036
|
lines.push(` Type: ${formatType(issue.type)} | Priority: P${issue.priority} | Status: ${formatStatus(issue.status)} (${statusTime})`);
|
|
@@ -1087,7 +1045,7 @@ function formatIssueListVerbose(issues, sectionHeader) {
|
|
|
1087
1045
|
lines.push(` Blocked by: ${blockers.join(", ")}`);
|
|
1088
1046
|
}
|
|
1089
1047
|
if (issue.type === "epic" && children > 0) {
|
|
1090
|
-
lines.push(` Children: ${children}
|
|
1048
|
+
lines.push(` Children: ${children}`);
|
|
1091
1049
|
}
|
|
1092
1050
|
lines.push("");
|
|
1093
1051
|
}
|
|
@@ -1100,11 +1058,10 @@ function outputIssueListVerbose(issues, pretty, sectionHeader, limitInfo) {
|
|
|
1100
1058
|
console.log(formatLimitMessage(limitInfo));
|
|
1101
1059
|
}
|
|
1102
1060
|
} else {
|
|
1103
|
-
const output = issues.map(({ issue, blocking, children,
|
|
1061
|
+
const output = issues.map(({ issue, blocking, children, blockers, ancestry }) => ({
|
|
1104
1062
|
...issue,
|
|
1105
1063
|
blocking,
|
|
1106
1064
|
childrenCount: issue.type === "epic" ? children : void 0,
|
|
1107
|
-
verificationsCount: verifications,
|
|
1108
1065
|
...blockers && { openBlockers: blockers },
|
|
1109
1066
|
...ancestry.length > 0 && { ancestry }
|
|
1110
1067
|
}));
|
|
@@ -1118,19 +1075,17 @@ function outputIssueListVerbose(issues, pretty, sectionHeader, limitInfo) {
|
|
|
1118
1075
|
|
|
1119
1076
|
// src/cli/commands/create.ts
|
|
1120
1077
|
function createCommand(program2) {
|
|
1121
|
-
program2.command("create <title>").description("Create a new issue").option("-t, --type <type>", "Issue type (task, bug, epic
|
|
1078
|
+
program2.command("create <title>").description("Create a new issue").option("-t, --type <type>", "Issue type (task, bug, epic)", "task").option("-p, --priority <priority>", "Priority (0-4)", "2").option("-d, --description <desc>", "Description").option("--parent <id>", "Parent issue ID").option("--blocked-by <ids>", "Comma-separated IDs of issues that block this one").option("--blocks <ids>", "Comma-separated IDs of issues this one will block").addHelpText("after", `
|
|
1079
|
+
Notes:
|
|
1080
|
+
IDs support partial matching (e.g., "abc" matches "PROJ-abc123")
|
|
1081
|
+
Creating a child under a closed parent auto-reopens the parent chain
|
|
1082
|
+
`).action(async (title, options) => {
|
|
1122
1083
|
const pretty = program2.opts().pretty ?? false;
|
|
1123
1084
|
try {
|
|
1124
|
-
|
|
1125
|
-
if (options.verifies && type !== "verification") {
|
|
1126
|
-
type = "verification";
|
|
1127
|
-
}
|
|
1085
|
+
const type = options.type;
|
|
1128
1086
|
if (!ISSUE_TYPES.includes(type)) {
|
|
1129
1087
|
throw new Error(`Invalid type: ${type}. Must be one of: ${ISSUE_TYPES.join(", ")}`);
|
|
1130
1088
|
}
|
|
1131
|
-
if (type === "verification" && !options.verifies) {
|
|
1132
|
-
throw new Error("Verification issues require --verifies <id> to specify the issue being verified");
|
|
1133
|
-
}
|
|
1134
1089
|
const priority = parseInt(options.priority, 10);
|
|
1135
1090
|
if (!PRIORITIES.includes(priority)) {
|
|
1136
1091
|
throw new Error(`Invalid priority: ${options.priority}. Must be 0-4`);
|
|
@@ -1145,9 +1100,6 @@ function createCommand(program2) {
|
|
|
1145
1100
|
if (!parent) {
|
|
1146
1101
|
throw new Error(`Parent issue not found: ${options.parent}`);
|
|
1147
1102
|
}
|
|
1148
|
-
if (parent.type === "verification") {
|
|
1149
|
-
throw new Error(`Verification issues cannot be parents`);
|
|
1150
|
-
}
|
|
1151
1103
|
const state = getComputedState();
|
|
1152
1104
|
let current = state.get(parentId);
|
|
1153
1105
|
while (current) {
|
|
@@ -1164,14 +1116,6 @@ function createCommand(program2) {
|
|
|
1164
1116
|
current = current.parent ? state.get(current.parent) : void 0;
|
|
1165
1117
|
}
|
|
1166
1118
|
}
|
|
1167
|
-
let verifiesId;
|
|
1168
|
-
if (options.verifies) {
|
|
1169
|
-
verifiesId = resolveId(options.verifies);
|
|
1170
|
-
const target = getIssue(verifiesId);
|
|
1171
|
-
if (!target) {
|
|
1172
|
-
throw new Error(`Target issue not found: ${options.verifies}`);
|
|
1173
|
-
}
|
|
1174
|
-
}
|
|
1175
1119
|
const blockedByIds = [];
|
|
1176
1120
|
const blockersReopened = [];
|
|
1177
1121
|
if (options.blockedBy) {
|
|
@@ -1218,8 +1162,7 @@ function createCommand(program2) {
|
|
|
1218
1162
|
type,
|
|
1219
1163
|
priority,
|
|
1220
1164
|
description: options.description,
|
|
1221
|
-
parent: parentId
|
|
1222
|
-
verifies: verifiesId
|
|
1165
|
+
parent: parentId
|
|
1223
1166
|
}
|
|
1224
1167
|
};
|
|
1225
1168
|
appendEvent(event, pebbleDir);
|
|
@@ -1262,7 +1205,7 @@ function createCommand(program2) {
|
|
|
1262
1205
|
|
|
1263
1206
|
// src/cli/commands/update.ts
|
|
1264
1207
|
function updateCommand(program2) {
|
|
1265
|
-
program2.command("update <ids...>").description("Update issues. Supports multiple IDs.").option("--status <status>", "Status (open, in_progress, blocked
|
|
1208
|
+
program2.command("update <ids...>").description("Update issues. Supports multiple IDs.").option("--status <status>", "Status (open, in_progress, blocked)").option("--priority <priority>", "Priority (0-4)").option("--title <title>", "Title").option("--description <desc>", "Description").option("--parent <id>", 'Parent issue ID (use "null" to remove parent)').action(async (ids, options) => {
|
|
1266
1209
|
const pretty = program2.opts().pretty ?? false;
|
|
1267
1210
|
try {
|
|
1268
1211
|
const pebbleDir = getOrCreatePebbleDir();
|
|
@@ -1306,9 +1249,6 @@ function updateCommand(program2) {
|
|
|
1306
1249
|
if (!parentIssue) {
|
|
1307
1250
|
throw new Error(`Parent issue not found: ${options.parent}`);
|
|
1308
1251
|
}
|
|
1309
|
-
if (parentIssue.type === "verification") {
|
|
1310
|
-
throw new Error(`Verification issues cannot be parents`);
|
|
1311
|
-
}
|
|
1312
1252
|
const state = getComputedState();
|
|
1313
1253
|
let current = state.get(parentId);
|
|
1314
1254
|
while (current) {
|
|
@@ -1428,25 +1368,6 @@ function closeCommand(program2) {
|
|
|
1428
1368
|
};
|
|
1429
1369
|
appendEvent(commentEvent, pebbleDir);
|
|
1430
1370
|
}
|
|
1431
|
-
const pendingVerifications = getVerifications(resolvedId).filter((v) => v.status !== "closed");
|
|
1432
|
-
if (pendingVerifications.length > 0) {
|
|
1433
|
-
const updateEvent = {
|
|
1434
|
-
type: "update",
|
|
1435
|
-
issueId: resolvedId,
|
|
1436
|
-
timestamp,
|
|
1437
|
-
data: {
|
|
1438
|
-
status: "pending_verification"
|
|
1439
|
-
}
|
|
1440
|
-
};
|
|
1441
|
-
appendEvent(updateEvent, pebbleDir);
|
|
1442
|
-
results.push({
|
|
1443
|
-
id: resolvedId,
|
|
1444
|
-
success: true,
|
|
1445
|
-
status: "pending_verification",
|
|
1446
|
-
pendingVerifications: pendingVerifications.map((v) => ({ id: v.id, title: v.title }))
|
|
1447
|
-
});
|
|
1448
|
-
continue;
|
|
1449
|
-
}
|
|
1450
1371
|
const closeEvent = {
|
|
1451
1372
|
type: "close",
|
|
1452
1373
|
issueId: resolvedId,
|
|
@@ -1457,31 +1378,10 @@ function closeCommand(program2) {
|
|
|
1457
1378
|
};
|
|
1458
1379
|
appendEvent(closeEvent, pebbleDir);
|
|
1459
1380
|
const unblocked = getNewlyUnblocked(resolvedId);
|
|
1460
|
-
let autoClosed;
|
|
1461
|
-
if (issue.verifies) {
|
|
1462
|
-
const targetIssue = getIssue(issue.verifies);
|
|
1463
|
-
if (targetIssue && targetIssue.status === "pending_verification") {
|
|
1464
|
-
const remainingVerifications = getVerifications(issue.verifies).filter((v) => v.status !== "closed");
|
|
1465
|
-
if (remainingVerifications.length === 0) {
|
|
1466
|
-
const autoCloseEvent = {
|
|
1467
|
-
type: "close",
|
|
1468
|
-
issueId: issue.verifies,
|
|
1469
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1470
|
-
data: {
|
|
1471
|
-
reason: "All verifications completed"
|
|
1472
|
-
}
|
|
1473
|
-
};
|
|
1474
|
-
appendEvent(autoCloseEvent, pebbleDir);
|
|
1475
|
-
autoClosed = { id: targetIssue.id, title: targetIssue.title };
|
|
1476
|
-
}
|
|
1477
|
-
}
|
|
1478
|
-
}
|
|
1479
1381
|
results.push({
|
|
1480
1382
|
id: resolvedId,
|
|
1481
1383
|
success: true,
|
|
1482
|
-
|
|
1483
|
-
unblocked: unblocked.length > 0 ? unblocked.map((i) => ({ id: i.id, title: i.title })) : void 0,
|
|
1484
|
-
autoClosed
|
|
1384
|
+
unblocked: unblocked.length > 0 ? unblocked.map((i) => ({ id: i.id, title: i.title })) : void 0
|
|
1485
1385
|
});
|
|
1486
1386
|
} catch (error) {
|
|
1487
1387
|
results.push({ id, success: false, error: error.message });
|
|
@@ -1491,38 +1391,19 @@ function closeCommand(program2) {
|
|
|
1491
1391
|
const result = results[0];
|
|
1492
1392
|
if (result.success) {
|
|
1493
1393
|
if (pretty) {
|
|
1494
|
-
|
|
1495
|
-
|
|
1394
|
+
console.log(`\u2713 ${result.id}`);
|
|
1395
|
+
if (result.unblocked && result.unblocked.length > 0) {
|
|
1496
1396
|
console.log(`
|
|
1497
|
-
Pending verifications:`);
|
|
1498
|
-
for (const v of result.pendingVerifications || []) {
|
|
1499
|
-
console.log(` \u2022 ${v.id} - ${v.title}`);
|
|
1500
|
-
}
|
|
1501
|
-
console.log(`
|
|
1502
|
-
Run: pb verifications ${result.id}`);
|
|
1503
|
-
} else {
|
|
1504
|
-
console.log(`\u2713 ${result.id}`);
|
|
1505
|
-
if (result.unblocked && result.unblocked.length > 0) {
|
|
1506
|
-
console.log(`
|
|
1507
1397
|
Unblocked:`);
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
if (result.autoClosed) {
|
|
1513
|
-
console.log(`
|
|
1514
|
-
\u2713 ${result.autoClosed.id} auto-closed (all verifications complete)`);
|
|
1398
|
+
for (const u of result.unblocked) {
|
|
1399
|
+
console.log(` \u2192 ${u.id} - ${u.title}`);
|
|
1515
1400
|
}
|
|
1516
1401
|
}
|
|
1517
1402
|
} else {
|
|
1518
1403
|
console.log(formatJson({
|
|
1519
1404
|
id: result.id,
|
|
1520
1405
|
success: true,
|
|
1521
|
-
|
|
1522
|
-
...result.pendingVerifications && { pendingVerifications: result.pendingVerifications },
|
|
1523
|
-
...result.pendingVerifications && { hint: `pb verifications ${result.id}` },
|
|
1524
|
-
...result.unblocked && { unblocked: result.unblocked },
|
|
1525
|
-
...result.autoClosed && { autoClosed: result.autoClosed }
|
|
1406
|
+
...result.unblocked && { unblocked: result.unblocked }
|
|
1526
1407
|
}));
|
|
1527
1408
|
}
|
|
1528
1409
|
} else {
|
|
@@ -1532,21 +1413,10 @@ Unblocked:`);
|
|
|
1532
1413
|
if (pretty) {
|
|
1533
1414
|
for (const result of results) {
|
|
1534
1415
|
if (result.success) {
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
for (const
|
|
1538
|
-
console.log(` \
|
|
1539
|
-
}
|
|
1540
|
-
console.log(` Run: pb verifications ${result.id}`);
|
|
1541
|
-
} else {
|
|
1542
|
-
console.log(`\u2713 ${result.id}`);
|
|
1543
|
-
if (result.unblocked && result.unblocked.length > 0) {
|
|
1544
|
-
for (const u of result.unblocked) {
|
|
1545
|
-
console.log(` \u2192 ${u.id} - ${u.title}`);
|
|
1546
|
-
}
|
|
1547
|
-
}
|
|
1548
|
-
if (result.autoClosed) {
|
|
1549
|
-
console.log(` \u2713 ${result.autoClosed.id} auto-closed (all verifications complete)`);
|
|
1416
|
+
console.log(`\u2713 ${result.id}`);
|
|
1417
|
+
if (result.unblocked && result.unblocked.length > 0) {
|
|
1418
|
+
for (const u of result.unblocked) {
|
|
1419
|
+
console.log(` \u2192 ${u.id} - ${u.title}`);
|
|
1550
1420
|
}
|
|
1551
1421
|
}
|
|
1552
1422
|
} else {
|
|
@@ -1557,12 +1427,8 @@ Unblocked:`);
|
|
|
1557
1427
|
console.log(formatJson(results.map((r) => ({
|
|
1558
1428
|
id: r.id,
|
|
1559
1429
|
success: r.success,
|
|
1560
|
-
status: r.status,
|
|
1561
1430
|
...r.error && { error: r.error },
|
|
1562
|
-
...r.
|
|
1563
|
-
...r.pendingVerifications && { hint: `pb verifications ${r.id}` },
|
|
1564
|
-
...r.unblocked && { unblocked: r.unblocked },
|
|
1565
|
-
...r.autoClosed && { autoClosed: r.autoClosed }
|
|
1431
|
+
...r.unblocked && { unblocked: r.unblocked }
|
|
1566
1432
|
}))));
|
|
1567
1433
|
}
|
|
1568
1434
|
}
|
|
@@ -1682,13 +1548,6 @@ function deleteCommand(program2) {
|
|
|
1682
1548
|
alreadyQueued.add(desc.id);
|
|
1683
1549
|
}
|
|
1684
1550
|
}
|
|
1685
|
-
const verifications = getVerifications(resolvedId);
|
|
1686
|
-
for (const v of verifications) {
|
|
1687
|
-
if (!alreadyQueued.has(v.id) && !v.deleted) {
|
|
1688
|
-
toDelete.push({ id: v.id, cascade: true });
|
|
1689
|
-
alreadyQueued.add(v.id);
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
1551
|
} catch (error) {
|
|
1693
1552
|
results.push({ id, success: false, error: error.message });
|
|
1694
1553
|
}
|
|
@@ -1899,7 +1758,7 @@ function claimCommand(program2) {
|
|
|
1899
1758
|
|
|
1900
1759
|
// src/cli/commands/list.ts
|
|
1901
1760
|
function listCommand(program2) {
|
|
1902
|
-
program2.command("list").description("List issues").option("--status <status>", "Filter by status").option("-t, --type <type>", "Filter by type").option("--priority <priority>", "Filter by priority").option("--parent <id>", "Filter by parent epic").option("-v, --verbose", "Show expanded details (parent, children, blocking
|
|
1761
|
+
program2.command("list").description("List issues").option("--status <status>", "Filter by status").option("-t, --type <type>", "Filter by type").option("--priority <priority>", "Filter by priority").option("--parent <id>", "Filter by parent epic").option("-v, --verbose", "Show expanded details (parent, children, blocking)").option("--flat", "Show flat list instead of hierarchical tree").option("--limit <n>", "Max issues to return (default: 30)").option("--all", "Show all issues (no limit)").action(async (options) => {
|
|
1903
1762
|
const pretty = program2.opts().pretty ?? false;
|
|
1904
1763
|
try {
|
|
1905
1764
|
getOrCreatePebbleDir();
|
|
@@ -1947,7 +1806,6 @@ function listCommand(program2) {
|
|
|
1947
1806
|
issue,
|
|
1948
1807
|
blocking: getBlocking(issue.id).map((i) => i.id),
|
|
1949
1808
|
children: getChildren(issue.id).length,
|
|
1950
|
-
verifications: getVerifications(issue.id).length,
|
|
1951
1809
|
ancestry: getAncestryChain(issue.id, state)
|
|
1952
1810
|
};
|
|
1953
1811
|
});
|
|
@@ -1984,11 +1842,10 @@ function showCommand(program2) {
|
|
|
1984
1842
|
}
|
|
1985
1843
|
const blocking = getBlocking(resolvedId);
|
|
1986
1844
|
const children = issue.type === "epic" ? getChildren(resolvedId) : [];
|
|
1987
|
-
const verifications = getVerifications(resolvedId);
|
|
1988
1845
|
const related = getRelated(resolvedId);
|
|
1989
1846
|
const state = getComputedState();
|
|
1990
1847
|
const ancestry = getAncestryChain(resolvedId, state);
|
|
1991
|
-
outputIssueDetail(issue, { blocking, children,
|
|
1848
|
+
outputIssueDetail(issue, { blocking, children, related, ancestry }, pretty);
|
|
1992
1849
|
} catch (error) {
|
|
1993
1850
|
outputError(error, pretty);
|
|
1994
1851
|
}
|
|
@@ -1997,7 +1854,7 @@ function showCommand(program2) {
|
|
|
1997
1854
|
|
|
1998
1855
|
// src/cli/commands/ready.ts
|
|
1999
1856
|
function readyCommand(program2) {
|
|
2000
|
-
program2.command("ready").description("Show issues ready for work (no open blockers)").option("-v, --verbose", "Show expanded details (parent, children, blocking
|
|
1857
|
+
program2.command("ready").description("Show issues ready for work (no open blockers)").option("-v, --verbose", "Show expanded details (parent, children, blocking)").option("-t, --type <type>", "Filter by type: task, bug, epic").option("--limit <n>", "Max issues to return (default: 30)").option("--all", "Show all issues (no limit)").action(async (options) => {
|
|
2001
1858
|
const pretty = program2.opts().pretty ?? false;
|
|
2002
1859
|
try {
|
|
2003
1860
|
getOrCreatePebbleDir();
|
|
@@ -2027,7 +1884,6 @@ function readyCommand(program2) {
|
|
|
2027
1884
|
issue,
|
|
2028
1885
|
blocking: getBlocking(issue.id).map((i) => i.id),
|
|
2029
1886
|
children: getChildren(issue.id).length,
|
|
2030
|
-
verifications: getVerifications(issue.id).length,
|
|
2031
1887
|
ancestry: getAncestryChain(issue.id, state)
|
|
2032
1888
|
};
|
|
2033
1889
|
});
|
|
@@ -2068,7 +1924,6 @@ function blockedCommand(program2) {
|
|
|
2068
1924
|
issue,
|
|
2069
1925
|
blocking: getBlocking(issue.id).map((i) => i.id),
|
|
2070
1926
|
children: getChildren(issue.id).length,
|
|
2071
|
-
verifications: getVerifications(issue.id).length,
|
|
2072
1927
|
blockers: openBlockers,
|
|
2073
1928
|
ancestry: getAncestryChain(issue.id, state)
|
|
2074
1929
|
};
|
|
@@ -2086,7 +1941,14 @@ function blockedCommand(program2) {
|
|
|
2086
1941
|
// src/cli/commands/dep.ts
|
|
2087
1942
|
function depCommand(program2) {
|
|
2088
1943
|
const dep = program2.command("dep").description("Manage dependencies");
|
|
2089
|
-
dep.command("add <id> [blockerId]").description("Add a blocking dependency
|
|
1944
|
+
dep.command("add <id> [blockerId]").description("Add a blocking dependency: <id> is blocked by [blockerId]").option("--needs <id>", "Issue that must be completed first (first arg needs this)").option("--blocks <id>", "Issue that this blocks (first arg blocks this)").addHelpText("after", `
|
|
1945
|
+
Examples:
|
|
1946
|
+
pb dep add B A B is blocked by A (A must close before B is ready)
|
|
1947
|
+
pb dep add B --needs A Same as above, self-documenting
|
|
1948
|
+
pb dep add A --blocks B Same effect: B is blocked by A
|
|
1949
|
+
|
|
1950
|
+
Think: "B needs A" \u2192 pb dep add B A
|
|
1951
|
+
`).action(async (id, blockerId, options) => {
|
|
2090
1952
|
const pretty = program2.opts().pretty ?? false;
|
|
2091
1953
|
try {
|
|
2092
1954
|
if (options.needs && options.blocks) {
|
|
@@ -2285,7 +2147,7 @@ function depCommand(program2) {
|
|
|
2285
2147
|
outputError(error, pretty);
|
|
2286
2148
|
}
|
|
2287
2149
|
});
|
|
2288
|
-
dep.command("tree <id>").description("Show issue tree (children
|
|
2150
|
+
dep.command("tree <id>").description("Show issue tree (children and full hierarchy)").action(async (id) => {
|
|
2289
2151
|
const pretty = program2.opts().pretty ?? false;
|
|
2290
2152
|
try {
|
|
2291
2153
|
const resolvedId = resolveId(id);
|
|
@@ -2314,7 +2176,7 @@ function buildIssueTree2(issueId, state) {
|
|
|
2314
2176
|
const buildChildren = (id, visited2) => {
|
|
2315
2177
|
const children = [];
|
|
2316
2178
|
for (const [, i] of state) {
|
|
2317
|
-
if (
|
|
2179
|
+
if (i.parent === id && !visited2.has(i.id)) {
|
|
2318
2180
|
visited2.add(i.id);
|
|
2319
2181
|
const nodeChildren = buildChildren(i.id, visited2);
|
|
2320
2182
|
children.push({
|
|
@@ -2350,7 +2212,7 @@ function buildIssueTree2(issueId, state) {
|
|
|
2350
2212
|
if (!parentIssue) break;
|
|
2351
2213
|
const siblings = [];
|
|
2352
2214
|
for (const [, i] of state) {
|
|
2353
|
-
if (
|
|
2215
|
+
if (i.parent === parentIssue.id && i.id !== currentIssue.id) {
|
|
2354
2216
|
siblings.push({
|
|
2355
2217
|
id: i.id,
|
|
2356
2218
|
title: i.title,
|
|
@@ -2383,7 +2245,7 @@ function formatIssueTreePretty2(node) {
|
|
|
2383
2245
|
const lines = [];
|
|
2384
2246
|
const formatNode = (n, prefix, isLast, isRoot) => {
|
|
2385
2247
|
const connector = isRoot ? "" : isLast ? "\u2514\u2500 " : "\u251C\u2500 ";
|
|
2386
|
-
const statusIcon = n.status === "closed" ? "\u2713" : n.status === "in_progress" ? "\u25B6" :
|
|
2248
|
+
const statusIcon = n.status === "closed" ? "\u2713" : n.status === "in_progress" ? "\u25B6" : "\u25CB";
|
|
2387
2249
|
const marker = n.isTarget ? " \u25C0" : "";
|
|
2388
2250
|
lines.push(`${prefix}${connector}${statusIcon} ${n.id}: ${n.title} [${n.type}] P${n.priority}${marker}`);
|
|
2389
2251
|
const children = n.children ?? [];
|
|
@@ -2868,10 +2730,6 @@ data: ${message}
|
|
|
2868
2730
|
res.status(400).json({ error: `Parent issue not found: ${parent}` });
|
|
2869
2731
|
return;
|
|
2870
2732
|
}
|
|
2871
|
-
if (parentIssue.type === "verification") {
|
|
2872
|
-
res.status(400).json({ error: "Verification issues cannot be parents" });
|
|
2873
|
-
return;
|
|
2874
|
-
}
|
|
2875
2733
|
const state = getComputedState();
|
|
2876
2734
|
let current = state.get(parent);
|
|
2877
2735
|
while (current) {
|
|
@@ -2944,23 +2802,7 @@ data: ${message}
|
|
|
2944
2802
|
results.push({ id: issueId, success: false, error: "Cannot close issue with open children" });
|
|
2945
2803
|
continue;
|
|
2946
2804
|
}
|
|
2947
|
-
const pendingVerifications = getVerifications(issueId).filter((v) => v.status !== "closed");
|
|
2948
2805
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2949
|
-
if (pendingVerifications.length > 0) {
|
|
2950
|
-
const updateEvent = {
|
|
2951
|
-
type: "update",
|
|
2952
|
-
issueId,
|
|
2953
|
-
timestamp,
|
|
2954
|
-
data: { status: "pending_verification" }
|
|
2955
|
-
};
|
|
2956
|
-
appendEvent(updateEvent, pebbleDir);
|
|
2957
|
-
results.push({
|
|
2958
|
-
id: issueId,
|
|
2959
|
-
success: true,
|
|
2960
|
-
error: `Moved to pending_verification (${pendingVerifications.length} verification(s) pending)`
|
|
2961
|
-
});
|
|
2962
|
-
continue;
|
|
2963
|
-
}
|
|
2964
2806
|
const event = {
|
|
2965
2807
|
issueId,
|
|
2966
2808
|
timestamp,
|
|
@@ -2991,7 +2833,7 @@ data: ${message}
|
|
|
2991
2833
|
return;
|
|
2992
2834
|
}
|
|
2993
2835
|
if (updates.status) {
|
|
2994
|
-
const validStatuses = ["open", "in_progress", "blocked"
|
|
2836
|
+
const validStatuses = ["open", "in_progress", "blocked"];
|
|
2995
2837
|
if (!validStatuses.includes(updates.status)) {
|
|
2996
2838
|
res.status(400).json({
|
|
2997
2839
|
error: `Invalid status: ${updates.status}. Use close endpoint to close issues.`
|
|
@@ -3136,20 +2978,12 @@ data: ${message}
|
|
|
3136
2978
|
res.status(400).json({ error: `Parent issue not found: ${parent}` });
|
|
3137
2979
|
return;
|
|
3138
2980
|
}
|
|
3139
|
-
if (parentFound.issue.type === "verification") {
|
|
3140
|
-
res.status(400).json({ error: "Verification issues cannot be parents" });
|
|
3141
|
-
return;
|
|
3142
|
-
}
|
|
3143
2981
|
} else {
|
|
3144
2982
|
const parentIssue = getIssue(parent);
|
|
3145
2983
|
if (!parentIssue) {
|
|
3146
2984
|
res.status(400).json({ error: `Parent issue not found: ${parent}` });
|
|
3147
2985
|
return;
|
|
3148
2986
|
}
|
|
3149
|
-
if (parentIssue.type === "verification") {
|
|
3150
|
-
res.status(400).json({ error: "Verification issues cannot be parents" });
|
|
3151
|
-
return;
|
|
3152
|
-
}
|
|
3153
2987
|
}
|
|
3154
2988
|
}
|
|
3155
2989
|
updates.parent = parent;
|
|
@@ -3254,30 +3088,6 @@ data: ${message}
|
|
|
3254
3088
|
}
|
|
3255
3089
|
const { reason } = req.body;
|
|
3256
3090
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
3257
|
-
let pendingVerifications = [];
|
|
3258
|
-
if (isMultiWorktree()) {
|
|
3259
|
-
const allIssues = mergeIssuesFromFiles(issueFiles);
|
|
3260
|
-
pendingVerifications = allIssues.filter(
|
|
3261
|
-
(i) => i.verifies === issueId && i.status !== "closed"
|
|
3262
|
-
);
|
|
3263
|
-
} else {
|
|
3264
|
-
pendingVerifications = getVerifications(issueId).filter((v) => v.status !== "closed");
|
|
3265
|
-
}
|
|
3266
|
-
if (pendingVerifications.length > 0) {
|
|
3267
|
-
const updateEvent = {
|
|
3268
|
-
type: "update",
|
|
3269
|
-
issueId,
|
|
3270
|
-
timestamp,
|
|
3271
|
-
data: { status: "pending_verification" }
|
|
3272
|
-
};
|
|
3273
|
-
appendEventToFile(updateEvent, targetFile);
|
|
3274
|
-
const updatedIssue = { ...issue, status: "pending_verification", updatedAt: timestamp };
|
|
3275
|
-
res.json({
|
|
3276
|
-
...updatedIssue,
|
|
3277
|
-
_pendingVerifications: pendingVerifications.map((v) => ({ id: v.id, title: v.title }))
|
|
3278
|
-
});
|
|
3279
|
-
return;
|
|
3280
|
-
}
|
|
3281
3091
|
const event = {
|
|
3282
3092
|
type: "close",
|
|
3283
3093
|
issueId,
|
|
@@ -3285,49 +3095,11 @@ data: ${message}
|
|
|
3285
3095
|
data: { reason }
|
|
3286
3096
|
};
|
|
3287
3097
|
appendEventToFile(event, targetFile);
|
|
3288
|
-
let autoClosed;
|
|
3289
|
-
if (issue.verifies) {
|
|
3290
|
-
let targetIssue;
|
|
3291
|
-
let targetVerifications = [];
|
|
3292
|
-
if (isMultiWorktree()) {
|
|
3293
|
-
const found = findIssueInSources(issue.verifies, issueFiles);
|
|
3294
|
-
if (found) {
|
|
3295
|
-
targetIssue = found.issue;
|
|
3296
|
-
const allIssues = mergeIssuesFromFiles(issueFiles);
|
|
3297
|
-
targetVerifications = allIssues.filter(
|
|
3298
|
-
(i) => i.verifies === issue.verifies && i.status !== "closed"
|
|
3299
|
-
);
|
|
3300
|
-
}
|
|
3301
|
-
} else {
|
|
3302
|
-
targetIssue = getIssue(issue.verifies);
|
|
3303
|
-
targetVerifications = getVerifications(issue.verifies).filter((v) => v.status !== "closed");
|
|
3304
|
-
}
|
|
3305
|
-
if (targetIssue && targetIssue.status === "pending_verification" && targetVerifications.length === 0) {
|
|
3306
|
-
const autoCloseEvent = {
|
|
3307
|
-
type: "close",
|
|
3308
|
-
issueId: issue.verifies,
|
|
3309
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3310
|
-
data: { reason: "All verifications completed" }
|
|
3311
|
-
};
|
|
3312
|
-
if (isMultiWorktree()) {
|
|
3313
|
-
const targetFound = findIssueInSources(issue.verifies, issueFiles);
|
|
3314
|
-
if (targetFound) {
|
|
3315
|
-
appendEventToFile(autoCloseEvent, targetFound.targetFile);
|
|
3316
|
-
}
|
|
3317
|
-
} else {
|
|
3318
|
-
const pebbleDir = getOrCreatePebbleDir();
|
|
3319
|
-
appendEventToFile(autoCloseEvent, path3.join(pebbleDir, "issues.jsonl"));
|
|
3320
|
-
}
|
|
3321
|
-
autoClosed = { id: targetIssue.id, title: targetIssue.title };
|
|
3322
|
-
}
|
|
3323
|
-
}
|
|
3324
3098
|
if (isMultiWorktree()) {
|
|
3325
3099
|
const updated = findIssueInSources(issueId, issueFiles);
|
|
3326
|
-
|
|
3327
|
-
res.json(autoClosed ? { ...result, _autoClosed: autoClosed } : result);
|
|
3100
|
+
res.json(updated?.issue || { ...issue, status: "closed", updatedAt: timestamp });
|
|
3328
3101
|
} else {
|
|
3329
|
-
|
|
3330
|
-
res.json(autoClosed ? { ...result, _autoClosed: autoClosed } : result);
|
|
3102
|
+
res.json(getIssue(issueId));
|
|
3331
3103
|
}
|
|
3332
3104
|
} catch (error) {
|
|
3333
3105
|
res.status(500).json({ error: error.message });
|
|
@@ -3424,13 +3196,6 @@ data: ${message}
|
|
|
3424
3196
|
alreadyQueued.add(desc.id);
|
|
3425
3197
|
}
|
|
3426
3198
|
}
|
|
3427
|
-
const verifications = getVerifications(issueId);
|
|
3428
|
-
for (const v of verifications) {
|
|
3429
|
-
if (!alreadyQueued.has(v.id) && !v.deleted) {
|
|
3430
|
-
toDelete.push({ id: v.id, cascade: true });
|
|
3431
|
-
alreadyQueued.add(v.id);
|
|
3432
|
-
}
|
|
3433
|
-
}
|
|
3434
3199
|
const cleanupReferences = (deletedId) => {
|
|
3435
3200
|
for (const [id, iss] of state) {
|
|
3436
3201
|
if (id === deletedId || iss.deleted) continue;
|
|
@@ -4016,23 +3781,11 @@ function countChildren(epicId) {
|
|
|
4016
3781
|
return {
|
|
4017
3782
|
total: children.length,
|
|
4018
3783
|
done: children.filter((c) => c.status === "closed").length,
|
|
4019
|
-
pending_verification: children.filter((c) => c.status === "pending_verification").length,
|
|
4020
3784
|
in_progress: children.filter((c) => c.status === "in_progress").length,
|
|
4021
3785
|
open: children.filter((c) => c.status === "open").length,
|
|
4022
3786
|
blocked: children.filter((c) => c.status === "blocked").length
|
|
4023
3787
|
};
|
|
4024
3788
|
}
|
|
4025
|
-
function countVerifications(epicId) {
|
|
4026
|
-
const children = getChildren(epicId);
|
|
4027
|
-
let total = 0;
|
|
4028
|
-
let done = 0;
|
|
4029
|
-
for (const child of children) {
|
|
4030
|
-
const verifications = getVerifications(child.id);
|
|
4031
|
-
total += verifications.length;
|
|
4032
|
-
done += verifications.filter((v) => v.status === "closed").length;
|
|
4033
|
-
}
|
|
4034
|
-
return { total, done };
|
|
4035
|
-
}
|
|
4036
3789
|
function getIssueComments(issueId) {
|
|
4037
3790
|
const events = readEvents();
|
|
4038
3791
|
return events.filter((e) => e.type === "comment" && e.issueId === issueId).sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()).map((e) => ({ text: e.data.text, timestamp: e.timestamp, source: e.source }));
|
|
@@ -4085,13 +3838,10 @@ function formatSummaryPretty(summaries, sectionHeader) {
|
|
|
4085
3838
|
lines.push(`## ${sectionHeader} (${summaries.length})`);
|
|
4086
3839
|
lines.push("");
|
|
4087
3840
|
for (const summary of summaries) {
|
|
4088
|
-
const { children
|
|
3841
|
+
const { children } = summary;
|
|
4089
3842
|
lines.push(`${summary.id}: ${summary.title}`);
|
|
4090
3843
|
lines.push(` Created: ${formatRelativeTime(summary.createdAt)} | Updated: ${formatRelativeTime(summary.updatedAt)}`);
|
|
4091
|
-
|
|
4092
|
-
const issueCount = `Issues: ${children.done}/${children.total} done${pendingStr}`;
|
|
4093
|
-
const verifCount = `Verifications: ${verifications.done}/${verifications.total} done`;
|
|
4094
|
-
lines.push(` ${issueCount} | ${verifCount}`);
|
|
3844
|
+
lines.push(` Issues: ${children.done}/${children.total} done`);
|
|
4095
3845
|
if (summary.parent) {
|
|
4096
3846
|
lines.push(` Parent: ${summary.parent.id} (${summary.parent.title})`);
|
|
4097
3847
|
}
|
|
@@ -4119,8 +3869,7 @@ function summaryCommand(program2) {
|
|
|
4119
3869
|
status: epic.status,
|
|
4120
3870
|
createdAt: epic.createdAt,
|
|
4121
3871
|
updatedAt: epic.updatedAt,
|
|
4122
|
-
children: countChildren(epic.id)
|
|
4123
|
-
verifications: countVerifications(epic.id)
|
|
3872
|
+
children: countChildren(epic.id)
|
|
4124
3873
|
};
|
|
4125
3874
|
if (epic.parent) {
|
|
4126
3875
|
const parentIssue = getIssue(epic.parent);
|
|
@@ -4416,63 +4165,6 @@ function searchCommand(program2) {
|
|
|
4416
4165
|
});
|
|
4417
4166
|
}
|
|
4418
4167
|
|
|
4419
|
-
// src/cli/commands/verifications.ts
|
|
4420
|
-
function verificationsCommand(program2) {
|
|
4421
|
-
program2.command("verifications <id>").description("List verification issues for a given issue").option("--limit <n>", "Max verifications to return (default: 30)").option("--all", "Show all verifications (no limit)").action(async (id, options) => {
|
|
4422
|
-
const pretty = program2.opts().pretty ?? false;
|
|
4423
|
-
try {
|
|
4424
|
-
getOrCreatePebbleDir();
|
|
4425
|
-
const resolvedId = resolveId(id);
|
|
4426
|
-
const issue = getIssue(resolvedId);
|
|
4427
|
-
if (!issue) {
|
|
4428
|
-
throw new Error(`Issue not found: ${id}`);
|
|
4429
|
-
}
|
|
4430
|
-
let verifications = getVerifications(resolvedId);
|
|
4431
|
-
verifications.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
4432
|
-
const total = verifications.length;
|
|
4433
|
-
const limit = options.all ? 0 : options.limit ? parseInt(options.limit, 10) : 30;
|
|
4434
|
-
if (limit > 0 && verifications.length > limit) {
|
|
4435
|
-
verifications = verifications.slice(0, limit);
|
|
4436
|
-
}
|
|
4437
|
-
const limitInfo = {
|
|
4438
|
-
total,
|
|
4439
|
-
shown: verifications.length,
|
|
4440
|
-
limited: limit > 0 && total > limit
|
|
4441
|
-
};
|
|
4442
|
-
if (pretty) {
|
|
4443
|
-
if (verifications.length === 0) {
|
|
4444
|
-
console.log(`No verifications for ${resolvedId}`);
|
|
4445
|
-
} else {
|
|
4446
|
-
console.log(`Verifications for ${resolvedId} "${issue.title}"`);
|
|
4447
|
-
console.log("\u2500".repeat(50));
|
|
4448
|
-
for (const v of verifications) {
|
|
4449
|
-
const status = v.status === "closed" ? "\u2713" : "\u25CB";
|
|
4450
|
-
console.log(` ${status} ${v.id} - ${v.title}`);
|
|
4451
|
-
}
|
|
4452
|
-
console.log("");
|
|
4453
|
-
console.log(`Total: ${verifications.length} verification(s)`);
|
|
4454
|
-
if (limitInfo.limited) {
|
|
4455
|
-
console.log(formatLimitMessage(limitInfo));
|
|
4456
|
-
}
|
|
4457
|
-
}
|
|
4458
|
-
} else {
|
|
4459
|
-
const output = {
|
|
4460
|
-
issueId: resolvedId,
|
|
4461
|
-
verifications: verifications.map((v) => ({
|
|
4462
|
-
id: v.id,
|
|
4463
|
-
title: v.title,
|
|
4464
|
-
status: v.status
|
|
4465
|
-
})),
|
|
4466
|
-
...limitInfo.limited && { _meta: limitInfo }
|
|
4467
|
-
};
|
|
4468
|
-
console.log(formatJson(output));
|
|
4469
|
-
}
|
|
4470
|
-
} catch (error) {
|
|
4471
|
-
outputError(error, pretty);
|
|
4472
|
-
}
|
|
4473
|
-
});
|
|
4474
|
-
}
|
|
4475
|
-
|
|
4476
4168
|
// src/cli/commands/init.ts
|
|
4477
4169
|
import * as path6 from "path";
|
|
4478
4170
|
function initCommand(program2) {
|
|
@@ -4501,7 +4193,7 @@ var __filename2 = fileURLToPath2(import.meta.url);
|
|
|
4501
4193
|
var __dirname2 = dirname2(__filename2);
|
|
4502
4194
|
var packageJson = JSON.parse(readFileSync2(join3(__dirname2, "../../package.json"), "utf-8"));
|
|
4503
4195
|
var program = new Command();
|
|
4504
|
-
program.name("pebble").description("A lightweight JSONL-based issue tracker").version(packageJson.version);
|
|
4196
|
+
program.name("pebble").description("A lightweight JSONL-based issue tracker").version(packageJson.version).addHelpText("after", '\nAll commands accept partial IDs (e.g., "abc" matches "PROJ-abc123")');
|
|
4505
4197
|
program.option("-P, --pretty", "Human-readable output (default: JSON)");
|
|
4506
4198
|
program.option("--json", "JSON output (this is the default, flag not needed)");
|
|
4507
4199
|
program.option("--local", "Use local .pebble directory even in a git worktree");
|
|
@@ -4530,7 +4222,6 @@ mergeCommand(program);
|
|
|
4530
4222
|
summaryCommand(program);
|
|
4531
4223
|
historyCommand(program);
|
|
4532
4224
|
searchCommand(program);
|
|
4533
|
-
verificationsCommand(program);
|
|
4534
4225
|
initCommand(program);
|
|
4535
4226
|
program.parse();
|
|
4536
4227
|
//# sourceMappingURL=index.js.map
|