@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 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", "verification"];
16
+ var ISSUE_TYPES = ["task", "bug", "epic"];
17
17
  var PRIORITIES = [0, 1, 2, 3, 4];
18
- var STATUSES = ["open", "in_progress", "blocked", "pending_verification", "closed"];
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" || issue.status === "pending_verification") {
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
- isUnblockedByThis = true;
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${pendingStr}):`);
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" : node.status === "pending_verification" ? "\u23F3" : "\u25CB";
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, verifications, blockers, ancestry } = info;
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} | Verifications: ${verifications}`);
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, verifications, blockers, ancestry }) => ({
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, verification)", "task").option("-p, --priority <priority>", "Priority (0-4)", "2").option("-d, --description <desc>", "Description").option("--parent <id>", "Parent issue ID").option("--verifies <id>", "ID of issue this verifies (sets type to verification)").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").action(async (title, options) => {
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
- let type = options.type;
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, closed)").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) => {
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
- status: "closed",
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
- if (result.status === "pending_verification") {
1495
- console.log(`\u23F3 ${result.id} \u2192 pending_verification`);
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
- for (const u of result.unblocked) {
1509
- console.log(` \u2192 ${u.id} - ${u.title}`);
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
- status: result.status,
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
- if (result.status === "pending_verification") {
1536
- console.log(`\u23F3 ${result.id} \u2192 pending_verification`);
1537
- for (const v of result.pendingVerifications || []) {
1538
- console.log(` \u2022 ${v.id} - ${v.title}`);
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.pendingVerifications && { pendingVerifications: r.pendingVerifications },
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, verifications)").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) => {
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, verifications, related, ancestry }, pretty);
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, verifications)").option("-t, --type <type>", "Filter by type: task, bug, epic, verification").option("--limit <n>", "Max issues to return (default: 30)").option("--all", "Show all issues (no limit)").action(async (options) => {
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. Use --needs or --blocks for self-documenting syntax.").option("--needs <id>", "Issue that must be completed first (first arg needs this)").option("--blocks <id>", "Issue that this blocks (first arg blocks this)").action(async (id, blockerId, options) => {
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, verifications, and full hierarchy)").action(async (id) => {
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 ((i.parent === id || i.verifies === id) && !visited2.has(i.id)) {
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 ((i.parent === parentIssue.id || i.verifies === parentIssue.id) && i.id !== currentIssue.id) {
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" : n.status === "pending_verification" ? "\u23F3" : "\u25CB";
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", "pending_verification"];
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
- const result = updated?.issue || { ...issue, status: "closed", updatedAt: timestamp };
3327
- res.json(autoClosed ? { ...result, _autoClosed: autoClosed } : result);
3100
+ res.json(updated?.issue || { ...issue, status: "closed", updatedAt: timestamp });
3328
3101
  } else {
3329
- const result = getIssue(issueId);
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, verifications } = summary;
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
- const pendingStr = children.pending_verification > 0 ? ` (${children.pending_verification} pending verification)` : "";
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