create-team-foundry 2.0.0 → 2.1.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.
- package/README.md +117 -62
- package/dist/index.js +508 -14
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,120 +1,175 @@
|
|
|
1
1
|
# team-foundry
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Make your AI coding outputs align with your product reality.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Your AI tool gives better answers when it knows what the product is for, who the customers are, and what quality means. team-foundry puts that context in your shared repo — where every AI tool on every teammate's machine reads the same files from the same repo.
|
|
6
6
|
|
|
7
|
-
```
|
|
7
|
+
```bash
|
|
8
8
|
npx create-team-foundry
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
**Run in your shared repo. Then say: `"Let's set up our team-foundry."`**
|
|
12
|
+
If your repo already has a README and commit history, setup takes about 1 minute — the AI reads your repo and pre-fills the answers. Starting fresh takes 15–25 minutes. Either way, your AI starts using the context immediately.
|
|
12
13
|
|
|
13
|
-
team-foundry
|
|
14
|
+
→ **[See what it looks like when populated](example/)** — a fully filled-in team-foundry for Clearline, a fictional 8-person B2B SaaS team. Open `example/` in Claude Code or Cursor and ask anything.
|
|
14
15
|
|
|
15
|
-
**
|
|
16
|
+
**[team-foundry.com](https://team-foundry.com)** · If this helps your team, [a star helps others find it](https://github.com/tomershahar/team-foundry).
|
|
16
17
|
|
|
17
18
|
---
|
|
18
19
|
|
|
19
|
-
##
|
|
20
|
+
## The problem
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
**Before team-foundry:**
|
|
23
|
+
> You ask the AI to help prioritize a sprint. It gives solid generic advice — but doesn't know your north star metric, hasn't seen your open assumptions, and has no idea that your top customer churned last month.
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
**After team-foundry:**
|
|
26
|
+
> The AI references your outcomes, flags an assumption that's been untested for 45 days, and notes that two roadmap items haven't been updated since 8 PRs shipped. It offers to draft the fixes. You confirm.
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
That context used to live in someone's head, a Notion page nobody reads, or a wiki 6 months stale. team-foundry puts it in your shared repo — where every AI tool reads it every session.
|
|
26
29
|
|
|
27
|
-
|
|
30
|
+
---
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
```bash
|
|
33
|
+
npx create-team-foundry
|
|
34
|
+
```
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
**Try it in 2 minutes.** Answer a few questions. Files appear. Open your AI tool. Done.
|
|
32
37
|
|
|
33
|
-
|
|
38
|
+
---
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
## Set up once. Everyone gets the same context.
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
**No cloud. No sync service. No extra accounts.** team-foundry uses your existing repo as the shared space.
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
**One person sets it up**
|
|
45
|
+
Run `npx create-team-foundry` in your shared repo. The CLI scaffolds a `team-foundry/` folder, generates the right tool file (`CLAUDE.md`, `GEMINI.md`, or `.cursor/rules/`), and commits everything to git.
|
|
40
46
|
|
|
41
|
-
|
|
47
|
+
**Everyone else pulls**
|
|
48
|
+
Teammates `git pull`. They now have the same team-foundry files locally. Their Claude Code, Cursor, or Gemini CLI reads from the same files yours does. No installs, no logins, no setup.
|
|
42
49
|
|
|
43
|
-
|
|
50
|
+
**Updates flow through git**
|
|
51
|
+
When someone updates a file — or the coach drafts an update they confirm — it's a normal git commit. Push, pull, review in PRs. Everyone stays in sync the same way they already sync code.
|
|
44
52
|
|
|
45
|
-
|
|
53
|
+
```
|
|
54
|
+
┌────────────────────────┐
|
|
55
|
+
│ Your shared repo │
|
|
56
|
+
│ (GitHub / GitLab) │
|
|
57
|
+
│ │
|
|
58
|
+
│ team-foundry/ │
|
|
59
|
+
│ ├─ outcomes.md │
|
|
60
|
+
│ ├─ customers.md │
|
|
61
|
+
│ ├─ decisions/ │
|
|
62
|
+
│ └─ ... │
|
|
63
|
+
└───────────┬────────────┘
|
|
64
|
+
│
|
|
65
|
+
git pull / push (no other sync)
|
|
66
|
+
│
|
|
67
|
+
┌─────────────────┼─────────────────┐
|
|
68
|
+
▼ ▼ ▼
|
|
69
|
+
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
70
|
+
│ PM │ │ Engineer │ │ Designer │
|
|
71
|
+
│ Claude │ │ Cursor │ │ Gemini │
|
|
72
|
+
│ Code │ │ │ │ CLI │
|
|
73
|
+
└──────────┘ └──────────┘ └──────────┘
|
|
74
|
+
```
|
|
46
75
|
|
|
47
|
-
|
|
76
|
+
*Your repo is the shared space. Git is the sync. team-foundry adds the structure and the coach.*
|
|
48
77
|
|
|
49
|
-
|
|
78
|
+
---
|
|
50
79
|
|
|
51
|
-
## What
|
|
80
|
+
## What gets created
|
|
52
81
|
|
|
53
|
-
|
|
82
|
+
**Solo profile (1–3 people):** 7 files, ~1 minute with repo scan / ~15 minutes fresh.
|
|
83
|
+
**Full profile (4–15 people):** 20 files, ~1 minute with repo scan / ~25 minutes fresh.
|
|
54
84
|
|
|
55
|
-
|
|
|
85
|
+
| Profile | Files | Includes |
|
|
56
86
|
|---|---|---|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
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
|
-
|
|
65
|
-
## Strategy in Git
|
|
87
|
+
| Solo | 7 | Root instruction file (CLAUDE.md/GEMINI.md), getting started guide, coach playbook, north star, outcomes, customers, stack |
|
|
88
|
+
| Full | 20 | Everything above + strategy, roadmap, assumptions, risks, trio, working agreement, AI practices, quality bar, decisions log, design principles, metrics, glossary, stakeholders |
|
|
89
|
+
| Full (federated) | 26 | Everything above + per-folder routing files for multi-instance setups |
|
|
66
90
|
|
|
67
|
-
|
|
91
|
+
Every file has YAML frontmatter (`purpose`, `read_when`, `last_updated`, `owner`) so the AI knows when to load it and why.
|
|
68
92
|
|
|
69
|
-
|
|
70
|
-
- During setup you choose repo visibility (public / internal / private) and the coach adjusts its language accordingly
|
|
71
|
-
- Nothing in the generated files requires sensitive data — personas use first names, metrics use your definitions, decisions record rationale not revenue
|
|
93
|
+
## Supported tools
|
|
72
94
|
|
|
73
|
-
|
|
95
|
+
| Tool | File generated |
|
|
96
|
+
|---|---|
|
|
97
|
+
| Claude Code | `CLAUDE.md` |
|
|
98
|
+
| Gemini CLI | `GEMINI.md` |
|
|
99
|
+
| Cursor | `.cursor/rules/team-foundry.mdc` |
|
|
100
|
+
| OpenAI Codex / generic agents | `AGENTS.md` |
|
|
101
|
+
| Both (Claude + Gemini) | `CLAUDE.md` + `GEMINI.md` |
|
|
74
102
|
|
|
75
103
|
## The coach
|
|
76
104
|
|
|
77
|
-
|
|
105
|
+
After setup, the coach watches your files for drift while you work. It runs in three modes:
|
|
78
106
|
|
|
79
|
-
- **Inline** — silent by default.
|
|
80
|
-
- **Explicit** —
|
|
81
|
-
- **Scheduled** —
|
|
107
|
+
- **Inline** — silent by default. Surfaces one sentence when a gap is directly relevant to what you're working on. Never interrupts.
|
|
108
|
+
- **Explicit** — `"let's do a team-foundry review"`. Full audit, all files, findings by severity.
|
|
109
|
+
- **Scheduled** — weekly check-in, top 3 findings, offers to draft fixes.
|
|
82
110
|
|
|
83
|
-
The coach never writes without your confirmation
|
|
111
|
+
**The coach never writes without your confirmation.**
|
|
84
112
|
|
|
85
|
-
###
|
|
113
|
+
### What it catches
|
|
86
114
|
|
|
87
|
-
|
|
115
|
+
| Pattern | Example |
|
|
116
|
+
|---|---|
|
|
117
|
+
| **Output-as-outcome drift** | `outcomes.md` says "ship the dashboard" instead of "reduce time-to-insight for SMB analysts" |
|
|
118
|
+
| **Assumption fossilization** | Core assumption logged 94 days ago, never retested, still driving three roadmap items |
|
|
119
|
+
| **Customer ghost syndrome** | Enterprise persona last interviewed in February. Three Q2 features built "for enterprise." |
|
|
120
|
+
| **Decision amnesia** | Q1 ADR rejects microservices. Q3 discussion reopens it with no reference to what changed. |
|
|
121
|
+
| **Reality drift** | 8 PRs shipped since `outcomes.md` was last updated. Coach cites the commit messages. |
|
|
122
|
+
| **Build-trap signal** | "Add collaborative editing" moves to Now with no linked assumption and no validation. |
|
|
123
|
+
|
|
124
|
+
Every finding cites the specific file, the specific content, and the evidence. Not "this looks stale."
|
|
125
|
+
|
|
126
|
+
### Triggering the coach
|
|
88
127
|
|
|
89
128
|
| What to say | What happens |
|
|
90
129
|
|---|---|
|
|
91
|
-
| `"let's do a team-foundry review"` | Full audit — all files
|
|
92
|
-
| `"coach mode"` | Same as above |
|
|
130
|
+
| `"let's do a team-foundry review"` | Full audit — all files, findings by severity |
|
|
93
131
|
| `"review our outcomes"` | Targeted review of one file |
|
|
94
|
-
| `"
|
|
95
|
-
| `"run the weekly
|
|
132
|
+
| `"tell me about feature X"` | Synthesizes status, rationale, customer evidence, open bets |
|
|
133
|
+
| `"run the weekly review"` | Top 3 issues, draft fixes offered |
|
|
96
134
|
|
|
97
|
-
|
|
135
|
+
## Status command
|
|
98
136
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
```
|
|
102
|
-
## team-foundry coach
|
|
103
|
-
inline-nudges: off
|
|
137
|
+
```bash
|
|
138
|
+
npx create-team-foundry status
|
|
104
139
|
```
|
|
105
140
|
|
|
106
|
-
|
|
141
|
+
Health table across all your files: last updated, days since update, PRs shipped since then, owner, health classification (ok / stale / empty / missing). Link integrity checks flag outcomes with no linked assumption, Now items with no validated bet, and metrics referenced but not defined.
|
|
107
142
|
|
|
108
|
-
|
|
143
|
+
---
|
|
109
144
|
|
|
110
|
-
|
|
111
|
-
-
|
|
145
|
+
```bash
|
|
146
|
+
npx create-team-foundry
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Run in your shared repo. Then say: `"Let's set up our team-foundry."`**
|
|
150
|
+
|
|
151
|
+
---
|
|
112
152
|
|
|
113
153
|
## What's next
|
|
114
154
|
|
|
115
|
-
|
|
155
|
+
**v2.x**
|
|
156
|
+
- `--json` output for `status` — pipe findings into CI or dashboards
|
|
157
|
+
- `--strict` mode — fail CI when critical drift is detected
|
|
158
|
+
- MCP server — expose team-foundry context as a tool for agents that don't read files natively
|
|
159
|
+
|
|
160
|
+
**v3 (exploring)**
|
|
161
|
+
- Cross-repo federation — one team-foundry for a platform team read by multiple product repos
|
|
162
|
+
- Status webhooks — post weekly drift report to Slack without leaving the terminal
|
|
163
|
+
- Team onboarding flow — guided interview for new team members joining an existing team-foundry
|
|
164
|
+
|
|
165
|
+
Have a use case or feature idea? [Open an issue](https://github.com/tomershahar/team-foundry/issues).
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Requirements
|
|
116
170
|
|
|
117
|
-
|
|
171
|
+
- Node 18+
|
|
172
|
+
- Claude Code, Gemini CLI, Cursor, or any AI tool that reads files from your repo
|
|
118
173
|
|
|
119
174
|
## Contributing
|
|
120
175
|
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ async function runPrompts() {
|
|
|
29
29
|
message: "Team size?",
|
|
30
30
|
options: [
|
|
31
31
|
{ value: "solo", label: "1\u20133 people (solo profile \u2014 7 files)" },
|
|
32
|
-
{ value: "full", label: "4\u201315 people (full profile \u2014
|
|
32
|
+
{ value: "full", label: "4\u201315 people (full profile \u2014 20 files)" }
|
|
33
33
|
]
|
|
34
34
|
});
|
|
35
35
|
cancelIfNeeded(profile);
|
|
@@ -57,15 +57,19 @@ async function runPrompts() {
|
|
|
57
57
|
const ingestion = await select({
|
|
58
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)",
|
|
59
59
|
options: [
|
|
60
|
-
{ value: "
|
|
61
|
-
{ value: "
|
|
62
|
-
{ value: "
|
|
60
|
+
{ value: "repo", label: "Repo signals only (README, package.json, git history, GitHub PRs/issues)" },
|
|
61
|
+
{ value: "repo+local", label: "Repo + local docs folder (repo signals + point me at a folder)" },
|
|
62
|
+
{ value: "repo+mcp", label: "Repo + MCP source (repo signals + Notion, Confluence, Google Drive)" },
|
|
63
|
+
{ value: "repo+paste", label: "Repo + paste content (repo signals + paste docs into paste-content.md)" },
|
|
64
|
+
{ value: "local", label: "Local docs folder only (no repo scan)" },
|
|
65
|
+
{ value: "mcp", label: "MCP source only (no repo scan)" },
|
|
66
|
+
{ value: "paste", label: "Paste content only (no repo scan)" },
|
|
63
67
|
{ value: "skip", label: "Skip (start fresh)" }
|
|
64
68
|
]
|
|
65
69
|
});
|
|
66
70
|
cancelIfNeeded(ingestion);
|
|
67
71
|
let ingestionPath;
|
|
68
|
-
if (ingestion === "local") {
|
|
72
|
+
if (ingestion === "local" || ingestion === "repo+local") {
|
|
69
73
|
const rawPath = await text({
|
|
70
74
|
message: "Path to the folder containing your docs?",
|
|
71
75
|
placeholder: "./docs or /Users/you/exports",
|
|
@@ -227,8 +231,11 @@ function rootClaudeTemplate(ctx) {
|
|
|
227
231
|
purpose: Identity, routing map, and coach activation \u2014 read at the start of every session
|
|
228
232
|
read_when: Every Claude Code session in this repo \u2014 this is the root instruction file
|
|
229
233
|
last_updated: ${ctx.date}
|
|
234
|
+
owner:
|
|
230
235
|
---
|
|
231
236
|
|
|
237
|
+
<!-- See AGENTS.md for cross-tool context routing (OpenAI Codex and other agents). -->
|
|
238
|
+
|
|
232
239
|
# CLAUDE.md
|
|
233
240
|
|
|
234
241
|
This repo uses **team-foundry** \u2014 structured files that give you real team context.
|
|
@@ -302,8 +309,11 @@ function rootGeminiTemplate(ctx) {
|
|
|
302
309
|
purpose: Identity, routing map, and coach activation \u2014 read at the start of every session
|
|
303
310
|
read_when: Every Gemini CLI session in this repo \u2014 this is the root instruction file
|
|
304
311
|
last_updated: ${ctx.date}
|
|
312
|
+
owner:
|
|
305
313
|
---
|
|
306
314
|
|
|
315
|
+
<!-- See AGENTS.md for cross-tool context routing (OpenAI Codex and other agents). -->
|
|
316
|
+
|
|
307
317
|
# GEMINI.md
|
|
308
318
|
|
|
309
319
|
This repo uses **team-foundry** \u2014 structured files that give you real team context.
|
|
@@ -377,9 +387,12 @@ function rootCursorTemplate(ctx) {
|
|
|
377
387
|
purpose: Identity, routing map, and coach activation \u2014 read at the start of every session
|
|
378
388
|
read_when: Every Cursor session in this repo \u2014 this is the root instruction file
|
|
379
389
|
last_updated: ${ctx.date}
|
|
390
|
+
owner:
|
|
380
391
|
alwaysApply: true
|
|
381
392
|
---
|
|
382
393
|
|
|
394
|
+
<!-- See AGENTS.md for cross-tool context routing (OpenAI Codex and other agents). -->
|
|
395
|
+
|
|
383
396
|
# team-foundry
|
|
384
397
|
|
|
385
398
|
This repo uses **team-foundry** \u2014 structured files that give you real team context.
|
|
@@ -1596,13 +1609,120 @@ Adjustments are soft \u2014 they change when you surface a behavior, not whether
|
|
|
1596
1609
|
its protocol. They reset after 30 days or when the team addresses the gap.
|
|
1597
1610
|
|
|
1598
1611
|
---
|
|
1612
|
+
${ctx.ingestion?.startsWith("repo") ? `
|
|
1613
|
+
## Repo auto-ingestion
|
|
1614
|
+
|
|
1615
|
+
**When this runs:** At the start of every onboarding interview when ingestion mode is
|
|
1616
|
+
\`repo\`, \`repo+local\`, \`repo+mcp\`, or \`repo+paste\`. Execute Steps 1\u20135 silently
|
|
1617
|
+
before saying anything to the user.
|
|
1618
|
+
|
|
1619
|
+
### Source priority
|
|
1620
|
+
|
|
1621
|
+
Read sources in this order. Highest-priority source wins per field \u2014 lower sources
|
|
1622
|
+
only fill gaps the higher ones leave empty.
|
|
1623
|
+
|
|
1624
|
+
| Priority | Source | What it supplies |
|
|
1625
|
+
|---|---|---|
|
|
1626
|
+
| 1 | Existing \`team-foundry/\` files | Everything \u2014 highest priority, re-run aware |
|
|
1627
|
+
| 2 | \`package.json\` / \`pyproject.toml\` / \`Cargo.toml\` | Product name, stack, language, deps |
|
|
1628
|
+
| 3 | \`README.md\` | Product description, what it does, who it's for |
|
|
1629
|
+
| 4 | Git log (last 30 commits) | Contributors, cadence, recent focus |
|
|
1630
|
+
| 5 | ADRs in \`docs/\`, \`decisions/\`, or \`engineering/decisions/\` | Architecture decisions |
|
|
1631
|
+
| 6 | GitHub PRs and issues via \`gh\` CLI (optional) | Current work, open problems |
|
|
1632
|
+
| 7 | Top-level folder structure | Repo layout, monorepo signals |
|
|
1633
|
+
|
|
1634
|
+
### Step 1 \u2014 Silent read
|
|
1635
|
+
|
|
1636
|
+
Read all available sources silently. Do not stream output during the read.
|
|
1637
|
+
|
|
1638
|
+
**GitHub signals (priority 6):** Run \`gh issue list\` and \`gh pr list\` to read open
|
|
1639
|
+
issues and recent PRs. If \`gh\` is not installed, not authenticated, or does not respond
|
|
1640
|
+
within 10 seconds, fall back to local repo signals only and note
|
|
1641
|
+
"GitHub signals unavailable \u2014 using local repo only" in the summary. Never block on
|
|
1642
|
+
GitHub \u2014 the flow continues regardless.
|
|
1643
|
+
|
|
1644
|
+
**Re-run detection:** Check whether \`team-foundry/\` files already exist and have
|
|
1645
|
+
content beyond gap markers. If yes, treat them as highest-priority source and skip to
|
|
1646
|
+
the re-run flow in Step 2b.
|
|
1647
|
+
|
|
1648
|
+
### Step 2 \u2014 Pre-fill summary
|
|
1649
|
+
|
|
1650
|
+
Present a single structured summary. Do not ask questions yet.
|
|
1651
|
+
|
|
1652
|
+
> Here's what I found in your repo. Tell me what's wrong and I'll fix it before writing
|
|
1653
|
+
> anything.
|
|
1654
|
+
>
|
|
1655
|
+
> **Product:** [name \u2014 from package.json]
|
|
1656
|
+
> **What it does:** [1\u20132 sentences \u2014 from README, first paragraph]
|
|
1657
|
+
> **Stack:** [language/framework \u2014 from package.json + README]
|
|
1658
|
+
> **Team:** [contributor count from git log \u2014 challenge me]${ctx.profile === "full" ? `
|
|
1659
|
+
> **Recent focus:** [inferred from last 30 commit messages \u2014 challenge me]
|
|
1660
|
+
> **Open assumptions I spotted:** [from open issues / PRs if available]
|
|
1661
|
+
> **Decisions already made:** [from ADRs if found]` : `
|
|
1662
|
+
> **Recent focus:** [inferred from last 30 commit messages \u2014 challenge me]`}
|
|
1663
|
+
>
|
|
1664
|
+
> What's missing or wrong? (say "looks good" to proceed, or correct anything above)
|
|
1665
|
+
|
|
1666
|
+
Per-field source attribution rules:
|
|
1667
|
+
- Fields read verbatim from a file: show \`(from manifest file)\`, \`(from README)\` etc. Use the actual filename (e.g. \`package.json\`, \`pyproject.toml\`, \`Cargo.toml\`) when it is unambiguous.
|
|
1668
|
+
- Fields inferred (e.g. team size estimated from contributor count): append \`\u2014 challenge me\`
|
|
1669
|
+
- Fields where no signal was found: omit the field from the summary
|
|
1670
|
+
|
|
1671
|
+
### Step 2b \u2014 Re-run flow (only when existing team-foundry/ files found)
|
|
1672
|
+
|
|
1673
|
+
If existing \`team-foundry/\` files are populated beyond gap markers, say instead:
|
|
1674
|
+
|
|
1675
|
+
> You've already run the onboarding interview. Here's what's currently populated.
|
|
1676
|
+
> What's changed since then?
|
|
1599
1677
|
|
|
1678
|
+
Show current values from the files. Only update fields the user says have changed.
|
|
1679
|
+
Leave confirmed fields untouched.
|
|
1680
|
+
|
|
1681
|
+
### Step 3 \u2014 One-message correction
|
|
1682
|
+
|
|
1683
|
+
Wait for the user's response. Apply any corrections they give. Do not re-ask confirmed
|
|
1684
|
+
fields. If the user says "looks good," proceed immediately to Step 4.
|
|
1685
|
+
|
|
1686
|
+
### Step 4 \u2014 Write files
|
|
1687
|
+
|
|
1688
|
+
Write all team-foundry files using confirmed data. For any field with no signal and
|
|
1689
|
+
no user-provided answer, write a \`<!-- GAP: ... -->\` marker. Follow the
|
|
1690
|
+
conversation-as-update protocol \u2014 show each file draft and wait for confirmation
|
|
1691
|
+
before writing.
|
|
1692
|
+
|
|
1693
|
+
**No silent writes.** Even high-confidence pre-filled answers require explicit
|
|
1694
|
+
confirmation before being written to disk. "Looks good" or "yes" is confirmation.
|
|
1695
|
+
Silence is not.
|
|
1696
|
+
|
|
1697
|
+
### Step 5 \u2014 Gap nudge
|
|
1698
|
+
|
|
1699
|
+
After writing, surface the top 3 gaps:
|
|
1700
|
+
|
|
1701
|
+
> Here's what still needs filling in \u2014 these are the most important:
|
|
1702
|
+
> 1. [Gap 1 \u2014 file and field]
|
|
1703
|
+
> 2. [Gap 2 \u2014 file and field]
|
|
1704
|
+
> 3. [Gap 3 \u2014 file and field]
|
|
1705
|
+
>
|
|
1706
|
+
> Want to fill any of these in now?
|
|
1707
|
+
|
|
1708
|
+
### No-signal fallback
|
|
1709
|
+
|
|
1710
|
+
If the repo has no \`README.md\`, no \`package.json\`, and fewer than 5 git commits,
|
|
1711
|
+
say:
|
|
1712
|
+
|
|
1713
|
+
> I couldn't find enough signals in this repo to pre-fill anything.
|
|
1714
|
+
> I'll ask you directly \u2014 this takes about ${timeEstimate}.
|
|
1715
|
+
|
|
1716
|
+
Then proceed with the standard interview sequence below, skipping Steps 1\u20135.
|
|
1717
|
+
|
|
1718
|
+
---
|
|
1719
|
+
` : ""}
|
|
1600
1720
|
## Onboarding interview
|
|
1601
1721
|
|
|
1602
1722
|
**Triggered by:** The user says "Let's set up our team-foundry," "run the onboarding
|
|
1603
1723
|
interview," or any close variant. Also triggered on first load if GETTING_STARTED.md
|
|
1604
1724
|
still exists and the "Who we are" section in the root file is empty.
|
|
1605
|
-
${ctx.ingestion === "mcp" ? `
|
|
1725
|
+
${ctx.ingestion === "mcp" || ctx.ingestion === "repo+mcp" ? `
|
|
1606
1726
|
**Existing docs \u2014 MCP source:** The user indicated they have docs in a connected MCP
|
|
1607
1727
|
source (Notion, Confluence, or Google Drive). Before asking any questions, query their
|
|
1608
1728
|
connected MCP servers, then follow the shared ingestion reference below.
|
|
@@ -1645,7 +1765,7 @@ Apply medium confidence to all content from undated or old docs.
|
|
|
1645
1765
|
Then apply Steps 2\u20134 from the **Shared ingestion reference** section below.
|
|
1646
1766
|
` : ctx.ingestionPath ? `
|
|
1647
1767
|
**Existing docs \u2014 local folder:** The user indicated they have docs to ingest at
|
|
1648
|
-
\`${ctx.ingestionPath}
|
|
1768
|
+
\`${ctx.ingestionPath}\`.${ctx.ingestion === "repo+local" ? ` Repo signals have already been processed in the Repo auto-ingestion section above. Now also read this local folder as a supplementary source \u2014 use it to fill gaps the repo signals could not supply.` : ` Before asking any questions, read all files in that folder,`}
|
|
1649
1769
|
then follow the shared ingestion reference below.
|
|
1650
1770
|
|
|
1651
1771
|
**Step 1 \u2014 Stale doc check.** Before reading content, check each file for dates.
|
|
@@ -1655,7 +1775,7 @@ If a file has no date fields, or all dates are older than 6 months, flag it:
|
|
|
1655
1775
|
Apply medium confidence to all content from undated or old files.
|
|
1656
1776
|
|
|
1657
1777
|
Then apply Steps 2\u20134 from the **Shared ingestion reference** section below.
|
|
1658
|
-
` : ctx.ingestion === "paste" ? `
|
|
1778
|
+
` : ctx.ingestion === "paste" || ctx.ingestion === "repo+paste" ? `
|
|
1659
1779
|
**Existing docs \u2014 paste content:** The user indicated they have docs to share by
|
|
1660
1780
|
pasting. Before starting the interview, say:
|
|
1661
1781
|
|
|
@@ -1684,7 +1804,7 @@ Wait for the user to confirm before proceeding.
|
|
|
1684
1804
|
|
|
1685
1805
|
Then apply Steps 2\u20134 from the **Shared ingestion reference** section below.
|
|
1686
1806
|
` : ""}
|
|
1687
|
-
${ctx.ingestionPath || ctx.ingestion === "mcp" || ctx.ingestion === "paste" ? `
|
|
1807
|
+
${ctx.ingestionPath || ctx.ingestion === "repo+local" || ctx.ingestion === "mcp" || ctx.ingestion === "paste" || ctx.ingestion === "repo+mcp" || ctx.ingestion === "repo+paste" ? `
|
|
1688
1808
|
### Shared ingestion reference
|
|
1689
1809
|
|
|
1690
1810
|
**Step 2 \u2014 Map content to files.** Route what you find to the right team-foundry file:
|
|
@@ -2104,9 +2224,14 @@ After the last question, do the following:
|
|
|
2104
2224
|
2. **List what's still a gap.** Name each empty or partially-filled file and the specific
|
|
2105
2225
|
missing piece. Don't apologize for the gaps \u2014 state them neutrally.
|
|
2106
2226
|
|
|
2107
|
-
3. **Suggest one next action
|
|
2108
|
-
|
|
2109
|
-
|
|
2227
|
+
3. **Suggest one next action and offer to start it now.** Name the single most valuable
|
|
2228
|
+
gap and immediately offer to fill it:
|
|
2229
|
+
> "Want to start with [most valuable gap]? That's usually the highest-leverage first
|
|
2230
|
+
> fill \u2014 everything else references it."
|
|
2231
|
+
Default priority: customers (shapes all other files) \u2192 outcomes (most time-sensitive)
|
|
2232
|
+
\u2192 north star (grounds the coach) \u2192 stack (engineering context).
|
|
2233
|
+
Wait for the user to say yes or choose a different file. If they say yes, begin the
|
|
2234
|
+
relevant questions immediately without re-running the full interview.
|
|
2110
2235
|
|
|
2111
2236
|
4. **Offer the coach.** End with:
|
|
2112
2237
|
> "Your team-foundry is set up. You can ask me to review it any time by saying
|
|
@@ -3094,7 +3219,77 @@ owner:
|
|
|
3094
3219
|
`;
|
|
3095
3220
|
}
|
|
3096
3221
|
|
|
3222
|
+
// src/templates/root-agents.ts
|
|
3223
|
+
function rootAgentsTemplate(ctx) {
|
|
3224
|
+
const isSolo = ctx.profile === "solo";
|
|
3225
|
+
return `---
|
|
3226
|
+
purpose: Entry point for AI agents \u2014 routes to team-foundry context files
|
|
3227
|
+
read_when: always
|
|
3228
|
+
last_updated: ${ctx.date}
|
|
3229
|
+
owner:
|
|
3230
|
+
---
|
|
3231
|
+
|
|
3232
|
+
# Agents
|
|
3233
|
+
|
|
3234
|
+
This repo uses **team-foundry** \u2014 structured files that give AI tools real team context.
|
|
3235
|
+
Use this file to orient yourself and find the right files before answering.
|
|
3236
|
+
|
|
3237
|
+
## Project overview
|
|
3238
|
+
|
|
3239
|
+
<!-- GAP: The project overview hasn't been filled in yet. Run the onboarding interview to populate it. -->
|
|
3240
|
+
|
|
3241
|
+
## Where to find context
|
|
3242
|
+
|
|
3243
|
+
| Topic | Path |
|
|
3244
|
+
|---|---|
|
|
3245
|
+
| Vision and north star metric | \`team-foundry/product/north-star.md\` |
|
|
3246
|
+
| This quarter's outcomes | \`team-foundry/product/outcomes.md\` |
|
|
3247
|
+
| Who our customers are | \`team-foundry/product/customers.md\` |
|
|
3248
|
+
| Tech stack and conventions | \`team-foundry/engineering/stack.md\` |${isSolo ? "" : `
|
|
3249
|
+
| Current roadmap | \`team-foundry/product/now-next-later.md\` |
|
|
3250
|
+
| Strategic logic | \`team-foundry/product/strategy.md\` |
|
|
3251
|
+
| Open assumptions | \`team-foundry/product/assumptions.md\` |
|
|
3252
|
+
| Key risks | \`team-foundry/product/risks.md\` |
|
|
3253
|
+
| Team norms and DoD | \`team-foundry/team/working-agreement.md\` |
|
|
3254
|
+
| How we use AI tools | \`team-foundry/team/ai-practices.md\` |
|
|
3255
|
+
| Quality stance | \`team-foundry/engineering/quality-bar.md\` |
|
|
3256
|
+
| Architecture decisions | \`team-foundry/engineering/decisions/\` |
|
|
3257
|
+
| Design principles | \`team-foundry/design/principles.md\` |
|
|
3258
|
+
| Metric definitions | \`team-foundry/data/metrics.md\` |
|
|
3259
|
+
| Domain terms | \`team-foundry/context/glossary.md\` |
|
|
3260
|
+
| Stakeholders | \`team-foundry/context/stakeholders.md\` |`}
|
|
3261
|
+
|
|
3262
|
+
## Setup
|
|
3263
|
+
|
|
3264
|
+
See \`team-foundry/engineering/stack.md\` for the tech stack, local setup steps, and environment requirements.
|
|
3265
|
+
|
|
3266
|
+
## Code conventions
|
|
3267
|
+
|
|
3268
|
+
See \`team-foundry/engineering/stack.md\` for language and framework conventions.${isSolo ? "" : `
|
|
3269
|
+
See \`team-foundry/engineering/quality-bar.md\` for the quality bar, bug triage policy, and definition of done.`}
|
|
3270
|
+
|
|
3271
|
+
## Areas of caution
|
|
3272
|
+
|
|
3273
|
+
- **Silent edits** \u2014 the team-foundry coach never writes files without the user's explicit confirmation. Do not write to \`team-foundry/\` files without confirmation.
|
|
3274
|
+
- **Private folder** \u2014 \`team-foundry/private/\` is gitignored. Do not read from or write to it.${isSolo ? "" : `
|
|
3275
|
+
- **ADR commitments** \u2014 architecture decisions in \`team-foundry/engineering/decisions/\` represent committed choices. Treat them as constraints unless the user explicitly opens a discussion.`}
|
|
3276
|
+
|
|
3277
|
+
## Working with the team-foundry coach
|
|
3278
|
+
|
|
3279
|
+
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.
|
|
3280
|
+
|
|
3281
|
+
**Draft-then-confirm rule:** the coach always shows a proposed file change and waits for confirmation before writing. Silence is not confirmation.
|
|
3282
|
+
${isSolo ? "" : `
|
|
3283
|
+
## Glossary
|
|
3284
|
+
|
|
3285
|
+
See \`team-foundry/context/glossary.md\` for domain terms, acronyms, and known naming inconsistencies between teams.
|
|
3286
|
+
`}`;
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3097
3289
|
// src/scaffold.ts
|
|
3290
|
+
var ALWAYS_ROOT_ENTRIES = [
|
|
3291
|
+
{ relativePath: "AGENTS.md", content: rootAgentsTemplate }
|
|
3292
|
+
];
|
|
3098
3293
|
var SOLO_ENTRIES = [
|
|
3099
3294
|
{ relativePath: "GETTING_STARTED.md", content: gettingStartedTemplate },
|
|
3100
3295
|
{ relativePath: ".team-foundry/coach.md", content: coachTemplate },
|
|
@@ -3148,6 +3343,7 @@ async function scaffold(options) {
|
|
|
3148
3343
|
const { targetDir, profile, tool, repoVisibility, date, ingestionPath, ingestion, federated } = options;
|
|
3149
3344
|
const ctx = { profile, tool, repoVisibility, date, ingestionPath, ingestion };
|
|
3150
3345
|
const entries = [
|
|
3346
|
+
...ALWAYS_ROOT_ENTRIES,
|
|
3151
3347
|
...rootEntries(tool),
|
|
3152
3348
|
...SOLO_ENTRIES,
|
|
3153
3349
|
...profile === "full" ? FULL_ONLY_ENTRIES : [],
|
|
@@ -3190,6 +3386,204 @@ async function writeGitignore(targetDir) {
|
|
|
3190
3386
|
import fs3 from "fs/promises";
|
|
3191
3387
|
import path3 from "path";
|
|
3192
3388
|
import { spawnSync } from "child_process";
|
|
3389
|
+
|
|
3390
|
+
// src/link-checker.ts
|
|
3391
|
+
import { readFile } from "fs/promises";
|
|
3392
|
+
var SEVERITY = {
|
|
3393
|
+
missing: 3,
|
|
3394
|
+
"outcome-metric": 3,
|
|
3395
|
+
"now-assumption": 3,
|
|
3396
|
+
"assumption-outcome": 3,
|
|
3397
|
+
stale: 2,
|
|
3398
|
+
empty: 2
|
|
3399
|
+
};
|
|
3400
|
+
function recencyFactor(prs) {
|
|
3401
|
+
if (prs > 3) return 2;
|
|
3402
|
+
if (prs > 0) return 1;
|
|
3403
|
+
return 0;
|
|
3404
|
+
}
|
|
3405
|
+
function escapeRe(s) {
|
|
3406
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3407
|
+
}
|
|
3408
|
+
function actionForHealth(health, file) {
|
|
3409
|
+
if (health === "missing") return `Run \`npx create-team-foundry\` to scaffold ${file}`;
|
|
3410
|
+
if (health === "empty") return `Fill in ${file} with real content (remove stub placeholder)`;
|
|
3411
|
+
return `Update last_updated in ${file} and review content for accuracy`;
|
|
3412
|
+
}
|
|
3413
|
+
function actionForLink(type, item, file) {
|
|
3414
|
+
if (type === "outcome-metric") return `Define "${item}" in data/metrics.md with formula, source, window, owner`;
|
|
3415
|
+
if (type === "now-assumption") return `Add assumption reference to "${item}" in ${file} or link it in assumptions.md`;
|
|
3416
|
+
return `Add cross-reference between "${item}" and a related outcome or assumption`;
|
|
3417
|
+
}
|
|
3418
|
+
function rankFindings(healthFindings, linkFindings) {
|
|
3419
|
+
const candidates = [];
|
|
3420
|
+
for (const h of healthFindings) {
|
|
3421
|
+
const severity = SEVERITY[h.health] ?? 1;
|
|
3422
|
+
const score = severity * 3 + recencyFactor(h.prs);
|
|
3423
|
+
candidates.push({
|
|
3424
|
+
item: h.file,
|
|
3425
|
+
file: h.file,
|
|
3426
|
+
detail: `File is ${h.health}: ${h.file.replace("team-foundry/", "")}`,
|
|
3427
|
+
score,
|
|
3428
|
+
action: actionForHealth(h.health, h.file)
|
|
3429
|
+
});
|
|
3430
|
+
}
|
|
3431
|
+
for (const l of linkFindings) {
|
|
3432
|
+
const severity = SEVERITY[l.type] ?? 1;
|
|
3433
|
+
const score = severity * 3;
|
|
3434
|
+
candidates.push({
|
|
3435
|
+
item: l.item,
|
|
3436
|
+
file: l.file,
|
|
3437
|
+
detail: l.detail,
|
|
3438
|
+
score,
|
|
3439
|
+
action: actionForLink(l.type, l.item, l.file)
|
|
3440
|
+
});
|
|
3441
|
+
}
|
|
3442
|
+
candidates.sort((a, b) => {
|
|
3443
|
+
if (b.score !== a.score) return b.score - a.score;
|
|
3444
|
+
return a.file.localeCompare(b.file);
|
|
3445
|
+
});
|
|
3446
|
+
return candidates.slice(0, 3);
|
|
3447
|
+
}
|
|
3448
|
+
function extractHeadings(content, section) {
|
|
3449
|
+
if (!content.trim()) return [];
|
|
3450
|
+
let source = content;
|
|
3451
|
+
if (section) {
|
|
3452
|
+
const sectionRe = new RegExp(
|
|
3453
|
+
`(?:^|\\n)###\\s+${escapeRe(section)}\\s*\\n([\\s\\S]*?)(?=\\n###|$)`
|
|
3454
|
+
);
|
|
3455
|
+
const match = source.match(sectionRe);
|
|
3456
|
+
source = match ? match[1] : "";
|
|
3457
|
+
}
|
|
3458
|
+
const headings = [];
|
|
3459
|
+
for (const line of source.split("\n")) {
|
|
3460
|
+
const m = line.match(/^##\s+(.+)$/);
|
|
3461
|
+
if (m) headings.push(m[1].trim());
|
|
3462
|
+
}
|
|
3463
|
+
return headings;
|
|
3464
|
+
}
|
|
3465
|
+
function extractSectionBodies(content) {
|
|
3466
|
+
const result = {};
|
|
3467
|
+
const lines = content.split("\n");
|
|
3468
|
+
let currentHeading = null;
|
|
3469
|
+
const bodyLines = [];
|
|
3470
|
+
for (const line of lines) {
|
|
3471
|
+
const m = line.match(/^##\s+(.+)$/);
|
|
3472
|
+
if (m) {
|
|
3473
|
+
if (currentHeading !== null) result[currentHeading] = bodyLines.join("\n").trim();
|
|
3474
|
+
currentHeading = m[1].trim();
|
|
3475
|
+
bodyLines.length = 0;
|
|
3476
|
+
} else if (currentHeading !== null) {
|
|
3477
|
+
bodyLines.push(line);
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
if (currentHeading !== null) result[currentHeading] = bodyLines.join("\n").trim();
|
|
3481
|
+
return result;
|
|
3482
|
+
}
|
|
3483
|
+
function checkOutcomeMetricLinks(outcomesContent, northStarContent, metricsContent) {
|
|
3484
|
+
const defined = new Set(extractHeadings(metricsContent));
|
|
3485
|
+
if (defined.size === 0) return [];
|
|
3486
|
+
const findings = [];
|
|
3487
|
+
const fromOutcomes = extractHeadings(outcomesContent, "Key Metrics");
|
|
3488
|
+
const fromNorthStar = extractHeadings(northStarContent, "Key Metrics");
|
|
3489
|
+
for (const heading of fromOutcomes) {
|
|
3490
|
+
if (!defined.has(heading)) {
|
|
3491
|
+
findings.push({
|
|
3492
|
+
type: "outcome-metric",
|
|
3493
|
+
file: "team-foundry/product/outcomes.md",
|
|
3494
|
+
item: heading,
|
|
3495
|
+
detail: `"${heading}" in outcomes.md#Key Metrics is not defined in data/metrics.md`
|
|
3496
|
+
});
|
|
3497
|
+
}
|
|
3498
|
+
}
|
|
3499
|
+
for (const heading of fromNorthStar) {
|
|
3500
|
+
if (!defined.has(heading)) {
|
|
3501
|
+
findings.push({
|
|
3502
|
+
type: "outcome-metric",
|
|
3503
|
+
file: "team-foundry/product/north-star.md",
|
|
3504
|
+
item: heading,
|
|
3505
|
+
detail: `"${heading}" in north-star.md#Key Metrics is not defined in data/metrics.md`
|
|
3506
|
+
});
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
return findings;
|
|
3510
|
+
}
|
|
3511
|
+
function checkNowAssumptionLinks(nowNextLaterContent, assumptionsContent) {
|
|
3512
|
+
if (!nowNextLaterContent.trim()) return [];
|
|
3513
|
+
const assumptionHeadings = extractHeadings(assumptionsContent);
|
|
3514
|
+
if (assumptionHeadings.length === 0) return [];
|
|
3515
|
+
const nowItems = extractHeadings(nowNextLaterContent, "Now");
|
|
3516
|
+
const bodies = extractSectionBodies(nowNextLaterContent);
|
|
3517
|
+
const findings = [];
|
|
3518
|
+
for (const item of nowItems) {
|
|
3519
|
+
const body = bodies[item] ?? "";
|
|
3520
|
+
const referencesAssumption = assumptionHeadings.some((a) => body.includes(a));
|
|
3521
|
+
if (!referencesAssumption) {
|
|
3522
|
+
findings.push({
|
|
3523
|
+
type: "now-assumption",
|
|
3524
|
+
file: "team-foundry/product/now-next-later.md",
|
|
3525
|
+
item,
|
|
3526
|
+
detail: `Now item "${item}" has no linked assumption in product/assumptions.md`
|
|
3527
|
+
});
|
|
3528
|
+
}
|
|
3529
|
+
}
|
|
3530
|
+
return findings;
|
|
3531
|
+
}
|
|
3532
|
+
function checkAssumptionOutcomeReciprocity(assumptionsContent, outcomesContent) {
|
|
3533
|
+
if (!assumptionsContent.trim() || !outcomesContent.trim()) return [];
|
|
3534
|
+
const outcomeHeadings = extractHeadings(outcomesContent);
|
|
3535
|
+
const assumptionHeadings = extractHeadings(assumptionsContent);
|
|
3536
|
+
const assumptionBodies = extractSectionBodies(assumptionsContent);
|
|
3537
|
+
const outcomeBodies = extractSectionBodies(outcomesContent);
|
|
3538
|
+
const findings = [];
|
|
3539
|
+
for (const assumption of assumptionHeadings) {
|
|
3540
|
+
const body = assumptionBodies[assumption] ?? "";
|
|
3541
|
+
if (!outcomeHeadings.some((o) => body.includes(o))) {
|
|
3542
|
+
findings.push({
|
|
3543
|
+
type: "assumption-outcome",
|
|
3544
|
+
file: "team-foundry/product/assumptions.md",
|
|
3545
|
+
item: assumption,
|
|
3546
|
+
detail: `Assumption "${assumption}" does not reference any outcome`
|
|
3547
|
+
});
|
|
3548
|
+
}
|
|
3549
|
+
}
|
|
3550
|
+
for (const outcome of outcomeHeadings) {
|
|
3551
|
+
const body = outcomeBodies[outcome] ?? "";
|
|
3552
|
+
if (!assumptionHeadings.some((a) => body.includes(a))) {
|
|
3553
|
+
findings.push({
|
|
3554
|
+
type: "assumption-outcome",
|
|
3555
|
+
file: "team-foundry/product/outcomes.md",
|
|
3556
|
+
item: outcome,
|
|
3557
|
+
detail: `Outcome "${outcome}" does not reference any assumption`
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3561
|
+
return findings;
|
|
3562
|
+
}
|
|
3563
|
+
async function readOptional(filePath) {
|
|
3564
|
+
try {
|
|
3565
|
+
return await readFile(filePath, "utf-8");
|
|
3566
|
+
} catch {
|
|
3567
|
+
return "";
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
async function runLinkChecks(targetDir) {
|
|
3571
|
+
const p = (rel) => `${targetDir}/${rel}`;
|
|
3572
|
+
const [outcomes, northStar, metrics, nowNext, assumptions] = await Promise.all([
|
|
3573
|
+
readOptional(p("team-foundry/product/outcomes.md")),
|
|
3574
|
+
readOptional(p("team-foundry/product/north-star.md")),
|
|
3575
|
+
readOptional(p("team-foundry/data/metrics.md")),
|
|
3576
|
+
readOptional(p("team-foundry/product/now-next-later.md")),
|
|
3577
|
+
readOptional(p("team-foundry/product/assumptions.md"))
|
|
3578
|
+
]);
|
|
3579
|
+
return [
|
|
3580
|
+
...checkOutcomeMetricLinks(outcomes, northStar, metrics),
|
|
3581
|
+
...checkNowAssumptionLinks(nowNext, assumptions),
|
|
3582
|
+
...checkAssumptionOutcomeReciprocity(assumptions, outcomes)
|
|
3583
|
+
];
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
// src/status.ts
|
|
3193
3587
|
var SOLO_FILES = [
|
|
3194
3588
|
"team-foundry/product/north-star.md",
|
|
3195
3589
|
"team-foundry/product/outcomes.md",
|
|
@@ -3331,6 +3725,51 @@ async function runStatus(targetDir) {
|
|
|
3331
3725
|
console.log();
|
|
3332
3726
|
console.log(` \u2713 ${ok.length} current ~ ${stale.length} stale \u25CB ${empty.length} empty \u2717 ${missing.length} missing
|
|
3333
3727
|
`);
|
|
3728
|
+
let linkFindings = [];
|
|
3729
|
+
if (isFullProfile) {
|
|
3730
|
+
linkFindings = await runLinkChecks(targetDir);
|
|
3731
|
+
if (linkFindings.length > 0) {
|
|
3732
|
+
console.log(` Link Integrity`);
|
|
3733
|
+
console.log(` ${"\u2500".repeat(60)}`);
|
|
3734
|
+
const byType = {};
|
|
3735
|
+
for (const f of linkFindings) {
|
|
3736
|
+
(byType[f.type] ??= []).push(f);
|
|
3737
|
+
}
|
|
3738
|
+
const typeLabels = {
|
|
3739
|
+
"outcome-metric": "Outcome references undefined metric",
|
|
3740
|
+
"now-assumption": "Now item missing linked assumption",
|
|
3741
|
+
"assumption-outcome": "Assumption/outcome unlinked"
|
|
3742
|
+
};
|
|
3743
|
+
for (const [type, items] of Object.entries(byType)) {
|
|
3744
|
+
console.log(`
|
|
3745
|
+
! ${typeLabels[type] ?? type}`);
|
|
3746
|
+
for (const item of items) {
|
|
3747
|
+
console.log(` ${item.file.replace("team-foundry/", "")} \u2192 ${item.detail}`);
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
console.log();
|
|
3751
|
+
}
|
|
3752
|
+
}
|
|
3753
|
+
const healthForRanking = results.filter((r) => r.health !== "ok").map((r) => ({
|
|
3754
|
+
file: r.relativePath,
|
|
3755
|
+
health: r.health,
|
|
3756
|
+
prs: r.prsSinceUpdate ?? 0
|
|
3757
|
+
}));
|
|
3758
|
+
const top3 = rankFindings(healthForRanking, linkFindings);
|
|
3759
|
+
console.log(` Top 3 Fix Suggestions`);
|
|
3760
|
+
console.log(` ${"\u2500".repeat(60)}`);
|
|
3761
|
+
if (top3.length === 0) {
|
|
3762
|
+
console.log(" No critical drift detected this week.\n");
|
|
3763
|
+
} else {
|
|
3764
|
+
for (let i = 0; i < top3.length; i++) {
|
|
3765
|
+
const s = top3[i];
|
|
3766
|
+
console.log(`
|
|
3767
|
+
${i + 1}) ${s.detail}`);
|
|
3768
|
+
console.log(` In: ${s.file.replace("team-foundry/", "")}`);
|
|
3769
|
+
console.log(` Action: ${s.action}`);
|
|
3770
|
+
}
|
|
3771
|
+
console.log();
|
|
3772
|
+
}
|
|
3334
3773
|
if (stale.length > 0) {
|
|
3335
3774
|
console.log(" Stale files \u2014 why this nudge:\n");
|
|
3336
3775
|
for (const s of stale) {
|
|
@@ -3437,7 +3876,7 @@ async function main() {
|
|
|
3437
3876
|
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
3438
3877
|
await scaffold({ ...answers, targetDir, date });
|
|
3439
3878
|
await writeGitignore(targetDir);
|
|
3440
|
-
if (answers.ingestion === "paste") {
|
|
3879
|
+
if (answers.ingestion === "paste" || answers.ingestion === "repo+paste") {
|
|
3441
3880
|
const pastePath = path4.join(targetDir, ".team-foundry", "paste-content.md");
|
|
3442
3881
|
try {
|
|
3443
3882
|
await fs4.access(pastePath);
|
|
@@ -3447,7 +3886,62 @@ async function main() {
|
|
|
3447
3886
|
}
|
|
3448
3887
|
const tool = TOOL_LABEL[answers.tool];
|
|
3449
3888
|
let ingestionNote;
|
|
3450
|
-
if (answers.ingestion === "
|
|
3889
|
+
if (answers.ingestion === "repo") {
|
|
3890
|
+
ingestionNote = `
|
|
3891
|
+
Next steps:
|
|
3892
|
+
|
|
3893
|
+
1. cd ${targetDir}
|
|
3894
|
+
|
|
3895
|
+
2. Open ${tool} and say:
|
|
3896
|
+
|
|
3897
|
+
"Let's set up our team-foundry."
|
|
3898
|
+
|
|
3899
|
+
The AI will read your repo (README, package.json, git history, GitHub
|
|
3900
|
+
PRs/issues) and pre-fill answers before asking questions.
|
|
3901
|
+
`;
|
|
3902
|
+
} else if (answers.ingestion === "repo+local") {
|
|
3903
|
+
ingestionNote = `
|
|
3904
|
+
Next steps:
|
|
3905
|
+
|
|
3906
|
+
1. cd ${targetDir}
|
|
3907
|
+
|
|
3908
|
+
2. Open ${tool} and say:
|
|
3909
|
+
|
|
3910
|
+
"Let's set up our team-foundry."
|
|
3911
|
+
|
|
3912
|
+
The AI will read your repo first, then supplement with the docs in
|
|
3913
|
+
${answers.ingestionPath ?? "[your docs folder]"}.
|
|
3914
|
+
`;
|
|
3915
|
+
} else if (answers.ingestion === "repo+mcp") {
|
|
3916
|
+
ingestionNote = `
|
|
3917
|
+
Next steps:
|
|
3918
|
+
|
|
3919
|
+
1. In ${tool} settings, connect your MCP server
|
|
3920
|
+
(Notion, Confluence, or Google Drive) if you haven't already.
|
|
3921
|
+
|
|
3922
|
+
2. cd ${targetDir}
|
|
3923
|
+
|
|
3924
|
+
3. Open ${tool} and say:
|
|
3925
|
+
|
|
3926
|
+
"Let's set up our team-foundry."
|
|
3927
|
+
|
|
3928
|
+
The AI will read your repo first, then supplement from your MCP source.
|
|
3929
|
+
`;
|
|
3930
|
+
} else if (answers.ingestion === "repo+paste") {
|
|
3931
|
+
ingestionNote = `
|
|
3932
|
+
Next steps:
|
|
3933
|
+
|
|
3934
|
+
1. Open .team-foundry/paste-content.md and paste in your existing docs
|
|
3935
|
+
(strategy, roadmaps, customer research). Save the file.
|
|
3936
|
+
|
|
3937
|
+
2. cd ${targetDir}
|
|
3938
|
+
|
|
3939
|
+
3. Open ${tool} and say:
|
|
3940
|
+
|
|
3941
|
+
"Let's set up our team-foundry. I've added docs to
|
|
3942
|
+
paste-content.md \u2014 use them to supplement the repo scan."
|
|
3943
|
+
`;
|
|
3944
|
+
} else if (answers.ingestion === "paste") {
|
|
3451
3945
|
ingestionNote = `
|
|
3452
3946
|
Next steps:
|
|
3453
3947
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-team-foundry",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Scaffold a team-foundry into any repo — structured context files for Claude Code
|
|
3
|
+
"version": "2.1.1",
|
|
4
|
+
"description": "Scaffold a team-foundry into any repo — structured context files for Claude Code, Gemini CLI, and Cursor",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|