@markmdev/pebble 0.1.2 → 0.1.4
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 +179 -26
- package/dist/cli/index.js.map +1 -1
- package/dist/ui/assets/index-BBrKbKcz.css +1 -0
- package/dist/ui/assets/index-Bk8EwO3i.js +343 -0
- package/dist/ui/index.html +2 -2
- package/package.json +1 -1
- package/dist/ui/assets/index-AlD6QW_g.js +0 -333
- package/dist/ui/assets/index-C5MfnA01.css +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/shared/types.ts
|
|
7
|
-
var ISSUE_TYPES = ["task", "bug", "epic"];
|
|
7
|
+
var ISSUE_TYPES = ["task", "bug", "epic", "verification"];
|
|
8
8
|
var PRIORITIES = [0, 1, 2, 3, 4];
|
|
9
9
|
var STATUSES = ["open", "in_progress", "blocked", "closed"];
|
|
10
10
|
var EVENT_TYPES = ["create", "update", "close", "reopen", "comment"];
|
|
@@ -24,7 +24,8 @@ var STATUS_LABELS = {
|
|
|
24
24
|
var TYPE_LABELS = {
|
|
25
25
|
task: "Task",
|
|
26
26
|
bug: "Bug",
|
|
27
|
-
epic: "Epic"
|
|
27
|
+
epic: "Epic",
|
|
28
|
+
verification: "Verification"
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
// src/cli/lib/storage.ts
|
|
@@ -176,6 +177,7 @@ function computeState(events) {
|
|
|
176
177
|
description: createEvent.data.description,
|
|
177
178
|
parent: createEvent.data.parent,
|
|
178
179
|
blockedBy: [],
|
|
180
|
+
verifies: createEvent.data.verifies,
|
|
179
181
|
comments: [],
|
|
180
182
|
createdAt: event.timestamp,
|
|
181
183
|
updatedAt: event.timestamp
|
|
@@ -316,6 +318,12 @@ function getReady() {
|
|
|
316
318
|
return false;
|
|
317
319
|
}
|
|
318
320
|
}
|
|
321
|
+
if (issue.type === "verification" && issue.verifies) {
|
|
322
|
+
const target = state.get(issue.verifies);
|
|
323
|
+
if (!target || target.status !== "closed") {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
319
327
|
return true;
|
|
320
328
|
});
|
|
321
329
|
}
|
|
@@ -402,10 +410,46 @@ function getChildren(epicId) {
|
|
|
402
410
|
const state = computeState(events);
|
|
403
411
|
return Array.from(state.values()).filter((issue) => issue.parent === epicId);
|
|
404
412
|
}
|
|
413
|
+
function getVerifications(issueId) {
|
|
414
|
+
const events = readEvents();
|
|
415
|
+
const state = computeState(events);
|
|
416
|
+
return Array.from(state.values()).filter((issue) => issue.verifies === issueId);
|
|
417
|
+
}
|
|
405
418
|
function hasOpenChildren(epicId) {
|
|
406
419
|
const children = getChildren(epicId);
|
|
407
420
|
return children.some((child) => child.status !== "closed");
|
|
408
421
|
}
|
|
422
|
+
function getNewlyUnblocked(closedIssueId) {
|
|
423
|
+
const events = readEvents();
|
|
424
|
+
const state = computeState(events);
|
|
425
|
+
const result = [];
|
|
426
|
+
for (const issue of state.values()) {
|
|
427
|
+
if (issue.status === "closed") continue;
|
|
428
|
+
let isUnblockedByThis = false;
|
|
429
|
+
if (issue.blockedBy.includes(closedIssueId)) {
|
|
430
|
+
const allBlockersClosed = issue.blockedBy.every((blockerId) => {
|
|
431
|
+
const blocker = state.get(blockerId);
|
|
432
|
+
return blocker?.status === "closed";
|
|
433
|
+
});
|
|
434
|
+
if (allBlockersClosed) {
|
|
435
|
+
isUnblockedByThis = true;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (issue.type === "verification" && issue.verifies === closedIssueId) {
|
|
439
|
+
const allBlockersClosed = issue.blockedBy.every((blockerId) => {
|
|
440
|
+
const blocker = state.get(blockerId);
|
|
441
|
+
return blocker?.status === "closed";
|
|
442
|
+
});
|
|
443
|
+
if (allBlockersClosed) {
|
|
444
|
+
isUnblockedByThis = true;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
if (isUnblockedByThis) {
|
|
448
|
+
result.push(issue);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return result;
|
|
452
|
+
}
|
|
409
453
|
|
|
410
454
|
// src/cli/lib/output.ts
|
|
411
455
|
function formatJson(data) {
|
|
@@ -427,7 +471,7 @@ function truncate(str, maxLength) {
|
|
|
427
471
|
function pad(str, width) {
|
|
428
472
|
return str.padEnd(width);
|
|
429
473
|
}
|
|
430
|
-
function
|
|
474
|
+
function formatIssuePrettyWithBlocking(issue, blocking) {
|
|
431
475
|
const lines = [];
|
|
432
476
|
lines.push(`${issue.id} - ${issue.title}`);
|
|
433
477
|
lines.push("\u2500".repeat(60));
|
|
@@ -446,6 +490,10 @@ function formatIssuePretty(issue) {
|
|
|
446
490
|
lines.push("");
|
|
447
491
|
lines.push(`Blocked by: ${issue.blockedBy.join(", ")}`);
|
|
448
492
|
}
|
|
493
|
+
if (blocking.length > 0) {
|
|
494
|
+
lines.push("");
|
|
495
|
+
lines.push(`Blocking: ${blocking.map((i) => i.id).join(", ")}`);
|
|
496
|
+
}
|
|
449
497
|
if (issue.comments.length > 0) {
|
|
450
498
|
lines.push("");
|
|
451
499
|
lines.push("Comments:");
|
|
@@ -526,11 +574,15 @@ function formatErrorPretty(error) {
|
|
|
526
574
|
const message = error instanceof Error ? error.message : error;
|
|
527
575
|
return `Error: ${message}`;
|
|
528
576
|
}
|
|
529
|
-
function
|
|
577
|
+
function outputIssueWithBlocking(issue, blocking, pretty) {
|
|
530
578
|
if (pretty) {
|
|
531
|
-
console.log(
|
|
579
|
+
console.log(formatIssuePrettyWithBlocking(issue, blocking));
|
|
532
580
|
} else {
|
|
533
|
-
|
|
581
|
+
const output = {
|
|
582
|
+
...issue,
|
|
583
|
+
blocking: blocking.map((i) => i.id)
|
|
584
|
+
};
|
|
585
|
+
console.log(formatJson(output));
|
|
534
586
|
}
|
|
535
587
|
}
|
|
536
588
|
function outputMutationSuccess(id, pretty) {
|
|
@@ -558,13 +610,19 @@ function outputError(error, pretty) {
|
|
|
558
610
|
|
|
559
611
|
// src/cli/commands/create.ts
|
|
560
612
|
function createCommand(program2) {
|
|
561
|
-
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 epic ID").action(async (title, options) => {
|
|
613
|
+
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 epic ID").option("--verifies <id>", "ID of issue this verifies (sets type to verification)").action(async (title, options) => {
|
|
562
614
|
const pretty = program2.opts().pretty ?? false;
|
|
563
615
|
try {
|
|
564
|
-
|
|
616
|
+
let type = options.type;
|
|
617
|
+
if (options.verifies && type !== "verification") {
|
|
618
|
+
type = "verification";
|
|
619
|
+
}
|
|
565
620
|
if (!ISSUE_TYPES.includes(type)) {
|
|
566
621
|
throw new Error(`Invalid type: ${type}. Must be one of: ${ISSUE_TYPES.join(", ")}`);
|
|
567
622
|
}
|
|
623
|
+
if (type === "verification" && !options.verifies) {
|
|
624
|
+
throw new Error("Verification issues require --verifies <id> to specify the issue being verified");
|
|
625
|
+
}
|
|
568
626
|
const priority = parseInt(options.priority, 10);
|
|
569
627
|
if (!PRIORITIES.includes(priority)) {
|
|
570
628
|
throw new Error(`Invalid priority: ${options.priority}. Must be 0-4`);
|
|
@@ -585,6 +643,14 @@ function createCommand(program2) {
|
|
|
585
643
|
throw new Error(`Cannot add children to closed epic: ${parentId}`);
|
|
586
644
|
}
|
|
587
645
|
}
|
|
646
|
+
let verifiesId;
|
|
647
|
+
if (options.verifies) {
|
|
648
|
+
verifiesId = resolveId(options.verifies);
|
|
649
|
+
const target = getIssue(verifiesId);
|
|
650
|
+
if (!target) {
|
|
651
|
+
throw new Error(`Target issue not found: ${options.verifies}`);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
588
654
|
const id = generateId(config.prefix);
|
|
589
655
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
590
656
|
const event = {
|
|
@@ -596,7 +662,8 @@ function createCommand(program2) {
|
|
|
596
662
|
type,
|
|
597
663
|
priority,
|
|
598
664
|
description: options.description,
|
|
599
|
-
parent: parentId
|
|
665
|
+
parent: parentId,
|
|
666
|
+
verifies: verifiesId
|
|
600
667
|
}
|
|
601
668
|
};
|
|
602
669
|
appendEvent(event, pebbleDir);
|
|
@@ -746,7 +813,12 @@ function closeCommand(program2) {
|
|
|
746
813
|
}
|
|
747
814
|
};
|
|
748
815
|
appendEvent(closeEvent, pebbleDir);
|
|
749
|
-
|
|
816
|
+
const unblocked = getNewlyUnblocked(resolvedId);
|
|
817
|
+
results.push({
|
|
818
|
+
id: resolvedId,
|
|
819
|
+
success: true,
|
|
820
|
+
unblocked: unblocked.length > 0 ? unblocked.map((i) => ({ id: i.id, title: i.title })) : void 0
|
|
821
|
+
});
|
|
750
822
|
} catch (error) {
|
|
751
823
|
results.push({ id, success: false, error: error.message });
|
|
752
824
|
}
|
|
@@ -754,7 +826,22 @@ function closeCommand(program2) {
|
|
|
754
826
|
if (allIds.length === 1) {
|
|
755
827
|
const result = results[0];
|
|
756
828
|
if (result.success) {
|
|
757
|
-
|
|
829
|
+
if (pretty) {
|
|
830
|
+
console.log(`\u2713 ${result.id}`);
|
|
831
|
+
if (result.unblocked && result.unblocked.length > 0) {
|
|
832
|
+
console.log(`
|
|
833
|
+
Unblocked:`);
|
|
834
|
+
for (const u of result.unblocked) {
|
|
835
|
+
console.log(` \u2192 ${u.id} - ${u.title}`);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
} else {
|
|
839
|
+
console.log(formatJson({
|
|
840
|
+
id: result.id,
|
|
841
|
+
success: true,
|
|
842
|
+
...result.unblocked && { unblocked: result.unblocked }
|
|
843
|
+
}));
|
|
844
|
+
}
|
|
758
845
|
} else {
|
|
759
846
|
throw new Error(result.error || "Unknown error");
|
|
760
847
|
}
|
|
@@ -763,6 +850,11 @@ function closeCommand(program2) {
|
|
|
763
850
|
for (const result of results) {
|
|
764
851
|
if (result.success) {
|
|
765
852
|
console.log(`\u2713 ${result.id}`);
|
|
853
|
+
if (result.unblocked && result.unblocked.length > 0) {
|
|
854
|
+
for (const u of result.unblocked) {
|
|
855
|
+
console.log(` \u2192 ${u.id} - ${u.title}`);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
766
858
|
} else {
|
|
767
859
|
console.log(`\u2717 ${result.id}: ${result.error}`);
|
|
768
860
|
}
|
|
@@ -771,7 +863,8 @@ function closeCommand(program2) {
|
|
|
771
863
|
console.log(formatJson(results.map((r) => ({
|
|
772
864
|
id: r.id,
|
|
773
865
|
success: r.success,
|
|
774
|
-
...r.error && { error: r.error }
|
|
866
|
+
...r.error && { error: r.error },
|
|
867
|
+
...r.unblocked && { unblocked: r.unblocked }
|
|
775
868
|
}))));
|
|
776
869
|
}
|
|
777
870
|
}
|
|
@@ -932,7 +1025,8 @@ function showCommand(program2) {
|
|
|
932
1025
|
if (!issue) {
|
|
933
1026
|
throw new Error(`Issue not found: ${id}`);
|
|
934
1027
|
}
|
|
935
|
-
|
|
1028
|
+
const blocking = getBlocking(resolvedId);
|
|
1029
|
+
outputIssueWithBlocking(issue, blocking, pretty);
|
|
936
1030
|
} catch (error) {
|
|
937
1031
|
outputError(error, pretty);
|
|
938
1032
|
}
|
|
@@ -2374,6 +2468,9 @@ function formatSummaryPretty(summaries) {
|
|
|
2374
2468
|
const { children } = summary;
|
|
2375
2469
|
const progress = children.total > 0 ? `(${children.done}/${children.total} done)` : "(no children)";
|
|
2376
2470
|
lines.push(`${summary.id} ${summary.title} ${progress}`);
|
|
2471
|
+
if (summary.parent) {
|
|
2472
|
+
lines.push(` Parent: ${summary.parent.id} "${summary.parent.title}"`);
|
|
2473
|
+
}
|
|
2377
2474
|
if (summary.description) {
|
|
2378
2475
|
const desc = summary.description.split("\n")[0];
|
|
2379
2476
|
const truncated = desc.length > 60 ? desc.slice(0, 57) + "..." : desc;
|
|
@@ -2405,13 +2502,25 @@ function summaryCommand(program2) {
|
|
|
2405
2502
|
if (limit > 0) {
|
|
2406
2503
|
epics = epics.slice(0, limit);
|
|
2407
2504
|
}
|
|
2408
|
-
const summaries = epics.map((epic) =>
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2505
|
+
const summaries = epics.map((epic) => {
|
|
2506
|
+
const summary = {
|
|
2507
|
+
id: epic.id,
|
|
2508
|
+
title: epic.title,
|
|
2509
|
+
description: epic.description,
|
|
2510
|
+
status: epic.status,
|
|
2511
|
+
children: countChildren(epic.id)
|
|
2512
|
+
};
|
|
2513
|
+
if (epic.parent) {
|
|
2514
|
+
const parentIssue = getIssue(epic.parent);
|
|
2515
|
+
if (parentIssue) {
|
|
2516
|
+
summary.parent = {
|
|
2517
|
+
id: parentIssue.id,
|
|
2518
|
+
title: parentIssue.title
|
|
2519
|
+
};
|
|
2520
|
+
}
|
|
2521
|
+
}
|
|
2522
|
+
return summary;
|
|
2523
|
+
});
|
|
2415
2524
|
if (pretty) {
|
|
2416
2525
|
console.log(formatSummaryPretty(summaries));
|
|
2417
2526
|
} else {
|
|
@@ -2469,7 +2578,7 @@ function formatHistoryPretty(entries) {
|
|
|
2469
2578
|
return lines.join("\n");
|
|
2470
2579
|
}
|
|
2471
2580
|
function historyCommand(program2) {
|
|
2472
|
-
program2.command("history").description("Show recent activity log").option("--limit <n>", "Max events to return", "20").option("--type <
|
|
2581
|
+
program2.command("history").description("Show recent activity log").option("--limit <n>", "Max events to return", "20").option("--type <types>", "Filter by event type(s), comma-separated (create,close,reopen,update,comment)").option("--since <duration>", 'Only show events since (e.g., "7d", "24h")').action(async (options) => {
|
|
2473
2582
|
const pretty = program2.opts().pretty ?? false;
|
|
2474
2583
|
try {
|
|
2475
2584
|
getOrCreatePebbleDir();
|
|
@@ -2477,11 +2586,13 @@ function historyCommand(program2) {
|
|
|
2477
2586
|
const state = computeState(events);
|
|
2478
2587
|
let filteredEvents = events;
|
|
2479
2588
|
if (options.type !== void 0) {
|
|
2480
|
-
const
|
|
2481
|
-
|
|
2482
|
-
|
|
2589
|
+
const types = options.type.split(",").map((t) => t.trim());
|
|
2590
|
+
for (const t of types) {
|
|
2591
|
+
if (!EVENT_TYPES.includes(t)) {
|
|
2592
|
+
throw new Error(`Invalid event type: ${t}. Must be one of: ${EVENT_TYPES.join(", ")}`);
|
|
2593
|
+
}
|
|
2483
2594
|
}
|
|
2484
|
-
filteredEvents = filteredEvents.filter((e) => e.type
|
|
2595
|
+
filteredEvents = filteredEvents.filter((e) => types.includes(e.type));
|
|
2485
2596
|
}
|
|
2486
2597
|
if (options.since !== void 0) {
|
|
2487
2598
|
const sinceMs = parseDuration(options.since);
|
|
@@ -2594,10 +2705,51 @@ function searchCommand(program2) {
|
|
|
2594
2705
|
});
|
|
2595
2706
|
}
|
|
2596
2707
|
|
|
2708
|
+
// src/cli/commands/verifications.ts
|
|
2709
|
+
function verificationsCommand(program2) {
|
|
2710
|
+
program2.command("verifications <id>").description("List verification issues for a given issue").action(async (id) => {
|
|
2711
|
+
const pretty = program2.opts().pretty ?? false;
|
|
2712
|
+
try {
|
|
2713
|
+
getOrCreatePebbleDir();
|
|
2714
|
+
const resolvedId = resolveId(id);
|
|
2715
|
+
const issue = getIssue(resolvedId);
|
|
2716
|
+
if (!issue) {
|
|
2717
|
+
throw new Error(`Issue not found: ${id}`);
|
|
2718
|
+
}
|
|
2719
|
+
const verifications = getVerifications(resolvedId);
|
|
2720
|
+
if (pretty) {
|
|
2721
|
+
if (verifications.length === 0) {
|
|
2722
|
+
console.log(`No verifications for ${resolvedId}`);
|
|
2723
|
+
} else {
|
|
2724
|
+
console.log(`Verifications for ${resolvedId} "${issue.title}"`);
|
|
2725
|
+
console.log("\u2500".repeat(50));
|
|
2726
|
+
for (const v of verifications) {
|
|
2727
|
+
const status = v.status === "closed" ? "\u2713" : "\u25CB";
|
|
2728
|
+
console.log(` ${status} ${v.id} - ${v.title}`);
|
|
2729
|
+
}
|
|
2730
|
+
console.log("");
|
|
2731
|
+
console.log(`Total: ${verifications.length} verification(s)`);
|
|
2732
|
+
}
|
|
2733
|
+
} else {
|
|
2734
|
+
console.log(formatJson({
|
|
2735
|
+
issueId: resolvedId,
|
|
2736
|
+
verifications: verifications.map((v) => ({
|
|
2737
|
+
id: v.id,
|
|
2738
|
+
title: v.title,
|
|
2739
|
+
status: v.status
|
|
2740
|
+
}))
|
|
2741
|
+
}));
|
|
2742
|
+
}
|
|
2743
|
+
} catch (error) {
|
|
2744
|
+
outputError(error, pretty);
|
|
2745
|
+
}
|
|
2746
|
+
});
|
|
2747
|
+
}
|
|
2748
|
+
|
|
2597
2749
|
// src/cli/index.ts
|
|
2598
2750
|
var program = new Command();
|
|
2599
2751
|
program.name("pebble").description("A lightweight JSONL-based issue tracker").version("0.1.0");
|
|
2600
|
-
program.option("--pretty", "Human-readable output (default: JSON)");
|
|
2752
|
+
program.option("-P, --pretty", "Human-readable output (default: JSON)");
|
|
2601
2753
|
createCommand(program);
|
|
2602
2754
|
updateCommand(program);
|
|
2603
2755
|
closeCommand(program);
|
|
@@ -2616,5 +2768,6 @@ mergeCommand(program);
|
|
|
2616
2768
|
summaryCommand(program);
|
|
2617
2769
|
historyCommand(program);
|
|
2618
2770
|
searchCommand(program);
|
|
2771
|
+
verificationsCommand(program);
|
|
2619
2772
|
program.parse();
|
|
2620
2773
|
//# sourceMappingURL=index.js.map
|