create-team-foundry 1.0.0 → 2.0.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.
Files changed (3) hide show
  1. package/README.md +40 -6
  2. package/dist/index.js +618 -31
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,19 +1,25 @@
1
1
  # team-foundry
2
2
 
3
- **Give your AI coding tool the context it needs to be a real product partner.**
3
+ **A Context Engine for product teams.**
4
4
 
5
- `npx create-team-foundry` scaffolds a `team-foundry/` directory of structured files — outcomes, customers, decisions, quality bar, strategy — in a format that Claude Code, Gemini CLI, and other AI tools read natively as context.
5
+ team-foundry scaffolds the shared brain your AI coding tools read from — outcomes, customers, decisions, quality bar — in a repo every team member commits to. Every AI tool, every team member, the same context. Automatically.
6
6
 
7
7
  ```
8
8
  npx create-team-foundry
9
9
  ```
10
10
 
11
- That's it. Answer 4 questions. Files appear in your repo.
11
+ Answer 4 questions. Files appear in your repo. That's it.
12
12
 
13
- **Run this in your shared product/engineering repo** the one the whole team commits to. That's how everyone's AI tool picks up the same context automatically. Running it in a personal or feature branch repo works for solo use but misses the point for teams.
13
+ team-foundry is agent-agnostic by design. The context files are the product. The AI tool reading them is a replaceable component.
14
+
15
+ **Run this in your shared product/engineering repo** — the one the whole team commits to. Running it in a personal repo works for solo use but misses the point for teams.
14
16
 
15
17
  ---
16
18
 
19
+ ## See what populated looks like
20
+
21
+ → **[example/](example/)** — a fully populated team-foundry for Clearline, a fictional 8-person B2B SaaS team. Clone this repo, open `example/` in Claude Code, and ask the AI anything about the team. This is what your team-foundry can look like after the setup conversation.
22
+
17
23
  ## What it does
18
24
 
19
25
  Most teams use AI tools for code. The AI gives better answers when it knows what the product is for, who the customers are, and what quality means to this team.
@@ -24,9 +30,9 @@ It also installs a coach: a set of behaviors that notice when your files go stal
24
30
 
25
31
  ## What gets created
26
32
 
27
- **Solo profile (1–3 people):** 7 files. Identity, north star, outcomes, customers, stack, coach.
33
+ **Solo profile (1–3 people):** 7 files. Root instruction file, getting started guide, coach playbook, north star, outcomes, customers, stack.
28
34
 
29
- **Full profile (4–15 people):** 15 files. Everything above plus strategy, roadmap, assumptions, risks, trio, working agreement, AI practices, quality bar, decisions log, design principles, metrics, glossary, stakeholders.
35
+ **Full profile (4–15 people):** 20 files. Everything above plus strategy, roadmap, assumptions, risks, trio, working agreement, AI practices, quality bar, decisions log, design principles, metrics, glossary, stakeholders.
30
36
 
31
37
  Every file has YAML frontmatter (`purpose`, `read_when`, `last_updated`) so the AI knows when to load it and why.
32
38
 
@@ -34,6 +40,28 @@ Every file has YAML frontmatter (`purpose`, `read_when`, `last_updated`) so the
34
40
 
35
41
  team-foundry enables the work of building a shared understanding of your product. It doesn't replace it. If your team isn't willing to have the hard conversations about outcomes, customers, and quality — no tool fixes that. What team-foundry does is make those conversations easier to start, and easier to keep current.
36
42
 
43
+ ## Shared floor, individual ceiling
44
+
45
+ team-foundry sets a shared baseline of context that every team member's AI tool reads from. Outcomes, customers, decisions, quality bar, glossary — the things everyone should be grounded in.
46
+
47
+ It does not cap what individuals do above that baseline. A senior PM can add their own prompts, their own discovery framework, their own depth. An engineer can bring their own AI practices. A designer can layer on top.
48
+
49
+ The floor is shared. The ceiling is individual. That's the point.
50
+
51
+ ## What drift looks like
52
+
53
+ The coach catches specific, named patterns. These are the most common:
54
+
55
+ | Pattern | What it is | Example |
56
+ |---|---|---|
57
+ | **Output-as-outcome drift** | An outcome that's actually a shipped feature | `outcomes.md` says "ship the new dashboard" instead of "reduce time-to-insight for SMB analysts" |
58
+ | **Assumption fossilization** | A core assumption logged long ago, never revisited, still driving decisions | `assumptions.md` lists "users want faster checkout" dated 94 days ago. Three roadmap items cite it. No one's tested it. |
59
+ | **Customer ghost syndrome** | A persona with no direct team contact in 60+ days, while roadmap items still claim to serve them | Enterprise persona last interviewed in February. Three Q2 features built "for enterprise." |
60
+ | **Metric ambiguity** | A metric cited across files without an agreed definition | "Active user" in `outcomes.md` and `metrics.md` with no shared definition. PM means weekly; engineer means daily. |
61
+ | **Decision amnesia** | A previous ADR rejecting an approach is invisible to a discussion heading the same direction | Q1 ADR rejects microservices migration. Q3 discussion reopens it with no reference to what changed. |
62
+ | **Output roadmap disguised as strategy** | `strategy.md` guiding policy is all "yes" — doesn't name what the team is deliberately not pursuing | "We will be the best tool for product teams." No "we will not build X for Y." |
63
+ | **Build-trap signal** | An item moves into "Now" with no linked experiment and no validated assumption | "Add collaborative editing" moves to Now. No assumption linked. Last validation: none. |
64
+
37
65
  ## Strategy in Git
38
66
 
39
67
  Putting strategy and product context in a repo might feel wrong. A few things worth knowing:
@@ -82,6 +110,12 @@ The coach will then only run when you explicitly ask for it.
82
110
  - Node 18+
83
111
  - Claude Code, Gemini CLI, or any AI tool that reads files from your repo as context
84
112
 
113
+ ## What's next
114
+
115
+ team-foundry v1 supports Claude Code and Gemini CLI. Cursor is the highest-priority v2 addition — it has the largest active user base of any AI coding tool and fits the same file-based context model.
116
+
117
+ v2 shortlist: Cursor support and scheduled coach runs via cron.
118
+
85
119
  ## Contributing
86
120
 
87
121
  See [CONTRIBUTING.md](CONTRIBUTING.md).
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import fs3 from "fs/promises";
5
- import path3 from "path";
4
+ import fs4 from "fs/promises";
5
+ import path4 from "path";
6
6
  import { outro as outro2, log, confirm } from "@clack/prompts";
7
7
 
8
8
  // src/prompts.ts
@@ -20,7 +20,8 @@ async function runPrompts() {
20
20
  options: [
21
21
  { value: "claude", label: "Claude Code" },
22
22
  { value: "gemini", label: "Gemini CLI" },
23
- { value: "both", label: "Both" }
23
+ { value: "cursor", label: "Cursor" },
24
+ { value: "both", label: "Multiple (Claude Code + Gemini CLI)" }
24
25
  ]
25
26
  });
26
27
  cancelIfNeeded(tool);
@@ -41,6 +42,18 @@ async function runPrompts() {
41
42
  ]
42
43
  });
43
44
  cancelIfNeeded(repoVisibility);
45
+ let federated;
46
+ if (profile === "full") {
47
+ const federatedAnswer = await select({
48
+ message: "Context layout?",
49
+ options: [
50
+ { value: "flat", label: "Flat (one root CLAUDE.md \u2014 simpler, recommended for most teams)" },
51
+ { value: "federated", label: "Federated (CLAUDE.md per folder \u2014 for larger teams, 8+ people)" }
52
+ ]
53
+ });
54
+ cancelIfNeeded(federatedAnswer);
55
+ federated = federatedAnswer === "federated";
56
+ }
44
57
  const ingestion = await select({
45
58
  message: "Do you have existing docs to ingest?\n (Strategy docs, old roadmaps, customer research \u2014 the interview uses them to pre-populate answers)",
46
59
  options: [
@@ -68,7 +81,8 @@ async function runPrompts() {
68
81
  profile,
69
82
  repoVisibility,
70
83
  ingestion,
71
- ingestionPath
84
+ ingestionPath,
85
+ federated
72
86
  };
73
87
  }
74
88
 
@@ -76,6 +90,137 @@ async function runPrompts() {
76
90
  import fs from "fs/promises";
77
91
  import path from "path";
78
92
 
93
+ // src/templates/federated/product-claude.ts
94
+ function federatedProductTemplate(ctx) {
95
+ return `---
96
+ purpose: Routing context for the product/ folder \u2014 read before answering product questions
97
+ read_when: Any question about outcomes, customers, roadmap, strategy, assumptions, or risks
98
+ last_updated: ${ctx.date}
99
+ ---
100
+
101
+ # Product context
102
+
103
+ This folder contains the team's product thinking. Read the relevant file before answering.
104
+
105
+ | Topic | File |
106
+ |---|---|
107
+ | Vision and north star metric | \`north-star.md\` |
108
+ | This quarter's outcomes | \`outcomes.md\` |
109
+ | Named customers and personas | \`customers.md\` |
110
+ | What we're building now / next / later | \`now-next-later.md\` |
111
+ | Strategic logic and guiding policy | \`strategy.md\` |
112
+ | Open assumptions and untested bets | \`assumptions.md\` |
113
+ | Key product risks | \`risks.md\` |
114
+
115
+ Files with recent \`last_updated\` dates are more reliable than older ones. If asked about freshness, note if a file hasn't been updated in 60+ days.
116
+ `;
117
+ }
118
+
119
+ // src/templates/federated/team-claude.ts
120
+ function federatedTeamTemplate(ctx) {
121
+ return `---
122
+ purpose: Routing context for the team/ folder \u2014 read before answering team questions
123
+ read_when: Any question about who owns decisions, how the team works, or AI tool usage
124
+ last_updated: ${ctx.date}
125
+ ---
126
+
127
+ # Team context
128
+
129
+ This folder describes how the product trio works and how the team operates.
130
+
131
+ | Topic | File |
132
+ |---|---|
133
+ | Who's on the trio and how decisions are made | \`trio.md\` |
134
+ | Ceremonies, DoD, working norms | \`working-agreement.md\` |
135
+ | How this team uses AI tools | \`ai-practices.md\` |
136
+
137
+ When answering questions about ownership or process, read \`trio.md\` first. It reflects how the team actually operates, not an org chart.
138
+ `;
139
+ }
140
+
141
+ // src/templates/federated/engineering-claude.ts
142
+ function federatedEngineeringTemplate(ctx) {
143
+ return `---
144
+ purpose: Routing context for the engineering/ folder \u2014 read before answering technical questions
145
+ read_when: Any question about the stack, conventions, tech debt, or past architecture decisions
146
+ last_updated: ${ctx.date}
147
+ ---
148
+
149
+ # Engineering context
150
+
151
+ This folder contains the team's technical context. Read before writing or reviewing code.
152
+
153
+ | Topic | File |
154
+ |---|---|
155
+ | Stack, conventions, deployment | \`stack.md\` |
156
+ | Quality stance and tech debt policy | \`quality-bar.md\` |
157
+ | Past architecture decisions | \`decisions/\` |
158
+
159
+ Always read \`stack.md\` before writing code for this repo \u2014 it contains the conventions and the non-obvious choices that would surprise an outsider. Read the relevant ADR in \`decisions/\` before making a technical decision that's already been decided.
160
+ `;
161
+ }
162
+
163
+ // src/templates/federated/design-claude.ts
164
+ function federatedDesignTemplate(ctx) {
165
+ return `---
166
+ purpose: Routing context for the design/ folder \u2014 read before answering design questions
167
+ read_when: Any question about UI copy, visual decisions, accessibility, or tone of voice
168
+ last_updated: ${ctx.date}
169
+ ---
170
+
171
+ # Design context
172
+
173
+ This folder contains the team's design principles and standards.
174
+
175
+ | Topic | File |
176
+ |---|---|
177
+ | Design principles, tone of voice, accessibility | \`principles.md\` |
178
+
179
+ Read \`principles.md\` before writing UI copy, reviewing designs, or making visual decisions. The tone and accessibility standards here apply to all customer-facing work.
180
+ `;
181
+ }
182
+
183
+ // src/templates/federated/data-claude.ts
184
+ function federatedDataTemplate(ctx) {
185
+ return `---
186
+ purpose: Routing context for the data/ folder \u2014 read before discussing metrics
187
+ read_when: Any question about metrics, success criteria, or what numbers mean
188
+ last_updated: ${ctx.date}
189
+ ---
190
+
191
+ # Data context
192
+
193
+ This folder contains metric definitions for this team.
194
+
195
+ | Topic | File |
196
+ |---|---|
197
+ | Metric definitions, ownership, data sources | \`metrics.md\` |
198
+
199
+ Read \`metrics.md\` before citing any metric or writing success criteria in a spec. If a metric isn't defined here, it doesn't have an agreed definition \u2014 flag that rather than inventing one.
200
+ `;
201
+ }
202
+
203
+ // src/templates/federated/context-claude.ts
204
+ function federatedContextTemplate(ctx) {
205
+ return `---
206
+ purpose: Routing context for the context/ folder \u2014 read before using domain terminology
207
+ read_when: Any question involving domain terms, acronyms, or stakeholder communication
208
+ last_updated: ${ctx.date}
209
+ ---
210
+
211
+ # Domain context
212
+
213
+ This folder contains the team's shared vocabulary and stakeholder map.
214
+
215
+ | Topic | File |
216
+ |---|---|
217
+ | Domain terms and acronyms | \`glossary.md\` |
218
+ | Stakeholders and what they care about | \`stakeholders.md\` |
219
+
220
+ Read \`glossary.md\` when writing specs, copy, or anything using domain-specific terms \u2014 especially terms flagged as "used inconsistently." Read \`stakeholders.md\` before drafting any communication intended for a specific stakeholder.
221
+ `;
222
+ }
223
+
79
224
  // src/templates/root-claude.ts
80
225
  function rootClaudeTemplate(ctx) {
81
226
  return `---
@@ -226,6 +371,82 @@ it notices something relevant to your current work. You can also invoke it direc
226
371
  `;
227
372
  }
228
373
 
374
+ // src/templates/root-cursor.ts
375
+ function rootCursorTemplate(ctx) {
376
+ return `---
377
+ purpose: Identity, routing map, and coach activation \u2014 read at the start of every session
378
+ read_when: Every Cursor session in this repo \u2014 this is the root instruction file
379
+ last_updated: ${ctx.date}
380
+ alwaysApply: true
381
+ ---
382
+
383
+ # team-foundry
384
+
385
+ This repo uses **team-foundry** \u2014 structured files that give you real team context.
386
+ Read this file first. It tells you where to find everything and how to activate the coach.
387
+
388
+ <!-- GAP: The onboarding interview hasn't run yet.
389
+ When the user says "Let's set up our team-foundry" or similar, do this:
390
+ 1. Read GETTING_STARTED.md for context on what to expect
391
+ 2. Load .team-foundry/coach.md \u2014 it contains the interview sequence
392
+ 3. Begin the onboarding interview as described there
393
+ Do not improvise the interview. Follow the sequence in coach.md. -->
394
+
395
+ ## Who we are
396
+
397
+ <!-- Filled in during the onboarding interview. -->
398
+
399
+ ## Routing map
400
+
401
+ When the user's question relates to any of the following, read the corresponding file
402
+ before answering. Files with recent \`last_updated\` dates are more reliable than older ones.
403
+
404
+ | Topic | File |
405
+ |---|---|
406
+ | Who we are / what this product does | team-foundry.mdc \u2014 "Who we are" section (this file) |
407
+ | What success looks like / vision | \`team-foundry/product/north-star.md\` |
408
+ | What we're working toward this quarter | \`team-foundry/product/outcomes.md\` |
409
+ | Who our customers are | \`team-foundry/product/customers.md\` |
410
+ | What we're building now / next / later | \`team-foundry/product/now-next-later.md\` |
411
+ | Strategic logic and guiding policy | \`team-foundry/product/strategy.md\` |
412
+ | Open assumptions and untested bets | \`team-foundry/product/assumptions.md\` |
413
+ | Key product risks | \`team-foundry/product/risks.md\` |
414
+ | How the product trio works | \`team-foundry/team/trio.md\` |
415
+ | Team norms, DoD, ceremonies | \`team-foundry/team/working-agreement.md\` |
416
+ | How we use AI tools | \`team-foundry/team/ai-practices.md\` |
417
+ | Tech stack and conventions | \`team-foundry/engineering/stack.md\` |
418
+ | Quality stance and tech debt policy | \`team-foundry/engineering/quality-bar.md\` |
419
+ | Past architecture decisions | \`team-foundry/engineering/decisions/\` |
420
+ | Design principles and tone | \`team-foundry/design/principles.md\` |
421
+ | Metric definitions | \`team-foundry/data/metrics.md\` |
422
+ | Domain terms and acronyms | \`team-foundry/context/glossary.md\` |
423
+ | Stakeholders and what they care about | \`team-foundry/context/stakeholders.md\` |
424
+
425
+ ## Coach
426
+
427
+ The team-foundry coach keeps these files honest over time. It runs automatically when
428
+ it notices something relevant to your current work. You can also invoke it directly:
429
+
430
+ | What to say | What happens |
431
+ |---|---|
432
+ | "Let's set up our team-foundry" | Runs the onboarding interview (first time only) |
433
+ | "let's do a team-foundry review" | Full audit \u2014 all files checked, findings listed |
434
+ | "coach mode" | Same as above |
435
+ | "review our [outcomes / customers / stack / etc.]" | Targeted review of one file |
436
+ | "what's missing from team-foundry?" | Lists gaps across all files |
437
+ | "run the weekly team-foundry review" | Weekly check-in, top 3 issues surfaced |
438
+
439
+ <!-- AI instructions:
440
+ - Normal coding sessions: do NOT load coach.md. Use the routing map above to load
441
+ specific files only when directly relevant to the user's question.
442
+ - Explicit mode / Scheduled mode / onboarding: load .team-foundry/coach.md in full
443
+ before activating any mode. Triggered only by the phrases in the table above.
444
+ - Inline mode nudges: if you notice a clear gap in a team-foundry file while answering
445
+ a normal question, surface it in one sentence \u2014 without loading the full coach.md.
446
+ Keep it brief and non-blocking. Do not coach unprompted on back-to-back messages. -->
447
+ `;
448
+ }
449
+
229
450
  // src/templates/getting-started.ts
230
451
  function gettingStartedTemplate(ctx) {
231
452
  const toolName = ctx.tool === "gemini" ? "Gemini CLI" : ctx.tool === "both" ? "Claude Code or Gemini CLI" : "Claude Code";
@@ -315,8 +536,14 @@ You can delete this file once the onboarding interview is complete.
315
536
  // src/templates/coach.ts
316
537
  function coachTemplate(ctx) {
317
538
  const isSolo = ctx.profile === "solo";
318
- const questionCount = isSolo ? "10" : "18\u201325";
539
+ const questionCount = isSolo ? "9" : "18\u201325";
319
540
  const timeEstimate = isSolo ? "15\u201320 minutes" : "25\u201335 minutes";
541
+ const featureQueryFullSteps = isSolo ? "" : `3. Read \`team-foundry/product/now-next-later.md\` \u2014 find the feature's current status (Now / Next / Later / shipped)
542
+ 4. Read \`team-foundry/product/assumptions.md\` \u2014 find any assumptions linked to this feature
543
+ 5. Read \`team-foundry/engineering/decisions/\` \u2014 find any ADRs related to how it's being built
544
+ `;
545
+ const featureQuerySynthesis = isSolo ? "why it's being built (outcome + customer evidence)." : "why it's being built (outcome + customer evidence), current status, open bets (assumptions), and any relevant technical decisions.";
546
+ const featureQueryIndexNote = isSolo ? "Your solo profile covers outcomes, customers, north-star, and stack \u2014 feature queries draw from those." : "For full profile teams, this covers status, rationale, customer evidence, open bets, and decisions.";
320
547
  return `---
321
548
  purpose: Full coach playbook \u2014 loaded on demand to preserve token budget
322
549
  read_when: When the user triggers coach mode (explicit, inline, scheduled review, or onboarding interview)
@@ -336,6 +563,67 @@ You are a mirror, not a template pack. The files in this repo are the team's own
336
563
  thinking. Your role is to reflect it back to them accurately, including the parts
337
564
  that have gone stale or were never written down.
338
565
 
566
+ ## Reality observation
567
+
568
+ This is the authoritative protocol for reading git activity. It supersedes any git-reading
569
+ instructions elsewhere in this file (including the "Why this nudge?" block and Behavior 5).
570
+ Those sections define what to surface \u2014 this section defines how to gather the evidence.
571
+
572
+ **When to run:**
573
+ - **Explicit and scheduled modes:** Run all four steps before any behavior fires.
574
+ - **Inline mode:** Run Steps 1\u20132 only when an inline trigger fires, scoped to the specific
575
+ file being evaluated. Do not run a full git audit on every inline response \u2014 that
576
+ contradicts the token-budget framing of this file.
577
+
578
+ **Step 1 \u2014 Read recent commits.**
579
+
580
+ \`\`\`
581
+ git log --oneline --since="30 days ago"
582
+ \`\`\`
583
+
584
+ This returns all commits regardless of merge strategy. Extract:
585
+ - Total commit count in the window
586
+ - Commit messages that describe shipped features, decisions, or major changes
587
+
588
+ **Merge strategy note:** \`git log --merges\` returns zero results on repos that use
589
+ squash-merge or rebase-merge (GitHub's common defaults). Do not use it as the primary
590
+ activity signal. Instead, use total commit count from the regular log. If commit volume
591
+ is high (>10 commits) but \`--merges\` returns zero, conclude "squash or rebase merge
592
+ strategy \u2014 using commit messages as activity proxy" and proceed accordingly.
593
+
594
+ **Step 2 \u2014 Check \`last_updated\` dates in team-foundry files.**
595
+
596
+ For each file being evaluated, note its \`last_updated\` frontmatter date. Calculate:
597
+ - Days between \`last_updated\` and today
598
+ - Commits since that date: \`git log --oneline --since="<last_updated date>"\`
599
+ - If the log returns no results, treat activity as zero and skip the high-activity threshold.
600
+
601
+ **Step 3 \u2014 Build the observed reality summary (internal, not shown to user).**
602
+
603
+ Synthesize into a mental model carried through the rest of the session:
604
+ - What shipped recently (from commit messages)
605
+ - Which team-foundry files haven't been updated since shipping started
606
+ - Activity level: high (>3 commits since last update) or low (\u22643 commits since last update).
607
+ High activity means push harder on drift. Low activity means lighter touch.
608
+
609
+ **Step 4 \u2014 Use this in every finding.**
610
+
611
+ When surfacing drift, always cite the observed reality explicitly. This is the single format
612
+ for "Why this nudge?" across all behaviors:
613
+
614
+ > "[N] commits have merged since outcomes.md was last updated ([X] days ago). Based on
615
+ > recent commit messages \u2014 [list 2-3 relevant ones] \u2014 it looks like [what changed].
616
+ > outcomes.md doesn't reflect this yet."
617
+
618
+ **Fallback when git is unavailable:** If the tool doesn't have shell access, fall back to
619
+ \`last_updated\` dates only and note: "I don't have access to git history \u2014 using
620
+ \`last_updated\` dates as the staleness signal."
621
+
622
+ **What counts as high activity:** More than 3 commits since the file was last updated.
623
+ This is the single threshold used throughout this file.
624
+
625
+ ---
626
+
339
627
  ## Activation modes
340
628
 
341
629
  You have three activation modes. Read which one applies and behave accordingly.
@@ -524,6 +812,31 @@ order and **name the conflict explicitly** rather than silently picking one:
524
812
  Say: "I see a conflict between [file A] and [file B]. Based on the context priority
525
813
  order, I'm going with [file A] \u2014 but you may want to reconcile these."
526
814
 
815
+ When running any coaching behavior, also load \`.team-foundry/team-lessons.md\` if it exists.
816
+ Apply Active rules from that file alongside built-in behaviors \u2014 they carry equal weight.
817
+
818
+ ## Feature queries
819
+
820
+ **Applies in explicit and scheduled modes.** In inline mode, treat feature questions as
821
+ potential B5 (reality drift) triggers rather than running this full multi-file read.
822
+
823
+ When the user asks about a specific feature \u2014 "tell me about X," "what's the status of Y,"
824
+ "what do we know about Z," "has X shipped?," "where are we on X?," "who owns X?," or any
825
+ close variant \u2014 read the following files in order:
826
+
827
+ 1. Read \`team-foundry/product/outcomes.md\` \u2014 find which outcome this feature supports (the why)
828
+ 2. Read \`team-foundry/product/customers.md\` \u2014 find customer quotes or personas that motivated it
829
+ ${featureQueryFullSteps}
830
+ For each file above: if the file doesn't exist on disk, skip it silently. If it exists
831
+ but doesn't mention the feature, note that gap explicitly \u2014 don't invent connections.
832
+
833
+ Synthesize into a single response: ${featureQuerySynthesis}
834
+
835
+ If the feature doesn't appear in any file, say: "I couldn't find [X] in any team-foundry
836
+ file \u2014 it may be undocumented or tracked under a different name. Want me to help capture it?"
837
+
838
+ The team-foundry files are the index. ${featureQueryIndexNote}
839
+
527
840
  ---
528
841
 
529
842
  ## Behaviors
@@ -537,6 +850,11 @@ For every finding: name it specifically (cite the file and the exact content),
537
850
  explain why it matters in one sentence, offer to draft the fix. Never list a finding
538
851
  without a proposed next step.
539
852
 
853
+ **"Why this nudge?" \u2014 required for every drift finding.** Use the evidence gathered in the
854
+ Reality observation section above. Always be specific \u2014 a team that understands why a nudge
855
+ fired is far more likely to act on it. Never say "this looks stale" without citing the
856
+ commit count, day delta, and (if blank) missing owner.
857
+
540
858
  ---
541
859
 
542
860
  ### Behavior 1: Outputs framed as outcomes
@@ -1094,6 +1412,61 @@ as a deliberate strategy update. If item should be removed: flag only \u2014 do
1094
1412
  the current strategy.md guiding policy explicitly excludes \u2014 or when strategy.md has no
1095
1413
  Guiding Policy filled in.
1096
1414
 
1415
+ ### Behavior 17: Team-specific lesson capture
1416
+
1417
+ **Severity:** Informational \u2014 surfaced as an offer, never a blocker.
1418
+
1419
+ **Trigger condition:** The user's message contains a recurring-pattern signal:
1420
+ - "we keep doing X"
1421
+ - "this is the third time we've had this problem"
1422
+ - "we always confuse Y with Z"
1423
+ - "every time we [situation], we [result]"
1424
+ - Any close variant signaling a pattern the team has noticed about itself.
1425
+
1426
+ **What to say:**
1427
+ > "Sounds like a recurring pattern. Want me to add a coaching rule to \`.team-foundry/team-lessons.md\`
1428
+ > so I watch for this on future reviews?"
1429
+
1430
+ **If the user confirms:**
1431
+ 1. Draft the rule in this format:
1432
+ \`- [date] [concise rule] \u2014 [brief context]\`
1433
+ 2. Ask: "Does this capture it, or want to edit the wording?"
1434
+ 3. After confirmation, write it to the Active rules section of \`.team-foundry/team-lessons.md\`.
1435
+ If the file doesn't exist, create it with this structure:
1436
+
1437
+ \`\`\`markdown
1438
+ ---
1439
+ purpose: Team-specific coaching rules learned from this team's patterns
1440
+ read_when: Coach runs any coaching behavior
1441
+ last_updated: [date]
1442
+ ---
1443
+
1444
+ # Team lessons
1445
+
1446
+ Rules this specific team has accumulated for their coach.
1447
+ Added when the team flags a recurring issue they want the coach to watch for.
1448
+
1449
+ ## Active rules
1450
+
1451
+ - [date] [rule] \u2014 [context]
1452
+
1453
+ ## Retired rules
1454
+
1455
+ <!-- Move rules here when no longer relevant, with the date retired. -->
1456
+ \`\`\`
1457
+
1458
+ **Rule retirement:** When a team says a rule is no longer relevant ("we fixed that," "we changed process"),
1459
+ offer to retire it: move it from Active rules to Retired rules with today's date prepended.
1460
+
1461
+ **Loading instruction:** When running any coaching behavior (inline, explicit, or scheduled),
1462
+ check if \`.team-foundry/team-lessons.md\` exists. If it does, load it and apply Active rules
1463
+ with equal weight to built-in behaviors, scoped to this team's context.
1464
+
1465
+ **What not to do:** Do not proactively suggest adding rules unless the user explicitly names a pattern.
1466
+ This behavior is listener-only \u2014 it waits for the signal, it does not fish for it.
1467
+
1468
+ ---
1469
+
1097
1470
  ## Quarterly retrospective
1098
1471
 
1099
1472
  ### Trigger
@@ -1491,11 +1864,11 @@ Example answers:
1491
1864
 
1492
1865
  ---
1493
1866
 
1494
- ### Theme 3: Measurement
1867
+ ${isSolo ? "" : `### Theme 3: Measurement
1495
1868
 
1496
1869
  *Files written: data/metrics.md*
1497
1870
 
1498
- **Q${isSolo ? "5" : "7"}. What are the 3\u20135 numbers you actually look at to know if the product is healthy?**
1871
+ **Q7. What are the 3\u20135 numbers you actually look at to know if the product is healthy?**
1499
1872
  *Why it matters: data/metrics.md is read whenever the AI is asked about product performance.
1500
1873
  Undefined metrics cause disagreements \u2014 two people reading the same number and reaching different conclusions.*
1501
1874
 
@@ -1506,7 +1879,7 @@ Example answers:
1506
1879
  - "Listing-to-sale rate \u2014 % of active listings that get bought within 30 days, from our DB."
1507
1880
  - "P1 bug count \u2014 open bugs tagged P1 in Linear, reviewed Monday mornings."
1508
1881
 
1509
- *After the answer: write each metric as a definition block to data/metrics.md.*
1882
+ *After the answer: write each metric as a definition block to data/metrics.md.*`}
1510
1883
 
1511
1884
  ---
1512
1885
 
@@ -1514,7 +1887,7 @@ Example answers:
1514
1887
 
1515
1888
  *Files written: product/customers.md*
1516
1889
 
1517
- **Q${isSolo ? "6" : "8"}. Name three customers you've spoken to directly.**
1890
+ **Q${isSolo ? "5" : "8"}. Name three customers you've spoken to directly.**
1518
1891
  *Why it matters: customers.md is read whenever the AI helps with prioritization, specs,
1519
1892
  or discovery. Generic personas don't resolve real disagreements. Named customers do.*
1520
1893
 
@@ -1576,7 +1949,7 @@ Example answers:
1576
1949
 
1577
1950
  *After the answer: write to engineering/quality-bar.md (tech debt stance).*`}
1578
1951
 
1579
- **Q${isSolo ? "7" : "12"}. What does "shipped" mean on your team?**
1952
+ **Q${isSolo ? "6" : "12"}. What does "shipped" mean on your team?**
1580
1953
  *Why it matters: misaligned definitions of done cause the most common sprint friction.
1581
1954
  Writing it down means the argument happens once, not every week.*
1582
1955
 
@@ -1658,7 +2031,7 @@ Example answers:
1658
2031
 
1659
2032
  *Files written: engineering/stack.md*
1660
2033
 
1661
- **Q${isSolo ? "8" : "18"}. What's the tech stack, and what would surprise an incoming engineer?**
2034
+ **Q${isSolo ? "7" : "18"}. What's the tech stack, and what would surprise an incoming engineer?**
1662
2035
  *Why it matters: stack.md is read every time the AI helps write or review code.
1663
2036
  The "what would surprise" framing surfaces the non-obvious conventions.*
1664
2037
 
@@ -1691,7 +2064,7 @@ If no: note that the decisions/ folder is ready when one comes up.
1691
2064
 
1692
2065
  *Files written: context/glossary.md${isSolo ? "" : ", context/stakeholders.md"}*
1693
2066
 
1694
- **Q${isSolo ? "9" : "21"}. What words does your team use that would confuse an outsider?**
2067
+ **Q${isSolo ? "8" : "21"}. What words does your team use that would confuse an outsider?**
1695
2068
  *Why it matters: glossary.md is read when the AI writes specs, reviews code, or
1696
2069
  discusses product strategy. Shared vocabulary prevents the AI from guessing at meaning.*
1697
2070
 
@@ -1712,7 +2085,7 @@ For each stakeholder: name/role, what they really care about, how they prefer to
1712
2085
 
1713
2086
  *After the answer: write to context/stakeholders.md.*`}
1714
2087
 
1715
- **Q${isSolo ? "10" : "23"}. Are there any terms your team uses inconsistently with each other?**
2088
+ **Q${isSolo ? "9" : "23"}. Are there any terms your team uses inconsistently with each other?**
1716
2089
  *Why it matters: inconsistent internal vocabulary is a reliable source of meeting friction.
1717
2090
  Naming it here gives the AI a flag to raise when it notices the inconsistency.*
1718
2091
 
@@ -1752,6 +2125,7 @@ function northStarTemplate(ctx) {
1752
2125
  purpose: The single metric that best captures whether we're creating the value we intend to create
1753
2126
  read_when: Setting quarterly direction, evaluating big bets, writing OKRs, onboarding new team members
1754
2127
  last_updated: ${ctx.date}
2128
+ owner:
1755
2129
  ---
1756
2130
 
1757
2131
  # North Star
@@ -1805,6 +2179,7 @@ function outcomesTemplate(ctx) {
1805
2179
  purpose: Current quarter outcomes \u2014 the changes in customer behavior that define success this quarter
1806
2180
  read_when: Prioritizing work, writing specs, deciding what to build next, evaluating tradeoffs
1807
2181
  last_updated: ${ctx.date}
2182
+ owner:
1808
2183
  ---
1809
2184
 
1810
2185
  # Outcomes
@@ -1848,6 +2223,7 @@ function customersTemplate(ctx) {
1848
2223
  purpose: Named customers, personas, jobs to be done, and direct quotes from real conversations
1849
2224
  read_when: Writing specs, prioritizing features, evaluating tradeoffs, any time you're guessing what customers want
1850
2225
  last_updated: ${ctx.date}
2226
+ owner:
1851
2227
  ---
1852
2228
 
1853
2229
  # Customers
@@ -1901,6 +2277,7 @@ function nowNextLaterTemplate(ctx) {
1901
2277
  purpose: What we're building now, what we're committed to next, and what's directional
1902
2278
  read_when: Sprint planning, stakeholder updates, evaluating new requests, prioritization discussions
1903
2279
  last_updated: ${ctx.date}
2280
+ owner:
1904
2281
  ---
1905
2282
 
1906
2283
  # Now / Next / Later
@@ -1960,6 +2337,7 @@ function assumptionsTemplate(ctx) {
1960
2337
  purpose: Open assumptions and untested beliefs \u2014 the bets the team is currently making
1961
2338
  read_when: Designing discovery work, scoping experiments, retros, any time a decision feels risky
1962
2339
  last_updated: ${ctx.date}
2340
+ owner:
1963
2341
  ---
1964
2342
 
1965
2343
  # Assumptions
@@ -2035,6 +2413,7 @@ function risksTemplate(ctx) {
2035
2413
  purpose: The four product risks \u2014 tracked so they don't become surprises at launch
2036
2414
  read_when: Scoping new features, go/no-go decisions, discovery planning, quarterly reviews
2037
2415
  last_updated: ${ctx.date}
2416
+ owner:
2038
2417
  ---
2039
2418
 
2040
2419
  # Risks
@@ -2094,6 +2473,7 @@ function trioTemplate(ctx) {
2094
2473
  purpose: The product trio \u2014 who owns what decisions and how the three roles work together
2095
2474
  read_when: Escalations, onboarding, clarifying ownership, any "who decides this?" conversation
2096
2475
  last_updated: ${ctx.date}
2476
+ owner:
2097
2477
  ---
2098
2478
 
2099
2479
  # Team Trio
@@ -2115,11 +2495,17 @@ last_updated: ${ctx.date}
2115
2495
 
2116
2496
  ## Members
2117
2497
 
2118
- | Role | Person | Focus area |
2119
- |---|---|---|
2120
- | Product Manager | <!-- name --> | What to build and why |
2121
- | Engineering Lead | <!-- name --> | How to build it, tech debt, architecture |
2122
- | Design Lead | <!-- name --> | UX, flows, visual quality |
2498
+ | Role | Person | GitHub (optional) | Focus area |
2499
+ |---|---|---|---|
2500
+ | Product Manager | <!-- name --> | <!-- @handle --> | What to build and why |
2501
+ | Engineering Lead | <!-- name --> | <!-- @handle --> | How to build it, tech debt, architecture |
2502
+ | Design Lead | <!-- name --> | <!-- @handle --> | UX, flows, visual quality |
2503
+
2504
+ <!-- Optional additional fields per member (add inline or as a sub-list):
2505
+ - slack: U0C3D4E5F (Slack member ID for @mentions)
2506
+ - linear: user ID (for issue assignment lookups)
2507
+ Fill in what's known; leave blank what isn't. These fields let AI tools
2508
+ take cross-platform actions when relevant. -->
2123
2509
 
2124
2510
  ## How we make decisions
2125
2511
 
@@ -2151,6 +2537,7 @@ function workingAgreementTemplate(ctx) {
2151
2537
  purpose: Definition of done, definition of ready, ceremonies, and team norms \u2014 the honest version
2152
2538
  read_when: Code review, sprint planning, retrospectives, any "this isn't how we said we'd work" moment
2153
2539
  last_updated: ${ctx.date}
2540
+ owner:
2154
2541
  ---
2155
2542
 
2156
2543
  # Working Agreement
@@ -2221,6 +2608,7 @@ function aiPracticesTemplate(ctx) {
2221
2608
  purpose: How this team uses AI tools \u2014 what's working, what we've decided not to do, and our norms
2222
2609
  read_when: Onboarding engineers, evaluating new AI tooling, retrospectives on AI-assisted work
2223
2610
  last_updated: ${ctx.date}
2611
+ owner:
2224
2612
  ---
2225
2613
 
2226
2614
  # AI Practices
@@ -2275,6 +2663,7 @@ function stackTemplate(ctx) {
2275
2663
  purpose: Tech stack, conventions, deployment pipeline, and local dev setup
2276
2664
  read_when: Writing code, reviewing PRs, evaluating new dependencies, onboarding engineers
2277
2665
  last_updated: ${ctx.date}
2666
+ owner:
2278
2667
  ---
2279
2668
 
2280
2669
  # Engineering Stack
@@ -2324,6 +2713,7 @@ function qualityBarTemplate(ctx) {
2324
2713
  purpose: The team's honest stance on tech debt, bugs, and what "shipped" actually means
2325
2714
  read_when: Code review, sprint planning, evaluating shortcuts, any quality-vs-speed conversation
2326
2715
  last_updated: ${ctx.date}
2716
+ owner:
2327
2717
  ---
2328
2718
 
2329
2719
  # Quality Bar
@@ -2394,6 +2784,7 @@ function decisionsReadmeTemplate(ctx) {
2394
2784
  purpose: Index and template for architecture decision records (ADRs)
2395
2785
  read_when: Evaluating architectural choices, understanding why the codebase looks the way it does
2396
2786
  last_updated: ${ctx.date}
2787
+ owner:
2397
2788
  ---
2398
2789
 
2399
2790
  # Architecture Decisions
@@ -2450,6 +2841,7 @@ function principlesTemplate(ctx) {
2450
2841
  purpose: Design principles, tone of voice, and accessibility stance
2451
2842
  read_when: Designing new features, writing copy, reviewing designs, evaluating UX tradeoffs
2452
2843
  last_updated: ${ctx.date}
2844
+ owner:
2453
2845
  ---
2454
2846
 
2455
2847
  # Design Principles
@@ -2507,6 +2899,7 @@ function metricsTemplate(ctx) {
2507
2899
  purpose: Metric definitions, ownership, and data sources \u2014 so the team means the same thing
2508
2900
  read_when: Building dashboards, writing OKRs, reviewing product health, debugging data discrepancies
2509
2901
  last_updated: ${ctx.date}
2902
+ owner:
2510
2903
  ---
2511
2904
 
2512
2905
  # Metrics
@@ -2553,6 +2946,7 @@ function glossaryTemplate(ctx) {
2553
2946
  purpose: Domain terms, acronyms, and jargon specific to this team and product
2554
2947
  read_when: Onboarding, writing specs, any time a term feels ambiguous or overloaded
2555
2948
  last_updated: ${ctx.date}
2949
+ owner:
2556
2950
  ---
2557
2951
 
2558
2952
  # Glossary
@@ -2594,6 +2988,7 @@ function stakeholdersTemplate(ctx) {
2594
2988
  purpose: Who cares about this product, what they care about, and how the team works with them
2595
2989
  read_when: Stakeholder updates, go/no-go decisions, escalations, quarterly planning
2596
2990
  last_updated: ${ctx.date}
2991
+ owner:
2597
2992
  ---
2598
2993
 
2599
2994
  # Stakeholders
@@ -2637,6 +3032,7 @@ function strategyTemplate(ctx) {
2637
3032
  purpose: The strategic logic connecting our north-star gap to what we're building. Read before adding anything to the roadmap.
2638
3033
  read_when: Roadmap planning, evaluating new feature requests, quarterly retrospective, when a new item is proposed for Now or Next, when a new team member is onboarding
2639
3034
  last_updated: ${ctx.date}
3035
+ owner:
2640
3036
  ---
2641
3037
 
2642
3038
  # Strategy
@@ -2725,6 +3121,14 @@ var FULL_ONLY_ENTRIES = [
2725
3121
  { relativePath: "team-foundry/context/stakeholders.md", content: stakeholdersTemplate },
2726
3122
  { relativePath: "team-foundry/product/strategy.md", content: strategyTemplate }
2727
3123
  ];
3124
+ var FEDERATED_ENTRIES = [
3125
+ { relativePath: "team-foundry/product/CLAUDE.md", content: federatedProductTemplate },
3126
+ { relativePath: "team-foundry/team/CLAUDE.md", content: federatedTeamTemplate },
3127
+ { relativePath: "team-foundry/engineering/CLAUDE.md", content: federatedEngineeringTemplate },
3128
+ { relativePath: "team-foundry/design/CLAUDE.md", content: federatedDesignTemplate },
3129
+ { relativePath: "team-foundry/data/CLAUDE.md", content: federatedDataTemplate },
3130
+ { relativePath: "team-foundry/context/CLAUDE.md", content: federatedContextTemplate }
3131
+ ];
2728
3132
  function rootEntries(tool) {
2729
3133
  if (tool === "claude") {
2730
3134
  return [{ relativePath: "CLAUDE.md", content: rootClaudeTemplate }];
@@ -2732,18 +3136,22 @@ function rootEntries(tool) {
2732
3136
  if (tool === "gemini") {
2733
3137
  return [{ relativePath: "GEMINI.md", content: rootGeminiTemplate }];
2734
3138
  }
3139
+ if (tool === "cursor") {
3140
+ return [{ relativePath: ".cursor/rules/team-foundry.mdc", content: rootCursorTemplate }];
3141
+ }
2735
3142
  return [
2736
3143
  { relativePath: "CLAUDE.md", content: rootClaudeTemplate },
2737
3144
  { relativePath: "GEMINI.md", content: rootGeminiTemplate }
2738
3145
  ];
2739
3146
  }
2740
3147
  async function scaffold(options) {
2741
- const { targetDir, profile, tool, repoVisibility, date, ingestionPath, ingestion } = options;
3148
+ const { targetDir, profile, tool, repoVisibility, date, ingestionPath, ingestion, federated } = options;
2742
3149
  const ctx = { profile, tool, repoVisibility, date, ingestionPath, ingestion };
2743
3150
  const entries = [
2744
3151
  ...rootEntries(tool),
2745
3152
  ...SOLO_ENTRIES,
2746
- ...profile === "full" ? FULL_ONLY_ENTRIES : []
3153
+ ...profile === "full" ? FULL_ONLY_ENTRIES : [],
3154
+ ...profile === "full" && federated ? FEDERATED_ENTRIES : []
2747
3155
  ];
2748
3156
  for (const entry of entries) {
2749
3157
  const fullPath = path.join(targetDir, entry.relativePath);
@@ -2778,10 +3186,185 @@ async function writeGitignore(targetDir) {
2778
3186
  `, "utf-8");
2779
3187
  }
2780
3188
 
3189
+ // src/status.ts
3190
+ import fs3 from "fs/promises";
3191
+ import path3 from "path";
3192
+ import { spawnSync } from "child_process";
3193
+ var SOLO_FILES = [
3194
+ "team-foundry/product/north-star.md",
3195
+ "team-foundry/product/outcomes.md",
3196
+ "team-foundry/product/customers.md",
3197
+ "team-foundry/engineering/stack.md"
3198
+ ];
3199
+ var FULL_ONLY_FILES = [
3200
+ "team-foundry/product/now-next-later.md",
3201
+ "team-foundry/product/assumptions.md",
3202
+ "team-foundry/product/risks.md",
3203
+ "team-foundry/product/strategy.md",
3204
+ "team-foundry/team/trio.md",
3205
+ "team-foundry/team/working-agreement.md",
3206
+ "team-foundry/team/ai-practices.md",
3207
+ "team-foundry/engineering/quality-bar.md",
3208
+ "team-foundry/design/principles.md",
3209
+ "team-foundry/data/metrics.md",
3210
+ "team-foundry/context/glossary.md",
3211
+ "team-foundry/context/stakeholders.md"
3212
+ ];
3213
+ var ALL_FILES = [...SOLO_FILES, ...FULL_ONLY_FILES];
3214
+ var STALE_DAYS = 45;
3215
+ function parseFrontmatter(content) {
3216
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
3217
+ if (!match) return {};
3218
+ const result = {};
3219
+ for (const line of match[1].split("\n")) {
3220
+ const idx = line.indexOf(":");
3221
+ if (idx === -1) continue;
3222
+ const key = line.slice(0, idx).trim();
3223
+ const value = line.slice(idx + 1).trim();
3224
+ result[key] = value;
3225
+ }
3226
+ return result;
3227
+ }
3228
+ function daysSince(dateStr) {
3229
+ const then = new Date(dateStr).getTime();
3230
+ if (isNaN(then)) return null;
3231
+ return Math.floor((Date.now() - then) / 864e5);
3232
+ }
3233
+ function prsSinceDate(targetDir, dateStr) {
3234
+ try {
3235
+ const since = new Date(dateStr).toISOString();
3236
+ if (isNaN(new Date(dateStr).getTime())) return null;
3237
+ const result = spawnSync(
3238
+ "git",
3239
+ ["-C", targetDir, "log", "--oneline", "--merges", `--since=${since}`],
3240
+ { encoding: "utf-8", timeout: 5e3 }
3241
+ );
3242
+ if (result.status !== 0) return null;
3243
+ return result.stdout.trim().split("\n").filter(Boolean).length;
3244
+ } catch {
3245
+ return null;
3246
+ }
3247
+ }
3248
+ function isEffectivelyEmpty(body) {
3249
+ const stripped = body.replace(/<!--[\s\S]*?-->/g, "").replace(/^#+\s.*$/gm, "").trim();
3250
+ return stripped.length < 30;
3251
+ }
3252
+ async function analyzeFile(targetDir, relativePath) {
3253
+ const fullPath = path3.join(targetDir, relativePath);
3254
+ let exists = false;
3255
+ let isEmpty = false;
3256
+ let lastUpdated = null;
3257
+ let owner = null;
3258
+ let daysSinceUpdate = null;
3259
+ let prsSince = null;
3260
+ try {
3261
+ const content = await fs3.readFile(fullPath, "utf-8");
3262
+ exists = true;
3263
+ const body = content.replace(/^---[\s\S]*?---\n?/, "").trim();
3264
+ isEmpty = isEffectivelyEmpty(body);
3265
+ const fm = parseFrontmatter(content);
3266
+ if (fm["last_updated"]) {
3267
+ lastUpdated = fm["last_updated"];
3268
+ daysSinceUpdate = daysSince(lastUpdated);
3269
+ prsSince = prsSinceDate(targetDir, lastUpdated);
3270
+ }
3271
+ owner = fm["owner"] || null;
3272
+ } catch {
3273
+ }
3274
+ let health = "ok";
3275
+ if (!exists) health = "missing";
3276
+ else if (isEmpty) health = "empty";
3277
+ else if (daysSinceUpdate !== null && daysSinceUpdate >= STALE_DAYS) health = "stale";
3278
+ return {
3279
+ relativePath,
3280
+ lastUpdated,
3281
+ owner,
3282
+ daysSinceUpdate,
3283
+ prsSinceUpdate: prsSince,
3284
+ exists,
3285
+ isEmpty,
3286
+ health
3287
+ };
3288
+ }
3289
+ function healthIcon(health) {
3290
+ if (health === "ok") return "\u2713";
3291
+ if (health === "stale") return "~";
3292
+ if (health === "empty") return "\u25CB";
3293
+ return "\u2717";
3294
+ }
3295
+ function formatRow(s) {
3296
+ const icon = healthIcon(s.health);
3297
+ const rawName = s.relativePath.replace("team-foundry/", "");
3298
+ const name = rawName.slice(0, 42).padEnd(42);
3299
+ const updated = s.lastUpdated ?? "\u2014";
3300
+ const age = s.daysSinceUpdate !== null ? `${s.daysSinceUpdate}d` : "\u2014";
3301
+ const prs = s.prsSinceUpdate !== null ? `${s.prsSinceUpdate} PRs` : "\u2014";
3302
+ const owner = s.owner || "\u2014";
3303
+ return ` ${icon} ${name} ${updated.padEnd(12)} ${age.padEnd(6)} ${prs.padEnd(8)} ${owner}`;
3304
+ }
3305
+ function whyNudge(s) {
3306
+ const parts = [];
3307
+ if (s.daysSinceUpdate !== null) parts.push(`${s.daysSinceUpdate} days since last update`);
3308
+ if (s.prsSinceUpdate !== null && s.prsSinceUpdate > 0)
3309
+ parts.push(`${s.prsSinceUpdate} PRs shipped since then`);
3310
+ if (!s.owner) parts.push("no owner set");
3311
+ return parts.join(", ");
3312
+ }
3313
+ async function runStatus(targetDir) {
3314
+ const fullProfileFile = path3.join(targetDir, "team-foundry/team/trio.md");
3315
+ let isFullProfile = false;
3316
+ try {
3317
+ await fs3.access(fullProfileFile);
3318
+ isFullProfile = true;
3319
+ } catch {
3320
+ }
3321
+ const filesToCheck = isFullProfile ? ALL_FILES : SOLO_FILES;
3322
+ const results = await Promise.all(filesToCheck.map((f) => analyzeFile(targetDir, f)));
3323
+ const ok = results.filter((r) => r.health === "ok");
3324
+ const stale = results.filter((r) => r.health === "stale");
3325
+ const empty = results.filter((r) => r.health === "empty");
3326
+ const missing = results.filter((r) => r.health === "missing");
3327
+ console.log("\n team-foundry status\n");
3328
+ console.log(` ${"File".padEnd(44)} ${"Last updated".padEnd(12)} ${"Age".padEnd(6)} ${"PRs".padEnd(8)} Owner`);
3329
+ console.log(` ${"\u2500".repeat(90)}`);
3330
+ for (const s of results) console.log(formatRow(s));
3331
+ console.log();
3332
+ console.log(` \u2713 ${ok.length} current ~ ${stale.length} stale \u25CB ${empty.length} empty \u2717 ${missing.length} missing
3333
+ `);
3334
+ if (stale.length > 0) {
3335
+ console.log(" Stale files \u2014 why this nudge:\n");
3336
+ for (const s of stale) {
3337
+ console.log(` ~ ${s.relativePath.replace("team-foundry/", "")}`);
3338
+ console.log(` ${whyNudge(s)}
3339
+ `);
3340
+ }
3341
+ }
3342
+ if (empty.length > 0) {
3343
+ console.log(" Empty files \u2014 not yet filled in:\n");
3344
+ for (const s of empty) {
3345
+ console.log(` \u25CB ${s.relativePath.replace("team-foundry/", "")}`);
3346
+ }
3347
+ console.log();
3348
+ }
3349
+ if (missing.length > 0) {
3350
+ console.log(" Missing files \u2014 run `npx create-team-foundry` to scaffold:\n");
3351
+ for (const s of missing) {
3352
+ console.log(` \u2717 ${s.relativePath.replace("team-foundry/", "")}`);
3353
+ }
3354
+ console.log();
3355
+ }
3356
+ const noOwner = results.filter((r) => r.exists && !r.owner);
3357
+ if (noOwner.length > 0) {
3358
+ console.log(` ${noOwner.length} file(s) have no owner set. Add \`owner: <name>\` to their frontmatter.
3359
+ `);
3360
+ }
3361
+ }
3362
+
2781
3363
  // src/index.ts
2782
3364
  var TOOL_LABEL = {
2783
3365
  claude: "Claude Code",
2784
3366
  gemini: "Gemini CLI",
3367
+ cursor: "Cursor",
2785
3368
  both: "Claude Code or Gemini CLI"
2786
3369
  };
2787
3370
  var PASTE_PLACEHOLDER = `# Paste your existing docs here
@@ -2799,16 +3382,16 @@ You can paste multiple documents \u2014 just separate them with a heading like:
2799
3382
  When you're done, save this file and start the onboarding interview.
2800
3383
  `;
2801
3384
  async function checkDirectory(targetDir) {
2802
- const prdPath = path3.join(targetDir, "team-foundry-prd-v2.md");
2803
- const scaffoldPath = path3.join(targetDir, "src", "scaffold.ts");
3385
+ const prdPath = path4.join(targetDir, "team-foundry-prd-v2.md");
3386
+ const scaffoldPath = path4.join(targetDir, "src", "scaffold.ts");
2804
3387
  let isSourceRepo = false;
2805
3388
  try {
2806
- await fs3.access(prdPath);
3389
+ await fs4.access(prdPath);
2807
3390
  isSourceRepo = true;
2808
3391
  } catch {
2809
3392
  }
2810
3393
  try {
2811
- await fs3.access(scaffoldPath);
3394
+ await fs4.access(scaffoldPath);
2812
3395
  isSourceRepo = true;
2813
3396
  } catch {
2814
3397
  }
@@ -2818,17 +3401,17 @@ async function checkDirectory(targetDir) {
2818
3401
  );
2819
3402
  process.exit(1);
2820
3403
  }
2821
- const pkgPath = path3.join(targetDir, "package.json");
2822
- const srcPath = path3.join(targetDir, "src");
3404
+ const pkgPath = path4.join(targetDir, "package.json");
3405
+ const srcPath = path4.join(targetDir, "src");
2823
3406
  let hasPkg = false;
2824
3407
  let hasSrc = false;
2825
3408
  try {
2826
- await fs3.access(pkgPath);
3409
+ await fs4.access(pkgPath);
2827
3410
  hasPkg = true;
2828
3411
  } catch {
2829
3412
  }
2830
3413
  try {
2831
- await fs3.access(srcPath);
3414
+ await fs4.access(srcPath);
2832
3415
  hasSrc = true;
2833
3416
  } catch {
2834
3417
  }
@@ -2845,17 +3428,21 @@ async function checkDirectory(targetDir) {
2845
3428
  }
2846
3429
  async function main() {
2847
3430
  const targetDir = process.cwd();
3431
+ if (process.argv[2] === "status") {
3432
+ await runStatus(targetDir);
3433
+ return;
3434
+ }
2848
3435
  await checkDirectory(targetDir);
2849
3436
  const answers = await runPrompts();
2850
3437
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
2851
3438
  await scaffold({ ...answers, targetDir, date });
2852
3439
  await writeGitignore(targetDir);
2853
3440
  if (answers.ingestion === "paste") {
2854
- const pastePath = path3.join(targetDir, ".team-foundry", "paste-content.md");
3441
+ const pastePath = path4.join(targetDir, ".team-foundry", "paste-content.md");
2855
3442
  try {
2856
- await fs3.access(pastePath);
3443
+ await fs4.access(pastePath);
2857
3444
  } catch {
2858
- await fs3.writeFile(pastePath, PASTE_PLACEHOLDER, "utf-8");
3445
+ await fs4.writeFile(pastePath, PASTE_PLACEHOLDER, "utf-8");
2859
3446
  }
2860
3447
  }
2861
3448
  const tool = TOOL_LABEL[answers.tool];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-team-foundry",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "Scaffold a team-foundry into any repo — structured context files for Claude Code and Gemini CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",