create-team-foundry 2.1.3 → 3.0.1

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 (3) hide show
  1. package/README.md +81 -23
  2. package/dist/index.js +946 -41
  3. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -1,12 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import fs4 from "fs/promises";
5
- import path4 from "path";
6
- import { outro as outro2, log, confirm } from "@clack/prompts";
4
+ import fs5 from "fs/promises";
5
+ import path5 from "path";
6
+ import { outro as outro3, log as log2, confirm as confirm2, select as select2, isCancel as isCancel2 } from "@clack/prompts";
7
7
 
8
8
  // src/prompts.ts
9
9
  import { intro, select, text, outro, isCancel } from "@clack/prompts";
10
+ function questionCount(profile, federated, ingestion) {
11
+ if (profile === void 0) return 5;
12
+ const hasLocalPath = ingestion === "local" || ingestion === "repo+local";
13
+ if (profile === "solo") return hasLocalPath ? 5 : 4;
14
+ const base = federated ? 6 : 5;
15
+ return hasLocalPath ? base + 1 : base;
16
+ }
10
17
  function cancelIfNeeded(value) {
11
18
  if (isCancel(value)) {
12
19
  outro("Cancelled.");
@@ -33,6 +40,8 @@ async function runPrompts() {
33
40
  ]
34
41
  });
35
42
  cancelIfNeeded(profile);
43
+ const profileTyped = profile;
44
+ const totalNoFed = questionCount(profileTyped, false, void 0);
36
45
  const repoVisibility = await select({
37
46
  message: "Is this repo public, internal-only, or private?",
38
47
  options: [
@@ -54,6 +63,8 @@ async function runPrompts() {
54
63
  cancelIfNeeded(federatedAnswer);
55
64
  federated = federatedAnswer === "federated";
56
65
  }
66
+ const federatedResolved = federated ?? false;
67
+ const ingestionQ = profileTyped === "solo" ? 4 : 5;
57
68
  const ingestion = await select({
58
69
  message: "Do you have existing docs to ingest?\n (Strategy docs, old roadmaps, customer research \u2014 the interview uses them to pre-populate answers)",
59
70
  options: [
@@ -70,6 +81,7 @@ async function runPrompts() {
70
81
  cancelIfNeeded(ingestion);
71
82
  let ingestionPath;
72
83
  if (ingestion === "local" || ingestion === "repo+local") {
84
+ const total = questionCount(profileTyped, federatedResolved, ingestion);
73
85
  const rawPath = await text({
74
86
  message: "Path to the folder containing your docs?",
75
87
  placeholder: "./docs or /Users/you/exports",
@@ -225,6 +237,328 @@ Read \`glossary.md\` when writing specs, copy, or anything using domain-specific
225
237
  `;
226
238
  }
227
239
 
240
+ // src/templates/skills/team-foundry-intro.ts
241
+ function introSkillTemplate(_ctx) {
242
+ return `---
243
+ description: Team and product overview \u2014 great for onboarding a new session or new team member
244
+ ---
245
+
246
+ # /team-foundry-intro
247
+
248
+ You have been asked to orient yourself to this team's product context.
249
+
250
+ ## What to do
251
+
252
+ Read the following files in order. After reading each one, note what you learned and what questions it raises.
253
+
254
+ 1. **Who we are and why we exist**
255
+ - \`team-foundry/product/north-star.md\` \u2014 mission, long-term vision, success metric
256
+ - \`team-foundry/product/customers.md\` \u2014 who we serve, validated segments, pain points
257
+
258
+ 2. **What we're working toward**
259
+ - \`team-foundry/product/outcomes.md\` \u2014 current cycle outcomes and their status
260
+ - \`team-foundry/product/strategy.md\` \u2014 how we're choosing to win (if present)
261
+
262
+ 3. **How we work**
263
+ - \`team-foundry/team/trio.md\` \u2014 PM, engineering lead, design lead (if present)
264
+ - \`team-foundry/team/working-agreement.md\` \u2014 norms and expectations (if present)
265
+
266
+ 4. **What we're building on**
267
+ - \`team-foundry/engineering/stack.md\` \u2014 tech stack, key constraints
268
+ - \`team-foundry/engineering/quality-bar.md\` \u2014 definition of done (if present)
269
+
270
+ 5. **Shared language**
271
+ - \`team-foundry/context/glossary.md\` \u2014 domain terms (if present)
272
+
273
+ ## After reading
274
+
275
+ Summarize what you know in three parts:
276
+
277
+ **This team exists to:** [one sentence from north-star]
278
+
279
+ **Right now they're focused on:** [current outcomes in plain language]
280
+
281
+ **Key constraints I should keep in mind:** [stack, quality bar, working agreement norms]
282
+
283
+ Then ask: "Is there anything outdated or missing from these files before we start?"
284
+
285
+ ## Files that may not exist
286
+
287
+ For solo profile setups, only a subset of these files is present. Skip any that are missing \u2014 do not report them as errors.
288
+ `;
289
+ }
290
+
291
+ // src/templates/skills/team-foundry-status.ts
292
+ function statusSkillTemplate(_ctx) {
293
+ return `---
294
+ description: Current sprint/cycle status \u2014 what's on track, what's at risk, what's blocked
295
+ ---
296
+
297
+ # /team-foundry-status
298
+
299
+ You have been asked for a status read on this team's current work.
300
+
301
+ ## What to do
302
+
303
+ Read these files:
304
+
305
+ - \`team-foundry/product/outcomes.md\` \u2014 current cycle outcomes and their validation status
306
+ - \`team-foundry/product/now-next-later.md\` \u2014 what's in flight, what's queued (if present)
307
+ - \`team-foundry/product/assumptions.md\` \u2014 open assumptions that could invalidate work (if present)
308
+ - \`team-foundry/product/risks.md\` \u2014 known risks and their mitigations (if present)
309
+
310
+ ## Report format
311
+
312
+ Produce a status report in this structure:
313
+
314
+ ### On track
315
+ [Outcomes or work items that are validated and progressing. Cite the source field or inline evidence if present.]
316
+
317
+ ### At risk
318
+ [Work that lacks validation, has an open assumption underneath it, or has a known risk with no mitigation. Be specific about what's missing.]
319
+
320
+ ### Blocked
321
+ [Anything explicitly marked blocked or waiting on a dependency.]
322
+
323
+ ### Gaps I noticed
324
+ [Files that exist but have placeholder content. Sections that are empty. Frontmatter with no \`last_validated\` date on items marked Validated. Claims in a Validated section with no \`source:\` reference.]
325
+
326
+ ## Tone
327
+
328
+ Be direct. "No validated outcomes this cycle" is a useful status. "Everything looks great" when files have placeholders is not.
329
+
330
+ If the files are mostly empty or stubbed, say: "The team-foundry files exist but haven't been filled in yet. The highest-value thing right now is completing [most important missing section]."
331
+ `;
332
+ }
333
+
334
+ // src/templates/skills/team-foundry-review.ts
335
+ function reviewSkillTemplate(_ctx) {
336
+ return `---
337
+ description: Full team-foundry audit by the coach \u2014 all files, findings by severity
338
+ ---
339
+
340
+ # /team-foundry-review
341
+
342
+ You have been asked to audit this team's team-foundry files and act as a diagnostic coach.
343
+
344
+ ## What to do
345
+
346
+ Read every team-foundry file that exists:
347
+
348
+ - \`team-foundry/product/\` \u2014 all files
349
+ - \`team-foundry/team/\` \u2014 all files (if present)
350
+ - \`team-foundry/engineering/\` \u2014 all files
351
+ - \`team-foundry/design/\` \u2014 all files (if present)
352
+ - \`team-foundry/data/\` \u2014 all files (if present)
353
+ - \`team-foundry/context/\` \u2014 all files (if present)
354
+ - \`.team-foundry/coach.md\` \u2014 coaching playbook (if present)
355
+
356
+ ## Audit findings format
357
+
358
+ Group findings by severity:
359
+
360
+ ### Critical \u2014 acting on bad information
361
+ [Claims marked Validated with no \`source:\` reference or inline evidence. Outcomes with no signal. Decisions that contradict each other. These are the cases where the AI is most likely to give wrong advice.]
362
+
363
+ ### Important \u2014 gaps that limit usefulness
364
+ [Files with placeholder content. Missing sections for the profile. Stale \`last_validated\` dates (>90 days). Outcomes with no owner. Assumptions with no test plan.]
365
+
366
+ ### Suggestions \u2014 would strengthen the context
367
+ [Things that would make AI answers more specific: adding a source to a hypothesis, filling in a glossary term, adding a stakeholder. Low urgency.]
368
+
369
+ ## After findings
370
+
371
+ Recommend the top 3 actions in priority order. For each one, say:
372
+ - What to do (one sentence)
373
+ - Which file to update
374
+ - Why it matters for AI accuracy
375
+
376
+ ## Tone
377
+
378
+ Name gaps directly. "outcomes.md has four outcomes, three of which are placeholders with no signal" is useful. Softening findings makes the audit pointless.
379
+ `;
380
+ }
381
+
382
+ // src/templates/skills/team-foundry-capture.ts
383
+ function captureSkillTemplate(_ctx) {
384
+ return `---
385
+ description: Capture what was learned in this session into the right team-foundry file
386
+ ---
387
+
388
+ # /team-foundry-capture
389
+
390
+ You have been asked to capture what was learned or decided in this session into the team's team-foundry files.
391
+
392
+ ## What to do
393
+
394
+ **Do not write any file until you have shown the proposed content and received explicit confirmation.**
395
+
396
+ ### Step 1 \u2014 identify what's worth keeping
397
+
398
+ Review this conversation for:
399
+
400
+ - **Validated claims**: something that was confirmed by data, user research, or a real event (e.g., "we tested X and users did Y")
401
+ - **Decisions**: something the team chose to do or not do, and why
402
+ - **Invalidated assumptions**: something the team believed that turned out to be wrong
403
+ - **New risks or blockers**: something that emerged that could affect the work
404
+ - **Glossary terms**: domain language that came up and should be defined for the AI
405
+
406
+ Ignore: process discussions, brainstorming that went nowhere, anything already in the files.
407
+
408
+ ### Step 2 \u2014 map to the right file
409
+
410
+ | What you learned | Where it goes |
411
+ |------------------|---------------|
412
+ | An outcome is now validated | \`team-foundry/product/outcomes.md\` \u2014 move to Validated section, add source |
413
+ | A customer segment was confirmed | \`team-foundry/product/customers.md\` \u2014 add to Validated with source |
414
+ | An assumption was tested | \`team-foundry/product/assumptions.md\` \u2014 update status, add result |
415
+ | A key decision was made | \`team-foundry/engineering/decisions/\` \u2014 new ADR or update existing |
416
+ | A new risk surfaced | \`team-foundry/product/risks.md\` \u2014 add with impact and mitigation |
417
+ | A term needs defining | \`team-foundry/context/glossary.md\` \u2014 add entry |
418
+ | A metric moved | \`team-foundry/data/metrics.md\` \u2014 update with date and source |
419
+
420
+ ### Step 3 \u2014 write the update
421
+
422
+ For each piece of learning, draft the update text and show it to the team before writing. Include:
423
+
424
+ - The claim or decision in plain language
425
+ - The source or evidence (who said it, what data, what date)
426
+ - For validated items: update \`last_validated:\` in the frontmatter if stale
427
+
428
+ ### Step 4 \u2014 confirm and write
429
+
430
+ Show the proposed changes. Ask: "Should I write these to the files?"
431
+
432
+ Do not write anything without confirmation.
433
+ `;
434
+ }
435
+
436
+ // src/templates/skills/team-foundry-decision.ts
437
+ function decisionSkillTemplate(_ctx) {
438
+ return `---
439
+ description: Draft a new architecture decision record (ADR) from the current conversation
440
+ ---
441
+
442
+ # /team-foundry-decision
443
+
444
+ You have been asked to draft an Architecture Decision Record (ADR) from this conversation.
445
+
446
+ ## What to do
447
+
448
+ **Do not write any file until you have shown the proposed content and received explicit confirmation.**
449
+
450
+ ### Step 1 \u2014 extract the decision
451
+
452
+ From the conversation, identify:
453
+
454
+ - **The decision**: what was chosen (one sentence)
455
+ - **The context**: what problem were we solving, what constraints existed
456
+ - **The options considered**: what else was on the table (even if briefly)
457
+ - **The rationale**: why this option over the others
458
+ - **The consequences**: what becomes easier, what becomes harder, what we're committing to
459
+
460
+ If any of these are unclear, ask before drafting.
461
+
462
+ ### Step 2 \u2014 draft the ADR
463
+
464
+ Use this format:
465
+
466
+ \`\`\`markdown
467
+ ---
468
+ title: [Short descriptive title]
469
+ date: [today's date]
470
+ status: Proposed
471
+ ---
472
+
473
+ ## Context
474
+ [What situation or problem led to this decision. What constraints were in play.]
475
+
476
+ ## Decision
477
+ [The choice made, stated as a clear action or commitment.]
478
+
479
+ ## Options considered
480
+ - **[Option A]**: [Brief description and why it was considered]
481
+ - **[Option B]**: [Brief description and why it was considered]
482
+
483
+ ## Rationale
484
+ [Why this option over the others. What evidence or reasoning tipped it.]
485
+
486
+ ## Consequences
487
+ - What becomes easier: [...]
488
+ - What becomes harder: [...]
489
+ - What we're committing to: [...]
490
+ \`\`\`
491
+
492
+ ### Step 3 \u2014 pick the file name
493
+
494
+ ADRs go in \`team-foundry/engineering/decisions/\`. Use the format \`NNN-short-title.md\` where NNN is the next sequential number. List the files in \`team-foundry/engineering/decisions/\` to find the highest existing number, then use that number + 1. If the directory is empty or does not exist, start at \`001\`.
495
+
496
+ ### Step 4 \u2014 confirm and write
497
+
498
+ Show the draft. Ask: "Should I write this to \`team-foundry/engineering/decisions/[filename].md\`?"
499
+
500
+ Do not write without confirmation.
501
+ `;
502
+ }
503
+
504
+ // src/templates/skills/team-foundry-feature.ts
505
+ function featureSkillTemplate(_ctx) {
506
+ return `---
507
+ description: Synthesize everything team-foundry knows about a specific feature or work item
508
+ ---
509
+
510
+ # /team-foundry-feature
511
+
512
+ You have been asked to synthesize the full team context for a specific feature or work item.
513
+
514
+ ## What to do
515
+
516
+ Ask (or infer from the conversation): **what feature or work item are we focusing on?**
517
+
518
+ Then read the team-foundry files for everything relevant to it:
519
+
520
+ ### Product context
521
+ - \`team-foundry/product/outcomes.md\` \u2014 which outcome(s) does this feature serve?
522
+ - \`team-foundry/product/customers.md\` \u2014 which customer segment is this for?
523
+ - \`team-foundry/product/now-next-later.md\` \u2014 where does this sit in the roadmap? (if present)
524
+ - \`team-foundry/product/assumptions.md\` \u2014 what assumptions is this feature resting on? (if present)
525
+
526
+ ### Design and quality
527
+ - \`team-foundry/design/principles.md\` \u2014 design constraints that apply (if present)
528
+ - \`team-foundry/engineering/quality-bar.md\` \u2014 definition of done (if present)
529
+
530
+ ### Prior decisions
531
+ - \`team-foundry/engineering/decisions/\` \u2014 any ADRs that affect this feature's approach
532
+
533
+ ### Domain context
534
+ - \`team-foundry/context/glossary.md\` \u2014 terms relevant to this feature (if present)
535
+
536
+ ## Output format
537
+
538
+ Produce a feature brief:
539
+
540
+ **Feature:** [name]
541
+
542
+ **Why this matters:** [which validated outcome it moves, for which customer segment]
543
+
544
+ **Assumptions underneath it:** [open assumptions from assumptions.md that this feature depends on]
545
+
546
+ **Design constraints:** [relevant principles or quality bar items]
547
+
548
+ **Prior decisions that shape the approach:** [relevant ADRs]
549
+
550
+ **What the AI should keep in mind while working on this:** [anything that would change how code is written, what trade-offs to make, what not to do]
551
+
552
+ ## If files are sparse
553
+
554
+ If the team-foundry files don't have enough to answer the questions above, say so explicitly:
555
+
556
+ "I can see this feature targets [X], but there's no validated customer segment in customers.md to confirm who it's for. Before we build, it's worth checking whether [assumption] holds."
557
+
558
+ Don't invent context. Surface the gap instead.
559
+ `;
560
+ }
561
+
228
562
  // src/templates/root-claude.ts
229
563
  function rootClaudeTemplate(ctx) {
230
564
  return `---
@@ -276,7 +610,8 @@ before answering. Files with recent \`last_updated\` dates are more reliable tha
276
610
  | Design principles and tone | \`team-foundry/design/principles.md\` |
277
611
  | Metric definitions | \`team-foundry/data/metrics.md\` |
278
612
  | Domain terms and acronyms | \`team-foundry/context/glossary.md\` |
279
- | Stakeholders and what they care about | \`team-foundry/context/stakeholders.md\` |
613
+ | Stakeholders and what they care about | \`team-foundry/context/stakeholders.md\` |${ctx.profile === "full" ? `
614
+ | Which source wins when context conflicts | \`.team-foundry/hierarchy.md\` |` : ""}
280
615
 
281
616
  ## Coach
282
617
 
@@ -300,6 +635,19 @@ it notices something relevant to your current work. You can also invoke it direc
300
635
  - Inline mode nudges: if you notice a clear gap in a team-foundry file while answering
301
636
  a normal question, surface it in one sentence \u2014 without loading the full coach.md.
302
637
  Keep it brief and non-blocking. Do not coach unprompted on back-to-back messages. -->
638
+
639
+ ## Skills
640
+
641
+ Pre-built Claude Code skills are in \`.claude/skills/\`. Invoke them with a slash command:
642
+
643
+ | Skill | What it does |
644
+ |---|---|
645
+ | \`/team-foundry-intro\` | Orient to the team \u2014 reads all context files, produces a summary |
646
+ | \`/team-foundry-status\` | Status read \u2014 what's on track, at risk, or blocked this cycle |
647
+ | \`/team-foundry-review\` | Full audit \u2014 all files checked, findings by severity |
648
+ | \`/team-foundry-capture\` | Capture what was learned in this session into the right file |
649
+ | \`/team-foundry-decision\` | Draft an ADR from the current conversation |
650
+ | \`/team-foundry-feature\` | Synthesize everything team-foundry knows about a specific feature |
303
651
  `;
304
652
  }
305
653
 
@@ -354,7 +702,8 @@ before answering. Files with recent \`last_updated\` dates are more reliable tha
354
702
  | Design principles and tone | \`team-foundry/design/principles.md\` |
355
703
  | Metric definitions | \`team-foundry/data/metrics.md\` |
356
704
  | Domain terms and acronyms | \`team-foundry/context/glossary.md\` |
357
- | Stakeholders and what they care about | \`team-foundry/context/stakeholders.md\` |
705
+ | Stakeholders and what they care about | \`team-foundry/context/stakeholders.md\` |${ctx.profile === "full" ? `
706
+ | Which source wins when context conflicts | \`.team-foundry/hierarchy.md\` |` : ""}
358
707
 
359
708
  ## Coach
360
709
 
@@ -463,7 +812,7 @@ it notices something relevant to your current work. You can also invoke it direc
463
812
  // src/templates/getting-started.ts
464
813
  function gettingStartedTemplate(ctx) {
465
814
  const toolName = ctx.tool === "gemini" ? "Gemini CLI" : ctx.tool === "both" ? "Claude Code or Gemini CLI" : "Claude Code";
466
- const questionCount = ctx.profile === "solo" ? "10" : "18\u201325";
815
+ const questionCount2 = ctx.profile === "solo" ? "10" : "18\u201325";
467
816
  const fileCount = ctx.profile === "solo" ? "7" : "19";
468
817
  const ingestionNote = ctx.ingestionPath ? `
469
818
  > **Tip:** Before saying the phrase above, tell ${toolName}:
@@ -485,7 +834,7 @@ You've scaffolded ${fileCount} files. They're mostly empty. One thing to do now:
485
834
 
486
835
  > Open this project in **${toolName}** and say: **"Let's set up our team-foundry."**
487
836
  ${ingestionNote}
488
- The AI will walk you through a setup conversation \u2014 ${questionCount} questions, about 30 minutes.
837
+ The AI will walk you through a setup conversation \u2014 ${questionCount2} questions, about 30 minutes.
489
838
  By the end, most files will be meaningfully populated.
490
839
 
491
840
  ## What the setup covers
@@ -549,7 +898,7 @@ You can delete this file once the onboarding interview is complete.
549
898
  // src/templates/coach.ts
550
899
  function coachTemplate(ctx) {
551
900
  const isSolo = ctx.profile === "solo";
552
- const questionCount = isSolo ? "9" : "18\u201325";
901
+ const questionCount2 = isSolo ? "9" : "18\u201325";
553
902
  const timeEstimate = isSolo ? "15\u201320 minutes" : "25\u201335 minutes";
554
903
  const featureQueryFullSteps = isSolo ? "" : `3. Read \`team-foundry/product/now-next-later.md\` \u2014 find the feature's current status (Now / Next / Later / shipped)
555
904
  4. Read \`team-foundry/product/assumptions.md\` \u2014 find any assumptions linked to this feature
@@ -671,7 +1020,7 @@ context of their actual work.
671
1020
  **How to behave:**
672
1021
  - Run all active coaching behaviors in priority order: B1 (outputs-vs-outcomes) \u2192
673
1022
  B2 (customer staleness) \u2192 B3 (stale assumptions) \u2192 B4 (decisions without rationale) \u2192
674
- ... \u2192 B12 (MCP suggestions) \u2192 discovery and strategy behaviors
1023
+ ... \u2192 B12 (MCP suggestions) \u2192 discovery and strategy behaviors \u2192 B18 (unsourced claims, full only)
675
1024
  - For each issue found: name it specifically (cite the file and exact content),
676
1025
  explain why it matters in one sentence, offer to draft the fix
677
1026
  - Group findings by severity: blockers (things actively misleading the AI or the team)
@@ -814,7 +1163,12 @@ window (inline mode) or until the next explicit review (explicit/scheduled mode)
814
1163
 
815
1164
  ## Context priority
816
1165
 
817
- When two team-foundry files appear to contradict each other, resolve using this
1166
+ ${!isSolo ? `Before reconciling any conflicting signals, read \`.team-foundry/hierarchy.md\`.
1167
+ It defines the authoritative precedence order across file types, data sources, expertise levels,
1168
+ and version authority. Apply it. Do not average conflicting claims \u2014 name the conflict and state
1169
+ which source wins.
1170
+
1171
+ ` : ""}When two team-foundry files appear to contradict each other, resolve using this
818
1172
  order and **name the conflict explicitly** rather than silently picking one:
819
1173
 
820
1174
  1. \`north-star.md\` \u2014 destination, never overridden
@@ -854,7 +1208,7 @@ The team-foundry files are the index. ${featureQueryIndexNote}
854
1208
 
855
1209
  ## Behaviors
856
1210
 
857
- Behaviors run in priority order (B1\u2192B12, then discovery and strategy behaviors). In explicit mode, run all of them.
1211
+ Behaviors run in priority order (B1\u2192B12, then discovery and strategy behaviors; B18\u2013B20 full profile only). In explicit mode, run all of them.
858
1212
  In inline mode, run only the highest-priority behavior whose inline trigger condition
859
1213
  is met for the user's current question. If multiple triggers apply, pick the
860
1214
  highest-priority one \u2014 do not surface multiple behaviors in a single inline nudge.
@@ -1475,12 +1829,98 @@ offer to retire it: move it from Active rules to Retired rules with today's date
1475
1829
  check if \`.team-foundry/team-lessons.md\` exists. If it does, load it and apply Active rules
1476
1830
  with equal weight to built-in behaviors, scoped to this team's context.
1477
1831
 
1832
+ **Knowledge routing:** B17 handles team process patterns only \u2014 things that should shape future coaching.
1833
+ For other types of learning surfaced in a session, route to the right file:
1834
+
1835
+ | What was learned | Where it goes | How |
1836
+ |---|---|---|
1837
+ | A team process pattern | \`.team-foundry/team-lessons.md\` | B17 (this behavior) |
1838
+ | A validated claim or customer insight | \`team-foundry/product/outcomes.md\` or \`customers.md\` | Offer \`/team-foundry-capture\` (B20) |
1839
+ | An architectural decision | \`team-foundry/engineering/decisions/\` (full profile only) | Offer \`/team-foundry-decision\` |
1840
+ | A tested assumption | \`team-foundry/product/assumptions.md\` (full profile only) | Offer \`/team-foundry-capture\` (B20) |
1841
+ | A new risk | \`team-foundry/product/risks.md\` (full profile only) | Offer \`/team-foundry-capture\` (B20) |
1842
+
1843
+ Do not mix routing: if the user names a pattern, use B17. If the session surfaces validated data, use B20.
1844
+
1478
1845
  **What not to do:** Do not proactively suggest adding rules unless the user explicitly names a pattern.
1479
1846
  This behavior is listener-only \u2014 it waits for the signal, it does not fish for it.
1480
1847
 
1481
1848
  ---
1482
1849
 
1483
- ## Quarterly retrospective
1850
+ ${!isSolo ? `### Behavior 18: Unsourced quantitative claim
1851
+
1852
+ **Severity:** Low \u2014 flag once, do not repeat in the same conversation.
1853
+
1854
+ **Trigger condition:** A team-foundry file contains a number, percentage, or currency figure
1855
+ (e.g. "62% win rate", "saves 4 hours/week", "$1.2M ARR") without either:
1856
+ - a \`source:\` frontmatter field that is not \`~\`, or
1857
+ - an inline source reference in the form \`(source, date)\` near the claim.
1858
+
1859
+ **What to say:**
1860
+ > "I noticed a quantitative claim in [filename]: '[the claim]'. I don't see a source for it.
1861
+ > If this comes from data, add a \`source:\` value to the frontmatter or an inline note like
1862
+ > \`(source, date)\` next to the figure. If it's an estimate or assumption, consider moving
1863
+ > it to assumptions.md so the team knows it hasn't been validated."
1864
+
1865
+ **What not to do:**
1866
+ - Do not flag every file on every review \u2014 one pass per file per session.
1867
+ - Do not treat the absence of a source as an error; it's a signal to investigate, not a blocker.
1868
+ - Do not ask for sources on qualitative statements (opinions, framings, examples).
1869
+
1870
+ ---
1871
+
1872
+ ### Behavior 19: Validated entry without evidence
1873
+
1874
+ **Severity:** Medium \u2014 the word "Validated" makes an implicit promise to readers.
1875
+
1876
+ **Trigger condition:** An entry appears under a validated section in any of the three target files
1877
+ but contains no source reference \u2014 neither an inline \`(source, date)\` near the entry, nor a
1878
+ populated \`source:\` frontmatter field in the same file.
1879
+
1880
+ The validated section headers are file-specific:
1881
+ - \`customers.md\` \u2192 \`## Validated\`
1882
+ - \`outcomes.md\` \u2192 \`## Validated outcomes\`
1883
+ - \`now-next-later.md\` \u2192 \`## Now (validated outcomes)\`
1884
+
1885
+ **What to say:**
1886
+ > "[filename] has an entry in the validated section that doesn't cite evidence:
1887
+ > '[the entry]'. Validated sections carry an implicit promise \u2014 readers trust them more.
1888
+ > If this is backed by data, add a source reference inline \`(source, date)\` or in the
1889
+ > file's \`source:\` frontmatter. If it's still an assumption, move it to the hypothesized
1890
+ > section so the team reads it with the right level of trust."
1891
+
1892
+ **What not to do:**
1893
+ - Do not flag entries in hypothesized sections \u2014 those explicitly signal unvalidated beliefs.
1894
+ - Do not demand a specific source format \u2014 any inline note or frontmatter value is sufficient.
1895
+ - Do not block work; surface the gap and offer to help relocate the entry if the team prefers.
1896
+
1897
+ ---
1898
+
1899
+ ### Behavior 20: Session-end knowledge capture
1900
+
1901
+ **Severity:** Low \u2014 this is an offer, not a finding.
1902
+
1903
+ **Trigger condition:** The session included at least one of:
1904
+ - A claim that was confirmed or invalidated by data, research, or a real event
1905
+ - A decision the team made (what to build, what to cut, what approach to take)
1906
+ - A risk or blocker that surfaced and was not previously documented
1907
+
1908
+ Do not trigger if the session was purely a coding or debugging session with no product/team learning.
1909
+
1910
+ **What to say:**
1911
+ > "Before we close \u2014 this session surfaced [brief summary: e.g., 'a validated customer segment' / 'a decision about X' / 'a new risk around Y']. Want me to run \`/team-foundry-capture\` to save it to the right files? It takes about two minutes and keeps the context current for future sessions."
1912
+
1913
+ Only proceed after the user explicitly confirms. The conversation-as-update confirmation rules apply \u2014 silence or an ambiguous response is not a yes.
1914
+
1915
+ **What not to do:**
1916
+ - Do not trigger on every session \u2014 only when there is something clearly worth preserving.
1917
+ - Do not run \`/team-foundry-capture\` without the team saying yes.
1918
+ - Do not summarize the entire conversation \u2014 name the specific learnable item only.
1919
+ - Do not offer if the user has already run \`/team-foundry-capture\` in this session.
1920
+
1921
+ ---
1922
+
1923
+ ` : ""}## Quarterly retrospective
1484
1924
 
1485
1925
  ### Trigger
1486
1926
 
@@ -1898,7 +2338,7 @@ outdated. Every answer needs the user's confirmation before it becomes a file.
1898
2338
 
1899
2339
  **Opening framing** (say this verbatim \u2014 the question count, time estimate, and file-writing detail are load-bearing):
1900
2340
 
1901
- > "We're going to set up your team-foundry \u2014 ${questionCount} questions across
2341
+ > "We're going to set up your team-foundry \u2014 ${questionCount2} questions across
1902
2342
  > 9 themes. Each answer goes directly into a file as we go,
1903
2343
  > so you'll see the files populate in real time. You can skip anything you don't
1904
2344
  > have an answer to right now \u2014 I'll mark it as a gap instead of leaving it blank.
@@ -2317,7 +2757,9 @@ function outcomesTemplate(ctx) {
2317
2757
  purpose: Current quarter outcomes \u2014 the changes in customer behavior that define success this quarter
2318
2758
  read_when: Prioritizing work, writing specs, deciding what to build next, evaluating tradeoffs
2319
2759
  last_updated: ${ctx.date}
2320
- owner:
2760
+ last_validated: ~
2761
+ source: ~
2762
+ owner:
2321
2763
  ---
2322
2764
 
2323
2765
  # Outcomes
@@ -2336,7 +2778,24 @@ owner:
2336
2778
  "Write your outcomes in the form 'we want X to change for Y customer segment.'
2337
2779
  What does winning this quarter look like for your customers, not your roadmap?" -->
2338
2780
 
2339
- ## This quarter
2781
+ ${ctx.profile === "full" ? `## Validated outcomes
2782
+
2783
+ <!-- Outcomes confirmed by data: cohort analysis, retention curves, usage metrics, or direct
2784
+ customer evidence. Each entry needs a source \u2014 inline (source, date) or in the frontmatter.
2785
+ If you can't name the evidence, move it to Hypothesized.
2786
+
2787
+ Format: outcome statement + evidence reference + date confirmed.
2788
+ Example: "New sellers list their first item within 48h of signup (cohort data, Q1 2026)" -->
2789
+
2790
+ ## Hypothesized outcomes
2791
+
2792
+ <!-- Outcomes you believe are worth pursuing but haven't yet validated with data.
2793
+ State the assumption and what would confirm it.
2794
+
2795
+ Format: outcome statement + assumption + validation signal.
2796
+ Example: "Ops managers close reconciliation in <30 min \u2014 assumed from 3 sales calls.
2797
+ Validate: measure time-on-task for 5 ops managers over one cycle." -->
2798
+ ` : `## This quarter
2340
2799
 
2341
2800
  <!-- List 2\u20134 outcome statements. Each should be falsifiable \u2014 you'll know at quarter-end
2342
2801
  whether it happened.
@@ -2351,6 +2810,7 @@ owner:
2351
2810
  - "Ship the new dashboard" (feature, not behavior change)
2352
2811
  - "Complete the API integration" (milestone, not customer outcome)
2353
2812
  - "Improve retention" (direction, not a measurable change) -->
2813
+ `}
2354
2814
  `;
2355
2815
  }
2356
2816
 
@@ -2361,7 +2821,9 @@ function customersTemplate(ctx) {
2361
2821
  purpose: Named customers, personas, jobs to be done, and direct quotes from real conversations
2362
2822
  read_when: Writing specs, prioritizing features, evaluating tradeoffs, any time you're guessing what customers want
2363
2823
  last_updated: ${ctx.date}
2364
- owner:
2824
+ last_validated: ~
2825
+ source: ~
2826
+ owner:
2365
2827
  ---
2366
2828
 
2367
2829
  # Customers
@@ -2385,7 +2847,40 @@ ${visibilityNote}
2385
2847
  "Name three customers you've spoken to directly. For each: what did you learn,
2386
2848
  and when was the last time you talked to them?" -->
2387
2849
 
2388
- ## Personas
2850
+ ${ctx.profile === "full" ? `## Validated
2851
+
2852
+ <!-- Customers confirmed by cohort data, win-rate analysis, or direct evidence.
2853
+ Every entry here needs a source \u2014 inline (source, date) or in the file's source: frontmatter.
2854
+ If you can't name the evidence, move the entry to Hypothesized.
2855
+
2856
+ Format per entry:
2857
+ ### [Segment name \u2014 e.g. "IT consultancies, 20\u201350 staff"]
2858
+ **Win rate / evidence:** [e.g. "62% win rate (cohort data, Q4 2025)"]
2859
+ **Job to be done:** When [situation], I want to [motivation], so I can [expected outcome].
2860
+ **Last direct contact:** YYYY-MM-DD
2861
+ **Quote:** "[Something a real customer said]" -->
2862
+
2863
+ ## Hypothesized
2864
+
2865
+ <!-- Customers you believe are a fit but haven't validated with data.
2866
+ State the assumption explicitly and what would confirm it.
2867
+
2868
+ Format per entry:
2869
+ ### [Segment name]
2870
+ **Why we believe this:** [reasoning \u2014 pattern from sales calls, founder intuition, etc.]
2871
+ **What would validate it:** [specific signal \u2014 cohort analysis, X wins in segment, etc.]
2872
+ **Last direct contact:** YYYY-MM-DD (or "never") -->
2873
+
2874
+ ## Anti-ICP
2875
+
2876
+ <!-- Explicitly who this product is NOT for \u2014 with reasoning.
2877
+ Anti-ICP entries prevent scope creep and help the AI avoid suggesting work
2878
+ that serves the wrong customer. Be explicit about why they're excluded.
2879
+
2880
+ Format per entry:
2881
+ ### [Segment name]
2882
+ **Why excluded:** [specific reason \u2014 wrong pain, wrong scale, misaligned incentives, etc.] -->
2883
+ ` : `## Personas
2389
2884
 
2390
2885
  <!-- For each persona below:
2391
2886
  - Give them a name or a specific role (not a label like "power user")
@@ -2406,6 +2901,7 @@ ${visibilityNote}
2406
2901
  **Quote:** "[Something they actually said]"
2407
2902
  **What we learned:** [The non-obvious thing \u2014 the thing that would surprise an outsider]
2408
2903
  -->
2904
+ `}
2409
2905
  `;
2410
2906
  }
2411
2907
 
@@ -2415,7 +2911,9 @@ function nowNextLaterTemplate(ctx) {
2415
2911
  purpose: What we're building now, what we're committed to next, and what's directional
2416
2912
  read_when: Sprint planning, stakeholder updates, evaluating new requests, prioritization discussions
2417
2913
  last_updated: ${ctx.date}
2418
- owner:
2914
+ last_validated: ~
2915
+ source: ~
2916
+ owner:
2419
2917
  ---
2420
2918
 
2421
2919
  # Now / Next / Later
@@ -2442,7 +2940,39 @@ owner:
2442
2940
  What have you committed to doing after that?
2443
2941
  What's directional but not yet committed?" -->
2444
2942
 
2445
- ## Now
2943
+ ${ctx.profile === "full" ? `## Now (validated outcomes)
2944
+
2945
+ <!-- Active work connected to a validated outcome \u2014 something backed by data or direct
2946
+ customer evidence. Each item has an outcome reference and a source.
2947
+
2948
+ Format: item \u2192 outcome reference | evidence | owner | started date.
2949
+ Example:
2950
+ - **Self-serve report fix flow** \u2192 "ops managers close reconciliation in <30 min"
2951
+ Evidence: usability study (March 2026) | Owner: [name] | Started: [date] -->
2952
+
2953
+ ## Now (hypothesized outcomes)
2954
+
2955
+ <!-- Active work connected to a hypothesized outcome \u2014 believed to matter but not yet
2956
+ validated. These are bets, not commitments. The outcome link should be explicit so the
2957
+ AI can flag it when the hypothesis is later confirmed or invalidated.
2958
+
2959
+ Format: item \u2192 hypothesis | what would validate | owner.
2960
+ Example:
2961
+ - **Onboarding redesign** \u2192 hypothesis: "new sellers list first item within 48h"
2962
+ Validate: track time-to-first-listing for next cohort | Owner: [name] -->
2963
+
2964
+ ## Next
2965
+
2966
+ <!-- Committed work, sequenced. Whether linked to a validated or hypothesized outcome,
2967
+ the commitment is real \u2014 there's a reason this follows from what's in Now.
2968
+
2969
+ If everything in Next could plausibly be first, it isn't sequenced \u2014 it's a list. -->
2970
+
2971
+ ## Later
2972
+
2973
+ <!-- Directional bets. Not scheduled, not promised. Label each with its outcome hypothesis
2974
+ so the AI can flag it if that hypothesis is validated or invalidated before you get here. -->
2975
+ ` : `## Now
2446
2976
 
2447
2977
  <!-- Active work. For each item: what it is, which outcome it serves, who owns it.
2448
2978
 
@@ -2466,6 +2996,7 @@ owner:
2466
2996
 
2467
2997
  It's okay for "later" to be short. A short, honest "later" is better than a long,
2468
2998
  wishful one. -->
2999
+ `}
2469
3000
  `;
2470
3001
  }
2471
3002
 
@@ -2475,7 +3006,9 @@ function assumptionsTemplate(ctx) {
2475
3006
  purpose: Open assumptions and untested beliefs \u2014 the bets the team is currently making
2476
3007
  read_when: Designing discovery work, scoping experiments, retros, any time a decision feels risky
2477
3008
  last_updated: ${ctx.date}
2478
- owner:
3009
+ last_validated: ~
3010
+ source: ~
3011
+ owner:
2479
3012
  ---
2480
3013
 
2481
3014
  # Assumptions
@@ -3037,7 +3570,9 @@ function metricsTemplate(ctx) {
3037
3570
  purpose: Metric definitions, ownership, and data sources \u2014 so the team means the same thing
3038
3571
  read_when: Building dashboards, writing OKRs, reviewing product health, debugging data discrepancies
3039
3572
  last_updated: ${ctx.date}
3040
- owner:
3573
+ last_validated: ~
3574
+ source: ~
3575
+ owner:
3041
3576
  ---
3042
3577
 
3043
3578
  # Metrics
@@ -3287,6 +3822,13 @@ See \`team-foundry/engineering/quality-bar.md\` for the quality bar, bug triage
3287
3822
  - **Private folder** \u2014 \`team-foundry/private/\` is gitignored. Do not read from or write to it.${isSolo ? "" : `
3288
3823
  - **ADR commitments** \u2014 architecture decisions in \`team-foundry/engineering/decisions/\` represent committed choices. Treat them as constraints unless the user explicitly opens a discussion.`}
3289
3824
 
3825
+ ## Claude Code skills
3826
+
3827
+ If you are running as Claude Code, pre-built skills are available in \`.claude/skills/\`:
3828
+ \`/team-foundry-intro\`, \`/team-foundry-status\`, \`/team-foundry-review\`,
3829
+ \`/team-foundry-capture\`, \`/team-foundry-decision\`, \`/team-foundry-feature\`.
3830
+ See CLAUDE.md for the full skill table.
3831
+
3290
3832
  ## Working with the team-foundry coach
3291
3833
 
3292
3834
  The coach runs in three modes \u2014 inline (always on, surfaces one-sentence nudges), explicit (triggered by "coach mode" or "let's do a team-foundry review"), and scheduled (weekly check-in, top 3 findings). See CLAUDE.md or GEMINI.md for the full trigger phrase table and loading instructions.
@@ -3299,6 +3841,165 @@ See \`team-foundry/context/glossary.md\` for domain terms, acronyms, and known n
3299
3841
  `}`;
3300
3842
  }
3301
3843
 
3844
+ // src/templates/hierarchy.ts
3845
+ function hierarchyTemplate(ctx) {
3846
+ return `---
3847
+ purpose: Trust precedence rules the coach applies when context conflicts across files
3848
+ read_when: Before reconciling conflicting signals; when claims in different files disagree
3849
+ last_updated: ${ctx.date}
3850
+ owner: team
3851
+ ---
3852
+
3853
+ # Context Hierarchy
3854
+
3855
+ When signals conflict across team-foundry files, apply these precedence rules in order.
3856
+ Do not average conflicting data \u2014 name the conflict and state which source wins and why.
3857
+
3858
+ ## File Precedence
3859
+
3860
+ 1. **Constitution** \u2014 CLAUDE.md / GEMINI.md (always-loaded rules and routing)
3861
+ 2. **Context files** \u2014 team-foundry/ files (the substance of what the team knows)
3862
+ 3. **Memory / notes** \u2014 session notes, conversation history
3863
+ 4. **Project logs** \u2014 git log, CI output, issue trackers
3864
+
3865
+ *When a context file contradicts a memory note, the context file wins.*
3866
+
3867
+ ## Data Source Precedence
3868
+
3869
+ 1. **Measured live data** \u2014 dashboards, analytics, cohort exports with a date
3870
+ 2. **Own assessment** \u2014 team's direct analysis with reasoning
3871
+ 3. **Own notes** \u2014 meeting notes, interview summaries
3872
+ 4. **Automatic notes** \u2014 AI-generated summaries, auto-transcripts
3873
+ 5. **Customer claims** \u2014 what customers say (without corroborating data)
3874
+
3875
+ *When cohort data disagrees with a customer claim, cohort data wins.*
3876
+ *Always note which source tier a claim comes from.*
3877
+
3878
+ ## Expertise Precedence
3879
+
3880
+ 1. **Domain expert** \u2014 someone with direct, validated experience in this area
3881
+ 2. **Team** \u2014 collective judgment based on what the team has observed
3882
+ 3. **AI general knowledge** \u2014 the model's training knowledge (no team-specific context)
3883
+ 4. **Web search** \u2014 external sources with no team context
3884
+
3885
+ *When your domain expert's view conflicts with AI general knowledge, the expert wins.*
3886
+
3887
+ ## Version Authority
3888
+
3889
+ - **Working docs** (team-foundry/ files, CLAUDE.md): latest commit wins
3890
+ - **Research** (customer interviews, experiments, market data): newer does not supersede older \u2014
3891
+ it adds a data point. A 2026 interview does not erase a 2025 cohort result; it adds signal.
3892
+ Surface both and name the gap.
3893
+
3894
+ ## How to Apply
3895
+
3896
+ When you see conflicting signals:
3897
+ 1. Name the conflict explicitly \u2014 do not silently pick one
3898
+ 2. State which source wins per the hierarchy above
3899
+ 3. Note what evidence would resolve the conflict
3900
+ `;
3901
+ }
3902
+
3903
+ // src/templates/instructions/hooks.ts
3904
+ function hooksTemplate(ctx) {
3905
+ return `---
3906
+ purpose: Enforced behavioral instructions \u2014 the AI must follow these before acting
3907
+ read_when: Before editing any team-foundry file; before using external tools or paid APIs
3908
+ last_updated: ${ctx.date}
3909
+ owner: team
3910
+ ---
3911
+
3912
+ # Hooks
3913
+
3914
+ These are markdown conventions, not executable scripts. They define behaviors the AI
3915
+ must treat as enforced \u2014 not suggestions to weigh against convenience.
3916
+
3917
+ In v3.1, an optional \`--with-hooks\` flag will generate real Claude Code hook scripts
3918
+ wired to \`.claude/settings.json\`. For now, treat this file as the enforcement layer.
3919
+
3920
+ ---
3921
+
3922
+ ## Before reconciling conflicting signals
3923
+
3924
+ Read \`.team-foundry/hierarchy.md\` first.
3925
+ Do not average conflicting claims. Name the conflict, state which source wins per the
3926
+ hierarchy, and note what evidence would resolve it.
3927
+
3928
+ **Never do:** silently pick the more recent or more confident-sounding claim without
3929
+ stating which hierarchy tier it comes from.
3930
+
3931
+ ---
3932
+
3933
+ ## Before editing any team-foundry file
3934
+
3935
+ Follow the conversation-as-update protocol in \`.team-foundry/coach.md\`.
3936
+
3937
+ 1. Surface the finding and name the gap (diagnosis message)
3938
+ 2. Draft the proposed change in a separate message
3939
+ 3. Wait for explicit confirmation before writing
3940
+
3941
+ **Never do:** edit team-foundry files without showing the draft and receiving
3942
+ a clear "yes", "looks good", "go ahead", or equivalent. Silence is not confirmation.
3943
+
3944
+ ---
3945
+
3946
+ ## Before using paid tools or external MCPs
3947
+
3948
+ Ask first. Paid tools include: web search APIs, external MCP servers billed per call,
3949
+ AI image generation, or any tool that incurs a cost outside this session.
3950
+
3951
+ State: what tool you want to use, why, and what the alternative is if the user declines.
3952
+
3953
+ **Never do:** trigger a paid or external tool call without the user's explicit approval
3954
+ in the current session.
3955
+ `;
3956
+ }
3957
+
3958
+ // src/templates/instructions/rules.ts
3959
+ function rulesTemplate(ctx) {
3960
+ return `---
3961
+ purpose: Always-loaded behavioral rules \u2014 kept minimal so they actually get followed
3962
+ read_when: Every coach session (explicit, scheduled, inline). Load before any coaching action.
3963
+ last_updated: ${ctx.date}
3964
+ owner: team
3965
+ ---
3966
+
3967
+ # Rules
3968
+
3969
+ Behavioral rules for the team-foundry coach. These are always loaded \u2014 keep this file
3970
+ minimal. If a rule applies to fewer than 80% of sessions, move it to a reference file
3971
+ and add a pointer here instead.
3972
+
3973
+ ---
3974
+
3975
+ 1. **Diagnostic-first.** Name the gap before suggesting a fix. Do not open with advice.
3976
+ "Your outcomes look like outputs" before "Here's how to reframe them."
3977
+
3978
+ 2. **Source every quantitative claim.** Any number, percentage, or currency figure in a
3979
+ team-foundry file needs a \`source:\` frontmatter value or an inline \`(source, date)\`.
3980
+ Flag unsourced claims \u2014 do not silently accept them.
3981
+
3982
+ 3. **Separate validated from hypothesized.** A claim in a Validated section without a
3983
+ \`source:\` reference or inline \`(source, date)\` is a hypothesis. Say so. \`last_validated:\`
3984
+ tracks staleness \u2014 it does not substitute for a source.
3985
+
3986
+ 4. **No silent writes.** Follow the conversation-as-update protocol. Draft \u2192 confirm \u2192
3987
+ write. Silence is not confirmation.
3988
+
3989
+ 5. **Hell-yes standard.** Every file, coaching nudge, and onboarding question must be
3990
+ obviously essential or it gets cut. Flag padding \u2014 don't add to it.
3991
+
3992
+ 6. **One nudge per response in inline mode.** Surface the single most important gap.
3993
+ Do not stack multiple coaching observations in the same inline message.
3994
+
3995
+ 7. **Respect nudge memory.** If the team has declined a finding in the last 7 days,
3996
+ do not resurface it unless they explicitly ask.
3997
+
3998
+ 8. **Never block work.** Coaching is observational. Note the gap, offer to help, then
3999
+ let the team proceed. Do not gate work on a coaching fix.
4000
+ `;
4001
+ }
4002
+
3302
4003
  // src/scaffold.ts
3303
4004
  var ALWAYS_ROOT_ENTRIES = [
3304
4005
  { relativePath: "AGENTS.md", content: rootAgentsTemplate }
@@ -3327,7 +4028,10 @@ var FULL_ONLY_ENTRIES = [
3327
4028
  { relativePath: "team-foundry/data/metrics.md", content: metricsTemplate },
3328
4029
  { relativePath: "team-foundry/context/glossary.md", content: glossaryTemplate },
3329
4030
  { relativePath: "team-foundry/context/stakeholders.md", content: stakeholdersTemplate },
3330
- { relativePath: "team-foundry/product/strategy.md", content: strategyTemplate }
4031
+ { relativePath: "team-foundry/product/strategy.md", content: strategyTemplate },
4032
+ { relativePath: ".team-foundry/hierarchy.md", content: hierarchyTemplate },
4033
+ { relativePath: ".team-foundry/instructions/hooks.md", content: hooksTemplate },
4034
+ { relativePath: ".team-foundry/instructions/rules.md", content: rulesTemplate }
3331
4035
  ];
3332
4036
  var FEDERATED_ENTRIES = [
3333
4037
  { relativePath: "team-foundry/product/CLAUDE.md", content: federatedProductTemplate },
@@ -3337,6 +4041,14 @@ var FEDERATED_ENTRIES = [
3337
4041
  { relativePath: "team-foundry/data/CLAUDE.md", content: federatedDataTemplate },
3338
4042
  { relativePath: "team-foundry/context/CLAUDE.md", content: federatedContextTemplate }
3339
4043
  ];
4044
+ var CLAUDE_SKILLS_ENTRIES = [
4045
+ { relativePath: ".claude/skills/team-foundry-intro.md", content: introSkillTemplate },
4046
+ { relativePath: ".claude/skills/team-foundry-status.md", content: statusSkillTemplate },
4047
+ { relativePath: ".claude/skills/team-foundry-review.md", content: reviewSkillTemplate },
4048
+ { relativePath: ".claude/skills/team-foundry-capture.md", content: captureSkillTemplate },
4049
+ { relativePath: ".claude/skills/team-foundry-decision.md", content: decisionSkillTemplate },
4050
+ { relativePath: ".claude/skills/team-foundry-feature.md", content: featureSkillTemplate }
4051
+ ];
3340
4052
  function rootEntries(tool) {
3341
4053
  if (tool === "claude") {
3342
4054
  return [{ relativePath: "CLAUDE.md", content: rootClaudeTemplate }];
@@ -3355,13 +4067,16 @@ function rootEntries(tool) {
3355
4067
  async function scaffold(options) {
3356
4068
  const { targetDir, profile, tool, repoVisibility, date, ingestionPath, ingestion, federated } = options;
3357
4069
  const ctx = { profile, tool, repoVisibility, date, ingestionPath, ingestion };
4070
+ const includesSkills = tool === "claude" || tool === "both";
3358
4071
  const entries = [
3359
4072
  ...ALWAYS_ROOT_ENTRIES,
3360
4073
  ...rootEntries(tool),
3361
4074
  ...SOLO_ENTRIES,
3362
4075
  ...profile === "full" ? FULL_ONLY_ENTRIES : [],
3363
- ...profile === "full" && federated ? FEDERATED_ENTRIES : []
4076
+ ...profile === "full" && federated ? FEDERATED_ENTRIES : [],
4077
+ ...includesSkills ? CLAUDE_SKILLS_ENTRIES : []
3364
4078
  ];
4079
+ const written = [];
3365
4080
  for (const entry of entries) {
3366
4081
  const fullPath = path.join(targetDir, entry.relativePath);
3367
4082
  const dir = path.dirname(fullPath);
@@ -3372,7 +4087,23 @@ async function scaffold(options) {
3372
4087
  } catch {
3373
4088
  }
3374
4089
  await fs.writeFile(fullPath, entry.content(ctx), "utf-8");
4090
+ written.push(entry.relativePath);
3375
4091
  }
4092
+ return written;
4093
+ }
4094
+ function gitAddCommand(tool) {
4095
+ const toolFiles = [];
4096
+ if (tool === "claude" || tool === "both") toolFiles.push("CLAUDE.md", ".claude/");
4097
+ if (tool === "gemini" || tool === "both") toolFiles.push("GEMINI.md");
4098
+ if (tool === "cursor") toolFiles.push(".cursor/");
4099
+ const paths = [
4100
+ "team-foundry/",
4101
+ ".team-foundry/",
4102
+ "AGENTS.md",
4103
+ "GETTING_STARTED.md",
4104
+ ...toolFiles
4105
+ ].join(" ");
4106
+ return `git add ${paths} && git commit -m "Add team-foundry"`;
3376
4107
  }
3377
4108
 
3378
4109
  // src/gitignore.ts
@@ -3812,7 +4543,131 @@ async function runStatus(targetDir) {
3812
4543
  }
3813
4544
  }
3814
4545
 
4546
+ // src/migrate.ts
4547
+ import fs4 from "fs/promises";
4548
+ import path4 from "path";
4549
+ import { confirm, log, outro as outro2 } from "@clack/prompts";
4550
+ async function detectTool(targetDir) {
4551
+ const checks = [
4552
+ [".cursor/rules/team-foundry.mdc", "cursor"],
4553
+ ["GEMINI.md", "gemini"],
4554
+ ["CLAUDE.md", "claude"]
4555
+ ];
4556
+ for (const [relPath, tool] of checks) {
4557
+ try {
4558
+ await fs4.access(path4.join(targetDir, relPath));
4559
+ return tool;
4560
+ } catch {
4561
+ }
4562
+ }
4563
+ return "claude";
4564
+ }
4565
+ async function detectMigrateState(targetDir) {
4566
+ const tfDir = path4.join(targetDir, ".team-foundry");
4567
+ try {
4568
+ await fs4.access(tfDir);
4569
+ } catch {
4570
+ return "none";
4571
+ }
4572
+ try {
4573
+ await fs4.access(path4.join(tfDir, "hierarchy.md"));
4574
+ return "v3";
4575
+ } catch {
4576
+ return "v2";
4577
+ }
4578
+ }
4579
+ async function writeIfAbsent(filePath, content) {
4580
+ await fs4.mkdir(path4.dirname(filePath), { recursive: true });
4581
+ try {
4582
+ await fs4.writeFile(filePath, content, { flag: "wx", encoding: "utf-8" });
4583
+ } catch (err) {
4584
+ if (err.code !== "EEXIST") throw err;
4585
+ }
4586
+ }
4587
+ async function appendFrontmatterFields(filePath) {
4588
+ let content;
4589
+ try {
4590
+ content = await fs4.readFile(filePath, "utf-8");
4591
+ } catch {
4592
+ return;
4593
+ }
4594
+ if (!content.startsWith("---")) return;
4595
+ const closeIdx = content.indexOf("\n---", 3);
4596
+ if (closeIdx === -1) return;
4597
+ const frontmatter = content.slice(0, closeIdx);
4598
+ const rest = content.slice(closeIdx);
4599
+ let updated = frontmatter;
4600
+ if (!frontmatter.includes("last_validated:")) {
4601
+ updated += "\nlast_validated: ~";
4602
+ }
4603
+ if (!frontmatter.includes("source:")) {
4604
+ updated += "\nsource: ~";
4605
+ }
4606
+ if (updated === frontmatter) return;
4607
+ await fs4.writeFile(filePath, updated + rest, "utf-8");
4608
+ }
4609
+ var DATA_HEAVY_FILES = [
4610
+ "team-foundry/product/outcomes.md",
4611
+ "team-foundry/product/customers.md",
4612
+ "team-foundry/data/metrics.md",
4613
+ "team-foundry/product/assumptions.md",
4614
+ "team-foundry/product/now-next-later.md"
4615
+ ];
4616
+ async function runMigrate(targetDir) {
4617
+ const state = await detectMigrateState(targetDir);
4618
+ if (state === "none") {
4619
+ log.error("No existing team-foundry found. Run npx create-team-foundry to scaffold v3.");
4620
+ process.exit(1);
4621
+ }
4622
+ if (state === "v3") {
4623
+ outro2("Already on v3. Nothing to migrate.");
4624
+ return;
4625
+ }
4626
+ log.info(
4627
+ "v2 team-foundry detected. Migration will add:\n\n .team-foundry/hierarchy.md\n .team-foundry/instructions/hooks.md\n .team-foundry/instructions/rules.md\n\nAnd append source: / last_validated: to frontmatter of:\n\n" + DATA_HEAVY_FILES.map((f) => ` ${f}`).join("\n") + "\n\nExisting files are never overwritten."
4628
+ );
4629
+ const ok = await confirm({ message: "Proceed with migration?" });
4630
+ if (!ok) {
4631
+ outro2("Migration cancelled. No files were changed.");
4632
+ return;
4633
+ }
4634
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
4635
+ const tool = await detectTool(targetDir);
4636
+ const ctx = {
4637
+ profile: "full",
4638
+ tool,
4639
+ repoVisibility: "internal",
4640
+ date
4641
+ };
4642
+ const tfDir = path4.join(targetDir, ".team-foundry");
4643
+ await writeIfAbsent(path4.join(tfDir, "hierarchy.md"), hierarchyTemplate(ctx));
4644
+ await writeIfAbsent(path4.join(tfDir, "instructions", "hooks.md"), hooksTemplate(ctx));
4645
+ await writeIfAbsent(path4.join(tfDir, "instructions", "rules.md"), rulesTemplate(ctx));
4646
+ for (const relPath of DATA_HEAVY_FILES) {
4647
+ const filePath = path4.join(targetDir, relPath);
4648
+ await appendFrontmatterFields(filePath);
4649
+ }
4650
+ outro2(
4651
+ `Migration complete.
4652
+
4653
+ New files added to .team-foundry/. Your existing content is untouched.
4654
+
4655
+ Open your AI tool and say: "Read .team-foundry/hierarchy.md and let's review
4656
+ what changed in v3."`
4657
+ );
4658
+ }
4659
+
3815
4660
  // src/index.ts
4661
+ function groupByFolder(paths) {
4662
+ const groups = {};
4663
+ for (const p of paths) {
4664
+ const parts = p.split("/");
4665
+ const folder = parts.length > 1 ? parts[0] : ".";
4666
+ const file = parts.length > 1 ? parts.slice(1).join("/") : p;
4667
+ (groups[folder] ??= []).push(file);
4668
+ }
4669
+ return groups;
4670
+ }
3816
4671
  var TOOL_LABEL = {
3817
4672
  claude: "Claude Code",
3818
4673
  gemini: "Gemini CLI",
@@ -3834,69 +4689,119 @@ You can paste multiple documents \u2014 just separate them with a heading like:
3834
4689
  When you're done, save this file and start the onboarding interview.
3835
4690
  `;
3836
4691
  async function checkDirectory(targetDir) {
3837
- const prdPath = path4.join(targetDir, "team-foundry-prd-v2.md");
3838
- const scaffoldPath = path4.join(targetDir, "src", "scaffold.ts");
4692
+ const prdPath = path5.join(targetDir, "team-foundry-prd-v2.md");
4693
+ const scaffoldPath = path5.join(targetDir, "src", "scaffold.ts");
3839
4694
  let isSourceRepo = false;
3840
4695
  try {
3841
- await fs4.access(prdPath);
4696
+ await fs5.access(prdPath);
3842
4697
  isSourceRepo = true;
3843
4698
  } catch {
3844
4699
  }
3845
4700
  try {
3846
- await fs4.access(scaffoldPath);
4701
+ await fs5.access(scaffoldPath);
3847
4702
  isSourceRepo = true;
3848
4703
  } catch {
3849
4704
  }
3850
4705
  if (isSourceRepo) {
3851
- log.error(
4706
+ log2.error(
3852
4707
  "You're running create-team-foundry inside the team-foundry source repo.\nThis will overwrite development files.\n\ncd to your product repo first, then run this command again."
3853
4708
  );
3854
4709
  process.exit(1);
3855
4710
  }
3856
- const pkgPath = path4.join(targetDir, "package.json");
3857
- const srcPath = path4.join(targetDir, "src");
4711
+ const pkgPath = path5.join(targetDir, "package.json");
4712
+ const srcPath = path5.join(targetDir, "src");
3858
4713
  let hasPkg = false;
3859
4714
  let hasSrc = false;
3860
4715
  try {
3861
- await fs4.access(pkgPath);
4716
+ await fs5.access(pkgPath);
3862
4717
  hasPkg = true;
3863
4718
  } catch {
3864
4719
  }
3865
4720
  try {
3866
- await fs4.access(srcPath);
4721
+ await fs5.access(srcPath);
3867
4722
  hasSrc = true;
3868
4723
  } catch {
3869
4724
  }
3870
4725
  if (hasPkg && hasSrc) {
3871
- log.warn(
4726
+ log2.warn(
3872
4727
  "This directory has a package.json and src/ \u2014 it looks like a Node.js project.\nteam-foundry works best in your product repo, not inside a library or CLI repo.\nIf this is the right place, continue. Otherwise Ctrl-C and cd to your product repo."
3873
4728
  );
3874
- const ok = await confirm({ message: "Continue anyway?" });
4729
+ const ok = await confirm2({ message: "Continue anyway?" });
3875
4730
  if (!ok) {
3876
- outro2("Cancelled. cd to your product repo and try again.");
4731
+ outro3("Cancelled. cd to your product repo and try again.");
3877
4732
  process.exit(0);
3878
4733
  }
3879
4734
  }
3880
4735
  }
4736
+ async function checkExistingInstall(targetDir) {
4737
+ try {
4738
+ await fs5.access(path5.join(targetDir, "team-foundry"));
4739
+ } catch {
4740
+ return null;
4741
+ }
4742
+ log2.warn(
4743
+ "team-foundry is already set up in this directory.\nWhat would you like to do?"
4744
+ );
4745
+ const choice = await select2({
4746
+ message: "Choose an option:",
4747
+ options: [
4748
+ { value: "status", label: "Run status \u2014 see which files are stale or missing" },
4749
+ { value: "migrate", label: "Run migrate \u2014 upgrade to the latest profile" },
4750
+ { value: "continue", label: "Continue anyway \u2014 re-run setup (adds any missing files)" }
4751
+ ]
4752
+ });
4753
+ if (isCancel2(choice)) {
4754
+ outro3("Cancelled.");
4755
+ process.exit(0);
4756
+ }
4757
+ return choice;
4758
+ }
3881
4759
  async function main() {
3882
4760
  const targetDir = process.cwd();
3883
4761
  if (process.argv[2] === "status") {
3884
4762
  await runStatus(targetDir);
3885
4763
  return;
3886
4764
  }
4765
+ if (process.argv[2] === "migrate") {
4766
+ await runMigrate(targetDir);
4767
+ return;
4768
+ }
3887
4769
  await checkDirectory(targetDir);
4770
+ const installChoice = await checkExistingInstall(targetDir);
4771
+ if (installChoice === "status") {
4772
+ await runStatus(targetDir);
4773
+ return;
4774
+ }
4775
+ if (installChoice === "migrate") {
4776
+ await runMigrate(targetDir);
4777
+ return;
4778
+ }
3888
4779
  const answers = await runPrompts();
3889
4780
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
3890
- await scaffold({ ...answers, targetDir, date });
4781
+ const writtenPaths = await scaffold({ ...answers, targetDir, date });
3891
4782
  await writeGitignore(targetDir);
3892
4783
  if (answers.ingestion === "paste" || answers.ingestion === "repo+paste") {
3893
- const pastePath = path4.join(targetDir, ".team-foundry", "paste-content.md");
4784
+ const pastePath = path5.join(targetDir, ".team-foundry", "paste-content.md");
3894
4785
  try {
3895
- await fs4.access(pastePath);
4786
+ await fs5.access(pastePath);
3896
4787
  } catch {
3897
- await fs4.writeFile(pastePath, PASTE_PLACEHOLDER, "utf-8");
4788
+ await fs5.writeFile(pastePath, PASTE_PLACEHOLDER, "utf-8");
4789
+ writtenPaths.push(".team-foundry/paste-content.md");
3898
4790
  }
3899
4791
  }
4792
+ if (writtenPaths.length > 0) {
4793
+ const grouped = groupByFolder(writtenPaths);
4794
+ const lines = ["", "Files created:"];
4795
+ for (const [folder, files] of Object.entries(grouped)) {
4796
+ lines.push(` ${folder}/`);
4797
+ for (const file of files) lines.push(` ${file}`);
4798
+ }
4799
+ lines.push("", "Commit when ready:");
4800
+ lines.push(` ${gitAddCommand(answers.tool)}`);
4801
+ log2.info(lines.join("\n"));
4802
+ } else {
4803
+ log2.info("No new files created \u2014 all files already exist.");
4804
+ }
3900
4805
  const tool = TOOL_LABEL[answers.tool];
3901
4806
  let ingestionNote;
3902
4807
  if (answers.ingestion === "repo") {
@@ -4009,7 +4914,7 @@ Next steps:
4009
4914
  You can add existing docs later by editing .team-foundry/paste-content.md.
4010
4915
  `;
4011
4916
  }
4012
- outro2(
4917
+ outro3(
4013
4918
  `Done! Your files are in:
4014
4919
 
4015
4920
  ${targetDir}
@@ -4021,6 +4926,6 @@ team commits to, so everyone's AI tool gets the same context.`
4021
4926
  );
4022
4927
  }
4023
4928
  main().catch((err) => {
4024
- log.error(err instanceof Error ? err.message : String(err));
4929
+ log2.error(err instanceof Error ? err.message : String(err));
4025
4930
  process.exit(1);
4026
4931
  });