create-battle-plan 1.4.1 → 1.4.2
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/bin/cli.js +51 -22
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -84,7 +84,11 @@ function getDirs(dir) {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
function
|
|
87
|
+
function isDirEmpty(dir) {
|
|
88
|
+
try { return fs.readdirSync(dir).length === 0; } catch { return false; }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function pickFolder(shortName) {
|
|
88
92
|
return new Promise((resolve) => {
|
|
89
93
|
// Pause readline so we can use raw mode
|
|
90
94
|
closeReadline();
|
|
@@ -97,8 +101,14 @@ function pickFolder(projectSlug) {
|
|
|
97
101
|
function getOptions() {
|
|
98
102
|
const dirs = getDirs(cwd);
|
|
99
103
|
const options = [];
|
|
104
|
+
if (isDirEmpty(cwd)) {
|
|
105
|
+
options.push({
|
|
106
|
+
label: `${GREEN}● Install in this folder ${BOLD}(${path.basename(cwd)}/)${RESET}${GREEN} — no subfolder${RESET}`,
|
|
107
|
+
action: 'here_no_sub',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
100
110
|
options.push({ label: `${GREEN}+ Create new folder here${RESET}`, action: 'create' });
|
|
101
|
-
options.push({ label: `${CYAN}» Install here as ${BOLD}${
|
|
111
|
+
options.push({ label: `${CYAN}» Install here as ${BOLD}${shortName}/${RESET}`, action: 'here' });
|
|
102
112
|
if (path.dirname(cwd) !== cwd) {
|
|
103
113
|
options.push({ label: `${DIM}../${RESET} ${DIM}(up)${RESET}`, action: 'up' });
|
|
104
114
|
}
|
|
@@ -116,14 +126,14 @@ function pickFolder(projectSlug) {
|
|
|
116
126
|
let output = '';
|
|
117
127
|
|
|
118
128
|
if (mode === 'input') {
|
|
119
|
-
output += `${CLEAR_LINE}\r${DIM}[
|
|
129
|
+
output += `${CLEAR_LINE}\r${DIM}[7/7]${RESET} ${BOLD}Folder name:${RESET} ${inputBuffer}\x1b[K`;
|
|
120
130
|
process.stdout.write(output);
|
|
121
131
|
return;
|
|
122
132
|
}
|
|
123
133
|
|
|
124
134
|
output += `\x1b[H\x1b[2J`; // clear screen
|
|
125
135
|
output += `\n`;
|
|
126
|
-
output += `${DIM}[
|
|
136
|
+
output += `${DIM}[7/7]${RESET} ${BOLD}Where do you want to install it?${RESET}\n`;
|
|
127
137
|
output += `${DIM} ${display}${RESET}\n`;
|
|
128
138
|
output += `\n`;
|
|
129
139
|
output += `${DIM} ↑↓ navigate · enter select · q cancel${RESET}\n`;
|
|
@@ -156,12 +166,14 @@ function pickFolder(projectSlug) {
|
|
|
156
166
|
const opt = options[selected];
|
|
157
167
|
if (opt.action === 'create') {
|
|
158
168
|
mode = 'input';
|
|
159
|
-
inputBuffer =
|
|
169
|
+
inputBuffer = shortName;
|
|
160
170
|
process.stdout.write(`\x1b[H\x1b[2J`);
|
|
161
171
|
process.stdout.write(`\n`);
|
|
162
|
-
process.stdout.write(`${DIM}[
|
|
172
|
+
process.stdout.write(`${DIM}[7/7]${RESET} ${BOLD}Folder name:${RESET} ${inputBuffer}`);
|
|
163
173
|
} else if (opt.action === 'here') {
|
|
164
|
-
finish(path.join(cwd,
|
|
174
|
+
finish(path.join(cwd, shortName));
|
|
175
|
+
} else if (opt.action === 'here_no_sub') {
|
|
176
|
+
finish(cwd);
|
|
165
177
|
} else if (opt.action === 'up') {
|
|
166
178
|
cwd = path.dirname(cwd);
|
|
167
179
|
selected = 0;
|
|
@@ -189,7 +201,7 @@ function pickFolder(projectSlug) {
|
|
|
189
201
|
// Backspace
|
|
190
202
|
inputBuffer = inputBuffer.slice(0, -1);
|
|
191
203
|
process.stdout.write(`\r${CLEAR_LINE}`);
|
|
192
|
-
process.stdout.write(`${DIM}[
|
|
204
|
+
process.stdout.write(`${DIM}[7/7]${RESET} ${BOLD}Folder name:${RESET} ${inputBuffer}`);
|
|
193
205
|
} else if (key === '\x1b' || key === '\x03') {
|
|
194
206
|
// Escape or ctrl-c → back to browse
|
|
195
207
|
mode = 'browse';
|
|
@@ -210,7 +222,7 @@ function pickFolder(projectSlug) {
|
|
|
210
222
|
cleanup();
|
|
211
223
|
process.stdout.write(`\x1b[H\x1b[2J`);
|
|
212
224
|
console.log('');
|
|
213
|
-
console.log(`${DIM}[
|
|
225
|
+
console.log(`${DIM}[7/7]${RESET} ${BOLD}Location:${RESET} ${shortPath(dir)}`);
|
|
214
226
|
console.log('');
|
|
215
227
|
resolve(dir);
|
|
216
228
|
}
|
|
@@ -297,37 +309,55 @@ async function main() {
|
|
|
297
309
|
|
|
298
310
|
initReadline();
|
|
299
311
|
|
|
300
|
-
// Question 1: Project name
|
|
301
|
-
const projectName = await ask(`${DIM}[1/
|
|
312
|
+
// Question 1: Project description (one sentence — used for context, not the folder name)
|
|
313
|
+
const projectName = await ask(`${DIM}[1/7]${RESET} ${BOLD}What's your project in one sentence?${RESET}\n> `);
|
|
302
314
|
if (!projectName) { console.log('Project name is required.'); process.exit(1); }
|
|
303
315
|
console.log('');
|
|
304
316
|
|
|
305
|
-
// Question 2:
|
|
317
|
+
// Question 2: Short name (the actual folder slug). Default = cwd basename when sensible.
|
|
318
|
+
const cwdBasename = path.basename(process.cwd());
|
|
319
|
+
const cwdSlug = slugify(cwdBasename);
|
|
320
|
+
const cwdIsEmpty = (() => {
|
|
321
|
+
try { return fs.readdirSync(process.cwd()).length === 0; } catch { return false; }
|
|
322
|
+
})();
|
|
323
|
+
const sentenceSlug = slugify(projectName);
|
|
324
|
+
const truncatedSentenceSlug = sentenceSlug.split('-').slice(0, 3).join('-');
|
|
325
|
+
const genericNames = new Set(['projects', 'code', 'src', 'work', 'dev', 'repos', 'workspace', 'documents', 'desktop']);
|
|
326
|
+
const defaultShortName = (cwdIsEmpty && cwdSlug && !genericNames.has(cwdSlug))
|
|
327
|
+
? cwdSlug
|
|
328
|
+
: (truncatedSentenceSlug || 'my-battle-plan');
|
|
329
|
+
const shortNameRaw = await ask(
|
|
330
|
+
`${DIM}[2/7]${RESET} ${BOLD}Short name for the folder?${RESET} ${DIM}(default: ${defaultShortName})${RESET}\n> `
|
|
331
|
+
);
|
|
332
|
+
const shortName = slugify(shortNameRaw) || defaultShortName;
|
|
333
|
+
console.log('');
|
|
334
|
+
|
|
335
|
+
// Question 3: Time horizon
|
|
306
336
|
const horizon = await ask(
|
|
307
|
-
`${DIM}[
|
|
337
|
+
`${DIM}[3/7]${RESET} ${BOLD}What's your time horizon?${RESET} ${DIM}(e.g., "3 weeks to demo day", "6 months to launch", "ongoing")${RESET}\n> `
|
|
308
338
|
);
|
|
309
339
|
console.log('');
|
|
310
340
|
|
|
311
|
-
// Question
|
|
341
|
+
// Question 4: Metrics
|
|
312
342
|
const metricsRaw = await ask(
|
|
313
|
-
`${DIM}[
|
|
343
|
+
`${DIM}[4/7]${RESET} ${BOLD}What are the 3-5 key metrics you want to track?${RESET} ${DIM}(comma-separated, e.g., "outreach sent, calls booked, LOIs signed")${RESET}\n> `
|
|
314
344
|
);
|
|
315
345
|
if (!metricsRaw) { console.log('At least one metric is required.'); process.exit(1); }
|
|
316
346
|
const metrics = metricsRaw.split(',').map((m) => m.trim()).filter(Boolean);
|
|
317
347
|
console.log('');
|
|
318
348
|
|
|
319
|
-
// Question
|
|
349
|
+
// Question 5: Domains
|
|
320
350
|
const suggested = suggestDomains(projectName);
|
|
321
351
|
const domainsRaw = await ask(
|
|
322
|
-
`${DIM}[
|
|
352
|
+
`${DIM}[5/7]${RESET} ${BOLD}What domains does your work cover?${RESET} ${DIM}(comma-separated)\nSuggested based on your project: ${suggested}${RESET}\n> `
|
|
323
353
|
);
|
|
324
354
|
if (!domainsRaw) { console.log('At least one domain is required.'); process.exit(1); }
|
|
325
355
|
const domains = domainsRaw.split(',').map((d) => d.trim().toLowerCase()).filter(Boolean);
|
|
326
356
|
console.log('');
|
|
327
357
|
|
|
328
|
-
// Question
|
|
358
|
+
// Question 6: People
|
|
329
359
|
const peopleRaw = await ask(
|
|
330
|
-
`${DIM}[
|
|
360
|
+
`${DIM}[6/7]${RESET} ${BOLD}Who are the key people you'll be working with?${RESET} ${DIM}(format: "Name:Role, Name:Role" — or press enter to skip)${RESET}\n> `
|
|
331
361
|
);
|
|
332
362
|
const people = peopleRaw
|
|
333
363
|
? peopleRaw.split(',').map((p) => {
|
|
@@ -337,9 +367,8 @@ async function main() {
|
|
|
337
367
|
: [];
|
|
338
368
|
console.log('');
|
|
339
369
|
|
|
340
|
-
// Question
|
|
341
|
-
const
|
|
342
|
-
const targetDir = await pickFolder(projectSlug);
|
|
370
|
+
// Question 7: Interactive folder picker
|
|
371
|
+
const targetDir = await pickFolder(shortName);
|
|
343
372
|
|
|
344
373
|
// Re-init readline for any future questions
|
|
345
374
|
initReadline();
|
package/package.json
CHANGED