chiefwiggum 1.3.12 → 1.3.14

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 +173 -36
  2. package/package.json +1 -1
package/dist/cli.cjs CHANGED
@@ -95,6 +95,108 @@ async function multilineText(options) {
95
95
  });
96
96
  });
97
97
  }
98
+ async function searchSelect(options) {
99
+ const { message, items, placeholder = "Type to filter...", maxVisible = 10 } = options;
100
+ const readline = await import("readline");
101
+ return new Promise((resolve) => {
102
+ let query = "";
103
+ let selectedIndex = 0;
104
+ let filtered = items.slice(0, maxVisible);
105
+ const filterItems = () => {
106
+ if (!query) {
107
+ filtered = items.slice(0, maxVisible);
108
+ } else {
109
+ const lower = query.toLowerCase();
110
+ filtered = items.filter((item) => item.toLowerCase().includes(lower)).slice(0, maxVisible);
111
+ }
112
+ if (selectedIndex >= filtered.length) {
113
+ selectedIndex = Math.max(0, filtered.length - 1);
114
+ }
115
+ };
116
+ const render = () => {
117
+ process.stdout.write("\x1B[?25l");
118
+ let output = "";
119
+ output += `${import_picocolors.default.cyan("?")} ${import_picocolors.default.bold(message)}
120
+ `;
121
+ output += `${import_picocolors.default.dim("\u203A")} ${query || import_picocolors.default.dim(placeholder)}
122
+ `;
123
+ output += "\n";
124
+ if (filtered.length === 0) {
125
+ output += import_picocolors.default.dim(" No matches found\n");
126
+ } else {
127
+ for (let i = 0; i < filtered.length; i++) {
128
+ const isSelected = i === selectedIndex;
129
+ const prefix = isSelected ? import_picocolors.default.cyan("\u276F") : " ";
130
+ const text3 = isSelected ? import_picocolors.default.cyan(filtered[i]) : filtered[i];
131
+ output += `${prefix} ${text3}
132
+ `;
133
+ }
134
+ }
135
+ output += `
136
+ ${import_picocolors.default.dim("\u2191/\u2193 navigate \u2022 enter select \u2022 esc cancel")}`;
137
+ process.stdout.write(output);
138
+ };
139
+ const clearOutput = () => {
140
+ const lineCount = filtered.length + 5;
141
+ process.stdout.write(`\x1B[${lineCount}A\x1B[0J`);
142
+ };
143
+ const cleanup2 = () => {
144
+ process.stdout.write("\x1B[?25h");
145
+ process.stdin.setRawMode(false);
146
+ process.stdin.removeListener("data", onKeypress);
147
+ };
148
+ const onKeypress = (key) => {
149
+ const str = key.toString();
150
+ if (str === "\x1B" || str === "") {
151
+ clearOutput();
152
+ cleanup2();
153
+ p.cancel("Operation cancelled.");
154
+ process.exit(0);
155
+ }
156
+ if (str === "\r" || str === "\n") {
157
+ clearOutput();
158
+ cleanup2();
159
+ if (filtered.length > 0) {
160
+ console.log(`${import_picocolors.default.green("\u2713")} ${import_picocolors.default.bold(message)} ${import_picocolors.default.dim("\xB7")} ${filtered[selectedIndex]}`);
161
+ resolve(filtered[selectedIndex]);
162
+ } else {
163
+ resolve(null);
164
+ }
165
+ return;
166
+ }
167
+ if (str === "\x1B[A") {
168
+ clearOutput();
169
+ selectedIndex = Math.max(0, selectedIndex - 1);
170
+ render();
171
+ return;
172
+ }
173
+ if (str === "\x1B[B") {
174
+ clearOutput();
175
+ selectedIndex = Math.min(filtered.length - 1, selectedIndex + 1);
176
+ render();
177
+ return;
178
+ }
179
+ if (str === "\x7F" || str === "\b") {
180
+ clearOutput();
181
+ query = query.slice(0, -1);
182
+ filterItems();
183
+ render();
184
+ return;
185
+ }
186
+ if (str.length === 1 && str >= " ") {
187
+ clearOutput();
188
+ query += str;
189
+ filterItems();
190
+ render();
191
+ }
192
+ };
193
+ filterItems();
194
+ render();
195
+ process.stdin.setRawMode(true);
196
+ process.stdin.resume();
197
+ process.stdin.on("data", onKeypress);
198
+ });
199
+ }
98
200
 
99
201
  // src/utils/banner.ts
100
202
  var BANNER = `
@@ -195,7 +297,8 @@ var config = {
195
297
  get templatesDir() {
196
298
  return (0, import_node_path.join)(this.chiefwiggumHome, "templates");
197
299
  },
198
- todoFile: process.env.TODO_FILE || "TODO.md",
300
+ todoFile: process.env.TODO_FILE || "docs/chiefwiggum/TODO.md",
301
+ specsDir: "docs/chiefwiggum/specs",
199
302
  // Timing
200
303
  cooldownSeconds: parseInt(process.env.COOLDOWN_SECONDS || "5", 10),
201
304
  iterationTimeoutMinutes: parseInt(process.env.ITERATION_TIMEOUT_MINUTES || "60", 10),
@@ -524,6 +627,34 @@ var __dirname = (0, import_node_path2.dirname)((0, import_node_url.fileURLToPath
524
627
  var LOCAL_TEMPLATES_DIR = ".chiefwiggum/templates";
525
628
  var CONFIG_FILE = ".chiefwiggum/CLAUDE.md";
526
629
  var BUNDLED_TEMPLATES_DIR = (0, import_node_path2.join)(__dirname, "..", "templates");
630
+ var SKIP_DIRS = /* @__PURE__ */ new Set([
631
+ "node_modules",
632
+ ".git",
633
+ ".chiefwiggum",
634
+ "dist",
635
+ "build",
636
+ ".next",
637
+ "vendor",
638
+ "coverage"
639
+ ]);
640
+ function findMarkdownFiles(dir, basePath = "") {
641
+ const results = [];
642
+ try {
643
+ const entries = (0, import_node_fs3.readdirSync)(dir, { withFileTypes: true });
644
+ for (const entry of entries) {
645
+ const relativePath = basePath ? `${basePath}/${entry.name}` : entry.name;
646
+ if (entry.isDirectory()) {
647
+ if (!SKIP_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
648
+ results.push(...findMarkdownFiles((0, import_node_path2.join)(dir, entry.name), relativePath));
649
+ }
650
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
651
+ results.push(relativePath);
652
+ }
653
+ }
654
+ } catch {
655
+ }
656
+ return results;
657
+ }
527
658
  async function cmdNew(planFile) {
528
659
  console.log(import_picocolors7.default.bold("Project Setup"));
529
660
  console.log();
@@ -550,24 +681,22 @@ async function cmdNew(planFile) {
550
681
  });
551
682
  switch (setupChoice) {
552
683
  case "plan": {
553
- if ((0, import_node_fs3.existsSync)("plans")) {
554
- const plans = (0, import_node_fs3.readdirSync)("plans").filter((f) => f.endsWith(".md"));
555
- if (plans.length > 0) {
556
- console.log();
557
- console.log(import_picocolors7.default.bold("Available plans:"));
558
- for (const f of plans) {
559
- console.log(` ${import_picocolors7.default.cyan(`plans/${f}`)}`);
560
- }
561
- console.log();
562
- }
684
+ const mdFiles = findMarkdownFiles(process.cwd());
685
+ if (mdFiles.length === 0) {
686
+ console.log(import_picocolors7.default.yellow("No markdown files found in project."));
687
+ console.log(import_picocolors7.default.dim("Create a plan file first, e.g., plans/plan.md"));
688
+ process.exit(1);
563
689
  }
564
- const planPath = await text2({
565
- message: "Path to plan file",
566
- defaultValue: "plans/plan.md"
690
+ console.log();
691
+ const planPath = await searchSelect({
692
+ message: "Select a plan file",
693
+ items: mdFiles,
694
+ placeholder: "Type to filter...",
695
+ maxVisible: 15
567
696
  });
568
- if (!(0, import_node_fs3.existsSync)(planPath)) {
569
- console.log(import_picocolors7.default.red(`Plan file not found: ${planPath}`));
570
- process.exit(1);
697
+ if (!planPath) {
698
+ console.log(import_picocolors7.default.yellow("No plan selected."));
699
+ process.exit(0);
571
700
  }
572
701
  await checkExistingFiles();
573
702
  await generateFromPlan(planPath);
@@ -609,8 +738,8 @@ async function cmdNew(planFile) {
609
738
  }
610
739
  async function checkExistingFiles() {
611
740
  const existingFiles = [];
612
- if ((0, import_node_fs3.existsSync)(config.todoFile)) existingFiles.push("TODO.md");
613
- if ((0, import_node_fs3.existsSync)("specs")) existingFiles.push("specs/");
741
+ if ((0, import_node_fs3.existsSync)(config.todoFile)) existingFiles.push(config.todoFile);
742
+ if ((0, import_node_fs3.existsSync)(config.specsDir)) existingFiles.push(`${config.specsDir}/`);
614
743
  if (existingFiles.length === 0) {
615
744
  return true;
616
745
  }
@@ -716,6 +845,10 @@ ${features}
716
845
  }
717
846
  async function generateFromPlan(planFile) {
718
847
  const planContent = (0, import_node_fs3.readFileSync)(planFile, "utf-8");
848
+ const specsDir = config.specsDir;
849
+ const todoFile = config.todoFile;
850
+ const prdPath = `${specsDir}/prd.md`;
851
+ const techPath = `${specsDir}/technical.md`;
719
852
  console.log();
720
853
  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"));
721
854
  console.log(import_picocolors7.default.green(` Generating specs from: ${planFile}`));
@@ -726,13 +859,13 @@ async function generateFromPlan(planFile) {
726
859
  console.log("Run 'chiefwiggum new' to set up templates first.");
727
860
  process.exit(1);
728
861
  }
729
- (0, import_node_fs3.mkdirSync)("specs", { recursive: true });
862
+ (0, import_node_fs3.mkdirSync)(specsDir, { recursive: true });
730
863
  const prdTemplate = (0, import_node_fs3.readFileSync)(`${LOCAL_TEMPLATES_DIR}/prd-template.md`, "utf-8");
731
864
  const techTemplate = (0, import_node_fs3.readFileSync)(`${LOCAL_TEMPLATES_DIR}/technical-template.md`, "utf-8");
732
865
  const todoTemplate = (0, import_node_fs3.readFileSync)(`${LOCAL_TEMPLATES_DIR}/TODO-template.md`, "utf-8");
733
866
  const claudeTemplate = (0, import_node_fs3.readFileSync)(`${LOCAL_TEMPLATES_DIR}/CLAUDE-template.md`, "utf-8");
734
867
  console.log();
735
- console.log(import_picocolors7.default.yellow("[1/4] Generating specs/prd.md..."));
868
+ console.log(import_picocolors7.default.yellow(`[1/4] Generating ${prdPath}...`));
736
869
  const prdPrompt = `You are filling in a PRD template based on a project plan.
737
870
 
738
871
  Here is the plan:
@@ -747,13 +880,13 @@ ${prdTemplate}
747
880
 
748
881
  Fill in the template with specific details from the plan.
749
882
  Replace all placeholder text in [brackets] with real content.
750
- Write the completed PRD directly to specs/prd.md.
883
+ Write the completed PRD directly to ${prdPath}.
751
884
  Do NOT ask questions \u2014 infer everything from the plan.`;
752
885
  await runClaude({ prompt: prdPrompt });
753
- console.log(import_picocolors7.default.green(" \u2713 specs/prd.md"));
886
+ console.log(import_picocolors7.default.green(` \u2713 ${prdPath}`));
754
887
  console.log();
755
- console.log(import_picocolors7.default.yellow("[2/4] Generating specs/technical.md..."));
756
- const prdGenerated = (0, import_node_fs3.existsSync)("specs/prd.md") ? (0, import_node_fs3.readFileSync)("specs/prd.md", "utf-8") : "";
888
+ console.log(import_picocolors7.default.yellow(`[2/4] Generating ${techPath}...`));
889
+ const prdGenerated = (0, import_node_fs3.existsSync)(prdPath) ? (0, import_node_fs3.readFileSync)(prdPath, "utf-8") : "";
757
890
  const techPrompt = `You are filling in a Technical Specification template based on a PRD.
758
891
 
759
892
  Here is the PRD:
@@ -768,16 +901,16 @@ ${techTemplate}
768
901
 
769
902
  Fill in the template with specific technical details.
770
903
  Replace all placeholder text in [brackets] with real content.
771
- Write the completed spec directly to specs/technical.md.
904
+ Write the completed spec directly to ${techPath}.
772
905
  Do NOT ask questions \u2014 infer everything from the PRD.`;
773
906
  await runClaude({ prompt: techPrompt });
774
- console.log(import_picocolors7.default.green(" \u2713 specs/technical.md"));
907
+ console.log(import_picocolors7.default.green(` \u2713 ${techPath}`));
775
908
  console.log();
776
909
  if ((0, import_node_fs3.existsSync)("CLAUDE.md")) {
777
910
  console.log(import_picocolors7.default.yellow("[3/4] Skipping CLAUDE.md (already exists)"));
778
911
  } else {
779
912
  console.log(import_picocolors7.default.yellow("[3/4] Generating CLAUDE.md..."));
780
- const techGenerated2 = (0, import_node_fs3.existsSync)("specs/technical.md") ? (0, import_node_fs3.readFileSync)("specs/technical.md", "utf-8") : "";
913
+ const techGenerated2 = (0, import_node_fs3.existsSync)(techPath) ? (0, import_node_fs3.readFileSync)(techPath, "utf-8") : "";
781
914
  const claudePrompt = `You are filling in a CLAUDE.md template for a project.
782
915
 
783
916
  Here is the PRD:
@@ -803,8 +936,12 @@ Write directly to CLAUDE.md.`;
803
936
  console.log(import_picocolors7.default.green(" \u2713 CLAUDE.md"));
804
937
  }
805
938
  console.log();
806
- console.log(import_picocolors7.default.yellow("[4/4] Generating TODO.md..."));
807
- const techGenerated = (0, import_node_fs3.existsSync)("specs/technical.md") ? (0, import_node_fs3.readFileSync)("specs/technical.md", "utf-8") : "";
939
+ console.log(import_picocolors7.default.yellow(`[4/4] Generating ${todoFile}...`));
940
+ const techGenerated = (0, import_node_fs3.existsSync)(techPath) ? (0, import_node_fs3.readFileSync)(techPath, "utf-8") : "";
941
+ const todoDir = (0, import_node_path2.dirname)(todoFile);
942
+ if (todoDir !== ".") {
943
+ (0, import_node_fs3.mkdirSync)(todoDir, { recursive: true });
944
+ }
808
945
  const todoPrompt = `You are filling in a TODO template based on specs.
809
946
 
810
947
  Here is the PRD:
@@ -826,20 +963,20 @@ Create a phased TODO.md following the template structure.
826
963
  Use checkbox format: - [ ] Task description
827
964
  Keep tasks granular (1-2 hours max each).
828
965
  End each phase with: - [ ] Phase N review
829
- Write directly to TODO.md.`;
966
+ Write directly to ${todoFile}.`;
830
967
  await runClaude({ prompt: todoPrompt });
831
- console.log(import_picocolors7.default.green(" \u2713 TODO.md"));
968
+ console.log(import_picocolors7.default.green(` \u2713 ${todoFile}`));
832
969
  console.log();
833
970
  console.log(import_picocolors7.default.green("Specs generated:"));
834
- console.log(" - specs/prd.md");
835
- console.log(" - specs/technical.md");
971
+ console.log(` - ${prdPath}`);
972
+ console.log(` - ${techPath}`);
836
973
  if (!(0, import_node_fs3.existsSync)("CLAUDE.md")) {
837
974
  console.log(" - CLAUDE.md");
838
975
  }
839
- console.log(" - TODO.md");
976
+ console.log(` - ${todoFile}`);
840
977
  try {
841
978
  (0, import_node_child_process6.execSync)("git rev-parse --git-dir", { stdio: "pipe" });
842
- (0, import_node_child_process6.execSync)("git add specs/ TODO.md CLAUDE.md 2>/dev/null || true", { stdio: "pipe" });
979
+ (0, import_node_child_process6.execSync)(`git add ${specsDir}/ ${todoFile} CLAUDE.md 2>/dev/null || true`, { stdio: "pipe" });
843
980
  (0, import_node_child_process6.execSync)('git commit -m "chore: generate specs from plan" 2>/dev/null || true', {
844
981
  stdio: "pipe"
845
982
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chiefwiggum",
3
- "version": "1.3.12",
3
+ "version": "1.3.14",
4
4
  "description": "Autonomous coding agent CLI. Point it at a plan, watch it build.",
5
5
  "type": "module",
6
6
  "bin": {