@cofoundr/init 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +140 -0
  2. package/bin/cofoundr.mjs +10 -0
  3. package/content/.claude-plugin/plugin.json +18 -0
  4. package/content/README.md +227 -0
  5. package/content/agents/brand-intake.md +129 -0
  6. package/content/agents/consolidate.md +154 -0
  7. package/content/agents/launch-kit-detect.md +109 -0
  8. package/content/agents/spec-phase.md +131 -0
  9. package/content/commands/audit.md +137 -0
  10. package/content/commands/constitution.md +107 -0
  11. package/content/commands/document.md +155 -0
  12. package/content/commands/implement.md +108 -0
  13. package/content/commands/new-feature.md +188 -0
  14. package/content/commands/new-project.md +243 -0
  15. package/content/commands/next.md +129 -0
  16. package/content/commands/onboard.md +176 -0
  17. package/content/commands/plan.md +138 -0
  18. package/content/commands/quick-brief.md +95 -0
  19. package/content/commands/resume.md +99 -0
  20. package/content/commands/review.md +76 -0
  21. package/content/commands/rules-check.md +54 -0
  22. package/content/commands/scope-guard.md +33 -0
  23. package/content/commands/setup-skills.md +109 -0
  24. package/content/commands/specify.md +53 -0
  25. package/content/commands/tasks.md +91 -0
  26. package/content/commands/translate.md +197 -0
  27. package/content/manifest.json +59 -0
  28. package/content/scaffold/.cofoundr/README.md +67 -0
  29. package/content/scaffold/.cofoundr/constitution.md.tmpl +54 -0
  30. package/content/scaffold/.cofoundr/manifest.json.tmpl +15 -0
  31. package/content/scaffold/.cofoundr/memory/decisions.md.tmpl +19 -0
  32. package/content/scaffold/.cofoundr/memory/knowledge.md.tmpl +23 -0
  33. package/content/scaffold/.cofoundr/memory/project.md.tmpl +27 -0
  34. package/content/scaffold/.cofoundr/specs/README.md +38 -0
  35. package/content/scaffold/AGENTS.md.tmpl +74 -0
  36. package/content/templates/agents.md +144 -0
  37. package/content/templates/brand.md +180 -0
  38. package/content/templates/feature.md +70 -0
  39. package/content/templates/phases/phase-template/README.md +65 -0
  40. package/content/templates/phases/phase-template/decisions.md +52 -0
  41. package/content/templates/phases/phase-template/research.md +59 -0
  42. package/content/templates/phases/phase-template/spec.md +90 -0
  43. package/content/templates/phases/phase-template/tests.md +65 -0
  44. package/content/templates/phases/phase-template/ui-spec.md +75 -0
  45. package/content/templates/phases.md +234 -0
  46. package/content/templates/prd.md +89 -0
  47. package/content/templates/product.md +73 -0
  48. package/content/templates/rules.md +99 -0
  49. package/content/templates/tech.md +129 -0
  50. package/package.json +39 -0
  51. package/src/adapters/aider.mjs +35 -0
  52. package/src/adapters/claude-code.mjs +114 -0
  53. package/src/adapters/cline.mjs +46 -0
  54. package/src/adapters/codex.mjs +29 -0
  55. package/src/adapters/copilot.mjs +54 -0
  56. package/src/adapters/cursor.mjs +69 -0
  57. package/src/adapters/gemini.mjs +41 -0
  58. package/src/adapters/index.mjs +14 -0
  59. package/src/adapters/windsurf.mjs +69 -0
  60. package/src/cli.mjs +124 -0
  61. package/src/commands/doctor.mjs +90 -0
  62. package/src/commands/init.mjs +190 -0
  63. package/src/commands/list.mjs +28 -0
  64. package/src/commands/onboard.mjs +130 -0
  65. package/src/commands/remove.mjs +89 -0
  66. package/src/commands/sync.mjs +81 -0
  67. package/src/core/detect.mjs +121 -0
  68. package/src/core/fs.mjs +42 -0
  69. package/src/core/license.mjs +170 -0
  70. package/src/core/log.mjs +32 -0
  71. package/src/core/prompts.mjs +62 -0
  72. package/src/core/scaffold.mjs +179 -0
  73. package/src/core/source.mjs +54 -0
  74. package/src/core/version.mjs +10 -0
@@ -0,0 +1,99 @@
1
+ ---
2
+ version: "1.0.0"
3
+ last_amended: YYYY-MM-DD
4
+ ---
5
+
6
+ # AI Rules
7
+
8
+ <!-- HOW-TO: This file is the single most important file for preventing the AI from breaking your app. Every rule here exists because a real founder lost real time to the mistake it prevents. Paste this into your AI assistant's context on every session. The three sections — Always, Ask First, Never — form a behavioral contract. "Always" is autopilot. "Ask First" is a speed bump. "Never" is a hard wall. -->
9
+
10
+ ## TL;DR
11
+
12
+ <!-- HOW-TO: ≤60 words. State the purpose of this file plainly. -->
13
+
14
+ *These rules govern every action the AI assistant takes in this project. "Always" rules are non-negotiable. "Ask First" rules require explicit human approval before proceeding. "Never" rules are hard stops — violating one can break the app, leak secrets, or destroy data.*
15
+
16
+ ## ✅ Always
17
+
18
+ <!-- HOW-TO: These 10 rules are non-negotiable. The AI must follow them on every prompt, every session, no exceptions. They're ordered by how early in a session they matter — setup rules first, then code-writing rules. Add your own project-specific rules at the end and number them 11, 12, etc. -->
19
+
20
+ 1. Initialize git and commit before the first AI prompt.
21
+ 2. Commit working state before any multi-file prompt.
22
+ 3. Read `tech.md` and `package.json` before writing import statements.
23
+ 4. Use server routes or Edge Functions for any call needing a secret key.
24
+ 5. Add `.env*` and `*.local` to `.gitignore` before the first commit.
25
+ 6. Add an RLS policy to every Supabase table on creation.
26
+ 7. Validate file uploads by magic bytes, not `Content-Type` header.
27
+ 8. Add CSRF middleware to every state-changing route.
28
+ 9. Pin major versions of key frameworks in this file: *Next.js 15, TypeScript 5, Supabase latest, Inngest 3*.
29
+ 10. Confirm rate-limit tier of any third-party API before integrating.
30
+
31
+ 11. After completing a task, update the feature doc in `docs/features/` — check off the task and note any decisions made.
32
+ 12. At the end of every session, add a Session Notes entry to the active feature doc — what was done, what's next, any blockers.
33
+ 13. When a feature ships, set its status to `Complete` in its feature doc and remove its `@` import from `docs/ACTIVE.md`.
34
+
35
+ <!-- HOW-TO: Add further project-specific "Always" rules below this line. Number them starting at 14. Example: "14. Always use the date-fns library for date formatting — no raw Date methods." -->
36
+
37
+ ## ⚠️ Ask First
38
+
39
+ <!-- HOW-TO: These 10 items require the AI to stop and ask you before proceeding. "Ask First" is a speed bump, not a wall — the answer might be "yes, go ahead," but the AI must ask. This prevents silent drift where the AI makes 5 reasonable-sounding decisions that compound into a mess. The AI should phrase the question clearly, explain what it wants to do and why, and wait for your answer before acting. -->
40
+
41
+ 1. Before adding any dependency not listed in `tech.md`.
42
+ 2. Before any database migration or schema change.
43
+ 3. Before modifying a function not named in the current task.
44
+ 4. Before deleting any file.
45
+ 5. Before upgrading a major package version.
46
+ 6. Before writing any new auth flow.
47
+ 7. Before introducing `exec`, `eval`, or dynamic SQL.
48
+ 8. Before changing RLS policies on tables containing PII or payment data.
49
+ 9. Before touching files with `prod`, `production`, or `live` in the path.
50
+ 10. Before adding a new SDK that requires a secret key.
51
+
52
+ <!-- HOW-TO: Add project-specific "Ask First" items below this line. Number them starting at 11. These should be things that are sometimes fine but need human judgment — for example: "11. Before adding a new API endpoint not listed in tech.md." -->
53
+
54
+ <never>
55
+ ## 🚫 Never
56
+
57
+ <!-- HOW-TO: These 10 rules are hard stops. Each one includes a reason after the em-dash because the AI needs to understand WHY, not just WHAT. Rules without reasons get ignored when the AI "reasons" its way around them. The reason is load-bearing — without it, the AI will rationalize exceptions. -->
58
+
59
+ 1. 🚫 Never reference `sk_live_*`, `SUPABASE_SERVICE_ROLE_KEY`, or any secret outside `/api`, `/server`, or an Edge Function — the type system can't catch a leaked key; your Stripe bill can.
60
+ 2. 🚫 Never run `DROP`, `TRUNCATE`, `terraform destroy`, `--force`, or `rm -rf` without explicit human confirmation — one command deletes what took weeks to build.
61
+ 3. 🚫 Never store secrets in client-side code, localStorage, or cookies — browser storage is readable by any script on the page.
62
+ 4. 🚫 Never disable RLS policies, even temporarily — "I'll re-enable it later" is how every data leak starts.
63
+ 5. 🚫 Never commit `.env` files, API keys, or credentials to git — once pushed, secrets live in git history forever even after deletion.
64
+ 6. 🚫 Never bypass auth checks with hardcoded user IDs or admin tokens — every bypassed check becomes a production backdoor.
65
+ 7. 🚫 Never use `any` as a type in TypeScript — it defeats the purpose of type safety and hides bugs the compiler would otherwise catch.
66
+ 8. 🚫 Never auto-approve or auto-merge pull requests without human review — AI-generated code needs human eyes on every merge.
67
+ 9. 🚫 Never modify database schemas in production without a migration file — untracked schema changes make rollbacks impossible.
68
+ 10. 🚫 Never install packages from unverified sources or with known vulnerabilities — check npm audit output before adding any dependency.
69
+
70
+ <!-- HOW-TO: Add project-specific "Never" rules below this line. Number them starting at 11. Every rule MUST include a reason after the em-dash. A rule without a reason will be ignored by the AI when it conflicts with what the AI thinks is a good idea. -->
71
+ </never>
72
+
73
+ ---
74
+
75
+ ## How to Maintain This File
76
+
77
+ <!-- HOW-TO: This section is for the founder, not the AI. It explains how to keep rules.md useful over time. -->
78
+
79
+ **Adding rules:** When your AI assistant makes a mistake you don't want repeated, add a rule. Put it in the right section:
80
+ - Did the AI do something that should always happen? Add to **Always**.
81
+ - Did the AI make a judgment call it shouldn't have? Add to **Ask First**.
82
+ - Did the AI do something that should never happen again? Add to **Never** with a reason.
83
+
84
+ **Rule format for Never:** Every Never rule needs a reason after the em-dash. Without the reason, the AI will eventually "reason" its way around the rule. The reason should describe the real-world consequence, not just restate the rule.
85
+
86
+ **Good:** *"Never disable TypeScript strict mode — non-strict mode hides null reference bugs that crash in production."*
87
+ **Bad:** *"Never disable TypeScript strict mode — because it should stay enabled."*
88
+
89
+ **Updating rules:** If a rule no longer applies (e.g., you changed frameworks), delete it rather than commenting it out. Commented-out rules confuse AI assistants that read the full file.
90
+
91
+ **Pinned versions:** Update line 9 in the Always section whenever you upgrade a major framework version. This line is the AI's source of truth for what versions to use.
92
+
93
+ ## Amendment Log
94
+
95
+ <!-- HOW-TO: Every time you add, remove, or change a rule, add a line here. This creates an audit trail so you (and the AI) know what changed and when. Bump the patch version (1.0.0 → 1.0.1) for new rules, minor version (1.0.0 → 1.1.0) for restructuring sections, major version (1.0.0 → 2.0.0) for framework changes. Update the `version` and `last_amended` fields in the frontmatter above to match. -->
96
+
97
+ | Version | Date | Change |
98
+ |---------|------|--------|
99
+ | 1.0.0 | *YYYY-MM-DD* | *Initial constitution generated from planning conversation* |
@@ -0,0 +1,129 @@
1
+ # Technical Specification
2
+
3
+ <!-- HOW-TO: This file is your AI assistant's ground truth for every technical decision. If it's not here, the AI will guess — and guesses cause rework. Pin versions, name services, and specify schemas up front. -->
4
+
5
+ ## TL;DR
6
+
7
+ <!-- HOW-TO: ≤60 words. Name the stack, the hosting, and the one architectural decision that matters most. -->
8
+
9
+ *Next.js 15 App Router on Vercel, Supabase Postgres for data + auth, Inngest for scheduled morning pulls. The key architectural decision: all third-party API calls go through server routes — no client-side secrets, ever.*
10
+
11
+ ## Stack
12
+
13
+ <!-- HOW-TO: Pin exact major versions. "Next.js 15 App Router" not "Next.js". "Postgres 16 + Supabase" not "a database". Your AI assistant will install whatever you write here — be specific or you'll get version conflicts on day one. -->
14
+
15
+ | Layer | Technology | Version | Why this choice |
16
+ |-------|-----------|---------|-----------------|
17
+ | Framework | *Next.js App Router* | *15.x* | *Server components reduce client bundle; App Router is the stable default* |
18
+ | Language | *TypeScript* | *5.x (strict mode)* | *Catches integration bugs at compile time; AI assistants produce better TS than JS* |
19
+ | Database | *Postgres + Supabase* | *Postgres 16, Supabase latest* | *Managed auth + database in one service; generous free tier for MVP* |
20
+ | Auth | *Supabase Auth* | *latest* | *Built-in OAuth flows for Slack; no separate auth service needed* |
21
+ | Hosting | *Vercel* | *latest* | *Zero-config Next.js deploys; preview URLs for every PR* |
22
+ | Payments | *Stripe* | *API v2024-12* | *Industry standard; strong docs; test mode for development* |
23
+ | File Storage | *Supabase Storage* | *latest* | *Same project as database; RLS policies apply to files too* |
24
+ | Background Jobs | *Inngest* | *3.x* | *Scheduled functions for morning pulls; retries and logging built in* |
25
+ | Styling | *Tailwind CSS* | *4.x* | *Utility-first; consistent with AI-generated component patterns* |
26
+
27
+ ## Schema
28
+
29
+ <!-- HOW-TO: Write plain SQL CREATE TABLE statements. Include ownership columns (created_by, updated_at) on every table. Include RLS policy stubs — every table touching user data needs a policy. Your AI assistant will copy-paste these into migrations, so make them runnable. -->
30
+
31
+ ```sql
32
+ -- Users (managed by Supabase Auth, extended here)
33
+ CREATE TABLE public.profiles (
34
+ id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
35
+ display_name TEXT NOT NULL,
36
+ timezone TEXT NOT NULL DEFAULT 'UTC',
37
+ pull_time TIME NOT NULL DEFAULT '07:00',
38
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
39
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
40
+ );
41
+
42
+ ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
43
+ CREATE POLICY "Users read own profile"
44
+ ON public.profiles FOR SELECT
45
+ USING (auth.uid() = id);
46
+ CREATE POLICY "Users update own profile"
47
+ ON public.profiles FOR UPDATE
48
+ USING (auth.uid() = id);
49
+
50
+ -- Connected integrations
51
+ CREATE TABLE public.integrations (
52
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
53
+ user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
54
+ provider TEXT NOT NULL CHECK (provider IN ('slack', 'trello', 'linear')),
55
+ access_token TEXT NOT NULL,
56
+ refresh_token TEXT,
57
+ workspace_name TEXT,
58
+ connected_at TIMESTAMPTZ NOT NULL DEFAULT now(),
59
+ created_by UUID NOT NULL REFERENCES auth.users(id),
60
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
61
+ UNIQUE(user_id, provider)
62
+ );
63
+
64
+ ALTER TABLE public.integrations ENABLE ROW LEVEL SECURITY;
65
+ CREATE POLICY "Users manage own integrations"
66
+ ON public.integrations FOR ALL
67
+ USING (auth.uid() = user_id);
68
+
69
+ -- Daily task items
70
+ CREATE TABLE public.tasks (
71
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
72
+ user_id UUID NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
73
+ source TEXT NOT NULL CHECK (source IN ('slack', 'trello', 'linear')),
74
+ external_id TEXT NOT NULL,
75
+ title TEXT NOT NULL,
76
+ body TEXT,
77
+ source_url TEXT,
78
+ priority INTEGER NOT NULL DEFAULT 0,
79
+ is_focus BOOLEAN NOT NULL DEFAULT false,
80
+ is_completed BOOLEAN NOT NULL DEFAULT false,
81
+ snoozed_until DATE,
82
+ pulled_at DATE NOT NULL DEFAULT CURRENT_DATE,
83
+ created_by UUID NOT NULL REFERENCES auth.users(id),
84
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
85
+ UNIQUE(user_id, source, external_id, pulled_at)
86
+ );
87
+
88
+ ALTER TABLE public.tasks ENABLE ROW LEVEL SECURITY;
89
+ CREATE POLICY "Users manage own tasks"
90
+ ON public.tasks FOR ALL
91
+ USING (auth.uid() = user_id);
92
+ ```
93
+
94
+ ## API Endpoints
95
+
96
+ <!-- HOW-TO: List every endpoint your app exposes. Auth column should say "public", "authenticated", or "admin". This table is your AI assistant's map — if it's not listed here, it shouldn't be built. -->
97
+
98
+ | Method | Path | Auth | Description |
99
+ |--------|------|------|-------------|
100
+ | *GET* | */api/integrations* | *authenticated* | *List user's connected integrations* |
101
+ | *POST* | */api/integrations/[provider]/connect* | *authenticated* | *Initiate OAuth flow for a provider* |
102
+ | *POST* | */api/integrations/[provider]/callback* | *authenticated* | *Handle OAuth callback, store tokens* |
103
+ | *DELETE* | */api/integrations/[provider]* | *authenticated* | *Disconnect an integration* |
104
+ | *POST* | */api/pull* | *authenticated* | *Trigger manual morning pull* |
105
+ | *GET* | */api/tasks* | *authenticated* | *Get today's task list* |
106
+ | *PATCH* | */api/tasks/[id]* | *authenticated* | *Update task (reorder, focus, complete, snooze)* |
107
+ | *POST* | */api/webhooks/inngest* | *public (signed)* | *Inngest webhook receiver for scheduled pulls* |
108
+ | *POST* | */api/stripe/webhook* | *public (signed)* | *Stripe webhook receiver for payment events* |
109
+
110
+ ## Third-Party Integrations
111
+
112
+ <!-- HOW-TO: Any row with "Yes" in Secret Key means this call MUST go through a server route, not client code. If you see "Yes" and the code imports it in a component file, something is wrong. -->
113
+
114
+ | Service | Purpose | Secret Key Required? |
115
+ |---------|---------|---------------------|
116
+ | *Slack API* | *Pull channel messages and mentions* | *Yes — OAuth token stored server-side* |
117
+ | *Trello API* | *Pull cards from user's boards* | *Yes — API key + OAuth token* |
118
+ | *Linear API* | *Pull issues assigned to user* | *Yes — API key* |
119
+ | *Inngest* | *Schedule and run morning pull jobs* | *Yes — signing key for webhooks* |
120
+ | *Stripe* | *Process subscription payments* | *Yes — sk_live never in client code* |
121
+ | *Supabase* | *Database, auth, storage* | *No — uses anon key client-side, service role key server-side only* |
122
+
123
+ ## Research Notes
124
+
125
+ <!-- HOW-TO: Use this section during the research phase to capture technical findings, API limitations, rate limits, or alternative approaches you evaluated. This prevents re-researching the same questions later. -->
126
+
127
+ *- Slack conversation.history API is rate-limited to 50 requests/minute per token — need to batch pulls for users with many channels.*
128
+ *- Linear's GraphQL API returns max 250 nodes per query — pagination needed for power users.*
129
+ *- Inngest free tier supports up to 25,000 function runs/month — sufficient for ~800 daily users.*
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@cofoundr/init",
3
+ "version": "1.5.0",
4
+ "description": "Universal installer for the CoFoundr starter kit. Drops a tool-agnostic spec-driven workspace into any project — Claude Code, Cursor, Windsurf, Cline, Codex, Aider, Copilot, Gemini.",
5
+ "keywords": [
6
+ "spec-driven-development",
7
+ "sdd",
8
+ "claude-code",
9
+ "cursor",
10
+ "windsurf",
11
+ "cline",
12
+ "codex",
13
+ "aider",
14
+ "copilot",
15
+ "gemini",
16
+ "agents.md",
17
+ "scaffold"
18
+ ],
19
+ "homepage": "https://cofoundr.ai",
20
+ "license": "SEE LICENSE",
21
+ "type": "module",
22
+ "bin": {
23
+ "cofoundr": "./bin/cofoundr.mjs"
24
+ },
25
+ "engines": {
26
+ "node": ">=20.0.0"
27
+ },
28
+ "files": [
29
+ "bin/",
30
+ "src/",
31
+ "content/",
32
+ "README.md"
33
+ ],
34
+ "scripts": {
35
+ "prepack": "node scripts/bundle-content.mjs",
36
+ "test": "node --test test/license.test.mjs && node test/smoke.mjs"
37
+ },
38
+ "dependencies": {}
39
+ }
@@ -0,0 +1,35 @@
1
+ // Aider adapter.
2
+ // Aider reads AGENTS.md natively. We additionally drop a .aider.conf.yml that
3
+ // pre-loads the constitution and AGENTS.md as read-only context for every Aider session.
4
+
5
+ import { join } from 'node:path';
6
+ import { writeIfChanged } from '../core/fs.mjs';
7
+
8
+ export const adapter = {
9
+ id: 'aider',
10
+ name: 'Aider',
11
+
12
+ async apply({ repoPath, manifest, opts }) {
13
+ const dest = join(repoPath, '.aider.conf.yml');
14
+ const body = `# CoFoundr-aware Aider config.
15
+ # Pre-loads the project constitution, AGENTS.md, and decisions log as read-only
16
+ # context so every Aider session knows the rules without manual /add.
17
+
18
+ read:
19
+ - AGENTS.md
20
+ - .cofoundr/constitution.md
21
+ - .cofoundr/memory/project.md
22
+ - .cofoundr/memory/decisions.md
23
+
24
+ # When the user says /cofoundr:<name>, Aider should /add .cofoundr/commands/<name>.md
25
+ # and follow it step-by-step. Aider does not have native slash commands, so the user
26
+ # can paste:
27
+ # /run cat .cofoundr/commands/<name>.md
28
+ # or just /add the file and ask Aider to follow it.
29
+ `;
30
+ const r = await writeIfChanged(dest, body, opts);
31
+ return r.wrote
32
+ ? { written: [r.path], skipped: [] }
33
+ : { written: [], skipped: [{ path: r.path, reason: r.reason }] };
34
+ },
35
+ };
@@ -0,0 +1,114 @@
1
+ // Claude Code adapter.
2
+ // Strategy: Claude Code's project-level slash commands live at .claude/commands/<name>.md
3
+ // and project-level agents at .claude/agents/<name>.md. We copy each canonical command
4
+ // and agent into those directories with a "cofoundr-" prefix, and append a CLAUDE.md
5
+ // block that imports the .cofoundr/ workspace via @-references.
6
+
7
+ import { join } from 'node:path';
8
+ import { readSourceFile } from '../core/source.mjs';
9
+ import { writeIfChanged, readIfExists } from '../core/fs.mjs';
10
+
11
+ export const adapter = {
12
+ id: 'claude-code',
13
+ name: 'Claude Code',
14
+ detect(repoPath) {
15
+ return ['/.claude/', '/CLAUDE.md', '/.claude-plugin/'].some(() => true) && true;
16
+ },
17
+
18
+ async apply({ repoPath, source, manifest, opts }) {
19
+ const written = [];
20
+ const skipped = [];
21
+
22
+ for (const cmd of manifest.commands) {
23
+ const body = await readSourceFile(source, cmd.file);
24
+ const out = `${frontmatterFor(cmd)}\n${stripFrontmatter(body)}`;
25
+ const dest = join(repoPath, '.claude', 'commands', `cofoundr-${cmd.id}.md`);
26
+ bump(await writeIfChanged(dest, out, opts), written, skipped);
27
+ }
28
+
29
+ for (const agent of manifest.agents) {
30
+ const body = await readSourceFile(source, agent.file);
31
+ const dest = join(repoPath, '.claude', 'agents', `${agent.id}.md`);
32
+ bump(await writeIfChanged(dest, body, opts), written, skipped);
33
+ }
34
+
35
+ // CLAUDE.md context block: append-only inside fenced markers so the user's existing
36
+ // content is preserved across syncs.
37
+ const claudeMdPath = join(repoPath, 'CLAUDE.md');
38
+ const existing = (await readIfExists(claudeMdPath)) ?? '';
39
+ const updated = upsertCofoundrBlock(existing, COFOUNDR_CLAUDE_BLOCK);
40
+ if (updated !== existing) {
41
+ bump(await writeIfChanged(claudeMdPath, updated, { ...opts, force: true }), written, skipped);
42
+ }
43
+
44
+ return { written, skipped };
45
+ },
46
+ };
47
+
48
+ const COFOUNDR_BEGIN = '<!-- cofoundr:start -->';
49
+ const COFOUNDR_END = '<!-- cofoundr:end -->';
50
+
51
+ const COFOUNDR_CLAUDE_BLOCK = `${COFOUNDR_BEGIN}
52
+ ## CoFoundr Workspace
53
+
54
+ This project uses **CoFoundr** — a tool-agnostic spec-driven development workspace.
55
+ Source of truth for specs, rules, and workflow commands lives in \`.cofoundr/\`.
56
+
57
+ @.cofoundr/constitution.md
58
+ @.cofoundr/memory/project.md
59
+ @.cofoundr/memory/decisions.md
60
+ @AGENTS.md
61
+
62
+ ### Session Start Protocol
63
+
64
+ Before any code: run \`/cofoundr:resume\`. It surfaces the active feature doc(s)
65
+ and the next unchecked task, and waits for confirmation.
66
+
67
+ ### Workflow commands
68
+
69
+ Project-level slash commands (each maps to \`.claude/commands/cofoundr-*.md\`):
70
+
71
+ - **Planning:** \`/cofoundr:quick-brief\`, \`/cofoundr:new-project\`, \`/cofoundr:plan\`, \`/cofoundr:next\`, \`/cofoundr:new-feature\`
72
+ - **Spec-driven (SDD):** \`/cofoundr:constitution\`, \`/cofoundr:specify\`, \`/cofoundr:tasks\`, \`/cofoundr:implement\`
73
+ - **Brownfield:** \`/cofoundr:onboard\`, \`/cofoundr:document\`, \`/cofoundr:audit\`
74
+ - **Guardrails:** \`/cofoundr:scope-guard\`, \`/cofoundr:rules-check\`, \`/cofoundr:review\`
75
+ - **Comms / setup:** \`/cofoundr:translate\`, \`/cofoundr:setup-skills\`, \`/cofoundr:resume\`
76
+
77
+ Refresh shims after a CoFoundr upgrade with \`npx @cofoundr/init sync\`.
78
+ ${COFOUNDR_END}`;
79
+
80
+ function upsertCofoundrBlock(existing, newBlock) {
81
+ if (!existing.trim()) return `${newBlock}\n`;
82
+ const startIdx = existing.indexOf(COFOUNDR_BEGIN);
83
+ const endIdx = existing.indexOf(COFOUNDR_END);
84
+ if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
85
+ const before = existing.slice(0, startIdx).trimEnd();
86
+ const after = existing.slice(endIdx + COFOUNDR_END.length).trimStart();
87
+ const sep = before ? `${before}\n\n` : '';
88
+ return `${sep}${newBlock}${after ? `\n\n${after}` : '\n'}`;
89
+ }
90
+ // No existing block — append at end.
91
+ const trimmed = existing.trimEnd();
92
+ return `${trimmed}\n\n${newBlock}\n`;
93
+ }
94
+
95
+ function frontmatterFor(cmd) {
96
+ const desc = cmd.summary.replace(/\n/g, ' ').trim();
97
+ return `---
98
+ description: >
99
+ ${desc}
100
+ allowed-tools: Read, Write, Edit, Glob, Grep, Bash, Task
101
+ ---`;
102
+ }
103
+
104
+ function stripFrontmatter(md) {
105
+ if (!md.startsWith('---')) return md;
106
+ const end = md.indexOf('\n---', 3);
107
+ if (end === -1) return md;
108
+ return md.slice(end + 4).replace(/^\s+/, '');
109
+ }
110
+
111
+ function bump(result, written, skipped) {
112
+ if (result.wrote) written.push(result.path);
113
+ else skipped.push({ path: result.path, reason: result.reason });
114
+ }
@@ -0,0 +1,46 @@
1
+ // Cline / Roo Code adapter.
2
+ // Both read .clinerules/ as a directory of markdown files. We drop a single cofoundr.md.
3
+
4
+ import { join } from 'node:path';
5
+ import { writeIfChanged } from '../core/fs.mjs';
6
+
7
+ export const adapter = {
8
+ id: 'cline',
9
+ name: 'Cline / Roo Code',
10
+
11
+ async apply({ repoPath, manifest, opts }) {
12
+ const dest = join(repoPath, '.clinerules', 'cofoundr.md');
13
+ const body = render(manifest);
14
+ const r = await writeIfChanged(dest, body, opts);
15
+ return r.wrote
16
+ ? { written: [r.path], skipped: [] }
17
+ : { written: [], skipped: [{ path: r.path, reason: r.reason }] };
18
+ },
19
+ };
20
+
21
+ function render(manifest) {
22
+ const cmds = manifest.commands.map((c) => `- \`/cofoundr:${c.id}\` — ${c.summary}`).join('\n');
23
+ return `# CoFoundr workspace
24
+
25
+ Read \`AGENTS.md\` and \`.cofoundr/constitution.md\` before any change.
26
+
27
+ ## Following a CoFoundr command
28
+
29
+ When the user says \`/cofoundr:<name>\` or \`cofoundr <name>\`:
30
+
31
+ 1. Read \`.cofoundr/commands/<name>.md\`.
32
+ 2. Execute step-by-step. Do not paraphrase.
33
+ 3. The Never list in \`.cofoundr/constitution.md\` is binding.
34
+
35
+ ## Reading order
36
+
37
+ 1. \`AGENTS.md\`
38
+ 2. \`.cofoundr/constitution.md\`
39
+ 3. \`.cofoundr/memory/project.md\`
40
+ 4. \`.cofoundr/memory/decisions.md\`
41
+
42
+ ## Commands
43
+
44
+ ${cmds}
45
+ `;
46
+ }
@@ -0,0 +1,29 @@
1
+ // OpenAI Codex CLI adapter.
2
+ // Codex reads AGENTS.md (which scaffold.mjs already writes). All this adapter does is
3
+ // confirm AGENTS.md will be written and optionally drop a .codex/instructions.md
4
+ // for project-scoped overrides.
5
+
6
+ import { join } from 'node:path';
7
+ import { writeIfChanged } from '../core/fs.mjs';
8
+
9
+ export const adapter = {
10
+ id: 'codex',
11
+ name: 'OpenAI Codex CLI',
12
+
13
+ async apply({ repoPath, manifest, opts }) {
14
+ const dest = join(repoPath, '.codex', 'instructions.md');
15
+ const body = `# Codex — CoFoundr instructions
16
+
17
+ The canonical instructions for this project live in \`AGENTS.md\` at the repo root.
18
+ This file exists only as an explicit signal for the Codex CLI. Read \`AGENTS.md\` and
19
+ \`.cofoundr/constitution.md\` before making any change.
20
+
21
+ When the user says \`/cofoundr:<name>\` or \`cofoundr <name>\`, read
22
+ \`.cofoundr/commands/<name>.md\` and follow it step-by-step.
23
+ `;
24
+ const r = await writeIfChanged(dest, body, opts);
25
+ return r.wrote
26
+ ? { written: [r.path], skipped: [] }
27
+ : { written: [], skipped: [{ path: r.path, reason: r.reason }] };
28
+ },
29
+ };
@@ -0,0 +1,54 @@
1
+ // GitHub Copilot adapter.
2
+ // Copilot reads .github/copilot-instructions.md as the repo-scoped instruction file.
3
+ // Like Cursor/Windsurf, it has no slash commands — it follows the instructions file.
4
+
5
+ import { join } from 'node:path';
6
+ import { writeIfChanged } from '../core/fs.mjs';
7
+
8
+ export const adapter = {
9
+ id: 'copilot',
10
+ name: 'GitHub Copilot',
11
+
12
+ async apply({ repoPath, manifest, opts }) {
13
+ const dest = join(repoPath, '.github', 'copilot-instructions.md');
14
+ const body = render(manifest);
15
+ const r = await writeIfChanged(dest, body, opts);
16
+ return r.wrote
17
+ ? { written: [r.path], skipped: [] }
18
+ : { written: [], skipped: [{ path: r.path, reason: r.reason }] };
19
+ },
20
+ };
21
+
22
+ function render(manifest) {
23
+ const cmds = manifest.commands.map((c) => `- **${c.id}** — ${c.summary}`).join('\n');
24
+ return `# Copilot instructions — CoFoundr workspace
25
+
26
+ This project uses **CoFoundr** — a tool-agnostic spec-driven workspace at \`.cofoundr/\`.
27
+
28
+ ## Read first
29
+
30
+ 1. \`AGENTS.md\` — universal project context.
31
+ 2. \`.cofoundr/constitution.md\` — binding rules. The \`Never\` section is non-negotiable.
32
+ 3. \`.cofoundr/memory/project.md\` — project facts.
33
+ 4. \`.cofoundr/memory/decisions.md\` — append-only decisions log.
34
+
35
+ ## Workflow commands
36
+
37
+ When a user references one of these (\`/cofoundr:<name>\` or \`cofoundr <name>\`),
38
+ read \`.cofoundr/commands/<name>.md\` and follow it step-by-step.
39
+
40
+ ${cmds}
41
+
42
+ ## Conventions
43
+
44
+ - Validate input server-side. Client-side validation is a UX hint, not a security control.
45
+ - Pin exact major versions. No \`^\` for security-sensitive deps.
46
+ - One commit per task: \`<phase or feature>: T<N> <imperative summary>\`.
47
+ - Never put secrets in client bundles. Never commit \`.env\`. Never use \`any\` in TypeScript.
48
+ - Always run \`/cofoundr:resume\` at the start of a session before writing any code.
49
+
50
+ ## Refresh
51
+
52
+ \`npx @cofoundr/init sync\` updates this file and all per-tool shims after a CoFoundr upgrade.
53
+ `;
54
+ }
@@ -0,0 +1,69 @@
1
+ // Cursor adapter.
2
+ // Cursor reads .cursor/rules/*.mdc with frontmatter ({ description, globs, alwaysApply }).
3
+ // We write a single rule file that points the Composer/agent to .cofoundr/.
4
+
5
+ import { join } from 'node:path';
6
+ import { writeIfChanged } from '../core/fs.mjs';
7
+
8
+ export const adapter = {
9
+ id: 'cursor',
10
+ name: 'Cursor',
11
+
12
+ async apply({ repoPath, manifest, opts }) {
13
+ const written = [];
14
+ const skipped = [];
15
+ const dest = join(repoPath, '.cursor', 'rules', 'cofoundr.mdc');
16
+ const body = renderRule(manifest);
17
+ const r = await writeIfChanged(dest, body, opts);
18
+ if (r.wrote) written.push(r.path);
19
+ else skipped.push({ path: r.path, reason: r.reason });
20
+ return { written, skipped };
21
+ },
22
+ };
23
+
24
+ function renderRule(manifest) {
25
+ const cmdLines = manifest.commands
26
+ .map((c) => `- \`/cofoundr:${c.id}\` — ${c.summary}`)
27
+ .join('\n');
28
+ return `---
29
+ description: CoFoundr — spec-driven workflow for this project. Source of truth in .cofoundr/.
30
+ globs:
31
+ - "**/*"
32
+ alwaysApply: true
33
+ ---
34
+
35
+ # CoFoundr workspace
36
+
37
+ This project uses **CoFoundr** — a tool-agnostic spec-driven workspace. The source of truth lives in \`.cofoundr/\`. The universal context file is \`AGENTS.md\` at the repo root — read it first.
38
+
39
+ ## How to follow CoFoundr commands
40
+
41
+ When the user types one of:
42
+
43
+ - \`/cofoundr:<name>\`
44
+ - \`cofoundr <name>\`
45
+ - \`run cofoundr <name>\`
46
+
47
+ …you must:
48
+
49
+ 1. Read \`.cofoundr/commands/<name>.md\`.
50
+ 2. Follow it step-by-step. Do not paraphrase. Do not skip steps.
51
+ 3. Honor the rules in \`.cofoundr/constitution.md\`. The \`Never\` section is binding.
52
+
53
+ ## Available commands
54
+
55
+ ${cmdLines}
56
+
57
+ ## Reading order at session start
58
+
59
+ 1. \`AGENTS.md\` — universal context.
60
+ 2. \`.cofoundr/constitution.md\` — binding rules.
61
+ 3. \`.cofoundr/memory/project.md\` — project facts.
62
+ 4. \`.cofoundr/memory/decisions.md\` — append-only decision log.
63
+ 5. The active feature doc in \`docs/features/\` if any.
64
+
65
+ ## Refresh
66
+
67
+ \`npx @cofoundr/init sync\` refreshes this rule and all shims after a CoFoundr upgrade.
68
+ `;
69
+ }
@@ -0,0 +1,41 @@
1
+ // Gemini CLI adapter.
2
+ // Gemini CLI reads GEMINI.md at the repo root (sibling to AGENTS.md).
3
+
4
+ import { join } from 'node:path';
5
+ import { writeIfChanged } from '../core/fs.mjs';
6
+
7
+ export const adapter = {
8
+ id: 'gemini',
9
+ name: 'Gemini CLI',
10
+
11
+ async apply({ repoPath, manifest, opts }) {
12
+ const dest = join(repoPath, 'GEMINI.md');
13
+ const body = render(manifest);
14
+ const r = await writeIfChanged(dest, body, opts);
15
+ return r.wrote
16
+ ? { written: [r.path], skipped: [] }
17
+ : { written: [], skipped: [{ path: r.path, reason: r.reason }] };
18
+ },
19
+ };
20
+
21
+ function render(manifest) {
22
+ const cmds = manifest.commands.map((c) => `- \`/cofoundr:${c.id}\` — ${c.summary}`).join('\n');
23
+ return `# Gemini CLI — CoFoundr workspace
24
+
25
+ The canonical project context is in \`AGENTS.md\` at the repo root. Read that first.
26
+
27
+ This file exists for tools that prefer \`GEMINI.md\` to \`AGENTS.md\`.
28
+
29
+ ## Quick rules
30
+
31
+ 1. Read \`AGENTS.md\`, \`.cofoundr/constitution.md\`, and \`.cofoundr/memory/project.md\` before any change.
32
+ 2. The \`Never\` list in the constitution is binding.
33
+ 3. When the user says \`/cofoundr:<name>\` or \`cofoundr <name>\`, read \`.cofoundr/commands/<name>.md\` and follow it step-by-step.
34
+
35
+ ## Commands
36
+
37
+ ${cmds}
38
+
39
+ Refresh with \`npx @cofoundr/init sync\`.
40
+ `;
41
+ }