create-battle-plan 1.4.3 → 1.5.0
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 +181 -85
- package/package.json +1 -1
- package/template/.claude/commands/good-morning.md +6 -53
- package/template/.claude/commands/welcome.md +112 -0
- package/template/CLAUDE.md +2 -1
package/bin/cli.js
CHANGED
|
@@ -71,14 +71,6 @@ function ask(question) {
|
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
async function askRequired(question, errorMsg) {
|
|
75
|
-
while (true) {
|
|
76
|
-
const answer = await ask(question);
|
|
77
|
-
if (answer) return answer;
|
|
78
|
-
console.log(`${YELLOW} ${errorMsg}${RESET}`);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
74
|
// ── Interactive folder picker (raw mode) ─────────────────
|
|
83
75
|
|
|
84
76
|
function getDirs(dir) {
|
|
@@ -317,65 +309,155 @@ async function main() {
|
|
|
317
309
|
|
|
318
310
|
initReadline();
|
|
319
311
|
|
|
320
|
-
//
|
|
321
|
-
|
|
322
|
-
`${DIM}[1/7]${RESET} ${BOLD}What's your project in one sentence?${RESET}\n> `,
|
|
323
|
-
'A one-sentence description is required — even rough is fine. Try again:'
|
|
324
|
-
);
|
|
312
|
+
// Type `:back` (or `:b`) at any prompt to revise the previous answer.
|
|
313
|
+
console.log(`${DIM} tip: type ${BOLD}:back${RESET}${DIM} at any prompt to revise the previous answer${RESET}`);
|
|
325
314
|
console.log('');
|
|
326
315
|
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
316
|
+
const isBack = (s) => {
|
|
317
|
+
const t = s.trim().toLowerCase();
|
|
318
|
+
return t === ':back' || t === ':b';
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Each step reads `a` (answers so far) and writes its key. Returns {back:true}
|
|
322
|
+
// to step backwards, or {value} to advance. When `a[key]` already has a value
|
|
323
|
+
// (revisit), prompts show "press enter to keep" and accept empty input.
|
|
324
|
+
const a = {};
|
|
325
|
+
|
|
326
|
+
async function stepProject() {
|
|
327
|
+
const prior = a.projectName;
|
|
328
|
+
const hint = prior ? ` ${DIM}(currently: "${prior.length > 40 ? prior.slice(0, 40) + '…' : prior}" — enter to keep)${RESET}` : '';
|
|
329
|
+
while (true) {
|
|
330
|
+
const raw = await ask(`${DIM}[1/7]${RESET} ${BOLD}What's your project in one sentence?${RESET}${hint}\n> `);
|
|
331
|
+
if (isBack(raw)) return { back: true };
|
|
332
|
+
const v = raw.trim() || prior;
|
|
333
|
+
if (!v) {
|
|
334
|
+
console.log(`${YELLOW} A one-sentence description is required — even rough is fine. Try again:${RESET}`);
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
return { value: v };
|
|
338
|
+
}
|
|
339
|
+
}
|
|
344
340
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
341
|
+
async function stepShortName() {
|
|
342
|
+
const cwdBasename = path.basename(process.cwd());
|
|
343
|
+
const cwdSlug = slugify(cwdBasename);
|
|
344
|
+
const cwdIsEmpty = (() => {
|
|
345
|
+
try { return fs.readdirSync(process.cwd()).length === 0; } catch { return false; }
|
|
346
|
+
})();
|
|
347
|
+
const sentenceSlug = slugify(a.projectName);
|
|
348
|
+
const truncatedSentenceSlug = sentenceSlug.split('-').slice(0, 3).join('-');
|
|
349
|
+
const genericNames = new Set(['projects', 'code', 'src', 'work', 'dev', 'repos', 'workspace', 'documents', 'desktop']);
|
|
350
|
+
const defaultShortName = (cwdIsEmpty && cwdSlug && !genericNames.has(cwdSlug))
|
|
351
|
+
? cwdSlug
|
|
352
|
+
: (truncatedSentenceSlug || 'my-battle-plan');
|
|
353
|
+
const prior = a.shortName;
|
|
354
|
+
const hint = prior
|
|
355
|
+
? `${DIM}(currently: "${prior}" — enter to keep)${RESET}`
|
|
356
|
+
: `${DIM}(default: ${defaultShortName})${RESET}`;
|
|
357
|
+
const raw = await ask(`${DIM}[2/7]${RESET} ${BOLD}Short name for the folder?${RESET} ${hint}\n> `);
|
|
358
|
+
if (isBack(raw)) return { back: true };
|
|
359
|
+
const trimmed = raw.trim();
|
|
360
|
+
const value = trimmed
|
|
361
|
+
? (slugify(trimmed) || defaultShortName)
|
|
362
|
+
: (prior || defaultShortName);
|
|
363
|
+
return { value };
|
|
364
|
+
}
|
|
350
365
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
366
|
+
async function stepHorizon() {
|
|
367
|
+
const prior = a.horizon;
|
|
368
|
+
const hint = prior
|
|
369
|
+
? `${DIM}(currently: "${prior}" — enter to keep)${RESET}`
|
|
370
|
+
: `${DIM}(e.g., "3 weeks to demo day", "6 months to launch", "ongoing")${RESET}`;
|
|
371
|
+
const raw = await ask(`${DIM}[3/7]${RESET} ${BOLD}What's your time horizon?${RESET} ${hint}\n> `);
|
|
372
|
+
if (isBack(raw)) return { back: true };
|
|
373
|
+
const trimmed = raw.trim();
|
|
374
|
+
return { value: trimmed || prior || '' };
|
|
375
|
+
}
|
|
358
376
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
377
|
+
async function stepMetrics() {
|
|
378
|
+
const prior = a.metrics;
|
|
379
|
+
const priorDisplay = prior && prior.length ? prior.join(', ') : '';
|
|
380
|
+
const hint = priorDisplay
|
|
381
|
+
? `${DIM}(currently: "${priorDisplay}" — enter to keep, type "none" to clear)${RESET}`
|
|
382
|
+
: `${DIM}(comma-separated, e.g., "outreach sent, calls booked, LOIs signed" — or press enter to skip)${RESET}`;
|
|
383
|
+
const raw = await ask(`${DIM}[4/7]${RESET} ${BOLD}Any metrics to track?${RESET} ${hint}\n> `);
|
|
384
|
+
if (isBack(raw)) return { back: true };
|
|
385
|
+
const trimmed = raw.trim();
|
|
386
|
+
if (trimmed.toLowerCase() === 'none') return { value: [] };
|
|
387
|
+
if (!trimmed) return { value: prior || [] };
|
|
388
|
+
return { value: trimmed.split(',').map((m) => m.trim()).filter(Boolean) };
|
|
389
|
+
}
|
|
367
390
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
391
|
+
async function stepDomains() {
|
|
392
|
+
const prior = a.domains;
|
|
393
|
+
const suggested = suggestDomains(a.projectName);
|
|
394
|
+
const priorDisplay = prior && prior.length ? prior.join(', ') : '';
|
|
395
|
+
const hint = priorDisplay
|
|
396
|
+
? `${DIM}(currently: "${priorDisplay}" — enter to keep)${RESET}`
|
|
397
|
+
: `${DIM}(comma-separated)\nSuggested based on your project: ${suggested}${RESET}`;
|
|
398
|
+
while (true) {
|
|
399
|
+
const raw = await ask(`${DIM}[5/7]${RESET} ${BOLD}What domains does your work cover?${RESET} ${hint}\n> `);
|
|
400
|
+
if (isBack(raw)) return { back: true };
|
|
401
|
+
const trimmed = raw.trim();
|
|
402
|
+
if (!trimmed) {
|
|
403
|
+
if (prior && prior.length) return { value: prior };
|
|
404
|
+
console.log(`${YELLOW} At least one domain is required — try the suggestions (${suggested}) or any topic area. Try again:${RESET}`);
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
return { value: trimmed.split(',').map((d) => d.trim().toLowerCase()).filter(Boolean) };
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
async function stepPeople() {
|
|
412
|
+
const prior = a.people;
|
|
413
|
+
const priorDisplay = prior && prior.length ? prior.map((p) => `${p.name}:${p.role}`).join(', ') : '';
|
|
414
|
+
const hint = priorDisplay
|
|
415
|
+
? `${DIM}(currently: "${priorDisplay}" — enter to keep, type "none" to clear)${RESET}`
|
|
416
|
+
: `${DIM}(format: "Name:Role, Name:Role" — or press enter to skip)${RESET}`;
|
|
417
|
+
const raw = await ask(`${DIM}[6/7]${RESET} ${BOLD}Who are the key people you'll be working with?${RESET} ${hint}\n> `);
|
|
418
|
+
if (isBack(raw)) return { back: true };
|
|
419
|
+
const trimmed = raw.trim();
|
|
420
|
+
if (trimmed.toLowerCase() === 'none') return { value: [] };
|
|
421
|
+
if (!trimmed) return { value: prior || [] };
|
|
422
|
+
return {
|
|
423
|
+
value: trimmed.split(',').map((p) => {
|
|
374
424
|
const [name, role] = p.split(':').map((s) => s.trim());
|
|
375
425
|
return { name: name || '', role: role || '' };
|
|
376
|
-
}).filter((p) => p.name)
|
|
377
|
-
|
|
378
|
-
|
|
426
|
+
}).filter((p) => p.name),
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
const steps = [
|
|
431
|
+
{ key: 'projectName', run: stepProject },
|
|
432
|
+
{ key: 'shortName', run: stepShortName },
|
|
433
|
+
{ key: 'horizon', run: stepHorizon },
|
|
434
|
+
{ key: 'metrics', run: stepMetrics },
|
|
435
|
+
{ key: 'domains', run: stepDomains },
|
|
436
|
+
{ key: 'people', run: stepPeople },
|
|
437
|
+
];
|
|
438
|
+
|
|
439
|
+
let i = 0;
|
|
440
|
+
while (i < steps.length) {
|
|
441
|
+
const r = await steps[i].run();
|
|
442
|
+
if (r.back) {
|
|
443
|
+
if (i === 0) {
|
|
444
|
+
console.log(`${YELLOW} Already at the first question.${RESET}`);
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
i--;
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
a[steps[i].key] = r.value;
|
|
451
|
+
console.log('');
|
|
452
|
+
i++;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const projectName = a.projectName;
|
|
456
|
+
const shortName = a.shortName;
|
|
457
|
+
const horizon = a.horizon;
|
|
458
|
+
const metrics = a.metrics;
|
|
459
|
+
const domains = a.domains;
|
|
460
|
+
const people = a.people;
|
|
379
461
|
|
|
380
462
|
// Question 7: Interactive folder picker
|
|
381
463
|
const targetDir = await pickFolder(shortName);
|
|
@@ -454,7 +536,11 @@ _Start adding content here._
|
|
|
454
536
|
|
|
455
537
|
console.log(`${DIM} + docs/ (${domains.length} domain${domains.length > 1 ? 's' : ''})${RESET}`);
|
|
456
538
|
|
|
457
|
-
// Create metrics.yml
|
|
539
|
+
// Create metrics.yml — file always exists so scripts have a target, even if no
|
|
540
|
+
// metrics were declared (journal-style projects). The LLM can add metrics later.
|
|
541
|
+
const metricsBody = metrics.length
|
|
542
|
+
? metrics.map((m) => `${metricKey(m)}: 0`).join('\n') + '\n'
|
|
543
|
+
: '# No metrics declared yet. Add them as `key: value` below when ready.\n';
|
|
458
544
|
const metricsContent = [
|
|
459
545
|
`# metrics.yml — project-wide metrics registry for ${projectName}`,
|
|
460
546
|
'# The LLM updates this file FIRST in any cascade, before touching docs.',
|
|
@@ -462,16 +548,33 @@ _Start adding content here._
|
|
|
462
548
|
'',
|
|
463
549
|
`last_updated: ${today}`,
|
|
464
550
|
'',
|
|
465
|
-
|
|
466
|
-
'',
|
|
551
|
+
metricsBody,
|
|
467
552
|
].join('\n');
|
|
468
553
|
fs.writeFileSync(path.join(targetDir, 'metrics.yml'), metricsContent);
|
|
469
|
-
console.log(
|
|
554
|
+
console.log(
|
|
555
|
+
`${DIM} + metrics.yml (${metrics.length === 0 ? 'no metrics yet' : `${metrics.length} metric${metrics.length > 1 ? 's' : ''}`})${RESET}`
|
|
556
|
+
);
|
|
557
|
+
|
|
558
|
+
// Create battle plan — conditionally include Key Metrics section
|
|
559
|
+
const metricsSection = metrics.length
|
|
560
|
+
? `## Key Metrics
|
|
561
|
+
|
|
562
|
+
| Metric | Target | Current |
|
|
563
|
+
|--------|--------|---------|
|
|
564
|
+
${metrics.map((m) => `| ${m} | _set target_ | **0** (→ metrics.yml#${metricKey(m)}) |`).join('\n')}
|
|
470
565
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
566
|
+
---
|
|
567
|
+
|
|
568
|
+
`
|
|
569
|
+
: '';
|
|
570
|
+
|
|
571
|
+
const tldr = metrics.length
|
|
572
|
+
? `${projectName} — just initialized. Time horizon: ${horizon || 'not set'}. All metrics at 0. First priority: fill in the battle plan with real tasks and targets.`
|
|
573
|
+
: `${projectName} — just initialized. Time horizon: ${horizon || 'not set'}. No metrics tracked yet (journal-style project). First priority: fill in the battle plan with real tasks.`;
|
|
574
|
+
|
|
575
|
+
const firstPriority = metrics.length
|
|
576
|
+
? '- [ ] Set targets for each metric\n- [ ] Fill in this week\'s tasks\n- [ ] Record any existing conversations in external-insights.md'
|
|
577
|
+
: '- [ ] Fill in this week\'s tasks\n- [ ] Record any existing conversations in external-insights.md';
|
|
475
578
|
|
|
476
579
|
fs.writeFileSync(
|
|
477
580
|
path.join(targetDir, 'docs', 'battle-plan.md'),
|
|
@@ -482,7 +585,7 @@ _Start adding content here._
|
|
|
482
585
|
**Role:** source-of-truth
|
|
483
586
|
**Compression:** chronological
|
|
484
587
|
|
|
485
|
-
**TL;DR:** ${
|
|
588
|
+
**TL;DR:** ${tldr}
|
|
486
589
|
|
|
487
590
|
---
|
|
488
591
|
|
|
@@ -495,19 +598,9 @@ _Start adding content here._
|
|
|
495
598
|
|
|
496
599
|
---
|
|
497
600
|
|
|
498
|
-
##
|
|
499
|
-
|
|
500
|
-
| Metric | Target | Current |
|
|
501
|
-
|--------|--------|---------|
|
|
502
|
-
${metricsTable}
|
|
503
|
-
|
|
504
|
-
---
|
|
505
|
-
|
|
506
|
-
## Today's Priorities
|
|
601
|
+
${metricsSection}## Today's Priorities
|
|
507
602
|
|
|
508
|
-
|
|
509
|
-
- [ ] Fill in this week's tasks
|
|
510
|
-
- [ ] Record any existing conversations in external-insights.md
|
|
603
|
+
${firstPriority}
|
|
511
604
|
|
|
512
605
|
---
|
|
513
606
|
|
|
@@ -574,7 +667,7 @@ ${peopleSections}
|
|
|
574
667
|
console.log(`${DIM} + docs/battle-plan.md${RESET}`);
|
|
575
668
|
console.log(`${DIM} + docs/external-insights.md${RESET}`);
|
|
576
669
|
|
|
577
|
-
// Save onboarding answers for Claude to read on first /
|
|
670
|
+
// Save onboarding answers for Claude to read on first /welcome
|
|
578
671
|
fs.writeFileSync(
|
|
579
672
|
path.join(targetDir, '.battle-plan-onboarding.json'),
|
|
580
673
|
JSON.stringify(
|
|
@@ -637,10 +730,13 @@ ${peopleSections}
|
|
|
637
730
|
console.log(`${DIM} │${RESET} ${BOLD}cd ${relPath} && claude${RESET}${DIM}${' '.repeat(Math.max(0, 37 - relPath.length - 12))}│${RESET}`);
|
|
638
731
|
console.log(`${DIM} └─────────────────────────────────────────┘${RESET}`);
|
|
639
732
|
console.log('');
|
|
640
|
-
console.log(` Once Claude is running, type ${GREEN}${BOLD}/
|
|
641
|
-
console.log(`
|
|
642
|
-
console.log(`
|
|
643
|
-
console.log(`
|
|
733
|
+
console.log(` Once Claude is running, type ${GREEN}${BOLD}/welcome${RESET}`);
|
|
734
|
+
console.log(` for a guided tour — Claude will introduce`);
|
|
735
|
+
console.log(` itself, walk you through how the system works,`);
|
|
736
|
+
console.log(` and tailor things to how you want to use it.`);
|
|
737
|
+
console.log('');
|
|
738
|
+
console.log(` ${DIM}(After that, use ${BOLD}/good-morning${RESET}${DIM} to start each day${RESET}`);
|
|
739
|
+
console.log(` ${DIM}and ${BOLD}/wrap-up${RESET}${DIM} to close it out.)${RESET}`);
|
|
644
740
|
console.log('');
|
|
645
741
|
}
|
|
646
742
|
|
package/package.json
CHANGED
|
@@ -6,63 +6,18 @@ description: Morning standup — status briefing, metrics snapshot, and today's
|
|
|
6
6
|
|
|
7
7
|
Run these steps in order.
|
|
8
8
|
|
|
9
|
-
## Step 0: First-
|
|
9
|
+
## Step 0: First-run gate
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
If `.battle-plan-onboarding.json` exists in the repo root, the user hasn't run `/welcome` yet. Don't run the standup — tell them:
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
> Hey — looks like this is a fresh install and you haven't gone through the welcome yet. Run `/welcome` first to get the proper tour, then come back to `/good-morning` tomorrow (or whenever you start your next session).
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Stop here. Don't proceed to Step 1.
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Example tone: "Welcome to your battle plan for [project]. You've got [horizon] and you're tracking [metrics] — I've got all of that loaded and ready to go."
|
|
20
|
-
|
|
21
|
-
### Part 2: What this is (the big picture)
|
|
22
|
-
|
|
23
|
-
Explain what they now have, in plain language:
|
|
24
|
-
|
|
25
|
-
- **This is your project's memory.** Every conversation, decision, metric, and insight lives here in markdown files. When you close this chat and come back tomorrow — or next week — I read these files and pick up exactly where we left off. Nothing gets lost between sessions.
|
|
26
|
-
- **You don't organize anything.** When you tell me something new — a call you had, research you found, a number that changed, a reply you got — I update the right files in the right order automatically. That's called the cascade.
|
|
27
|
-
- **The cascade works like this:** new info flows into `metrics.yml` first (if a number changed), then into your battle plan (the operating doc that tracks where you are), then into the source docs for each domain. At the end, a verification script checks that everything is consistent.
|
|
28
|
-
- **Think of it as dumping context.** You can paste a full call transcript, a messy list of notes, a forwarded email, a research summary — anything. You don't need to format it or tell me where it goes. Just dump it in and I'll cascade it to the right places.
|
|
29
|
-
|
|
30
|
-
### Part 3: How to use it day to day
|
|
31
|
-
|
|
32
|
-
Explain the rhythm:
|
|
33
|
-
|
|
34
|
-
- **Start each session** with `/good-morning` (what you just ran). I'll brief you on where you stand — metrics, priorities, what's stuck, what's next. Think of it as a daily standup with your AI co-pilot.
|
|
35
|
-
- **During the day**, just talk to me. Tell me what happened, paste in notes, ask me to update things. Every piece of new info triggers the cascade automatically.
|
|
36
|
-
- **End each session** with `/wrap-up`. I'll review the day, show you what changed, sync everything, and offer to commit. This is how you close out clean without forgetting to log something.
|
|
37
|
-
- **When docs get long**, run `/distill`. Over time, your daily logs and conversation journals will grow. `/distill` compresses older content into a thorough summary and archives the raw text — nothing is ever deleted, but I can read the docs faster.
|
|
38
|
-
|
|
39
|
-
### Part 4: How metrics work
|
|
40
|
-
|
|
41
|
-
- Your key metrics live in `metrics.yml` — it's the single numeric source of truth for the whole project.
|
|
42
|
-
- Every number that appears in any doc traces back to that file via source annotations. When a metric changes, the cascade propagates the new value everywhere it's referenced.
|
|
43
|
-
- Shell scripts verify the numbers stay in sync. You'll never have a stale "42" in one doc when the real number is "47" in another.
|
|
44
|
-
- Right now all your metrics are at zero. One of the first things we'll do is set targets.
|
|
45
|
-
|
|
46
|
-
### Part 5: What to do right now
|
|
47
|
-
|
|
48
|
-
Transition into action:
|
|
49
|
-
|
|
50
|
-
- "Let's start by getting some real data into the system. Here's what I'd suggest..."
|
|
51
|
-
- Prompt them to set targets for their metrics
|
|
52
|
-
- Ask if there's any existing context they already have — calls they've had, research they've done, decisions they've already made. Anything they can tell you now will make the battle plan immediately useful.
|
|
53
|
-
|
|
54
|
-
### After the welcome
|
|
55
|
-
|
|
56
|
-
Rename `.battle-plan-onboarding.json` to `.battle-plan-onboarding-done.json` so this welcome doesn't repeat.
|
|
57
|
-
|
|
58
|
-
Then show a **compact metrics table** (all zeros, no targets yet) and transition into the onboarding questions naturally. Don't run the full standup format (Steps 1-4 below) on the first run — there's nothing to report yet. Instead, go straight to helping them populate the battle plan.
|
|
59
|
-
|
|
60
|
-
---
|
|
17
|
+
If neither `.battle-plan-onboarding.json` nor `.battle-plan-onboarding-done.json` exists, continue normally — the project may have been bootstrapped without the create-battle-plan CLI.
|
|
61
18
|
|
|
62
19
|
## Step 1: Gather State (parallel reads)
|
|
63
20
|
|
|
64
|
-
*Skip this on first run (Step 0 handles it). On all subsequent runs, start here.*
|
|
65
|
-
|
|
66
21
|
Run these in parallel:
|
|
67
22
|
- `node tools/tasks/render-today.js --quiet` — regenerate `docs/today.md` from `tasks.yml`
|
|
68
23
|
- Read `metrics.yml` — current numbers
|
|
@@ -122,6 +77,4 @@ After the user answers:
|
|
|
122
77
|
|
|
123
78
|
## Tone
|
|
124
79
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
On subsequent runs: direct, no fluff. Think military briefing, not newsletter.
|
|
80
|
+
Direct, no fluff. Think military briefing, not newsletter. The user is starting a work session — they want their position and their next move, not a pep talk. (First-session warmth lives in `/welcome`, not here.)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: First-session welcome — introduces the system, asks about project archetype, tailors onboarding. Runs ONCE after install. Use /good-morning for daily standups thereafter.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Welcome
|
|
6
|
+
|
|
7
|
+
Run this ONCE, on the user's very first session after `npx create-battle-plan`.
|
|
8
|
+
|
|
9
|
+
## Has welcome already run?
|
|
10
|
+
|
|
11
|
+
Check the repo root:
|
|
12
|
+
- If `.battle-plan-onboarding-done.json` exists → welcome already completed. Tell the user: `/welcome` is for first-time setup only. Suggest `/good-morning` (start of day) or `/wrap-up` (end of day) instead. Don't run the rest of this skill.
|
|
13
|
+
- If neither `.battle-plan-onboarding.json` nor `.battle-plan-onboarding-done.json` exists → the project wasn't scaffolded via `create-battle-plan`. Tell the user `/welcome` is meant for first-run after install and there's no onboarding context to load. Offer to walk through the system manually if they want.
|
|
14
|
+
- If `.battle-plan-onboarding.json` exists → continue.
|
|
15
|
+
|
|
16
|
+
Read `.battle-plan-onboarding.json` for project context (name, horizon, metrics, domains, people). Keep these handy — you'll reference them throughout.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Step 1 — Personal welcome (one message)
|
|
21
|
+
|
|
22
|
+
Greet by name. Show you already know their project — reference their horizon, the specific domains they picked, the people they named. Do this naturally, not as a recital. The user should feel like they're continuing a conversation, not booting up a tool.
|
|
23
|
+
|
|
24
|
+
If they declared metrics, mention them. If they didn't (`metrics: []` in the json), don't pretend they did — acknowledge this is a journal-style or open-ended project. Example: "I see you didn't declare specific metrics — that's fine, plenty of projects don't need them upfront. We can stay narrative and add them later if patterns emerge."
|
|
25
|
+
|
|
26
|
+
End this message with: "Before we go anywhere, let me show you around — and then I want to ask you a couple of things about how you want to use this."
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Step 2 — Plain-language tour (one message, no jargon dumps)
|
|
31
|
+
|
|
32
|
+
Explain what they actually have. Cover, in this order, in your own words:
|
|
33
|
+
|
|
34
|
+
1. **It's your project's memory.** Markdown files in `docs/`, plus three structured YAML files (`metrics.yml`, `tasks.yml`, `events.yml`). When the user closes the session and comes back next week, you read these and pick up exactly where you left off. Nothing gets lost.
|
|
35
|
+
2. **They don't organize anything.** When the user tells you something new — a call they had, an article they read, a number that changed, an idea, a forwarded email — you update the right files in the right order. That's the cascade.
|
|
36
|
+
3. **The cascade flows top-down.** Raw state (metrics / events / tasks) → `battle-plan.md` (current state) → source docs per domain → verification script. The user never has to know this exists — they just dump stuff in chat.
|
|
37
|
+
4. **The chat is the UI.** The cascade is your memory, not theirs. `docs/today.md` is the only file they should ever need to open — it's a thin clickable surface they can tick through in Obsidian or any markdown viewer.
|
|
38
|
+
5. **Compression keeps it readable.** Daily logs and conversation journals grow over time. `/distill` collapses old content into a thorough summary, archives the raw text, nothing is ever deleted.
|
|
39
|
+
|
|
40
|
+
Keep this to ~6–10 sentences total. Don't enumerate every file. Don't show code. The goal is to land the *shape* of the system, not the schema.
|
|
41
|
+
|
|
42
|
+
End with: "Now — quick question so I can tailor this to you."
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Step 3 — Detect the archetype (ASK, don't assume)
|
|
47
|
+
|
|
48
|
+
The system was originally built for founders validating an idea (metric-heavy: outreach, calls booked, conversion). But it works just as well for several other shapes. Ask:
|
|
49
|
+
|
|
50
|
+
> "Which of these sounds closest to what you're doing here? You can pick one, or say 'mix' if it's blended.
|
|
51
|
+
>
|
|
52
|
+
> **(a) Founder / operator validating an idea.** Metric-driven. Outreach, calls, replies, conversion. You want to know if this idea is real.
|
|
53
|
+
> **(b) Metric-heavy ops project.** Compliance work, sales pipeline, a specific build. Clear numbers to track, deadlines, deliverables.
|
|
54
|
+
> **(c) Journal / second-brain.** Dream journal, life admin, ongoing context layer. Few or no metrics — just want to dump stuff and have it organized.
|
|
55
|
+
> **(d) Something else.** Tell me about it."
|
|
56
|
+
|
|
57
|
+
Wait for the answer.
|
|
58
|
+
|
|
59
|
+
### How to tailor based on the archetype
|
|
60
|
+
|
|
61
|
+
| Archetype | Tailoring |
|
|
62
|
+
|---|---|
|
|
63
|
+
| **(a) founder-validation** | Default behavior. Push toward setting outreach/conversion targets. Expect them to track leads (suggest the outreach add-on if they don't have it — see `outreach/README.md`). Daily rhythm: `/good-morning` → work → `/wrap-up`. Weekly: `/weekly-triage`. |
|
|
64
|
+
| **(b) metric-heavy ops** | Same daily rhythm. Help them define the right metrics for their domain (deadlines, deliverables, throughput, error rate, whatever). Suggest setting `events.yml` for any scheduled meetings or deadlines. |
|
|
65
|
+
| **(c) journal / second-brain** | Lower-touch. They probably don't need `/wrap-up` daily — once a week is fine. The cascade still works, but the value is in *querying* the dump later, not in metric drift. Tell them: just talk to you in chat, and you'll keep the docs organized. They can rerun `/distill` when things get long. |
|
|
66
|
+
| **(d) other / mix** | Ask 1–2 follow-up questions to figure out the shape, then map to (a)/(b)/(c) or invent a hybrid. |
|
|
67
|
+
|
|
68
|
+
After they answer, give them a short, archetype-specific reflection: "Got it — so for you, the rhythm is going to look like X. Here's what I'd suggest as a starting point…"
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Step 4 — Tour the slash commands (one message)
|
|
73
|
+
|
|
74
|
+
Show the four daily/weekly commands with one-line descriptions, tailored to their archetype:
|
|
75
|
+
|
|
76
|
+
- **`/good-morning`** — start of session. I brief you on where you stand, what's open, what to push on today. Use every day you sit down with this. *(Mention this is the one they'll run tomorrow.)*
|
|
77
|
+
- **`/wrap-up`** — end of session. I reconcile the day, run the cascade, show what changed, offer to commit. *(For journal-style users: "Every few days is fine, doesn't have to be daily.")*
|
|
78
|
+
- **`/weekly-triage`** — weekly sweep of `tasks.yml`. Catches stale tasks, drift, overdue items. *(Skip if they're archetype (c).)*
|
|
79
|
+
- **`/distill`** — when a doc gets long. Compresses old content, archives raw, keeps recent. Run when you notice me being slow to load a file.
|
|
80
|
+
|
|
81
|
+
Don't dump the full skill list. The user can discover the others later.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Step 5 — Seed the system with existing context
|
|
86
|
+
|
|
87
|
+
Ask:
|
|
88
|
+
|
|
89
|
+
> "One last thing before we close this out: is there anything you already know about this project that I should have? Past calls, research you've read, decisions you've made, key contacts, anything. You don't need to structure it — just dump it and I'll cascade it into the right docs."
|
|
90
|
+
|
|
91
|
+
If they share anything, run the full cascade (Steps 0–4 from `CLAUDE.md`). If they say "nothing yet," skip.
|
|
92
|
+
|
|
93
|
+
For archetype (a) or (b): if they declared metrics in onboarding but didn't set targets, prompt them now: "What would success look like for [metric]? Even a rough number is fine." Update `battle-plan.md` with the targets.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Step 6 — Mark welcome complete
|
|
98
|
+
|
|
99
|
+
Rename `.battle-plan-onboarding.json` → `.battle-plan-onboarding-done.json` so this skill doesn't trigger again. Use the `mv` command via Bash, or the `Write` + delete pattern if `mv` isn't permitted.
|
|
100
|
+
|
|
101
|
+
Tell the user:
|
|
102
|
+
- "You're set up. Welcome doesn't run again."
|
|
103
|
+
- "Tomorrow, start with `/good-morning`. End with `/wrap-up` whenever you wind down."
|
|
104
|
+
- "Anything you tell me in between just gets cascaded — no special commands needed."
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Tone
|
|
109
|
+
|
|
110
|
+
Warm, thorough, but not preachy. The user just installed something — they're curious and a little uncertain. You're a knowledgeable co-pilot introducing themselves, not a product tour with arrows and tooltips. Default to plain English; reach for terminology (cascade, distill, compression mode) only after you've explained what it means. Never read the user a list of internal filenames unless they ask.
|
|
111
|
+
|
|
112
|
+
It's fine to take 4–6 messages to get through this — don't try to compress it into one wall of text.
|
package/template/CLAUDE.md
CHANGED
|
@@ -197,7 +197,8 @@ Tags grow forward with `, ` separator. Notes grow forward with `| ` separator
|
|
|
197
197
|
**Outreach scripts (Profile B):** `tools/outreach/` — `daily-targets.js`, `flush-targets.js`, `flush-updates.js`, `flush-accepts.js`, `flush-inbox.js`, `sync-metrics.js`, `update-dashboard.js`, `stats.js`, `lookup.js`, `stale-invitations.js`.
|
|
198
198
|
|
|
199
199
|
**Skills (`.claude/commands/`):**
|
|
200
|
-
- `
|
|
200
|
+
- `welcome` — first-session tour (runs once, after `npx create-battle-plan`). Introduces the system, detects project archetype, tailors onboarding.
|
|
201
|
+
- `good-morning` — morning standup (metrics + today.md + events context-debt check). Refuses to run until `/welcome` has completed.
|
|
201
202
|
- `wrap-up` — end-of-day reconciliation + task hygiene + events gate
|
|
202
203
|
- `weekly-triage` — task-pile sweep
|
|
203
204
|
- `distill` — compress chronological / amended doc history
|