chiefwiggum 1.3.52 → 1.3.55
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.cjs +712 -549
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -300,54 +300,6 @@ var init_config = __esm({
|
|
|
300
300
|
});
|
|
301
301
|
|
|
302
302
|
// src/lib/claude.ts
|
|
303
|
-
function getProjectBoardContext() {
|
|
304
|
-
try {
|
|
305
|
-
const owner = (0, import_node_child_process4.execSync)("gh repo view --json owner -q .owner.login", {
|
|
306
|
-
encoding: "utf-8",
|
|
307
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
308
|
-
timeout: 3e4
|
|
309
|
-
}).trim();
|
|
310
|
-
const projectNumber = (0, import_node_child_process4.execSync)(
|
|
311
|
-
"gh repo view --json projectsV2 -q '.projectsV2.Nodes[0].number'",
|
|
312
|
-
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
313
|
-
).trim();
|
|
314
|
-
if (!projectNumber || projectNumber === "null") return null;
|
|
315
|
-
const projectId = (0, import_node_child_process4.execSync)(
|
|
316
|
-
"gh repo view --json projectsV2 -q '.projectsV2.Nodes[0].id'",
|
|
317
|
-
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
318
|
-
).trim();
|
|
319
|
-
const fieldsOutput = (0, import_node_child_process4.execSync)(
|
|
320
|
-
`gh project field-list ${projectNumber} --owner ${owner} --format json`,
|
|
321
|
-
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
322
|
-
);
|
|
323
|
-
const fields = JSON.parse(fieldsOutput);
|
|
324
|
-
const statusField = fields.fields?.find((f) => f.name === "Status");
|
|
325
|
-
if (!statusField) return null;
|
|
326
|
-
const findOption = (names) => {
|
|
327
|
-
for (const name of names) {
|
|
328
|
-
const option = statusField.options?.find(
|
|
329
|
-
(o) => o.name.toLowerCase() === name.toLowerCase()
|
|
330
|
-
);
|
|
331
|
-
if (option) return option;
|
|
332
|
-
}
|
|
333
|
-
return null;
|
|
334
|
-
};
|
|
335
|
-
const todoOption = findOption(["Todo", "To Do", "To do", "Backlog"]);
|
|
336
|
-
const inProgressOption = findOption(["In Progress", "In progress", "Doing", "Active"]);
|
|
337
|
-
const doneOption = findOption(["Done", "Completed", "Closed", "Finished"]);
|
|
338
|
-
return {
|
|
339
|
-
owner,
|
|
340
|
-
projectNumber,
|
|
341
|
-
projectId,
|
|
342
|
-
statusFieldId: statusField.id,
|
|
343
|
-
todoOptionId: todoOption?.id || "",
|
|
344
|
-
inProgressOptionId: inProgressOption?.id || "",
|
|
345
|
-
doneOptionId: doneOption?.id || ""
|
|
346
|
-
};
|
|
347
|
-
} catch {
|
|
348
|
-
return null;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
303
|
async function runClaude(options) {
|
|
352
304
|
const { prompt, streaming = false, onOutput } = options;
|
|
353
305
|
const timeoutMs = options.timeoutMs || config.iterationTimeoutMinutes * 60 * 1e3;
|
|
@@ -440,8 +392,88 @@ async function runClaude(options) {
|
|
|
440
392
|
});
|
|
441
393
|
});
|
|
442
394
|
}
|
|
443
|
-
function
|
|
444
|
-
|
|
395
|
+
async function runTaskIteration(iteration, prompt) {
|
|
396
|
+
let lastResult = {
|
|
397
|
+
success: false,
|
|
398
|
+
output: "",
|
|
399
|
+
error: "No attempts made"
|
|
400
|
+
};
|
|
401
|
+
for (let attempt = 1; attempt <= config.maxClaudeAttempts; attempt++) {
|
|
402
|
+
console.log(import_picocolors5.default.yellow(`Attempt ${attempt}/${config.maxClaudeAttempts}`));
|
|
403
|
+
const result = await runClaude({
|
|
404
|
+
prompt,
|
|
405
|
+
streaming: true,
|
|
406
|
+
onOutput: (line) => {
|
|
407
|
+
process.stdout.write(line);
|
|
408
|
+
if (!line.endsWith("\n")) {
|
|
409
|
+
process.stdout.write("\n");
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
lastResult = result;
|
|
414
|
+
if (result.success) {
|
|
415
|
+
return result;
|
|
416
|
+
}
|
|
417
|
+
const isTransient = result.output.includes("No messages returned") || result.output.includes("ECONNRESET") || result.output.includes("ETIMEDOUT") || result.error?.includes("ECONNRESET") || result.error?.includes("ETIMEDOUT");
|
|
418
|
+
if (isTransient && attempt < config.maxClaudeAttempts) {
|
|
419
|
+
console.log(import_picocolors5.default.yellow("Transient error, retrying..."));
|
|
420
|
+
await sleep(attempt * 5e3);
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
return lastResult;
|
|
426
|
+
}
|
|
427
|
+
var import_node_child_process4, import_node_readline, import_picocolors5;
|
|
428
|
+
var init_claude = __esm({
|
|
429
|
+
"src/lib/claude.ts"() {
|
|
430
|
+
"use strict";
|
|
431
|
+
init_cjs_shims();
|
|
432
|
+
import_node_child_process4 = require("child_process");
|
|
433
|
+
import_node_readline = require("readline");
|
|
434
|
+
import_picocolors5 = __toESM(require("picocolors"), 1);
|
|
435
|
+
init_config();
|
|
436
|
+
init_process();
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// src/lib/tracker/todo.ts
|
|
441
|
+
var import_node_fs2, TodoTracker;
|
|
442
|
+
var init_todo = __esm({
|
|
443
|
+
"src/lib/tracker/todo.ts"() {
|
|
444
|
+
"use strict";
|
|
445
|
+
init_cjs_shims();
|
|
446
|
+
import_node_fs2 = require("fs");
|
|
447
|
+
init_config();
|
|
448
|
+
TodoTracker = class {
|
|
449
|
+
type = "todo";
|
|
450
|
+
displayName = "TODO.md";
|
|
451
|
+
/**
|
|
452
|
+
* Validate that the TODO.md file exists
|
|
453
|
+
* @throws Error if file is missing
|
|
454
|
+
*/
|
|
455
|
+
async initialize() {
|
|
456
|
+
if (!(0, import_node_fs2.existsSync)(config.todoFile)) {
|
|
457
|
+
throw new Error(`Missing ${config.todoFile}`);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
getStats() {
|
|
461
|
+
const remaining = this.countUncheckedTasks();
|
|
462
|
+
return {
|
|
463
|
+
remaining,
|
|
464
|
+
completed: 0,
|
|
465
|
+
// TODO.md doesn't track completed separately
|
|
466
|
+
total: remaining,
|
|
467
|
+
label: "Tasks"
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
hasRemainingTasks() {
|
|
471
|
+
if (!(0, import_node_fs2.existsSync)(config.todoFile)) return false;
|
|
472
|
+
const content = (0, import_node_fs2.readFileSync)(config.todoFile, "utf-8");
|
|
473
|
+
return /^\s*-\s*\[\s\]/m.test(content);
|
|
474
|
+
}
|
|
475
|
+
getTaskPrompt() {
|
|
476
|
+
return `You are an autonomous coding agent. Complete ONE task from TODO.md.
|
|
445
477
|
|
|
446
478
|
## Before Starting
|
|
447
479
|
Read these ROOT-LEVEL files only (ignore subdirectories for project context):
|
|
@@ -469,34 +501,174 @@ IMPORTANT: Only use the root-level files above for project context. Do not read
|
|
|
469
501
|
## When Done
|
|
470
502
|
Output: RALPH_COMPLETE
|
|
471
503
|
If no tasks remain: RALPH_ALL_DONE`;
|
|
504
|
+
}
|
|
505
|
+
getContext() {
|
|
506
|
+
return {};
|
|
507
|
+
}
|
|
508
|
+
countUncheckedTasks() {
|
|
509
|
+
if (!(0, import_node_fs2.existsSync)(config.todoFile)) return 0;
|
|
510
|
+
const content = (0, import_node_fs2.readFileSync)(config.todoFile, "utf-8");
|
|
511
|
+
const lines = content.split("\n");
|
|
512
|
+
let count = 0;
|
|
513
|
+
for (const line of lines) {
|
|
514
|
+
if (/^\s*-\s*\[\s\]/.test(line)) {
|
|
515
|
+
count++;
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return count;
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
// src/lib/tracker/github-board.ts
|
|
525
|
+
function getProjectBoardContext() {
|
|
526
|
+
try {
|
|
527
|
+
const owner = (0, import_node_child_process5.execSync)("gh repo view --json owner -q .owner.login", {
|
|
528
|
+
encoding: "utf-8",
|
|
529
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
530
|
+
timeout: 3e4
|
|
531
|
+
}).trim();
|
|
532
|
+
const projectNumber = (0, import_node_child_process5.execSync)(
|
|
533
|
+
"gh repo view --json projectsV2 -q '.projectsV2.Nodes[0].number'",
|
|
534
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
535
|
+
).trim();
|
|
536
|
+
if (!projectNumber || projectNumber === "null") return null;
|
|
537
|
+
const projectId = (0, import_node_child_process5.execSync)(
|
|
538
|
+
"gh repo view --json projectsV2 -q '.projectsV2.Nodes[0].id'",
|
|
539
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
540
|
+
).trim();
|
|
541
|
+
const fieldsOutput = (0, import_node_child_process5.execSync)(
|
|
542
|
+
`gh project field-list ${projectNumber} --owner ${owner} --format json`,
|
|
543
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
544
|
+
);
|
|
545
|
+
const fields = JSON.parse(fieldsOutput);
|
|
546
|
+
const statusField = fields.fields?.find((f) => f.name === "Status");
|
|
547
|
+
if (!statusField) return null;
|
|
548
|
+
const findOption = (names) => {
|
|
549
|
+
for (const name of names) {
|
|
550
|
+
const option = statusField.options?.find(
|
|
551
|
+
(o) => o.name.toLowerCase() === name.toLowerCase()
|
|
552
|
+
);
|
|
553
|
+
if (option) return option;
|
|
554
|
+
}
|
|
555
|
+
return null;
|
|
556
|
+
};
|
|
557
|
+
const todoOption = findOption(["Todo", "To Do", "To do", "Backlog"]);
|
|
558
|
+
const inProgressOption = findOption(["In Progress", "In progress", "Doing", "Active"]);
|
|
559
|
+
const doneOption = findOption(["Done", "Completed", "Closed", "Finished"]);
|
|
560
|
+
return {
|
|
561
|
+
owner,
|
|
562
|
+
projectNumber,
|
|
563
|
+
projectId,
|
|
564
|
+
statusFieldId: statusField.id,
|
|
565
|
+
todoOptionId: todoOption?.id || "",
|
|
566
|
+
inProgressOptionId: inProgressOption?.id || "",
|
|
567
|
+
doneOptionId: doneOption?.id || ""
|
|
568
|
+
};
|
|
569
|
+
} catch {
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
472
572
|
}
|
|
473
|
-
|
|
474
|
-
|
|
573
|
+
var import_node_child_process5;
|
|
574
|
+
var init_github_board = __esm({
|
|
575
|
+
"src/lib/tracker/github-board.ts"() {
|
|
576
|
+
"use strict";
|
|
577
|
+
init_cjs_shims();
|
|
578
|
+
import_node_child_process5 = require("child_process");
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
|
|
582
|
+
// src/lib/tracker/github.ts
|
|
583
|
+
var import_node_child_process6, import_picocolors6, GitHubTracker;
|
|
584
|
+
var init_github = __esm({
|
|
585
|
+
"src/lib/tracker/github.ts"() {
|
|
586
|
+
"use strict";
|
|
587
|
+
init_cjs_shims();
|
|
588
|
+
import_node_child_process6 = require("child_process");
|
|
589
|
+
import_picocolors6 = __toESM(require("picocolors"), 1);
|
|
590
|
+
init_github_board();
|
|
591
|
+
init_new();
|
|
592
|
+
init_prompts();
|
|
593
|
+
GitHubTracker = class {
|
|
594
|
+
type = "github";
|
|
595
|
+
displayName = "GitHub Issues";
|
|
596
|
+
boardContext = null;
|
|
597
|
+
todoIssueNumbers = [];
|
|
598
|
+
/**
|
|
599
|
+
* Initialize the GitHub tracker
|
|
600
|
+
* - Ensures a project board is linked
|
|
601
|
+
* - Fetches board context (IDs for status updates)
|
|
602
|
+
* - Fetches issues in "Todo" column
|
|
603
|
+
*/
|
|
604
|
+
async initialize() {
|
|
605
|
+
await this.ensureProjectLinked();
|
|
606
|
+
console.log(import_picocolors6.default.dim("Fetching project board context..."));
|
|
607
|
+
this.boardContext = getProjectBoardContext();
|
|
608
|
+
if (this.boardContext) {
|
|
609
|
+
console.log(import_picocolors6.default.green(`\u2713 Board context loaded (Project #${this.boardContext.projectNumber})`));
|
|
610
|
+
if (!this.boardContext.inProgressOptionId || !this.boardContext.doneOptionId) {
|
|
611
|
+
console.log(import_picocolors6.default.yellow(" \u26A0 Missing status options - status updates may not work"));
|
|
612
|
+
}
|
|
613
|
+
this.todoIssueNumbers = this.fetchTodoIssueNumbers();
|
|
614
|
+
if (this.todoIssueNumbers.length > 0) {
|
|
615
|
+
console.log(import_picocolors6.default.green(`\u2713 Found ${this.todoIssueNumbers.length} issue(s) in Todo column`));
|
|
616
|
+
} else {
|
|
617
|
+
console.log(import_picocolors6.default.yellow("\u26A0 No issues found in Todo column - will fall back to open issues"));
|
|
618
|
+
}
|
|
619
|
+
} else {
|
|
620
|
+
console.log(import_picocolors6.default.yellow("\u26A0 Could not fetch project board context."));
|
|
621
|
+
console.log(import_picocolors6.default.dim(" Status updates will not work. Continuing anyway..."));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
getStats() {
|
|
625
|
+
const remaining = this.countOpenIssues();
|
|
626
|
+
const completed = this.countClosedIssues();
|
|
627
|
+
return {
|
|
628
|
+
remaining,
|
|
629
|
+
completed,
|
|
630
|
+
total: remaining + completed,
|
|
631
|
+
label: "Issues"
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
hasRemainingTasks() {
|
|
635
|
+
try {
|
|
636
|
+
const output = (0, import_node_child_process6.execSync)(
|
|
637
|
+
"gh issue list --state open --limit 1 --json number --jq 'length'",
|
|
638
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
639
|
+
);
|
|
640
|
+
return parseInt(output.trim(), 10) > 0;
|
|
641
|
+
} catch {
|
|
642
|
+
return false;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
getTaskPrompt(planContent) {
|
|
646
|
+
const boardInstructions = this.boardContext && this.boardContext.inProgressOptionId && this.boardContext.doneOptionId ? `
|
|
475
647
|
## Project Board Status Updates (REQUIRED)
|
|
476
648
|
|
|
477
649
|
Pre-computed values for this project:
|
|
478
|
-
- Owner: ${boardContext.owner}
|
|
479
|
-
- Project Number: ${boardContext.projectNumber}
|
|
480
|
-
- Project ID: ${boardContext.projectId}
|
|
481
|
-
- Status Field ID: ${boardContext.statusFieldId}
|
|
482
|
-
- In Progress Option ID: ${boardContext.inProgressOptionId}
|
|
483
|
-
- Done Option ID: ${boardContext.doneOptionId}
|
|
650
|
+
- Owner: ${this.boardContext.owner}
|
|
651
|
+
- Project Number: ${this.boardContext.projectNumber}
|
|
652
|
+
- Project ID: ${this.boardContext.projectId}
|
|
653
|
+
- Status Field ID: ${this.boardContext.statusFieldId}
|
|
654
|
+
- In Progress Option ID: ${this.boardContext.inProgressOptionId}
|
|
655
|
+
- Done Option ID: ${this.boardContext.doneOptionId}
|
|
484
656
|
|
|
485
657
|
### Step 1: Get Item ID for the Issue
|
|
486
658
|
Run this to get the ITEM_ID (replace ISSUE_NUMBER with the actual issue number):
|
|
487
659
|
\`\`\`bash
|
|
488
|
-
ITEM_ID=$(gh project item-list ${boardContext.projectNumber} --owner ${boardContext.owner} --format json | jq -r '.items[] | select(.content.number == ISSUE_NUMBER) | .id')
|
|
660
|
+
ITEM_ID=$(gh project item-list ${this.boardContext.projectNumber} --owner ${this.boardContext.owner} --format json | jq -r '.items[] | select(.content.number == ISSUE_NUMBER) | .id')
|
|
489
661
|
echo "Item ID: $ITEM_ID"
|
|
490
662
|
\`\`\`
|
|
491
663
|
|
|
492
664
|
### Step 2: Mark In Progress (do this FIRST, before any code changes)
|
|
493
665
|
\`\`\`bash
|
|
494
|
-
gh project item-edit --project-id ${boardContext.projectId} --id $ITEM_ID --field-id ${boardContext.statusFieldId} --single-select-option-id ${boardContext.inProgressOptionId}
|
|
666
|
+
gh project item-edit --project-id ${this.boardContext.projectId} --id $ITEM_ID --field-id ${this.boardContext.statusFieldId} --single-select-option-id ${this.boardContext.inProgressOptionId}
|
|
495
667
|
\`\`\`
|
|
496
668
|
|
|
497
669
|
### Step 3: Mark Done (do this AFTER closing the issue)
|
|
498
670
|
\`\`\`bash
|
|
499
|
-
gh project item-edit --project-id ${boardContext.projectId} --id $ITEM_ID --field-id ${boardContext.statusFieldId} --single-select-option-id ${boardContext.doneOptionId}
|
|
671
|
+
gh project item-edit --project-id ${this.boardContext.projectId} --id $ITEM_ID --field-id ${this.boardContext.statusFieldId} --single-select-option-id ${this.boardContext.doneOptionId}
|
|
500
672
|
\`\`\`
|
|
501
673
|
|
|
502
674
|
**You MUST update the board status at each step. Do not skip this.**
|
|
@@ -504,18 +676,29 @@ gh project item-edit --project-id ${boardContext.projectId} --id $ITEM_ID --fiel
|
|
|
504
676
|
## Project Board Status Updates
|
|
505
677
|
No project board is linked or Status field not found. Skip board status updates.
|
|
506
678
|
`;
|
|
507
|
-
|
|
679
|
+
const planSection = planContent ? `
|
|
680
|
+
## Project Plan (for context)
|
|
681
|
+
The following plan describes the overall project. Use this to understand priorities and pick the most impactful issue.
|
|
682
|
+
GitHub Issues are the source of truth for what needs to be done - the plan provides context.
|
|
508
683
|
|
|
684
|
+
<plan>
|
|
685
|
+
${planContent}
|
|
686
|
+
</plan>
|
|
687
|
+
` : "";
|
|
688
|
+
const todoIssueNumbers = this.todoIssueNumbers;
|
|
689
|
+
return `You are an autonomous coding agent. Complete ONE GitHub Issue.
|
|
690
|
+
${planSection}
|
|
509
691
|
## Before Starting (REQUIRED - Read these first)
|
|
510
|
-
1. Read
|
|
511
|
-
2.
|
|
512
|
-
3. List open issues: gh issue list --state open --json number,title
|
|
692
|
+
1. Read CLAUDE.md for coding conventions
|
|
693
|
+
${todoIssueNumbers && todoIssueNumbers.length > 0 ? `2. Issues in "Todo" column (pick from these): ${todoIssueNumbers.join(", ")}` : `2. List open issues: gh issue list --state open --json number,title,labels`}
|
|
513
694
|
|
|
514
695
|
IMPORTANT: Only use root-level files for project context. Do not read subdirectory README files.
|
|
515
696
|
|
|
516
697
|
## Pick an Issue
|
|
517
|
-
|
|
518
|
-
|
|
698
|
+
${todoIssueNumbers && todoIssueNumbers.length > 0 ? `Pick one issue from the Todo list above: ${todoIssueNumbers.join(", ")}
|
|
699
|
+
These are the issues explicitly ready to work on (in "Todo" status on the project board).` : `Use the plan context above (if provided) to choose the most impactful issue.
|
|
700
|
+
Consider: dependencies between issues, priority labels, and logical order.
|
|
701
|
+
If unsure, pick the lowest numbered open issue.`}
|
|
519
702
|
|
|
520
703
|
## Workflow
|
|
521
704
|
1. Read the issue: gh issue view <number>
|
|
@@ -536,300 +719,281 @@ ${boardInstructions}
|
|
|
536
719
|
## When Done
|
|
537
720
|
Output: RALPH_COMPLETE
|
|
538
721
|
If no issues remain: RALPH_ALL_DONE`;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
722
|
+
}
|
|
723
|
+
getContext() {
|
|
724
|
+
return {
|
|
725
|
+
boardContext: this.boardContext,
|
|
726
|
+
todoIssueNumbers: this.todoIssueNumbers
|
|
727
|
+
};
|
|
728
|
+
}
|
|
729
|
+
getTodoTaskIds() {
|
|
730
|
+
return this.todoIssueNumbers;
|
|
731
|
+
}
|
|
732
|
+
// =========== Private Methods ===========
|
|
733
|
+
countOpenIssues() {
|
|
734
|
+
try {
|
|
735
|
+
const output = (0, import_node_child_process6.execSync)(
|
|
736
|
+
"gh issue list --state open --json number --jq 'length'",
|
|
737
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
738
|
+
);
|
|
739
|
+
return parseInt(output.trim(), 10) || 0;
|
|
740
|
+
} catch {
|
|
741
|
+
return 0;
|
|
556
742
|
}
|
|
557
743
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
744
|
+
countClosedIssues() {
|
|
745
|
+
try {
|
|
746
|
+
const output = (0, import_node_child_process6.execSync)(
|
|
747
|
+
"gh issue list --state closed --json number --jq 'length'",
|
|
748
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
749
|
+
);
|
|
750
|
+
return parseInt(output.trim(), 10) || 0;
|
|
751
|
+
} catch {
|
|
752
|
+
return 0;
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Get issue numbers that are in "Todo" status on the project board
|
|
757
|
+
*/
|
|
758
|
+
fetchTodoIssueNumbers() {
|
|
759
|
+
if (!this.boardContext?.todoOptionId) return [];
|
|
760
|
+
try {
|
|
761
|
+
const output = (0, import_node_child_process6.execSync)(
|
|
762
|
+
`gh project item-list ${this.boardContext.projectNumber} --owner ${this.boardContext.owner} --format json`,
|
|
763
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
764
|
+
);
|
|
765
|
+
const data = JSON.parse(output);
|
|
766
|
+
return data.items?.filter(
|
|
767
|
+
(item) => item.status?.optionId === this.boardContext?.todoOptionId
|
|
768
|
+
)?.map((item) => item.content?.number)?.filter((n) => typeof n === "number") || [];
|
|
769
|
+
} catch {
|
|
770
|
+
return [];
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Check if a GitHub project is linked to the current repo
|
|
775
|
+
*/
|
|
776
|
+
getLinkedProjectNumber() {
|
|
777
|
+
try {
|
|
778
|
+
const output = (0, import_node_child_process6.execSync)(
|
|
779
|
+
"gh repo view --json projectsV2 -q '.projectsV2.Nodes[0].number'",
|
|
780
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
781
|
+
);
|
|
782
|
+
const num = output.trim();
|
|
783
|
+
return num && num !== "null" ? num : null;
|
|
784
|
+
} catch {
|
|
785
|
+
return null;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
/**
|
|
789
|
+
* Ensure a project board is linked when using GitHub tracker
|
|
790
|
+
* If not linked, offer to link one or create a new one
|
|
791
|
+
*/
|
|
792
|
+
async ensureProjectLinked() {
|
|
793
|
+
if (this.getLinkedProjectNumber()) {
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
const configuredProject = getGithubProject();
|
|
797
|
+
console.log();
|
|
798
|
+
console.log(import_picocolors6.default.yellow("\u26A0 No project board is linked to this repo."));
|
|
799
|
+
console.log(import_picocolors6.default.dim(" This is required for automatic status updates."));
|
|
800
|
+
console.log();
|
|
801
|
+
let repoName = "";
|
|
802
|
+
let owner = "";
|
|
803
|
+
try {
|
|
804
|
+
repoName = (0, import_node_child_process6.execSync)("gh repo view --json nameWithOwner -q .nameWithOwner", {
|
|
805
|
+
encoding: "utf-8",
|
|
806
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
807
|
+
}).trim();
|
|
808
|
+
owner = repoName.split("/")[0] ?? "";
|
|
809
|
+
} catch {
|
|
810
|
+
console.log(import_picocolors6.default.red("Could not detect GitHub repo."));
|
|
811
|
+
console.log(import_picocolors6.default.dim("Continuing without project board integration..."));
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
let existingProjects = [];
|
|
815
|
+
try {
|
|
816
|
+
const projectsOutput = (0, import_node_child_process6.execSync)(`gh project list --owner ${owner} --format json`, {
|
|
817
|
+
encoding: "utf-8",
|
|
818
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
819
|
+
});
|
|
820
|
+
const projects = JSON.parse(projectsOutput);
|
|
821
|
+
existingProjects = projects.projects?.map((p2) => ({
|
|
822
|
+
title: p2.title,
|
|
823
|
+
number: p2.number
|
|
824
|
+
})) || [];
|
|
825
|
+
} catch {
|
|
826
|
+
}
|
|
827
|
+
const options = [
|
|
828
|
+
...existingProjects.map((p2) => ({
|
|
829
|
+
value: String(p2.number),
|
|
830
|
+
label: p2.title,
|
|
831
|
+
hint: configuredProject === p2.title ? "configured" : void 0
|
|
832
|
+
})),
|
|
833
|
+
{ value: "__new__", label: "Create new board..." },
|
|
834
|
+
{ value: "__skip__", label: "Skip (automation will be limited)" }
|
|
835
|
+
];
|
|
836
|
+
const selected = await select2({
|
|
837
|
+
message: "Link a project board to this repo?",
|
|
838
|
+
options
|
|
839
|
+
});
|
|
840
|
+
if (selected === "__skip__") {
|
|
841
|
+
console.log(import_picocolors6.default.dim("Continuing without project board integration..."));
|
|
842
|
+
return;
|
|
843
|
+
}
|
|
844
|
+
if (selected === "__new__") {
|
|
845
|
+
const existingNames = new Set(existingProjects.map((p2) => p2.title.toLowerCase()));
|
|
846
|
+
const projectName = await text2({
|
|
847
|
+
message: "Board name",
|
|
848
|
+
placeholder: "e.g., My Project Kanban",
|
|
849
|
+
validate: (value) => {
|
|
850
|
+
if (!value || !value.trim()) {
|
|
851
|
+
return "Board name is required";
|
|
852
|
+
}
|
|
853
|
+
if (existingNames.has(value.trim().toLowerCase())) {
|
|
854
|
+
return `A board named "${value.trim()}" already exists. Choose a different name.`;
|
|
855
|
+
}
|
|
856
|
+
return void 0;
|
|
857
|
+
}
|
|
858
|
+
});
|
|
859
|
+
console.log(import_picocolors6.default.cyan(`Creating board "${projectName}"...`));
|
|
860
|
+
try {
|
|
861
|
+
const createOutput = (0, import_node_child_process6.execSync)(
|
|
862
|
+
`gh project create --owner ${owner} --title "${projectName}"`,
|
|
863
|
+
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
864
|
+
);
|
|
865
|
+
const projectNumberMatch = createOutput.match(/projects\/(\d+)/);
|
|
866
|
+
const projectNumber = projectNumberMatch ? projectNumberMatch[1] : null;
|
|
867
|
+
if (projectNumber) {
|
|
868
|
+
(0, import_node_child_process6.execSync)(`gh project link ${projectNumber} --owner ${owner} --repo ${repoName}`, {
|
|
869
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
870
|
+
});
|
|
871
|
+
console.log(import_picocolors6.default.green(`\u2713 Board "${projectName}" created and linked`));
|
|
872
|
+
}
|
|
873
|
+
} catch {
|
|
874
|
+
console.log(import_picocolors6.default.yellow("Could not create board. Continuing without project integration..."));
|
|
875
|
+
}
|
|
876
|
+
} else {
|
|
877
|
+
const projectNumber = selected;
|
|
878
|
+
const projectTitle = existingProjects.find((p2) => String(p2.number) === projectNumber)?.title || projectNumber;
|
|
879
|
+
try {
|
|
880
|
+
(0, import_node_child_process6.execSync)(`gh project link ${projectNumber} --owner ${owner} --repo ${repoName}`, {
|
|
881
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
882
|
+
});
|
|
883
|
+
console.log(import_picocolors6.default.green(`\u2713 Linked "${projectTitle}" to ${repoName}`));
|
|
884
|
+
} catch {
|
|
885
|
+
console.log(import_picocolors6.default.yellow("Could not link project. It may already be linked."));
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
console.log();
|
|
889
|
+
}
|
|
890
|
+
};
|
|
570
891
|
}
|
|
571
|
-
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
// src/lib/tracker/index.ts
|
|
895
|
+
function createTracker() {
|
|
896
|
+
const type = getProjectTracker();
|
|
897
|
+
return type === "github" ? new GitHubTracker() : new TodoTracker();
|
|
572
898
|
}
|
|
573
|
-
var
|
|
574
|
-
|
|
575
|
-
"src/lib/claude.ts"() {
|
|
899
|
+
var init_tracker = __esm({
|
|
900
|
+
"src/lib/tracker/index.ts"() {
|
|
576
901
|
"use strict";
|
|
577
902
|
init_cjs_shims();
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
903
|
+
init_new();
|
|
904
|
+
init_todo();
|
|
905
|
+
init_github();
|
|
906
|
+
init_github_board();
|
|
907
|
+
init_todo();
|
|
908
|
+
init_github();
|
|
583
909
|
}
|
|
584
910
|
});
|
|
585
911
|
|
|
586
912
|
// src/commands/loop.ts
|
|
587
|
-
function
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
return count;
|
|
598
|
-
}
|
|
599
|
-
function hasUncheckedTasks() {
|
|
600
|
-
if (!(0, import_node_fs2.existsSync)(config.todoFile)) return false;
|
|
601
|
-
const content = (0, import_node_fs2.readFileSync)(config.todoFile, "utf-8");
|
|
602
|
-
return /^\s*-\s*\[\s\]/m.test(content);
|
|
603
|
-
}
|
|
604
|
-
function countOpenIssues() {
|
|
605
|
-
try {
|
|
606
|
-
const output = (0, import_node_child_process5.execSync)(
|
|
607
|
-
"gh issue list --state open --json number --jq 'length'",
|
|
608
|
-
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
609
|
-
);
|
|
610
|
-
return parseInt(output.trim(), 10) || 0;
|
|
611
|
-
} catch {
|
|
612
|
-
return 0;
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
function hasOpenIssues() {
|
|
616
|
-
try {
|
|
617
|
-
const output = (0, import_node_child_process5.execSync)(
|
|
618
|
-
"gh issue list --state open --limit 1 --json number --jq 'length'",
|
|
619
|
-
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
620
|
-
);
|
|
621
|
-
return parseInt(output.trim(), 10) > 0;
|
|
622
|
-
} catch {
|
|
623
|
-
return false;
|
|
624
|
-
}
|
|
913
|
+
function getPlanPathFromPRD() {
|
|
914
|
+
const prdPath = "specs/prd.md";
|
|
915
|
+
if (!(0, import_node_fs3.existsSync)(prdPath)) return null;
|
|
916
|
+
const content = (0, import_node_fs3.readFileSync)(prdPath, "utf-8");
|
|
917
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
918
|
+
if (!frontmatterMatch) return null;
|
|
919
|
+
const sourcePlanMatch = frontmatterMatch[1].match(/^source_plan:\s*"?([^"\n]+)"?/m);
|
|
920
|
+
return sourcePlanMatch ? sourcePlanMatch[1].trim() : null;
|
|
625
921
|
}
|
|
626
|
-
function
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
922
|
+
function loadPlanContent() {
|
|
923
|
+
const planFilename = getPlanPathFromPRD();
|
|
924
|
+
if (!planFilename) return null;
|
|
925
|
+
const locations = [
|
|
926
|
+
`docs/plans/${planFilename}`,
|
|
927
|
+
planFilename
|
|
928
|
+
// Might be a full path
|
|
929
|
+
];
|
|
930
|
+
for (const path of locations) {
|
|
931
|
+
if ((0, import_node_fs3.existsSync)(path)) {
|
|
932
|
+
console.log(import_picocolors7.default.dim(`Loading plan context from ${path}...`));
|
|
933
|
+
return (0, import_node_fs3.readFileSync)(path, "utf-8");
|
|
934
|
+
}
|
|
635
935
|
}
|
|
936
|
+
return null;
|
|
636
937
|
}
|
|
637
938
|
function getCurrentCommit() {
|
|
638
939
|
try {
|
|
639
|
-
return (0,
|
|
940
|
+
return (0, import_node_child_process7.execSync)("git rev-parse HEAD", { encoding: "utf-8" }).trim();
|
|
640
941
|
} catch {
|
|
641
942
|
return "none";
|
|
642
943
|
}
|
|
643
944
|
}
|
|
644
|
-
function getLinkedProjectNumber() {
|
|
645
|
-
try {
|
|
646
|
-
const output = (0, import_node_child_process5.execSync)(
|
|
647
|
-
"gh repo view --json projectsV2 -q '.projectsV2.Nodes[0].number'",
|
|
648
|
-
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
649
|
-
);
|
|
650
|
-
const num = output.trim();
|
|
651
|
-
return num && num !== "null" ? num : null;
|
|
652
|
-
} catch {
|
|
653
|
-
return null;
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
async function ensureProjectLinked() {
|
|
657
|
-
if (getLinkedProjectNumber()) {
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
const configuredProject = getGithubProject();
|
|
661
|
-
console.log();
|
|
662
|
-
console.log(import_picocolors6.default.yellow("\u26A0 No project board is linked to this repo."));
|
|
663
|
-
console.log(import_picocolors6.default.dim(" This is required for automatic status updates."));
|
|
664
|
-
console.log();
|
|
665
|
-
let repoName = "";
|
|
666
|
-
let owner = "";
|
|
667
|
-
try {
|
|
668
|
-
repoName = (0, import_node_child_process5.execSync)("gh repo view --json nameWithOwner -q .nameWithOwner", {
|
|
669
|
-
encoding: "utf-8",
|
|
670
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
671
|
-
}).trim();
|
|
672
|
-
owner = repoName.split("/")[0];
|
|
673
|
-
} catch {
|
|
674
|
-
console.log(import_picocolors6.default.red("Could not detect GitHub repo."));
|
|
675
|
-
console.log(import_picocolors6.default.dim("Continuing without project board integration..."));
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
let existingProjects = [];
|
|
679
|
-
try {
|
|
680
|
-
const projectsOutput = (0, import_node_child_process5.execSync)(`gh project list --owner ${owner} --format json`, {
|
|
681
|
-
encoding: "utf-8",
|
|
682
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
683
|
-
});
|
|
684
|
-
const projects = JSON.parse(projectsOutput);
|
|
685
|
-
existingProjects = projects.projects?.map((p2) => ({
|
|
686
|
-
title: p2.title,
|
|
687
|
-
number: p2.number
|
|
688
|
-
})) || [];
|
|
689
|
-
} catch {
|
|
690
|
-
}
|
|
691
|
-
const options = [
|
|
692
|
-
...existingProjects.map((p2) => ({
|
|
693
|
-
value: String(p2.number),
|
|
694
|
-
label: p2.title,
|
|
695
|
-
hint: configuredProject === p2.title ? "configured" : void 0
|
|
696
|
-
})),
|
|
697
|
-
{ value: "__new__", label: "Create new board..." },
|
|
698
|
-
{ value: "__skip__", label: "Skip (automation will be limited)" }
|
|
699
|
-
];
|
|
700
|
-
const selected = await select2({
|
|
701
|
-
message: "Link a project board to this repo?",
|
|
702
|
-
options
|
|
703
|
-
});
|
|
704
|
-
if (selected === "__skip__") {
|
|
705
|
-
console.log(import_picocolors6.default.dim("Continuing without project board integration..."));
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
708
|
-
if (selected === "__new__") {
|
|
709
|
-
const existingNames = new Set(existingProjects.map((p2) => p2.title.toLowerCase()));
|
|
710
|
-
const projectName = await text2({
|
|
711
|
-
message: "Board name",
|
|
712
|
-
placeholder: "e.g., My Project Kanban",
|
|
713
|
-
validate: (value) => {
|
|
714
|
-
if (!value || !value.trim()) {
|
|
715
|
-
return "Board name is required";
|
|
716
|
-
}
|
|
717
|
-
if (existingNames.has(value.trim().toLowerCase())) {
|
|
718
|
-
return `A board named "${value.trim()}" already exists. Choose a different name.`;
|
|
719
|
-
}
|
|
720
|
-
return void 0;
|
|
721
|
-
}
|
|
722
|
-
});
|
|
723
|
-
console.log(import_picocolors6.default.cyan(`Creating board "${projectName}"...`));
|
|
724
|
-
try {
|
|
725
|
-
const createOutput = (0, import_node_child_process5.execSync)(
|
|
726
|
-
`gh project create --owner ${owner} --title "${projectName}"`,
|
|
727
|
-
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
728
|
-
);
|
|
729
|
-
const projectNumberMatch = createOutput.match(/projects\/(\d+)/);
|
|
730
|
-
const projectNumber = projectNumberMatch ? projectNumberMatch[1] : null;
|
|
731
|
-
if (projectNumber) {
|
|
732
|
-
(0, import_node_child_process5.execSync)(`gh project link ${projectNumber} --owner ${owner} --repo ${repoName}`, {
|
|
733
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
734
|
-
});
|
|
735
|
-
console.log(import_picocolors6.default.green(`\u2713 Board "${projectName}" created and linked`));
|
|
736
|
-
}
|
|
737
|
-
} catch (err) {
|
|
738
|
-
console.log(import_picocolors6.default.yellow("Could not create board. Continuing without project integration..."));
|
|
739
|
-
}
|
|
740
|
-
} else {
|
|
741
|
-
const projectNumber = selected;
|
|
742
|
-
const projectTitle = existingProjects.find((p2) => String(p2.number) === projectNumber)?.title || projectNumber;
|
|
743
|
-
try {
|
|
744
|
-
(0, import_node_child_process5.execSync)(`gh project link ${projectNumber} --owner ${owner} --repo ${repoName}`, {
|
|
745
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
746
|
-
});
|
|
747
|
-
console.log(import_picocolors6.default.green(`\u2713 Linked "${projectTitle}" to ${repoName}`));
|
|
748
|
-
} catch {
|
|
749
|
-
console.log(import_picocolors6.default.yellow("Could not link project. It may already be linked."));
|
|
750
|
-
}
|
|
751
|
-
}
|
|
752
|
-
console.log();
|
|
753
|
-
}
|
|
754
945
|
function pushToOrigin() {
|
|
755
946
|
try {
|
|
756
|
-
(0,
|
|
947
|
+
(0, import_node_child_process7.execSync)("git push origin HEAD", { stdio: "pipe" });
|
|
757
948
|
console.log("Pushed to origin");
|
|
758
949
|
} catch {
|
|
759
950
|
}
|
|
760
951
|
}
|
|
761
952
|
async function cmdLoop() {
|
|
762
|
-
const tracker =
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
if (isGitHub) {
|
|
769
|
-
console.log(import_picocolors6.default.dim("Fetching project board context..."));
|
|
770
|
-
boardContext = getProjectBoardContext();
|
|
771
|
-
if (boardContext) {
|
|
772
|
-
console.log(import_picocolors6.default.green(`\u2713 Board context loaded (Project #${boardContext.projectNumber})`));
|
|
773
|
-
if (!boardContext.inProgressOptionId || !boardContext.doneOptionId) {
|
|
774
|
-
console.log(import_picocolors6.default.yellow(" \u26A0 Missing status options - status updates may not work"));
|
|
775
|
-
}
|
|
776
|
-
} else {
|
|
777
|
-
console.log(import_picocolors6.default.yellow("\u26A0 Could not fetch project board context."));
|
|
778
|
-
console.log(import_picocolors6.default.dim(" Status updates will not work. Continuing anyway..."));
|
|
953
|
+
const tracker = createTracker();
|
|
954
|
+
try {
|
|
955
|
+
await tracker.initialize();
|
|
956
|
+
} catch (error) {
|
|
957
|
+
if (error instanceof Error) {
|
|
958
|
+
console.log(import_picocolors7.default.red(error.message));
|
|
779
959
|
}
|
|
960
|
+
process.exit(1);
|
|
780
961
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
trackerLabel = "Issues";
|
|
790
|
-
if (maxIterations === 0) {
|
|
791
|
-
console.log(import_picocolors6.default.green("No open GitHub Issues. All done!"));
|
|
792
|
-
process.exit(0);
|
|
793
|
-
}
|
|
794
|
-
} else {
|
|
795
|
-
if (!(0, import_node_fs2.existsSync)(config.todoFile)) {
|
|
796
|
-
console.log(import_picocolors6.default.red(`Missing ${config.todoFile}`));
|
|
797
|
-
process.exit(1);
|
|
798
|
-
}
|
|
799
|
-
maxIterations = countUncheckedTasks();
|
|
800
|
-
hasRemainingTasks = hasUncheckedTasks;
|
|
801
|
-
trackerLabel = "Tasks";
|
|
802
|
-
if (maxIterations === 0) {
|
|
803
|
-
console.log(import_picocolors6.default.green(`No unchecked tasks in ${config.todoFile}. All done!`));
|
|
804
|
-
process.exit(0);
|
|
805
|
-
}
|
|
962
|
+
const planContent = tracker.type === "github" ? loadPlanContent() : null;
|
|
963
|
+
if (planContent) {
|
|
964
|
+
console.log(import_picocolors7.default.green("\u2713 Plan context loaded"));
|
|
965
|
+
}
|
|
966
|
+
const stats = tracker.getStats();
|
|
967
|
+
if (stats.remaining === 0) {
|
|
968
|
+
console.log(import_picocolors7.default.green(`No ${stats.label.toLowerCase()} remaining. All done!`));
|
|
969
|
+
process.exit(0);
|
|
806
970
|
}
|
|
807
971
|
console.log();
|
|
808
|
-
console.log(
|
|
809
|
-
console.log(
|
|
810
|
-
if (
|
|
811
|
-
|
|
812
|
-
console.log(import_picocolors6.default.green(` ${trackerLabel}: ${maxIterations} remaining, ${completedCount} done (${total} total)`));
|
|
972
|
+
console.log(import_picocolors7.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
|
|
973
|
+
console.log(import_picocolors7.default.green(" Starting Build Loop"));
|
|
974
|
+
if (stats.completed > 0) {
|
|
975
|
+
console.log(import_picocolors7.default.green(` ${stats.label}: ${stats.remaining} remaining, ${stats.completed} done (${stats.total} total)`));
|
|
813
976
|
} else {
|
|
814
|
-
console.log(
|
|
977
|
+
console.log(import_picocolors7.default.green(` ${stats.label} remaining: ${stats.remaining}`));
|
|
815
978
|
}
|
|
816
|
-
console.log(
|
|
817
|
-
console.log(
|
|
979
|
+
console.log(import_picocolors7.default.green(` Timeout: ${config.iterationTimeoutMinutes}m per task`));
|
|
980
|
+
console.log(import_picocolors7.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
|
|
818
981
|
let consecutiveFailures = 0;
|
|
819
982
|
let noCommitCycles = 0;
|
|
820
983
|
let lastCommit = getCurrentCommit();
|
|
821
|
-
for (let i = 1; i <=
|
|
984
|
+
for (let i = 1; i <= stats.remaining; i++) {
|
|
822
985
|
console.log();
|
|
823
|
-
console.log(
|
|
986
|
+
console.log(import_picocolors7.default.yellow(`\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 Task ${i} of ${stats.remaining} \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550`));
|
|
824
987
|
cleanupStaleProcesses();
|
|
825
988
|
const startTime = Date.now();
|
|
826
|
-
const
|
|
989
|
+
const prompt = tracker.getTaskPrompt(planContent);
|
|
990
|
+
const result = await runTaskIteration(i, prompt);
|
|
827
991
|
const duration = Math.round((Date.now() - startTime) / 1e3);
|
|
828
992
|
console.log();
|
|
829
993
|
console.log(`Completed in ${duration}s (exit: ${result.success ? 0 : 1})`);
|
|
830
994
|
if (!result.success) {
|
|
831
995
|
consecutiveFailures++;
|
|
832
|
-
console.log(
|
|
996
|
+
console.log(import_picocolors7.default.red(`Failure ${consecutiveFailures}/${config.maxConsecutiveFailures}`));
|
|
833
997
|
await sleep(3e4);
|
|
834
998
|
} else {
|
|
835
999
|
consecutiveFailures = 0;
|
|
@@ -838,48 +1002,47 @@ async function cmdLoop() {
|
|
|
838
1002
|
if (currentCommit === lastCommit) {
|
|
839
1003
|
if (result.success) {
|
|
840
1004
|
noCommitCycles++;
|
|
841
|
-
console.log(
|
|
1005
|
+
console.log(import_picocolors7.default.yellow(`No commit (${noCommitCycles}/${config.maxNoCommitCycles})`));
|
|
842
1006
|
}
|
|
843
1007
|
} else {
|
|
844
1008
|
noCommitCycles = 0;
|
|
845
1009
|
lastCommit = currentCommit;
|
|
846
|
-
console.log(
|
|
1010
|
+
console.log(import_picocolors7.default.green(`New commit: ${currentCommit.substring(0, 7)}`));
|
|
847
1011
|
pushToOrigin();
|
|
848
1012
|
}
|
|
849
1013
|
if (consecutiveFailures >= config.maxConsecutiveFailures) {
|
|
850
|
-
console.log(
|
|
1014
|
+
console.log(import_picocolors7.default.red(`Stopping: ${config.maxConsecutiveFailures} consecutive failures`));
|
|
851
1015
|
process.exit(1);
|
|
852
1016
|
}
|
|
853
1017
|
if (noCommitCycles >= config.maxNoCommitCycles) {
|
|
854
|
-
console.log(
|
|
1018
|
+
console.log(import_picocolors7.default.red(`Stopping: No commits for ${config.maxNoCommitCycles} cycles`));
|
|
855
1019
|
process.exit(1);
|
|
856
1020
|
}
|
|
857
|
-
if (!hasRemainingTasks()) {
|
|
1021
|
+
if (!tracker.hasRemainingTasks()) {
|
|
858
1022
|
console.log();
|
|
859
|
-
console.log(
|
|
860
|
-
console.log(
|
|
861
|
-
console.log(
|
|
1023
|
+
console.log(import_picocolors7.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
|
|
1024
|
+
console.log(import_picocolors7.default.green(` All ${stats.label.toLowerCase()} completed!`));
|
|
1025
|
+
console.log(import_picocolors7.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
|
|
862
1026
|
pushToOrigin();
|
|
863
1027
|
process.exit(0);
|
|
864
1028
|
}
|
|
865
1029
|
await sleep(config.cooldownSeconds * 1e3);
|
|
866
1030
|
}
|
|
867
|
-
console.log(
|
|
1031
|
+
console.log(import_picocolors7.default.yellow(`Reached max iterations (${stats.remaining})`));
|
|
868
1032
|
pushToOrigin();
|
|
869
1033
|
}
|
|
870
|
-
var
|
|
1034
|
+
var import_node_fs3, import_node_child_process7, import_picocolors7;
|
|
871
1035
|
var init_loop = __esm({
|
|
872
1036
|
"src/commands/loop.ts"() {
|
|
873
1037
|
"use strict";
|
|
874
1038
|
init_cjs_shims();
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
1039
|
+
import_node_fs3 = require("fs");
|
|
1040
|
+
import_node_child_process7 = require("child_process");
|
|
1041
|
+
import_picocolors7 = __toESM(require("picocolors"), 1);
|
|
878
1042
|
init_config();
|
|
879
1043
|
init_claude();
|
|
880
1044
|
init_process();
|
|
881
|
-
|
|
882
|
-
init_prompts();
|
|
1045
|
+
init_tracker();
|
|
883
1046
|
}
|
|
884
1047
|
});
|
|
885
1048
|
|
|
@@ -894,28 +1057,28 @@ function stripTrailingCommas(json) {
|
|
|
894
1057
|
return json.replace(/,(\s*[}\]])/g, "$1");
|
|
895
1058
|
}
|
|
896
1059
|
function getConfig() {
|
|
897
|
-
if (!(0,
|
|
1060
|
+
if (!(0, import_node_fs4.existsSync)(CONFIG_FILE)) {
|
|
898
1061
|
return { projectTracker: "todo" };
|
|
899
1062
|
}
|
|
900
|
-
const raw = (0,
|
|
1063
|
+
const raw = (0, import_node_fs4.readFileSync)(CONFIG_FILE, "utf-8");
|
|
901
1064
|
try {
|
|
902
1065
|
return JSON.parse(raw);
|
|
903
1066
|
} catch {
|
|
904
1067
|
try {
|
|
905
1068
|
const fixed = stripTrailingCommas(raw);
|
|
906
1069
|
const config2 = JSON.parse(fixed);
|
|
907
|
-
console.log(
|
|
1070
|
+
console.log(import_picocolors8.default.yellow("Note: Fixed trailing comma in config file."));
|
|
908
1071
|
saveConfig(config2);
|
|
909
1072
|
return config2;
|
|
910
1073
|
} catch {
|
|
911
|
-
console.log(
|
|
1074
|
+
console.log(import_picocolors8.default.yellow("Warning: Could not parse config file. Using defaults."));
|
|
912
1075
|
return { projectTracker: "todo" };
|
|
913
1076
|
}
|
|
914
1077
|
}
|
|
915
1078
|
}
|
|
916
1079
|
function saveConfig(config2) {
|
|
917
|
-
(0,
|
|
918
|
-
(0,
|
|
1080
|
+
(0, import_node_fs4.mkdirSync)((0, import_node_path2.dirname)(CONFIG_FILE), { recursive: true });
|
|
1081
|
+
(0, import_node_fs4.writeFileSync)(CONFIG_FILE, JSON.stringify(config2, null, 2) + "\n");
|
|
919
1082
|
}
|
|
920
1083
|
function getProjectTracker() {
|
|
921
1084
|
return getConfig().projectTracker || "todo";
|
|
@@ -924,13 +1087,13 @@ function getGithubProject() {
|
|
|
924
1087
|
return getConfig().githubProject;
|
|
925
1088
|
}
|
|
926
1089
|
function countTodoTasks() {
|
|
927
|
-
if (!(0,
|
|
928
|
-
const content = (0,
|
|
1090
|
+
if (!(0, import_node_fs4.existsSync)(config.todoFile)) return 0;
|
|
1091
|
+
const content = (0, import_node_fs4.readFileSync)(config.todoFile, "utf-8");
|
|
929
1092
|
return (content.match(/^\s*-\s*\[\s\]/gm) || []).length;
|
|
930
1093
|
}
|
|
931
1094
|
function countOpenGitHubIssues() {
|
|
932
1095
|
try {
|
|
933
|
-
const output = (0,
|
|
1096
|
+
const output = (0, import_node_child_process8.execSync)(
|
|
934
1097
|
"gh issue list --state open --json number --jq 'length'",
|
|
935
1098
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 3e4 }
|
|
936
1099
|
);
|
|
@@ -958,7 +1121,7 @@ function parsePRDFrontmatter(content) {
|
|
|
958
1121
|
function findMarkdownFiles(dir, basePath = "") {
|
|
959
1122
|
const results = [];
|
|
960
1123
|
try {
|
|
961
|
-
const entries = (0,
|
|
1124
|
+
const entries = (0, import_node_fs4.readdirSync)(dir, { withFileTypes: true });
|
|
962
1125
|
for (const entry of entries) {
|
|
963
1126
|
const relativePath = basePath ? `${basePath}/${entry.name}` : entry.name;
|
|
964
1127
|
if (entry.isDirectory()) {
|
|
@@ -974,14 +1137,14 @@ function findMarkdownFiles(dir, basePath = "") {
|
|
|
974
1137
|
return results;
|
|
975
1138
|
}
|
|
976
1139
|
async function cmdNew(planFile) {
|
|
977
|
-
console.log(
|
|
1140
|
+
console.log(import_picocolors8.default.bold("Project Setup"));
|
|
978
1141
|
console.log();
|
|
979
|
-
console.log(`Working directory: ${
|
|
1142
|
+
console.log(`Working directory: ${import_picocolors8.default.cyan(process.cwd())}`);
|
|
980
1143
|
await setupProjectConfig();
|
|
981
1144
|
await setupTemplates();
|
|
982
1145
|
if (planFile) {
|
|
983
|
-
if (!(0,
|
|
984
|
-
console.log(
|
|
1146
|
+
if (!(0, import_node_fs4.existsSync)(planFile)) {
|
|
1147
|
+
console.log(import_picocolors8.default.red(`Plan file not found: ${planFile}`));
|
|
985
1148
|
process.exit(1);
|
|
986
1149
|
}
|
|
987
1150
|
await checkExistingFiles();
|
|
@@ -1001,8 +1164,8 @@ async function cmdNew(planFile) {
|
|
|
1001
1164
|
case "plan": {
|
|
1002
1165
|
const mdFiles = findMarkdownFiles(process.cwd());
|
|
1003
1166
|
if (mdFiles.length === 0) {
|
|
1004
|
-
console.log(
|
|
1005
|
-
console.log(
|
|
1167
|
+
console.log(import_picocolors8.default.yellow("No markdown files found in project."));
|
|
1168
|
+
console.log(import_picocolors8.default.dim("Create a plan file first, e.g., plans/plan.md"));
|
|
1006
1169
|
process.exit(1);
|
|
1007
1170
|
}
|
|
1008
1171
|
console.log();
|
|
@@ -1013,7 +1176,7 @@ async function cmdNew(planFile) {
|
|
|
1013
1176
|
maxVisible: 15
|
|
1014
1177
|
});
|
|
1015
1178
|
if (!planPath) {
|
|
1016
|
-
console.log(
|
|
1179
|
+
console.log(import_picocolors8.default.yellow("No plan selected."));
|
|
1017
1180
|
process.exit(0);
|
|
1018
1181
|
}
|
|
1019
1182
|
await checkExistingFiles();
|
|
@@ -1025,8 +1188,8 @@ async function cmdNew(planFile) {
|
|
|
1025
1188
|
await interactiveDescribe();
|
|
1026
1189
|
break;
|
|
1027
1190
|
case "existing":
|
|
1028
|
-
if (!(0,
|
|
1029
|
-
console.log(
|
|
1191
|
+
if (!(0, import_node_fs4.existsSync)(config.todoFile)) {
|
|
1192
|
+
console.log(import_picocolors8.default.red("No TODO.md found. Run 'chiefwiggum new' to set up first."));
|
|
1030
1193
|
process.exit(1);
|
|
1031
1194
|
}
|
|
1032
1195
|
break;
|
|
@@ -1055,23 +1218,23 @@ async function cmdNew(planFile) {
|
|
|
1055
1218
|
break;
|
|
1056
1219
|
case "review":
|
|
1057
1220
|
console.log();
|
|
1058
|
-
console.log(
|
|
1059
|
-
console.log(` ${
|
|
1221
|
+
console.log(import_picocolors8.default.green("Setup complete!") + " Review your files, then run:");
|
|
1222
|
+
console.log(` ${import_picocolors8.default.cyan("chiefwiggum loop")}`);
|
|
1060
1223
|
break;
|
|
1061
1224
|
case "exit":
|
|
1062
|
-
console.log(
|
|
1225
|
+
console.log(import_picocolors8.default.yellow("Exiting."));
|
|
1063
1226
|
break;
|
|
1064
1227
|
}
|
|
1065
1228
|
}
|
|
1066
1229
|
async function checkExistingFiles() {
|
|
1067
1230
|
const existingFiles = [];
|
|
1068
|
-
if ((0,
|
|
1069
|
-
if ((0,
|
|
1231
|
+
if ((0, import_node_fs4.existsSync)(config.todoFile)) existingFiles.push(config.todoFile);
|
|
1232
|
+
if ((0, import_node_fs4.existsSync)(config.specsDir)) existingFiles.push(`${config.specsDir}/`);
|
|
1070
1233
|
if (existingFiles.length === 0) {
|
|
1071
1234
|
return true;
|
|
1072
1235
|
}
|
|
1073
1236
|
console.log();
|
|
1074
|
-
console.log(
|
|
1237
|
+
console.log(import_picocolors8.default.yellow("Existing files found:"));
|
|
1075
1238
|
for (const f of existingFiles) {
|
|
1076
1239
|
console.log(` - ${f}`);
|
|
1077
1240
|
}
|
|
@@ -1081,39 +1244,39 @@ async function checkExistingFiles() {
|
|
|
1081
1244
|
initialValue: false
|
|
1082
1245
|
});
|
|
1083
1246
|
if (!shouldOverwrite) {
|
|
1084
|
-
console.log(
|
|
1247
|
+
console.log(import_picocolors8.default.yellow("Aborted."));
|
|
1085
1248
|
process.exit(0);
|
|
1086
1249
|
}
|
|
1087
1250
|
return true;
|
|
1088
1251
|
}
|
|
1089
1252
|
async function setupTemplates() {
|
|
1090
|
-
if ((0,
|
|
1253
|
+
if ((0, import_node_fs4.existsSync)(LOCAL_TEMPLATES_DIR)) {
|
|
1091
1254
|
return;
|
|
1092
1255
|
}
|
|
1093
|
-
if (!(0,
|
|
1094
|
-
console.log(
|
|
1256
|
+
if (!(0, import_node_fs4.existsSync)(BUNDLED_TEMPLATES_DIR)) {
|
|
1257
|
+
console.log(import_picocolors8.default.red("Bundled templates not found. Please reinstall chiefwiggum."));
|
|
1095
1258
|
process.exit(1);
|
|
1096
1259
|
}
|
|
1097
|
-
console.log(
|
|
1098
|
-
(0,
|
|
1099
|
-
(0,
|
|
1100
|
-
console.log(
|
|
1101
|
-
console.log(
|
|
1260
|
+
console.log(import_picocolors8.default.cyan(`Setting up templates in ${LOCAL_TEMPLATES_DIR}...`));
|
|
1261
|
+
(0, import_node_fs4.mkdirSync)(LOCAL_TEMPLATES_DIR, { recursive: true });
|
|
1262
|
+
(0, import_node_fs4.cpSync)(BUNDLED_TEMPLATES_DIR, LOCAL_TEMPLATES_DIR, { recursive: true });
|
|
1263
|
+
console.log(import_picocolors8.default.green(`\u2713 Templates copied to ${LOCAL_TEMPLATES_DIR}`));
|
|
1264
|
+
console.log(import_picocolors8.default.dim(" You can customize these templates to change how specs are generated."));
|
|
1102
1265
|
console.log();
|
|
1103
1266
|
}
|
|
1104
1267
|
function checkGitHubCLI() {
|
|
1105
1268
|
try {
|
|
1106
|
-
(0,
|
|
1269
|
+
(0, import_node_child_process8.execSync)("which gh", { stdio: ["pipe", "pipe", "pipe"] });
|
|
1107
1270
|
} catch {
|
|
1108
1271
|
return { ok: false, reason: "not_installed" };
|
|
1109
1272
|
}
|
|
1110
1273
|
try {
|
|
1111
|
-
(0,
|
|
1274
|
+
(0, import_node_child_process8.execSync)("gh auth status", { stdio: ["pipe", "pipe", "pipe"] });
|
|
1112
1275
|
} catch {
|
|
1113
1276
|
return { ok: false, reason: "not_authenticated" };
|
|
1114
1277
|
}
|
|
1115
1278
|
try {
|
|
1116
|
-
const repo = (0,
|
|
1279
|
+
const repo = (0, import_node_child_process8.execSync)("gh repo view --json nameWithOwner -q .nameWithOwner", {
|
|
1117
1280
|
encoding: "utf-8",
|
|
1118
1281
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1119
1282
|
}).trim();
|
|
@@ -1124,7 +1287,7 @@ function checkGitHubCLI() {
|
|
|
1124
1287
|
}
|
|
1125
1288
|
async function setupProjectConfig() {
|
|
1126
1289
|
const existingConfig = getConfig();
|
|
1127
|
-
if ((0,
|
|
1290
|
+
if ((0, import_node_fs4.existsSync)(CONFIG_FILE)) {
|
|
1128
1291
|
if (existingConfig.projectTracker === "github" && !existingConfig.githubProject) {
|
|
1129
1292
|
const ghCheck = checkGitHubCLI();
|
|
1130
1293
|
if (ghCheck.ok) {
|
|
@@ -1132,7 +1295,7 @@ async function setupProjectConfig() {
|
|
|
1132
1295
|
const config3 = { ...existingConfig, githubProject: projectName };
|
|
1133
1296
|
saveConfig(config3);
|
|
1134
1297
|
if (projectName) {
|
|
1135
|
-
console.log(
|
|
1298
|
+
console.log(import_picocolors8.default.green(`\u2713 Project board saved: ${projectName}`));
|
|
1136
1299
|
}
|
|
1137
1300
|
}
|
|
1138
1301
|
}
|
|
@@ -1151,25 +1314,25 @@ async function setupProjectConfig() {
|
|
|
1151
1314
|
if (!ghCheck.ok) {
|
|
1152
1315
|
console.log();
|
|
1153
1316
|
if (ghCheck.reason === "not_installed") {
|
|
1154
|
-
console.log(
|
|
1317
|
+
console.log(import_picocolors8.default.yellow("GitHub CLI (gh) is not installed."));
|
|
1155
1318
|
console.log();
|
|
1156
1319
|
console.log("To use GitHub Issues, install it first:");
|
|
1157
|
-
console.log(
|
|
1158
|
-
console.log(
|
|
1159
|
-
console.log(
|
|
1320
|
+
console.log(import_picocolors8.default.cyan(" brew install gh # macOS"));
|
|
1321
|
+
console.log(import_picocolors8.default.cyan(" sudo apt install gh # Ubuntu/Debian"));
|
|
1322
|
+
console.log(import_picocolors8.default.dim(" https://cli.github.com for other platforms"));
|
|
1160
1323
|
} else if (ghCheck.reason === "not_authenticated") {
|
|
1161
|
-
console.log(
|
|
1324
|
+
console.log(import_picocolors8.default.yellow("GitHub CLI is not authenticated."));
|
|
1162
1325
|
console.log();
|
|
1163
1326
|
console.log("Run this command to log in:");
|
|
1164
|
-
console.log(
|
|
1327
|
+
console.log(import_picocolors8.default.cyan(" gh auth login"));
|
|
1165
1328
|
} else if (ghCheck.reason === "not_github_repo") {
|
|
1166
|
-
console.log(
|
|
1329
|
+
console.log(import_picocolors8.default.yellow("Not in a GitHub repository."));
|
|
1167
1330
|
console.log();
|
|
1168
1331
|
console.log("Run chiefwiggum from inside a project directory:");
|
|
1169
|
-
console.log(
|
|
1332
|
+
console.log(import_picocolors8.default.cyan(" cd your-project && chiefwiggum new"));
|
|
1170
1333
|
console.log();
|
|
1171
1334
|
console.log("Or create a new repo here:");
|
|
1172
|
-
console.log(
|
|
1335
|
+
console.log(import_picocolors8.default.cyan(" gh repo create"));
|
|
1173
1336
|
}
|
|
1174
1337
|
console.log();
|
|
1175
1338
|
let exitHint = "Then run chiefwiggum new again";
|
|
@@ -1194,10 +1357,10 @@ async function setupProjectConfig() {
|
|
|
1194
1357
|
if (fallback === "exit") {
|
|
1195
1358
|
console.log();
|
|
1196
1359
|
if (exitCommand) {
|
|
1197
|
-
console.log(
|
|
1360
|
+
console.log(import_picocolors8.default.cyan(` ${exitCommand}`));
|
|
1198
1361
|
console.log();
|
|
1199
1362
|
}
|
|
1200
|
-
console.log(
|
|
1363
|
+
console.log(import_picocolors8.default.dim("Then run 'chiefwiggum new' again."));
|
|
1201
1364
|
process.exit(0);
|
|
1202
1365
|
}
|
|
1203
1366
|
config2.projectTracker = "todo";
|
|
@@ -1207,11 +1370,11 @@ async function setupProjectConfig() {
|
|
|
1207
1370
|
}
|
|
1208
1371
|
}
|
|
1209
1372
|
saveConfig(config2);
|
|
1210
|
-
console.log(
|
|
1373
|
+
console.log(import_picocolors8.default.green(`\u2713 Config saved to ${CONFIG_FILE}`));
|
|
1211
1374
|
if (config2.projectTracker === "github") {
|
|
1212
|
-
console.log(
|
|
1375
|
+
console.log(import_picocolors8.default.dim(" Tasks will be created as GitHub Issues."));
|
|
1213
1376
|
if (config2.githubProject) {
|
|
1214
|
-
console.log(
|
|
1377
|
+
console.log(import_picocolors8.default.dim(` Issues will be added to board: ${config2.githubProject}`));
|
|
1215
1378
|
}
|
|
1216
1379
|
}
|
|
1217
1380
|
console.log();
|
|
@@ -1220,18 +1383,18 @@ async function selectOrCreateProjectBoard(knownRepo) {
|
|
|
1220
1383
|
let repoName = knownRepo || "";
|
|
1221
1384
|
if (!repoName) {
|
|
1222
1385
|
try {
|
|
1223
|
-
repoName = (0,
|
|
1386
|
+
repoName = (0, import_node_child_process8.execSync)("gh repo view --json nameWithOwner -q .nameWithOwner", {
|
|
1224
1387
|
encoding: "utf-8",
|
|
1225
1388
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1226
1389
|
}).trim();
|
|
1227
1390
|
} catch {
|
|
1228
|
-
console.log(
|
|
1391
|
+
console.log(import_picocolors8.default.yellow("Could not detect GitHub repo. Skipping project board setup."));
|
|
1229
1392
|
return null;
|
|
1230
1393
|
}
|
|
1231
1394
|
}
|
|
1232
1395
|
let existingProjects = [];
|
|
1233
1396
|
try {
|
|
1234
|
-
const projectsOutput = (0,
|
|
1397
|
+
const projectsOutput = (0, import_node_child_process8.execSync)(`gh project list --owner ${repoName.split("/")[0]} --format json`, {
|
|
1235
1398
|
encoding: "utf-8",
|
|
1236
1399
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1237
1400
|
});
|
|
@@ -1240,8 +1403,8 @@ async function selectOrCreateProjectBoard(knownRepo) {
|
|
|
1240
1403
|
} catch {
|
|
1241
1404
|
}
|
|
1242
1405
|
console.log();
|
|
1243
|
-
console.log(
|
|
1244
|
-
console.log(
|
|
1406
|
+
console.log(import_picocolors8.default.bold(`GitHub Project Board`));
|
|
1407
|
+
console.log(import_picocolors8.default.dim(`Organize issues in a Kanban-style board for ${repoName}`));
|
|
1245
1408
|
console.log();
|
|
1246
1409
|
const projectOptions = [
|
|
1247
1410
|
...existingProjects.map((name) => ({ value: name, label: name })),
|
|
@@ -1257,44 +1420,44 @@ async function selectOrCreateProjectBoard(knownRepo) {
|
|
|
1257
1420
|
message: "Board name",
|
|
1258
1421
|
placeholder: "e.g., SVG Icon Generator v2"
|
|
1259
1422
|
});
|
|
1260
|
-
console.log(
|
|
1423
|
+
console.log(import_picocolors8.default.cyan(`Creating board "${projectName}"...`));
|
|
1261
1424
|
try {
|
|
1262
|
-
const createOutput = (0,
|
|
1425
|
+
const createOutput = (0, import_node_child_process8.execSync)(
|
|
1263
1426
|
`gh project create --owner ${repoName.split("/")[0]} --title "${projectName}"`,
|
|
1264
1427
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
1265
1428
|
);
|
|
1266
1429
|
const projectNumberMatch = createOutput.match(/projects\/(\d+)/);
|
|
1267
1430
|
const projectNumber = projectNumberMatch ? projectNumberMatch[1] : null;
|
|
1268
|
-
console.log(
|
|
1431
|
+
console.log(import_picocolors8.default.green(`\u2713 Board "${projectName}" created`));
|
|
1269
1432
|
if (projectNumber) {
|
|
1270
1433
|
try {
|
|
1271
|
-
(0,
|
|
1434
|
+
(0, import_node_child_process8.execSync)(`gh project link ${projectNumber} --owner ${repoName.split("/")[0]} --repo ${repoName}`, {
|
|
1272
1435
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1273
1436
|
});
|
|
1274
|
-
console.log(
|
|
1437
|
+
console.log(import_picocolors8.default.green(`\u2713 Linked to ${repoName}`));
|
|
1275
1438
|
} catch {
|
|
1276
|
-
console.log(
|
|
1439
|
+
console.log(import_picocolors8.default.dim(` Note: Board created at org level. Link manually from repo's Projects tab.`));
|
|
1277
1440
|
}
|
|
1278
1441
|
}
|
|
1279
1442
|
return projectName;
|
|
1280
1443
|
} catch {
|
|
1281
|
-
console.log(
|
|
1444
|
+
console.log(import_picocolors8.default.yellow("Could not create board. Issues will be created without one."));
|
|
1282
1445
|
return null;
|
|
1283
1446
|
}
|
|
1284
1447
|
} else if (selectedProject !== "__none__") {
|
|
1285
1448
|
try {
|
|
1286
|
-
const projectsOutput = (0,
|
|
1449
|
+
const projectsOutput = (0, import_node_child_process8.execSync)(
|
|
1287
1450
|
`gh project list --owner ${repoName.split("/")[0]} --format json`,
|
|
1288
1451
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
1289
1452
|
);
|
|
1290
1453
|
const projects = JSON.parse(projectsOutput);
|
|
1291
1454
|
const project = projects.projects?.find((p2) => p2.title === selectedProject);
|
|
1292
1455
|
if (project?.number) {
|
|
1293
|
-
(0,
|
|
1456
|
+
(0, import_node_child_process8.execSync)(
|
|
1294
1457
|
`gh project link ${project.number} --owner ${repoName.split("/")[0]} --repo ${repoName}`,
|
|
1295
1458
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
1296
1459
|
);
|
|
1297
|
-
console.log(
|
|
1460
|
+
console.log(import_picocolors8.default.green(`\u2713 Linked "${selectedProject}" to ${repoName}`));
|
|
1298
1461
|
}
|
|
1299
1462
|
} catch {
|
|
1300
1463
|
}
|
|
@@ -1304,7 +1467,7 @@ async function selectOrCreateProjectBoard(knownRepo) {
|
|
|
1304
1467
|
}
|
|
1305
1468
|
async function interactiveDescribe() {
|
|
1306
1469
|
console.log();
|
|
1307
|
-
console.log(
|
|
1470
|
+
console.log(import_picocolors8.default.bold("Tell me about your project"));
|
|
1308
1471
|
console.log();
|
|
1309
1472
|
const projectName = await text2({
|
|
1310
1473
|
message: "Project name"
|
|
@@ -1345,36 +1508,36 @@ ${techStack}
|
|
|
1345
1508
|
## Key Features
|
|
1346
1509
|
${features}
|
|
1347
1510
|
`;
|
|
1348
|
-
(0,
|
|
1511
|
+
(0, import_node_fs4.mkdirSync)("plans", { recursive: true });
|
|
1349
1512
|
const planPath = "plans/plan.md";
|
|
1350
|
-
(0,
|
|
1513
|
+
(0, import_node_fs4.writeFileSync)(planPath, planContent);
|
|
1351
1514
|
console.log();
|
|
1352
|
-
console.log(
|
|
1515
|
+
console.log(import_picocolors8.default.green(`Plan saved to ${planPath}`));
|
|
1353
1516
|
await generateFromPlan(planPath);
|
|
1354
1517
|
}
|
|
1355
1518
|
async function generateFromPlan(planFile) {
|
|
1356
|
-
const planContent = (0,
|
|
1519
|
+
const planContent = (0, import_node_fs4.readFileSync)(planFile, "utf-8");
|
|
1357
1520
|
const specsDir = config.specsDir;
|
|
1358
1521
|
const todoFile = config.todoFile;
|
|
1359
1522
|
const prdPath = `${specsDir}/prd.md`;
|
|
1360
1523
|
const techPath = `${specsDir}/technical.md`;
|
|
1361
1524
|
console.log();
|
|
1362
|
-
console.log(
|
|
1363
|
-
console.log(
|
|
1364
|
-
console.log(
|
|
1365
|
-
if (!(0,
|
|
1366
|
-
console.log(
|
|
1525
|
+
console.log(import_picocolors8.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
|
|
1526
|
+
console.log(import_picocolors8.default.green(` Generating specs from: ${planFile}`));
|
|
1527
|
+
console.log(import_picocolors8.default.green("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
|
|
1528
|
+
if (!(0, import_node_fs4.existsSync)(LOCAL_TEMPLATES_DIR)) {
|
|
1529
|
+
console.log(import_picocolors8.default.red(`Templates not found at: ${LOCAL_TEMPLATES_DIR}`));
|
|
1367
1530
|
console.log();
|
|
1368
1531
|
console.log("Run 'chiefwiggum new' to set up templates first.");
|
|
1369
1532
|
process.exit(1);
|
|
1370
1533
|
}
|
|
1371
|
-
(0,
|
|
1372
|
-
const prdTemplate = (0,
|
|
1373
|
-
const techTemplate = (0,
|
|
1374
|
-
const todoTemplate = (0,
|
|
1375
|
-
const claudeTemplate = (0,
|
|
1534
|
+
(0, import_node_fs4.mkdirSync)(specsDir, { recursive: true });
|
|
1535
|
+
const prdTemplate = (0, import_node_fs4.readFileSync)(`${LOCAL_TEMPLATES_DIR}/prd-template.md`, "utf-8");
|
|
1536
|
+
const techTemplate = (0, import_node_fs4.readFileSync)(`${LOCAL_TEMPLATES_DIR}/technical-template.md`, "utf-8");
|
|
1537
|
+
const todoTemplate = (0, import_node_fs4.readFileSync)(`${LOCAL_TEMPLATES_DIR}/TODO-template.md`, "utf-8");
|
|
1538
|
+
const claudeTemplate = (0, import_node_fs4.readFileSync)(`${LOCAL_TEMPLATES_DIR}/CLAUDE-template.md`, "utf-8");
|
|
1376
1539
|
console.log();
|
|
1377
|
-
console.log(
|
|
1540
|
+
console.log(import_picocolors8.default.yellow(`[1/4] Generating ${prdPath}...`));
|
|
1378
1541
|
const planFilename = planFile.split("/").pop() || planFile;
|
|
1379
1542
|
const prdPrompt = `You are filling in a PRD template based on a project plan.
|
|
1380
1543
|
|
|
@@ -1405,10 +1568,10 @@ For User Stories section:
|
|
|
1405
1568
|
Write the completed PRD directly to ${prdPath}.
|
|
1406
1569
|
Do NOT ask questions \u2014 infer everything from the plan.`;
|
|
1407
1570
|
await runClaude({ prompt: prdPrompt });
|
|
1408
|
-
console.log(
|
|
1571
|
+
console.log(import_picocolors8.default.green(` \u2713 ${prdPath}`));
|
|
1409
1572
|
console.log();
|
|
1410
|
-
console.log(
|
|
1411
|
-
const prdGenerated = (0,
|
|
1573
|
+
console.log(import_picocolors8.default.yellow(`[2/4] Generating ${techPath}...`));
|
|
1574
|
+
const prdGenerated = (0, import_node_fs4.existsSync)(prdPath) ? (0, import_node_fs4.readFileSync)(prdPath, "utf-8") : "";
|
|
1412
1575
|
const techPrompt = `You are filling in a Technical Specification template based on a PRD.
|
|
1413
1576
|
|
|
1414
1577
|
Here is the PRD:
|
|
@@ -1426,13 +1589,13 @@ Replace all placeholder text in [brackets] with real content.
|
|
|
1426
1589
|
Write the completed spec directly to ${techPath}.
|
|
1427
1590
|
Do NOT ask questions \u2014 infer everything from the PRD.`;
|
|
1428
1591
|
await runClaude({ prompt: techPrompt });
|
|
1429
|
-
console.log(
|
|
1592
|
+
console.log(import_picocolors8.default.green(` \u2713 ${techPath}`));
|
|
1430
1593
|
console.log();
|
|
1431
|
-
if ((0,
|
|
1432
|
-
console.log(
|
|
1594
|
+
if ((0, import_node_fs4.existsSync)("CLAUDE.md")) {
|
|
1595
|
+
console.log(import_picocolors8.default.yellow("[3/4] Skipping CLAUDE.md (already exists)"));
|
|
1433
1596
|
} else {
|
|
1434
|
-
console.log(
|
|
1435
|
-
const techGenerated2 = (0,
|
|
1597
|
+
console.log(import_picocolors8.default.yellow("[3/4] Generating CLAUDE.md..."));
|
|
1598
|
+
const techGenerated2 = (0, import_node_fs4.existsSync)(techPath) ? (0, import_node_fs4.readFileSync)(techPath, "utf-8") : "";
|
|
1436
1599
|
const claudePrompt = `You are filling in a CLAUDE.md template for a project.
|
|
1437
1600
|
|
|
1438
1601
|
Here is the PRD:
|
|
@@ -1455,28 +1618,28 @@ Replace all placeholder text in [brackets] with real content.
|
|
|
1455
1618
|
Keep it concise - this is a quick reference for AI agents.
|
|
1456
1619
|
Write directly to CLAUDE.md.`;
|
|
1457
1620
|
await runClaude({ prompt: claudePrompt });
|
|
1458
|
-
console.log(
|
|
1621
|
+
console.log(import_picocolors8.default.green(" \u2713 CLAUDE.md"));
|
|
1459
1622
|
}
|
|
1460
1623
|
const tracker = getProjectTracker();
|
|
1461
|
-
const techGenerated = (0,
|
|
1624
|
+
const techGenerated = (0, import_node_fs4.existsSync)(techPath) ? (0, import_node_fs4.readFileSync)(techPath, "utf-8") : "";
|
|
1462
1625
|
if (tracker === "github") {
|
|
1463
1626
|
console.log();
|
|
1464
1627
|
let repoName = "";
|
|
1465
1628
|
try {
|
|
1466
|
-
repoName = (0,
|
|
1629
|
+
repoName = (0, import_node_child_process8.execSync)("gh repo view --json nameWithOwner -q .nameWithOwner", {
|
|
1467
1630
|
encoding: "utf-8",
|
|
1468
1631
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1469
1632
|
}).trim();
|
|
1470
1633
|
} catch {
|
|
1471
|
-
console.log(
|
|
1634
|
+
console.log(import_picocolors8.default.red("Could not detect GitHub repo. Make sure you're in a git repo with a GitHub remote."));
|
|
1472
1635
|
process.exit(1);
|
|
1473
1636
|
}
|
|
1474
1637
|
const projectName = getGithubProject();
|
|
1475
|
-
console.log(
|
|
1638
|
+
console.log(import_picocolors8.default.yellow("[4/4] Creating GitHub Issues..."));
|
|
1476
1639
|
if (projectName) {
|
|
1477
|
-
console.log(
|
|
1640
|
+
console.log(import_picocolors8.default.dim(` Issues will be added to board: ${projectName}`));
|
|
1478
1641
|
}
|
|
1479
|
-
console.log(
|
|
1642
|
+
console.log(import_picocolors8.default.dim(" Planning issues..."));
|
|
1480
1643
|
const issuesPrompt = `You are planning GitHub Issues based on specs.
|
|
1481
1644
|
|
|
1482
1645
|
Here is the PRD:
|
|
@@ -1514,7 +1677,7 @@ Output ONLY valid JSON, no other text:
|
|
|
1514
1677
|
} catch {
|
|
1515
1678
|
}
|
|
1516
1679
|
if (issues.length === 0) {
|
|
1517
|
-
console.log(
|
|
1680
|
+
console.log(import_picocolors8.default.dim(" Generating issues directly..."));
|
|
1518
1681
|
const fallbackPrompt = `Create GitHub Issues using gh CLI for this project.
|
|
1519
1682
|
PRD: ${prdGenerated.slice(0, 2e3)}
|
|
1520
1683
|
Tech: ${techGenerated.slice(0, 2e3)}
|
|
@@ -1527,17 +1690,17 @@ Create 5-15 issues.${projectName ? ` Add each to project "${projectName}".` : ""
|
|
|
1527
1690
|
const total = issues.length;
|
|
1528
1691
|
const owner = repoName.split("/")[0];
|
|
1529
1692
|
try {
|
|
1530
|
-
(0,
|
|
1693
|
+
(0, import_node_child_process8.execSync)(
|
|
1531
1694
|
`gh label create chiefwiggum --description "Created by Chief Wiggum CLI" --color "FFA500" 2>/dev/null || true`,
|
|
1532
1695
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
1533
1696
|
);
|
|
1534
|
-
(0,
|
|
1697
|
+
(0, import_node_child_process8.execSync)(
|
|
1535
1698
|
`gh label create epic --description "Parent tracking issue" --color "6366F1" 2>/dev/null || true`,
|
|
1536
1699
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
1537
1700
|
);
|
|
1538
1701
|
} catch {
|
|
1539
1702
|
}
|
|
1540
|
-
console.log(
|
|
1703
|
+
console.log(import_picocolors8.default.dim(" Creating epic from PRD..."));
|
|
1541
1704
|
const prdFrontmatter = parsePRDFrontmatter(prdGenerated);
|
|
1542
1705
|
let epicTitle;
|
|
1543
1706
|
if (prdFrontmatter?.project) {
|
|
@@ -1557,7 +1720,7 @@ _Creating issues..._
|
|
|
1557
1720
|
let epicNumber = null;
|
|
1558
1721
|
let epicUrl = null;
|
|
1559
1722
|
try {
|
|
1560
|
-
const result2 = (0,
|
|
1723
|
+
const result2 = (0, import_node_child_process8.spawnSync)("gh", [
|
|
1561
1724
|
"issue",
|
|
1562
1725
|
"create",
|
|
1563
1726
|
"--title",
|
|
@@ -1577,23 +1740,23 @@ _Creating issues..._
|
|
|
1577
1740
|
const epicNumMatch = epicUrl.match(/\/issues\/(\d+)$/);
|
|
1578
1741
|
epicNumber = epicNumMatch ? parseInt(epicNumMatch[1], 10) : null;
|
|
1579
1742
|
if (epicNumber) {
|
|
1580
|
-
console.log(
|
|
1743
|
+
console.log(import_picocolors8.default.green(` \u2713 Created epic #${epicNumber}`));
|
|
1581
1744
|
}
|
|
1582
1745
|
} catch (err) {
|
|
1583
|
-
console.log(
|
|
1746
|
+
console.log(import_picocolors8.default.yellow(" Could not create epic issue. Continuing without parent tracking..."));
|
|
1584
1747
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1585
1748
|
if (errMsg) {
|
|
1586
|
-
console.log(
|
|
1749
|
+
console.log(import_picocolors8.default.dim(` ${errMsg.slice(0, 100)}`));
|
|
1587
1750
|
}
|
|
1588
1751
|
}
|
|
1589
|
-
console.log(
|
|
1752
|
+
console.log(import_picocolors8.default.dim(` Creating ${total} child issues...`));
|
|
1590
1753
|
let projectNumber = null;
|
|
1591
1754
|
let projectId = null;
|
|
1592
1755
|
let statusFieldId = null;
|
|
1593
1756
|
let todoOptionId = null;
|
|
1594
1757
|
if (projectName) {
|
|
1595
1758
|
try {
|
|
1596
|
-
const projectsOutput = (0,
|
|
1759
|
+
const projectsOutput = (0, import_node_child_process8.execSync)(
|
|
1597
1760
|
`gh project list --owner ${owner} --format json`,
|
|
1598
1761
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
1599
1762
|
);
|
|
@@ -1602,14 +1765,14 @@ _Creating issues..._
|
|
|
1602
1765
|
projectNumber = project?.number?.toString() || null;
|
|
1603
1766
|
if (projectNumber) {
|
|
1604
1767
|
try {
|
|
1605
|
-
(0,
|
|
1768
|
+
(0, import_node_child_process8.execSync)(
|
|
1606
1769
|
`gh project link ${projectNumber} --owner ${owner} --repo ${repoName}`,
|
|
1607
1770
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
1608
1771
|
);
|
|
1609
1772
|
} catch {
|
|
1610
1773
|
}
|
|
1611
1774
|
try {
|
|
1612
|
-
const repoProjectsOutput = (0,
|
|
1775
|
+
const repoProjectsOutput = (0, import_node_child_process8.execSync)(
|
|
1613
1776
|
`gh repo view --json projectsV2 -q '.projectsV2.Nodes[] | select(.number == ${projectNumber}) | .id'`,
|
|
1614
1777
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
1615
1778
|
);
|
|
@@ -1618,7 +1781,7 @@ _Creating issues..._
|
|
|
1618
1781
|
}
|
|
1619
1782
|
if (projectId) {
|
|
1620
1783
|
try {
|
|
1621
|
-
const fieldsOutput = (0,
|
|
1784
|
+
const fieldsOutput = (0, import_node_child_process8.execSync)(
|
|
1622
1785
|
`gh project field-list ${projectNumber} --owner ${owner} --format json`,
|
|
1623
1786
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
1624
1787
|
);
|
|
@@ -1638,7 +1801,7 @@ _Creating issues..._
|
|
|
1638
1801
|
}
|
|
1639
1802
|
if (epicUrl && projectNumber) {
|
|
1640
1803
|
try {
|
|
1641
|
-
(0,
|
|
1804
|
+
(0, import_node_child_process8.execSync)(
|
|
1642
1805
|
`gh project item-add ${projectNumber} --owner ${owner} --url "${epicUrl}"`,
|
|
1643
1806
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
1644
1807
|
);
|
|
@@ -1647,7 +1810,7 @@ _Creating issues..._
|
|
|
1647
1810
|
}
|
|
1648
1811
|
let availableLabels = /* @__PURE__ */ new Set();
|
|
1649
1812
|
try {
|
|
1650
|
-
const labelsOutput = (0,
|
|
1813
|
+
const labelsOutput = (0, import_node_child_process8.execSync)("gh label list --json name --jq '.[].name'", {
|
|
1651
1814
|
encoding: "utf-8",
|
|
1652
1815
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1653
1816
|
});
|
|
@@ -1667,12 +1830,12 @@ _Creating issues..._
|
|
|
1667
1830
|
const barWidth = 20;
|
|
1668
1831
|
const filled = Math.round(progress / 100 * barWidth);
|
|
1669
1832
|
const empty = barWidth - filled;
|
|
1670
|
-
const bar =
|
|
1833
|
+
const bar = import_picocolors8.default.green("\u2588".repeat(filled)) + import_picocolors8.default.dim("\u2591".repeat(empty));
|
|
1671
1834
|
process.stdout.write(`\r ${bar} ${progress}% (${i + 1}/${total}) Creating: ${issue.title.slice(0, 40)}...`);
|
|
1672
1835
|
} else {
|
|
1673
1836
|
const milestone = Math.floor(progress / 25) * 25;
|
|
1674
1837
|
if (milestone > 0 && !loggedMilestones.has(milestone) && milestone < 100) {
|
|
1675
|
-
console.log(
|
|
1838
|
+
console.log(import_picocolors8.default.dim(` ${milestone}% complete (${i + 1}/${total})...`));
|
|
1676
1839
|
loggedMilestones.add(milestone);
|
|
1677
1840
|
}
|
|
1678
1841
|
}
|
|
@@ -1687,7 +1850,7 @@ _Creating issues..._
|
|
|
1687
1850
|
|
|
1688
1851
|
${issue.body}`;
|
|
1689
1852
|
}
|
|
1690
|
-
const createResult = (0,
|
|
1853
|
+
const createResult = (0, import_node_child_process8.spawnSync)("gh", [
|
|
1691
1854
|
"issue",
|
|
1692
1855
|
"create",
|
|
1693
1856
|
"--title",
|
|
@@ -1710,7 +1873,7 @@ ${issue.body}`;
|
|
|
1710
1873
|
}
|
|
1711
1874
|
if (projectNumber && issueUrl) {
|
|
1712
1875
|
try {
|
|
1713
|
-
const addOutput = (0,
|
|
1876
|
+
const addOutput = (0, import_node_child_process8.execSync)(
|
|
1714
1877
|
`gh project item-add ${projectNumber} --owner ${owner} --url "${issueUrl}" --format json`,
|
|
1715
1878
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
1716
1879
|
);
|
|
@@ -1719,7 +1882,7 @@ ${issue.body}`;
|
|
|
1719
1882
|
const addResult = JSON.parse(addOutput);
|
|
1720
1883
|
const itemId = addResult.id;
|
|
1721
1884
|
if (itemId) {
|
|
1722
|
-
(0,
|
|
1885
|
+
(0, import_node_child_process8.execSync)(
|
|
1723
1886
|
`gh project item-edit --project-id ${projectId} --id ${itemId} --field-id ${statusFieldId} --single-select-option-id ${todoOptionId}`,
|
|
1724
1887
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
1725
1888
|
);
|
|
@@ -1740,22 +1903,22 @@ ${issue.body}`;
|
|
|
1740
1903
|
if (supportsInPlace) {
|
|
1741
1904
|
process.stdout.write("\r" + " ".repeat(80) + "\r");
|
|
1742
1905
|
}
|
|
1743
|
-
console.log(
|
|
1906
|
+
console.log(import_picocolors8.default.green(` \u2713 Created ${created} GitHub Issues`));
|
|
1744
1907
|
if (failed > 0) {
|
|
1745
|
-
console.log(
|
|
1908
|
+
console.log(import_picocolors8.default.yellow(` \u26A0 ${failed} issues failed to create:`));
|
|
1746
1909
|
for (const f of failedIssues) {
|
|
1747
|
-
console.log(
|
|
1910
|
+
console.log(import_picocolors8.default.dim(` - ${f.title.slice(0, 50)}`));
|
|
1748
1911
|
const errMatch = f.error.match(/stderr: "([^"]+)"/);
|
|
1749
1912
|
if (errMatch) {
|
|
1750
|
-
console.log(
|
|
1913
|
+
console.log(import_picocolors8.default.dim(` ${errMatch[1]}`));
|
|
1751
1914
|
}
|
|
1752
1915
|
}
|
|
1753
1916
|
}
|
|
1754
1917
|
if (projectName && projectNumber) {
|
|
1755
1918
|
if (todoOptionId) {
|
|
1756
|
-
console.log(
|
|
1919
|
+
console.log(import_picocolors8.default.green(` \u2713 Added to board "${projectName}" (Todo column)`));
|
|
1757
1920
|
} else {
|
|
1758
|
-
console.log(
|
|
1921
|
+
console.log(import_picocolors8.default.green(` \u2713 Added to board "${projectName}"`));
|
|
1759
1922
|
}
|
|
1760
1923
|
}
|
|
1761
1924
|
if (epicNumber && createdIssueNumbers.length > 0) {
|
|
@@ -1768,7 +1931,7 @@ ${issue.body}`;
|
|
|
1768
1931
|
|
|
1769
1932
|
${tasklist}`;
|
|
1770
1933
|
try {
|
|
1771
|
-
const editResult = (0,
|
|
1934
|
+
const editResult = (0, import_node_child_process8.spawnSync)("gh", [
|
|
1772
1935
|
"issue",
|
|
1773
1936
|
"edit",
|
|
1774
1937
|
String(epicNumber),
|
|
@@ -1779,26 +1942,26 @@ ${tasklist}`;
|
|
|
1779
1942
|
stdio: ["pipe", "pipe", "pipe"]
|
|
1780
1943
|
});
|
|
1781
1944
|
if (editResult.status === 0) {
|
|
1782
|
-
console.log(
|
|
1945
|
+
console.log(import_picocolors8.default.green(` \u2713 Updated epic #${epicNumber} with tasklist`));
|
|
1783
1946
|
} else {
|
|
1784
1947
|
throw new Error(editResult.stderr || "Failed");
|
|
1785
1948
|
}
|
|
1786
1949
|
} catch {
|
|
1787
|
-
console.log(
|
|
1950
|
+
console.log(import_picocolors8.default.yellow(` Could not update epic with tasklist`));
|
|
1788
1951
|
}
|
|
1789
1952
|
}
|
|
1790
1953
|
} else {
|
|
1791
|
-
console.log(
|
|
1954
|
+
console.log(import_picocolors8.default.green(" \u2713 GitHub Issues created"));
|
|
1792
1955
|
if (projectName) {
|
|
1793
|
-
console.log(
|
|
1956
|
+
console.log(import_picocolors8.default.green(` \u2713 Added to board "${projectName}"`));
|
|
1794
1957
|
}
|
|
1795
1958
|
}
|
|
1796
1959
|
} else {
|
|
1797
1960
|
console.log();
|
|
1798
|
-
console.log(
|
|
1961
|
+
console.log(import_picocolors8.default.yellow(`[4/4] Generating ${todoFile}...`));
|
|
1799
1962
|
const todoDir = (0, import_node_path2.dirname)(todoFile);
|
|
1800
1963
|
if (todoDir !== ".") {
|
|
1801
|
-
(0,
|
|
1964
|
+
(0, import_node_fs4.mkdirSync)(todoDir, { recursive: true });
|
|
1802
1965
|
}
|
|
1803
1966
|
const todoPrompt = `You are filling in a TODO template based on specs.
|
|
1804
1967
|
|
|
@@ -1823,13 +1986,13 @@ Keep tasks granular (1-2 hours max each).
|
|
|
1823
1986
|
End each phase with: - [ ] Phase N review
|
|
1824
1987
|
Write directly to ${todoFile}.`;
|
|
1825
1988
|
await runClaude({ prompt: todoPrompt });
|
|
1826
|
-
console.log(
|
|
1989
|
+
console.log(import_picocolors8.default.green(` \u2713 ${todoFile}`));
|
|
1827
1990
|
}
|
|
1828
1991
|
console.log();
|
|
1829
|
-
console.log(
|
|
1992
|
+
console.log(import_picocolors8.default.green("Specs generated:"));
|
|
1830
1993
|
console.log(` - ${prdPath}`);
|
|
1831
1994
|
console.log(` - ${techPath}`);
|
|
1832
|
-
if (!(0,
|
|
1995
|
+
if (!(0, import_node_fs4.existsSync)("CLAUDE.md")) {
|
|
1833
1996
|
console.log(" - CLAUDE.md");
|
|
1834
1997
|
}
|
|
1835
1998
|
if (tracker === "github") {
|
|
@@ -1838,25 +2001,25 @@ Write directly to ${todoFile}.`;
|
|
|
1838
2001
|
console.log(` - ${todoFile}`);
|
|
1839
2002
|
}
|
|
1840
2003
|
try {
|
|
1841
|
-
(0,
|
|
2004
|
+
(0, import_node_child_process8.execSync)("git rev-parse --git-dir", { stdio: "pipe" });
|
|
1842
2005
|
const filesToAdd = tracker === "github" ? `${specsDir}/ CLAUDE.md` : `${specsDir}/ ${todoFile} CLAUDE.md`;
|
|
1843
|
-
(0,
|
|
1844
|
-
(0,
|
|
2006
|
+
(0, import_node_child_process8.execSync)(`git add ${filesToAdd} 2>/dev/null || true`, { stdio: "pipe" });
|
|
2007
|
+
(0, import_node_child_process8.execSync)('git commit -m "chore: generate specs from plan" 2>/dev/null || true', {
|
|
1845
2008
|
stdio: "pipe"
|
|
1846
2009
|
});
|
|
1847
2010
|
} catch {
|
|
1848
2011
|
}
|
|
1849
2012
|
}
|
|
1850
|
-
var
|
|
2013
|
+
var import_node_fs4, import_node_child_process8, import_node_path2, import_node_url, import_picocolors8, __dirname, LOCAL_TEMPLATES_DIR, CONFIG_FILE, BUNDLED_TEMPLATES_DIR, SKIP_DIRS;
|
|
1851
2014
|
var init_new = __esm({
|
|
1852
2015
|
"src/commands/new.ts"() {
|
|
1853
2016
|
"use strict";
|
|
1854
2017
|
init_cjs_shims();
|
|
1855
|
-
|
|
1856
|
-
|
|
2018
|
+
import_node_fs4 = require("fs");
|
|
2019
|
+
import_node_child_process8 = require("child_process");
|
|
1857
2020
|
import_node_path2 = require("path");
|
|
1858
2021
|
import_node_url = require("url");
|
|
1859
|
-
|
|
2022
|
+
import_picocolors8 = __toESM(require("picocolors"), 1);
|
|
1860
2023
|
init_config();
|
|
1861
2024
|
init_prompts();
|
|
1862
2025
|
init_claude();
|
|
@@ -1881,8 +2044,8 @@ var init_new = __esm({
|
|
|
1881
2044
|
// src/cli.ts
|
|
1882
2045
|
init_cjs_shims();
|
|
1883
2046
|
var import_commander = require("commander");
|
|
1884
|
-
var
|
|
1885
|
-
var
|
|
2047
|
+
var import_picocolors11 = __toESM(require("picocolors"), 1);
|
|
2048
|
+
var import_node_fs7 = require("fs");
|
|
1886
2049
|
var import_node_path3 = require("path");
|
|
1887
2050
|
var import_node_url2 = require("url");
|
|
1888
2051
|
|
|
@@ -2017,9 +2180,9 @@ init_loop();
|
|
|
2017
2180
|
|
|
2018
2181
|
// src/commands/clean.ts
|
|
2019
2182
|
init_cjs_shims();
|
|
2020
|
-
var
|
|
2021
|
-
var
|
|
2022
|
-
var
|
|
2183
|
+
var import_node_fs5 = require("fs");
|
|
2184
|
+
var import_node_child_process9 = require("child_process");
|
|
2185
|
+
var import_picocolors9 = __toESM(require("picocolors"), 1);
|
|
2023
2186
|
init_prompts();
|
|
2024
2187
|
init_new();
|
|
2025
2188
|
async function removeIssues(numbers, action) {
|
|
@@ -2028,14 +2191,14 @@ async function removeIssues(numbers, action) {
|
|
|
2028
2191
|
for (const num of numbers) {
|
|
2029
2192
|
try {
|
|
2030
2193
|
if (action === "delete") {
|
|
2031
|
-
(0,
|
|
2194
|
+
(0, import_node_child_process9.execSync)(`gh issue delete ${num} --yes`, { stdio: ["pipe", "pipe", "pipe"] });
|
|
2032
2195
|
} else {
|
|
2033
|
-
(0,
|
|
2196
|
+
(0, import_node_child_process9.execSync)(`gh issue close ${num}`, { stdio: ["pipe", "pipe", "pipe"] });
|
|
2034
2197
|
}
|
|
2035
|
-
console.log(
|
|
2198
|
+
console.log(import_picocolors9.default.green(`\u2713 #${num} ${action === "delete" ? "deleted" : "closed"}`));
|
|
2036
2199
|
success++;
|
|
2037
2200
|
} catch {
|
|
2038
|
-
console.log(
|
|
2201
|
+
console.log(import_picocolors9.default.red(`\u2717 #${num} failed`));
|
|
2039
2202
|
failed++;
|
|
2040
2203
|
}
|
|
2041
2204
|
}
|
|
@@ -2045,17 +2208,17 @@ async function removeIssues(numbers, action) {
|
|
|
2045
2208
|
async function cleanGitHubIssues() {
|
|
2046
2209
|
let issues = [];
|
|
2047
2210
|
try {
|
|
2048
|
-
const output = (0,
|
|
2211
|
+
const output = (0, import_node_child_process9.execSync)(
|
|
2049
2212
|
`gh issue list --label chiefwiggum --state open --json number,title,createdAt,labels --limit 100`,
|
|
2050
2213
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
2051
2214
|
);
|
|
2052
2215
|
issues = JSON.parse(output);
|
|
2053
2216
|
} catch {
|
|
2054
|
-
console.log(
|
|
2217
|
+
console.log(import_picocolors9.default.yellow("Could not fetch GitHub issues."));
|
|
2055
2218
|
return;
|
|
2056
2219
|
}
|
|
2057
2220
|
if (issues.length === 0) {
|
|
2058
|
-
console.log(
|
|
2221
|
+
console.log(import_picocolors9.default.dim("No open issues with 'chiefwiggum' label found."));
|
|
2059
2222
|
return;
|
|
2060
2223
|
}
|
|
2061
2224
|
const isEpic = (i) => i.labels.some((l) => l.name === "epic");
|
|
@@ -2064,15 +2227,15 @@ async function cleanGitHubIssues() {
|
|
|
2064
2227
|
console.log();
|
|
2065
2228
|
console.log(`Found ${issues.length} issues with 'chiefwiggum' label:`);
|
|
2066
2229
|
if (epics.length > 0) {
|
|
2067
|
-
console.log(
|
|
2230
|
+
console.log(import_picocolors9.default.dim(" Epics:"));
|
|
2068
2231
|
for (const issue of epics) {
|
|
2069
|
-
console.log(` ${
|
|
2232
|
+
console.log(` ${import_picocolors9.default.magenta(`#${issue.number}`)} ${import_picocolors9.default.bold("\u{1F4CB}")} ${issue.title}`);
|
|
2070
2233
|
}
|
|
2071
2234
|
}
|
|
2072
2235
|
if (childIssues.length > 0) {
|
|
2073
|
-
console.log(
|
|
2236
|
+
console.log(import_picocolors9.default.dim(" Tasks:"));
|
|
2074
2237
|
for (const issue of childIssues) {
|
|
2075
|
-
console.log(` ${
|
|
2238
|
+
console.log(` ${import_picocolors9.default.cyan(`#${issue.number}`)} - ${issue.title}`);
|
|
2076
2239
|
}
|
|
2077
2240
|
}
|
|
2078
2241
|
console.log();
|
|
@@ -2098,7 +2261,7 @@ async function cleanGitHubIssues() {
|
|
|
2098
2261
|
}))
|
|
2099
2262
|
});
|
|
2100
2263
|
if (selected.length === 0) {
|
|
2101
|
-
console.log(
|
|
2264
|
+
console.log(import_picocolors9.default.yellow("No issues selected."));
|
|
2102
2265
|
return;
|
|
2103
2266
|
}
|
|
2104
2267
|
const pickAction = await select2({
|
|
@@ -2122,7 +2285,7 @@ async function cleanGitHubIssues() {
|
|
|
2122
2285
|
}
|
|
2123
2286
|
}
|
|
2124
2287
|
async function cmdClean() {
|
|
2125
|
-
console.log(
|
|
2288
|
+
console.log(import_picocolors9.default.bold("Clean Project"));
|
|
2126
2289
|
console.log();
|
|
2127
2290
|
const tracker = getProjectTracker();
|
|
2128
2291
|
if (tracker === "github") {
|
|
@@ -2136,16 +2299,16 @@ async function cmdClean() {
|
|
|
2136
2299
|
console.log();
|
|
2137
2300
|
}
|
|
2138
2301
|
const filesToRemove = [];
|
|
2139
|
-
if ((0,
|
|
2140
|
-
if ((0,
|
|
2141
|
-
if ((0,
|
|
2302
|
+
if ((0, import_node_fs5.existsSync)(".chiefwiggum")) filesToRemove.push(".chiefwiggum/");
|
|
2303
|
+
if ((0, import_node_fs5.existsSync)("specs")) filesToRemove.push("specs/");
|
|
2304
|
+
if ((0, import_node_fs5.existsSync)("TODO.md")) filesToRemove.push("TODO.md");
|
|
2142
2305
|
if (filesToRemove.length === 0) {
|
|
2143
|
-
console.log(
|
|
2306
|
+
console.log(import_picocolors9.default.dim("No chiefwiggum files found to clean."));
|
|
2144
2307
|
return;
|
|
2145
2308
|
}
|
|
2146
2309
|
console.log("Files to remove:");
|
|
2147
2310
|
for (const f of filesToRemove) {
|
|
2148
|
-
console.log(` ${
|
|
2311
|
+
console.log(` ${import_picocolors9.default.red(f)}`);
|
|
2149
2312
|
}
|
|
2150
2313
|
console.log();
|
|
2151
2314
|
const shouldClean = await confirm2({
|
|
@@ -2153,65 +2316,65 @@ async function cmdClean() {
|
|
|
2153
2316
|
initialValue: false
|
|
2154
2317
|
});
|
|
2155
2318
|
if (!shouldClean) {
|
|
2156
|
-
console.log(
|
|
2319
|
+
console.log(import_picocolors9.default.yellow("Aborted."));
|
|
2157
2320
|
return;
|
|
2158
2321
|
}
|
|
2159
2322
|
for (const f of filesToRemove) {
|
|
2160
2323
|
const path = f.replace(/\/$/, "");
|
|
2161
|
-
(0,
|
|
2162
|
-
console.log(
|
|
2324
|
+
(0, import_node_fs5.rmSync)(path, { recursive: true, force: true });
|
|
2325
|
+
console.log(import_picocolors9.default.red(`\u2717 Removed ${f}`));
|
|
2163
2326
|
}
|
|
2164
2327
|
console.log();
|
|
2165
|
-
console.log(
|
|
2328
|
+
console.log(import_picocolors9.default.green("Clean complete."));
|
|
2166
2329
|
}
|
|
2167
2330
|
|
|
2168
2331
|
// src/commands/config.ts
|
|
2169
2332
|
init_cjs_shims();
|
|
2170
|
-
var
|
|
2171
|
-
var
|
|
2172
|
-
var
|
|
2333
|
+
var import_node_fs6 = require("fs");
|
|
2334
|
+
var import_node_child_process10 = require("child_process");
|
|
2335
|
+
var import_picocolors10 = __toESM(require("picocolors"), 1);
|
|
2173
2336
|
init_prompts();
|
|
2174
2337
|
var CONFIG_FILE2 = ".chiefwiggum/CLAUDE.md";
|
|
2175
2338
|
function stripTrailingCommas2(json) {
|
|
2176
2339
|
return json.replace(/,(\s*[}\]])/g, "$1");
|
|
2177
2340
|
}
|
|
2178
2341
|
function getConfig2() {
|
|
2179
|
-
if (!(0,
|
|
2342
|
+
if (!(0, import_node_fs6.existsSync)(CONFIG_FILE2)) {
|
|
2180
2343
|
return { projectTracker: "todo" };
|
|
2181
2344
|
}
|
|
2182
|
-
const raw = (0,
|
|
2345
|
+
const raw = (0, import_node_fs6.readFileSync)(CONFIG_FILE2, "utf-8");
|
|
2183
2346
|
try {
|
|
2184
2347
|
return JSON.parse(raw);
|
|
2185
2348
|
} catch {
|
|
2186
2349
|
try {
|
|
2187
2350
|
const fixed = stripTrailingCommas2(raw);
|
|
2188
2351
|
const config2 = JSON.parse(fixed);
|
|
2189
|
-
console.log(
|
|
2352
|
+
console.log(import_picocolors10.default.yellow("Note: Fixed trailing comma in config file."));
|
|
2190
2353
|
saveConfig2(config2);
|
|
2191
2354
|
return config2;
|
|
2192
2355
|
} catch {
|
|
2193
|
-
console.log(
|
|
2356
|
+
console.log(import_picocolors10.default.yellow("Warning: Could not parse config file. Using defaults."));
|
|
2194
2357
|
return { projectTracker: "todo" };
|
|
2195
2358
|
}
|
|
2196
2359
|
}
|
|
2197
2360
|
}
|
|
2198
2361
|
function saveConfig2(config2) {
|
|
2199
|
-
(0,
|
|
2200
|
-
(0,
|
|
2362
|
+
(0, import_node_fs6.mkdirSync)(".chiefwiggum", { recursive: true });
|
|
2363
|
+
(0, import_node_fs6.writeFileSync)(CONFIG_FILE2, JSON.stringify(config2, null, 2) + "\n");
|
|
2201
2364
|
}
|
|
2202
2365
|
function checkGitHubCLI2() {
|
|
2203
2366
|
try {
|
|
2204
|
-
(0,
|
|
2367
|
+
(0, import_node_child_process10.execSync)("which gh", { stdio: ["pipe", "pipe", "pipe"] });
|
|
2205
2368
|
} catch {
|
|
2206
2369
|
return { ok: false, reason: "not_installed" };
|
|
2207
2370
|
}
|
|
2208
2371
|
try {
|
|
2209
|
-
(0,
|
|
2372
|
+
(0, import_node_child_process10.execSync)("gh auth status", { stdio: ["pipe", "pipe", "pipe"] });
|
|
2210
2373
|
} catch {
|
|
2211
2374
|
return { ok: false, reason: "not_authenticated" };
|
|
2212
2375
|
}
|
|
2213
2376
|
try {
|
|
2214
|
-
const repo = (0,
|
|
2377
|
+
const repo = (0, import_node_child_process10.execSync)("gh repo view --json nameWithOwner -q .nameWithOwner", {
|
|
2215
2378
|
encoding: "utf-8",
|
|
2216
2379
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2217
2380
|
}).trim();
|
|
@@ -2221,12 +2384,12 @@ function checkGitHubCLI2() {
|
|
|
2221
2384
|
}
|
|
2222
2385
|
}
|
|
2223
2386
|
async function cmdConfig() {
|
|
2224
|
-
console.log(
|
|
2387
|
+
console.log(import_picocolors10.default.bold("Configuration"));
|
|
2225
2388
|
console.log();
|
|
2226
2389
|
const config2 = getConfig2();
|
|
2227
|
-
console.log(`Current tracker: ${
|
|
2390
|
+
console.log(`Current tracker: ${import_picocolors10.default.cyan(config2.projectTracker)}`);
|
|
2228
2391
|
if (config2.projectTracker === "github") {
|
|
2229
|
-
console.log(`GitHub Project Board: ${
|
|
2392
|
+
console.log(`GitHub Project Board: ${import_picocolors10.default.cyan(config2.githubProject || "(none)")}`);
|
|
2230
2393
|
}
|
|
2231
2394
|
console.log();
|
|
2232
2395
|
const newTracker = await select2({
|
|
@@ -2243,24 +2406,24 @@ async function cmdConfig() {
|
|
|
2243
2406
|
if (!ghCheck.ok) {
|
|
2244
2407
|
console.log();
|
|
2245
2408
|
if (ghCheck.reason === "not_installed") {
|
|
2246
|
-
console.log(
|
|
2409
|
+
console.log(import_picocolors10.default.yellow("GitHub CLI (gh) is not installed."));
|
|
2247
2410
|
console.log();
|
|
2248
2411
|
console.log("To use GitHub Issues, install it first:");
|
|
2249
|
-
console.log(
|
|
2250
|
-
console.log(
|
|
2251
|
-
console.log(
|
|
2412
|
+
console.log(import_picocolors10.default.cyan(" brew install gh # macOS"));
|
|
2413
|
+
console.log(import_picocolors10.default.cyan(" sudo apt install gh # Ubuntu/Debian"));
|
|
2414
|
+
console.log(import_picocolors10.default.dim(" https://cli.github.com for other platforms"));
|
|
2252
2415
|
} else if (ghCheck.reason === "not_authenticated") {
|
|
2253
|
-
console.log(
|
|
2416
|
+
console.log(import_picocolors10.default.yellow("GitHub CLI is not authenticated."));
|
|
2254
2417
|
console.log();
|
|
2255
2418
|
console.log("Run this command to log in:");
|
|
2256
|
-
console.log(
|
|
2419
|
+
console.log(import_picocolors10.default.cyan(" gh auth login"));
|
|
2257
2420
|
} else if (ghCheck.reason === "not_github_repo") {
|
|
2258
|
-
console.log(
|
|
2421
|
+
console.log(import_picocolors10.default.yellow("This doesn't appear to be a GitHub repository."));
|
|
2259
2422
|
console.log();
|
|
2260
2423
|
console.log("Make sure you're in a git repo with a GitHub remote.");
|
|
2261
2424
|
}
|
|
2262
2425
|
console.log();
|
|
2263
|
-
console.log(
|
|
2426
|
+
console.log(import_picocolors10.default.dim("Cannot switch to GitHub Issues. Keeping current tracker."));
|
|
2264
2427
|
return;
|
|
2265
2428
|
}
|
|
2266
2429
|
config2.projectTracker = "github";
|
|
@@ -2287,32 +2450,32 @@ async function cmdConfig() {
|
|
|
2287
2450
|
if (changed) {
|
|
2288
2451
|
saveConfig2(config2);
|
|
2289
2452
|
console.log();
|
|
2290
|
-
console.log(
|
|
2291
|
-
console.log(
|
|
2453
|
+
console.log(import_picocolors10.default.green(`\u2713 Config updated`));
|
|
2454
|
+
console.log(import_picocolors10.default.dim(` Tracker: ${config2.projectTracker}`));
|
|
2292
2455
|
if (config2.projectTracker === "github" && config2.githubProject) {
|
|
2293
|
-
console.log(
|
|
2456
|
+
console.log(import_picocolors10.default.dim(` Project board: ${config2.githubProject}`));
|
|
2294
2457
|
}
|
|
2295
2458
|
} else {
|
|
2296
2459
|
console.log();
|
|
2297
|
-
console.log(
|
|
2460
|
+
console.log(import_picocolors10.default.dim("No changes made."));
|
|
2298
2461
|
}
|
|
2299
2462
|
}
|
|
2300
2463
|
async function selectOrCreateProjectBoard2(knownRepo) {
|
|
2301
2464
|
let repoName = knownRepo || "";
|
|
2302
2465
|
if (!repoName) {
|
|
2303
2466
|
try {
|
|
2304
|
-
repoName = (0,
|
|
2467
|
+
repoName = (0, import_node_child_process10.execSync)("gh repo view --json nameWithOwner -q .nameWithOwner", {
|
|
2305
2468
|
encoding: "utf-8",
|
|
2306
2469
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2307
2470
|
}).trim();
|
|
2308
2471
|
} catch {
|
|
2309
|
-
console.log(
|
|
2472
|
+
console.log(import_picocolors10.default.yellow("Could not detect GitHub repo. Skipping project board setup."));
|
|
2310
2473
|
return null;
|
|
2311
2474
|
}
|
|
2312
2475
|
}
|
|
2313
2476
|
let existingProjects = [];
|
|
2314
2477
|
try {
|
|
2315
|
-
const projectsOutput = (0,
|
|
2478
|
+
const projectsOutput = (0, import_node_child_process10.execSync)(`gh project list --owner ${repoName.split("/")[0]} --format json`, {
|
|
2316
2479
|
encoding: "utf-8",
|
|
2317
2480
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2318
2481
|
});
|
|
@@ -2321,8 +2484,8 @@ async function selectOrCreateProjectBoard2(knownRepo) {
|
|
|
2321
2484
|
} catch {
|
|
2322
2485
|
}
|
|
2323
2486
|
console.log();
|
|
2324
|
-
console.log(
|
|
2325
|
-
console.log(
|
|
2487
|
+
console.log(import_picocolors10.default.bold(`GitHub Project Board`));
|
|
2488
|
+
console.log(import_picocolors10.default.dim(`Organize issues in a Kanban-style board for ${repoName}`));
|
|
2326
2489
|
console.log();
|
|
2327
2490
|
const projectOptions = [
|
|
2328
2491
|
...existingProjects.map((name) => ({ value: name, label: name })),
|
|
@@ -2338,44 +2501,44 @@ async function selectOrCreateProjectBoard2(knownRepo) {
|
|
|
2338
2501
|
message: "Board name",
|
|
2339
2502
|
placeholder: "e.g., SVG Icon Generator v2"
|
|
2340
2503
|
});
|
|
2341
|
-
console.log(
|
|
2504
|
+
console.log(import_picocolors10.default.cyan(`Creating board "${projectName}"...`));
|
|
2342
2505
|
try {
|
|
2343
|
-
const createOutput = (0,
|
|
2506
|
+
const createOutput = (0, import_node_child_process10.execSync)(
|
|
2344
2507
|
`gh project create --owner ${repoName.split("/")[0]} --title "${projectName}"`,
|
|
2345
2508
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
2346
2509
|
);
|
|
2347
2510
|
const projectNumberMatch = createOutput.match(/projects\/(\d+)/);
|
|
2348
2511
|
const projectNumber = projectNumberMatch ? projectNumberMatch[1] : null;
|
|
2349
|
-
console.log(
|
|
2512
|
+
console.log(import_picocolors10.default.green(`\u2713 Board "${projectName}" created`));
|
|
2350
2513
|
if (projectNumber) {
|
|
2351
2514
|
try {
|
|
2352
|
-
(0,
|
|
2515
|
+
(0, import_node_child_process10.execSync)(`gh project link ${projectNumber} --owner ${repoName.split("/")[0]} --repo ${repoName}`, {
|
|
2353
2516
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2354
2517
|
});
|
|
2355
|
-
console.log(
|
|
2518
|
+
console.log(import_picocolors10.default.green(`\u2713 Linked to ${repoName}`));
|
|
2356
2519
|
} catch {
|
|
2357
|
-
console.log(
|
|
2520
|
+
console.log(import_picocolors10.default.dim(` Note: Board created at org level. Link manually from repo's Projects tab.`));
|
|
2358
2521
|
}
|
|
2359
2522
|
}
|
|
2360
2523
|
return projectName;
|
|
2361
2524
|
} catch {
|
|
2362
|
-
console.log(
|
|
2525
|
+
console.log(import_picocolors10.default.yellow("Could not create board."));
|
|
2363
2526
|
return null;
|
|
2364
2527
|
}
|
|
2365
2528
|
} else if (selectedProject !== "__none__") {
|
|
2366
2529
|
try {
|
|
2367
|
-
const projectsOutput = (0,
|
|
2530
|
+
const projectsOutput = (0, import_node_child_process10.execSync)(
|
|
2368
2531
|
`gh project list --owner ${repoName.split("/")[0]} --format json`,
|
|
2369
2532
|
{ encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
2370
2533
|
);
|
|
2371
2534
|
const projects = JSON.parse(projectsOutput);
|
|
2372
2535
|
const project = projects.projects?.find((p2) => p2.title === selectedProject);
|
|
2373
2536
|
if (project?.number) {
|
|
2374
|
-
(0,
|
|
2537
|
+
(0, import_node_child_process10.execSync)(
|
|
2375
2538
|
`gh project link ${project.number} --owner ${repoName.split("/")[0]} --repo ${repoName}`,
|
|
2376
2539
|
{ stdio: ["pipe", "pipe", "pipe"] }
|
|
2377
2540
|
);
|
|
2378
|
-
console.log(
|
|
2541
|
+
console.log(import_picocolors10.default.green(`\u2713 Linked "${selectedProject}" to ${repoName}`));
|
|
2379
2542
|
}
|
|
2380
2543
|
} catch {
|
|
2381
2544
|
}
|
|
@@ -2389,7 +2552,7 @@ var __dirname2 = (0, import_node_path3.dirname)((0, import_node_url2.fileURLToPa
|
|
|
2389
2552
|
function getVersion() {
|
|
2390
2553
|
try {
|
|
2391
2554
|
const pkgPath = (0, import_node_path3.join)(__dirname2, "..", "package.json");
|
|
2392
|
-
const pkg = JSON.parse((0,
|
|
2555
|
+
const pkg = JSON.parse((0, import_node_fs7.readFileSync)(pkgPath, "utf-8"));
|
|
2393
2556
|
return pkg.version || "dev";
|
|
2394
2557
|
} catch {
|
|
2395
2558
|
return "dev";
|
|
@@ -2422,19 +2585,19 @@ async function main() {
|
|
|
2422
2585
|
});
|
|
2423
2586
|
program.action(async () => {
|
|
2424
2587
|
await showBanner(version);
|
|
2425
|
-
const { existsSync:
|
|
2588
|
+
const { existsSync: existsSync7 } = await import("fs");
|
|
2426
2589
|
const { getProjectTracker: getProjectTracker2 } = await Promise.resolve().then(() => (init_new(), new_exports));
|
|
2427
2590
|
const tracker = getProjectTracker2();
|
|
2428
|
-
if (tracker === "github" ||
|
|
2591
|
+
if (tracker === "github" || existsSync7("TODO.md") || existsSync7(".chiefwiggum/CLAUDE.md")) {
|
|
2429
2592
|
await cmdLoop();
|
|
2430
2593
|
} else {
|
|
2431
|
-
console.log(
|
|
2594
|
+
console.log(import_picocolors11.default.yellow("No project configured. Run 'chiefwiggum new' to get started."));
|
|
2432
2595
|
process.exit(1);
|
|
2433
2596
|
}
|
|
2434
2597
|
});
|
|
2435
2598
|
await program.parseAsync();
|
|
2436
2599
|
}
|
|
2437
2600
|
main().catch((err) => {
|
|
2438
|
-
console.error(
|
|
2601
|
+
console.error(import_picocolors11.default.red("Error:"), err.message);
|
|
2439
2602
|
process.exit(1);
|
|
2440
2603
|
});
|