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