@markmdev/pebble 0.1.7 → 0.1.9
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 +119 -17
- package/dist/cli/index.js.map +1 -1
- package/dist/ui/assets/index-DzH8YyE_.js +322 -0
- package/dist/ui/index.html +1 -1
- package/package.json +1 -1
- package/dist/ui/assets/index-D7K46Jfk.js +0 -317
package/dist/cli/index.js
CHANGED
|
@@ -512,7 +512,7 @@ function truncate(str, maxLength) {
|
|
|
512
512
|
function pad(str, width) {
|
|
513
513
|
return str.padEnd(width);
|
|
514
514
|
}
|
|
515
|
-
function
|
|
515
|
+
function formatIssueDetailPretty(issue, ctx) {
|
|
516
516
|
const lines = [];
|
|
517
517
|
lines.push(`${issue.id} - ${issue.title}`);
|
|
518
518
|
lines.push("\u2500".repeat(60));
|
|
@@ -527,13 +527,38 @@ function formatIssuePrettyWithBlocking(issue, blocking) {
|
|
|
527
527
|
lines.push("Description:");
|
|
528
528
|
lines.push(issue.description);
|
|
529
529
|
}
|
|
530
|
+
if (ctx.children.length > 0) {
|
|
531
|
+
const closedChildren = ctx.children.filter((c) => c.status === "closed");
|
|
532
|
+
lines.push("");
|
|
533
|
+
lines.push(`Children (${closedChildren.length}/${ctx.children.length} done):`);
|
|
534
|
+
for (const child of ctx.children) {
|
|
535
|
+
const statusIcon = child.status === "closed" ? "\u2713" : child.status === "in_progress" ? "\u25B6" : "\u25CB";
|
|
536
|
+
lines.push(` ${statusIcon} ${child.id} - ${child.title} [${child.status}]`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
530
539
|
if (issue.blockedBy.length > 0) {
|
|
531
540
|
lines.push("");
|
|
532
541
|
lines.push(`Blocked by: ${issue.blockedBy.join(", ")}`);
|
|
533
542
|
}
|
|
534
|
-
if (blocking.length > 0) {
|
|
543
|
+
if (ctx.blocking.length > 0) {
|
|
544
|
+
lines.push("");
|
|
545
|
+
lines.push(`Blocking: ${ctx.blocking.map((i) => i.id).join(", ")}`);
|
|
546
|
+
}
|
|
547
|
+
if (ctx.verifications.length > 0) {
|
|
548
|
+
const closedVerifications = ctx.verifications.filter((v) => v.status === "closed");
|
|
549
|
+
lines.push("");
|
|
550
|
+
lines.push(`Verifications (${closedVerifications.length}/${ctx.verifications.length} done):`);
|
|
551
|
+
for (const v of ctx.verifications) {
|
|
552
|
+
const statusIcon = v.status === "closed" ? "\u2713" : "\u25CB";
|
|
553
|
+
lines.push(` ${statusIcon} ${v.id} - ${v.title} [${v.status}]`);
|
|
554
|
+
}
|
|
555
|
+
} else if (issue.status === "pending_verification") {
|
|
556
|
+
lines.push("");
|
|
557
|
+
lines.push("Verifications: None found (status may be stale)");
|
|
558
|
+
}
|
|
559
|
+
if (ctx.related.length > 0) {
|
|
535
560
|
lines.push("");
|
|
536
|
-
lines.push(`
|
|
561
|
+
lines.push(`Related: ${ctx.related.map((r) => r.id).join(", ")}`);
|
|
537
562
|
}
|
|
538
563
|
if (issue.comments.length > 0) {
|
|
539
564
|
lines.push("");
|
|
@@ -549,6 +574,20 @@ function formatIssuePrettyWithBlocking(issue, blocking) {
|
|
|
549
574
|
lines.push(`Updated: ${new Date(issue.updatedAt).toLocaleString()}`);
|
|
550
575
|
return lines.join("\n");
|
|
551
576
|
}
|
|
577
|
+
function outputIssueDetail(issue, ctx, pretty) {
|
|
578
|
+
if (pretty) {
|
|
579
|
+
console.log(formatIssueDetailPretty(issue, ctx));
|
|
580
|
+
} else {
|
|
581
|
+
const output = {
|
|
582
|
+
...issue,
|
|
583
|
+
blocking: ctx.blocking.map((i) => i.id),
|
|
584
|
+
children: ctx.children.map((i) => ({ id: i.id, title: i.title, status: i.status })),
|
|
585
|
+
verifications: ctx.verifications.map((i) => ({ id: i.id, title: i.title, status: i.status })),
|
|
586
|
+
related: ctx.related.map((i) => i.id)
|
|
587
|
+
};
|
|
588
|
+
console.log(formatJson(output));
|
|
589
|
+
}
|
|
590
|
+
}
|
|
552
591
|
function formatIssueListPretty(issues) {
|
|
553
592
|
if (issues.length === 0) {
|
|
554
593
|
return "No issues found.";
|
|
@@ -625,17 +664,6 @@ function formatErrorPretty(error) {
|
|
|
625
664
|
const message = error instanceof Error ? error.message : error;
|
|
626
665
|
return `Error: ${message}`;
|
|
627
666
|
}
|
|
628
|
-
function outputIssueWithBlocking(issue, blocking, pretty) {
|
|
629
|
-
if (pretty) {
|
|
630
|
-
console.log(formatIssuePrettyWithBlocking(issue, blocking));
|
|
631
|
-
} else {
|
|
632
|
-
const output = {
|
|
633
|
-
...issue,
|
|
634
|
-
blocking: blocking.map((i) => i.id)
|
|
635
|
-
};
|
|
636
|
-
console.log(formatJson(output));
|
|
637
|
-
}
|
|
638
|
-
}
|
|
639
667
|
function outputMutationSuccess(id, pretty) {
|
|
640
668
|
if (pretty) {
|
|
641
669
|
console.log(`\u2713 ${id}`);
|
|
@@ -1233,7 +1261,10 @@ function showCommand(program2) {
|
|
|
1233
1261
|
throw new Error(`Issue not found: ${id}`);
|
|
1234
1262
|
}
|
|
1235
1263
|
const blocking = getBlocking(resolvedId);
|
|
1236
|
-
|
|
1264
|
+
const children = issue.type === "epic" ? getChildren(resolvedId) : [];
|
|
1265
|
+
const verifications = getVerifications(resolvedId);
|
|
1266
|
+
const related = getRelated(resolvedId);
|
|
1267
|
+
outputIssueDetail(issue, { blocking, children, verifications, related }, pretty);
|
|
1237
1268
|
} catch (error) {
|
|
1238
1269
|
outputError(error, pretty);
|
|
1239
1270
|
}
|
|
@@ -2072,9 +2103,26 @@ data: ${message}
|
|
|
2072
2103
|
results.push({ id: issueId, success: false, error: "Cannot close epic with open children" });
|
|
2073
2104
|
continue;
|
|
2074
2105
|
}
|
|
2106
|
+
const pendingVerifications = getVerifications(issueId).filter((v) => v.status !== "closed");
|
|
2107
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2108
|
+
if (pendingVerifications.length > 0) {
|
|
2109
|
+
const updateEvent = {
|
|
2110
|
+
type: "update",
|
|
2111
|
+
issueId,
|
|
2112
|
+
timestamp,
|
|
2113
|
+
data: { status: "pending_verification" }
|
|
2114
|
+
};
|
|
2115
|
+
appendEvent(updateEvent, pebbleDir);
|
|
2116
|
+
results.push({
|
|
2117
|
+
id: issueId,
|
|
2118
|
+
success: true,
|
|
2119
|
+
error: `Moved to pending_verification (${pendingVerifications.length} verification(s) pending)`
|
|
2120
|
+
});
|
|
2121
|
+
continue;
|
|
2122
|
+
}
|
|
2075
2123
|
const event = {
|
|
2076
2124
|
issueId,
|
|
2077
|
-
timestamp
|
|
2125
|
+
timestamp,
|
|
2078
2126
|
type: "close",
|
|
2079
2127
|
data: { reason: "Bulk close" }
|
|
2080
2128
|
};
|
|
@@ -2170,7 +2218,7 @@ data: ${message}
|
|
|
2170
2218
|
issue = localIssue;
|
|
2171
2219
|
targetFile = path2.join(pebbleDir, "issues.jsonl");
|
|
2172
2220
|
}
|
|
2173
|
-
const { title, type, priority, status, description, parent } = req.body;
|
|
2221
|
+
const { title, type, priority, status, description, parent, relatedTo } = req.body;
|
|
2174
2222
|
const updates = {};
|
|
2175
2223
|
if (title !== void 0) {
|
|
2176
2224
|
if (typeof title !== "string" || title.trim() === "") {
|
|
@@ -2229,6 +2277,28 @@ data: ${message}
|
|
|
2229
2277
|
}
|
|
2230
2278
|
updates.parent = parent;
|
|
2231
2279
|
}
|
|
2280
|
+
if (relatedTo !== void 0) {
|
|
2281
|
+
if (!Array.isArray(relatedTo)) {
|
|
2282
|
+
res.status(400).json({ error: "relatedTo must be an array" });
|
|
2283
|
+
return;
|
|
2284
|
+
}
|
|
2285
|
+
for (const relatedId of relatedTo) {
|
|
2286
|
+
if (isMultiWorktree()) {
|
|
2287
|
+
const found = findIssueInSources(relatedId, issueFiles);
|
|
2288
|
+
if (!found) {
|
|
2289
|
+
res.status(400).json({ error: `Related issue not found: ${relatedId}` });
|
|
2290
|
+
return;
|
|
2291
|
+
}
|
|
2292
|
+
} else {
|
|
2293
|
+
const relatedIssue = getIssue(relatedId);
|
|
2294
|
+
if (!relatedIssue) {
|
|
2295
|
+
res.status(400).json({ error: `Related issue not found: ${relatedId}` });
|
|
2296
|
+
return;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
updates.relatedTo = relatedTo;
|
|
2301
|
+
}
|
|
2232
2302
|
if (Object.keys(updates).length === 0) {
|
|
2233
2303
|
res.status(400).json({ error: "No valid updates provided" });
|
|
2234
2304
|
return;
|
|
@@ -2286,6 +2356,30 @@ data: ${message}
|
|
|
2286
2356
|
}
|
|
2287
2357
|
const { reason } = req.body;
|
|
2288
2358
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
2359
|
+
let pendingVerifications = [];
|
|
2360
|
+
if (isMultiWorktree()) {
|
|
2361
|
+
const allIssues = mergeIssuesFromFiles(issueFiles);
|
|
2362
|
+
pendingVerifications = allIssues.filter(
|
|
2363
|
+
(i) => i.verifies === issueId && i.status !== "closed"
|
|
2364
|
+
);
|
|
2365
|
+
} else {
|
|
2366
|
+
pendingVerifications = getVerifications(issueId).filter((v) => v.status !== "closed");
|
|
2367
|
+
}
|
|
2368
|
+
if (pendingVerifications.length > 0) {
|
|
2369
|
+
const updateEvent = {
|
|
2370
|
+
type: "update",
|
|
2371
|
+
issueId,
|
|
2372
|
+
timestamp,
|
|
2373
|
+
data: { status: "pending_verification" }
|
|
2374
|
+
};
|
|
2375
|
+
appendEventToFile(updateEvent, targetFile);
|
|
2376
|
+
const updatedIssue = { ...issue, status: "pending_verification", updatedAt: timestamp };
|
|
2377
|
+
res.json({
|
|
2378
|
+
...updatedIssue,
|
|
2379
|
+
_pendingVerifications: pendingVerifications.map((v) => ({ id: v.id, title: v.title }))
|
|
2380
|
+
});
|
|
2381
|
+
return;
|
|
2382
|
+
}
|
|
2289
2383
|
const event = {
|
|
2290
2384
|
type: "close",
|
|
2291
2385
|
issueId,
|
|
@@ -3160,6 +3254,14 @@ function initCommand(program2) {
|
|
|
3160
3254
|
var program = new Command();
|
|
3161
3255
|
program.name("pebble").description("A lightweight JSONL-based issue tracker").version("0.1.0");
|
|
3162
3256
|
program.option("-P, --pretty", "Human-readable output (default: JSON)");
|
|
3257
|
+
program.option("--json", "JSON output (this is the default, flag not needed)");
|
|
3258
|
+
program.hook("preAction", () => {
|
|
3259
|
+
const opts = program.opts();
|
|
3260
|
+
if (opts.json) {
|
|
3261
|
+
console.error("Note: --json flag is unnecessary. JSON is the default output format.");
|
|
3262
|
+
console.error("Use --pretty (-P) for human-readable output.");
|
|
3263
|
+
}
|
|
3264
|
+
});
|
|
3163
3265
|
createCommand(program);
|
|
3164
3266
|
updateCommand(program);
|
|
3165
3267
|
closeCommand(program);
|