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.
Files changed (2) hide show
  1. package/bin/cli.js +51 -22
  2. 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 pickFolder(projectSlug) {
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}${projectSlug}/${RESET}`, action: 'here' });
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}[6/6]${RESET} ${BOLD}Folder name:${RESET} ${inputBuffer}\x1b[K`;
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}[6/6]${RESET} ${BOLD}Where do you want to install it?${RESET}\n`;
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 = projectSlug;
169
+ inputBuffer = shortName;
160
170
  process.stdout.write(`\x1b[H\x1b[2J`);
161
171
  process.stdout.write(`\n`);
162
- process.stdout.write(`${DIM}[6/6]${RESET} ${BOLD}Folder name:${RESET} ${inputBuffer}`);
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, projectSlug));
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}[6/6]${RESET} ${BOLD}Folder name:${RESET} ${inputBuffer}`);
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}[6/6]${RESET} ${BOLD}Location:${RESET} ${shortPath(dir)}`);
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/6]${RESET} ${BOLD}What's your project in one sentence?${RESET}\n> `);
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: Time horizon
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}[2/6]${RESET} ${BOLD}What's your time horizon?${RESET} ${DIM}(e.g., "3 weeks to demo day", "6 months to launch", "ongoing")${RESET}\n> `
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 3: Metrics
341
+ // Question 4: Metrics
312
342
  const metricsRaw = await ask(
313
- `${DIM}[3/6]${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> `
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 4: Domains
349
+ // Question 5: Domains
320
350
  const suggested = suggestDomains(projectName);
321
351
  const domainsRaw = await ask(
322
- `${DIM}[4/6]${RESET} ${BOLD}What domains does your work cover?${RESET} ${DIM}(comma-separated)\nSuggested based on your project: ${suggested}${RESET}\n> `
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 5: People
358
+ // Question 6: People
329
359
  const peopleRaw = await ask(
330
- `${DIM}[5/6]${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> `
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 6: Interactive folder picker
341
- const projectSlug = slugify(projectName) || 'my-battle-plan';
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-battle-plan",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "Scaffold a Battle Plan project — a markdown-based context system for LLM-powered project management",
5
5
  "bin": {
6
6
  "create-battle-plan": "./bin/cli.js"