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.
- package/dist/cli.cjs +173 -36
- 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
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
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
|
-
|
|
565
|
-
|
|
566
|
-
|
|
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 (!
|
|
569
|
-
console.log(import_picocolors7.default.
|
|
570
|
-
process.exit(
|
|
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(
|
|
613
|
-
if ((0, import_node_fs3.existsSync)(
|
|
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)(
|
|
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(
|
|
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
|
|
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(
|
|
886
|
+
console.log(import_picocolors7.default.green(` \u2713 ${prdPath}`));
|
|
754
887
|
console.log();
|
|
755
|
-
console.log(import_picocolors7.default.yellow(
|
|
756
|
-
const prdGenerated = (0, import_node_fs3.existsSync)(
|
|
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
|
|
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(
|
|
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)(
|
|
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(
|
|
807
|
-
const techGenerated = (0, import_node_fs3.existsSync)(
|
|
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
|
|
966
|
+
Write directly to ${todoFile}.`;
|
|
830
967
|
await runClaude({ prompt: todoPrompt });
|
|
831
|
-
console.log(import_picocolors7.default.green(
|
|
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(
|
|
835
|
-
console.log(
|
|
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(
|
|
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)(
|
|
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
|
});
|