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