@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,179 @@
1
+ import { join, basename } from 'node:path';
2
+ import { readdir, stat } from 'node:fs/promises';
3
+ import { writeIfChanged, writeAlways } from './fs.mjs';
4
+ import { readSourceFile } from './source.mjs';
5
+
6
+ export async function writeScaffold({ repoPath, source, manifest, projectName, selectedTools, dryRun, force }) {
7
+ const today = new Date().toISOString().slice(0, 10);
8
+ const written = [];
9
+ const skipped = [];
10
+
11
+ const subs = {
12
+ PROJECT_NAME: projectName || basename(repoPath),
13
+ PROJECT_DESCRIPTION:
14
+ '<!-- Add a one-paragraph description. /cofoundr:plan or /cofoundr:onboard will fill this in. -->',
15
+ BUILD_INSTRUCTIONS:
16
+ '<!-- One block of copy-paste-ready commands. Examples: install, dev, test, build. -->',
17
+ CODE_CONVENTIONS:
18
+ '<!-- 3-6 bullet points. Stack pins, lint commands, format commands, test runner. -->',
19
+ TODAY: today,
20
+ COFOUNDR_VERSION: manifest.version,
21
+ TOOLS_JSON: JSON.stringify(selectedTools, null, 2),
22
+ TOOL_SHIMS: renderToolShims(selectedTools, manifest),
23
+ };
24
+
25
+ // 1) AGENTS.md at repo root.
26
+ const agentsTpl = await readSourceFile(source, 'scaffold/AGENTS.md.tmpl');
27
+ const agentsOut = applySubs(agentsTpl, subs);
28
+ const r = await writeIfChanged(join(repoPath, 'AGENTS.md'), agentsOut, { dryRun, force });
29
+ bump(r, written, skipped);
30
+
31
+ // 2) .cofoundr/ scaffold.
32
+ await copyTreeWithSubs({
33
+ sourceRoot: source.root,
34
+ sourceRel: 'scaffold/.cofoundr',
35
+ destRoot: join(repoPath, '.cofoundr'),
36
+ subs,
37
+ dryRun,
38
+ force,
39
+ written,
40
+ skipped,
41
+ });
42
+
43
+ // 3) Bundle the canonical commands and agents into .cofoundr/ so non-Claude tools have a single
44
+ // well-known location to read from. Templates from cofoundr-system/templates/ are also copied
45
+ // in as a project-local override surface (read-only by default).
46
+ await copyTree({
47
+ sourceRoot: source.root,
48
+ sourceRel: 'commands',
49
+ destRoot: join(repoPath, '.cofoundr', 'commands'),
50
+ dryRun,
51
+ force,
52
+ written,
53
+ skipped,
54
+ });
55
+ await copyTree({
56
+ sourceRoot: source.root,
57
+ sourceRel: 'agents',
58
+ destRoot: join(repoPath, '.cofoundr', 'agents'),
59
+ dryRun,
60
+ force,
61
+ written,
62
+ skipped,
63
+ });
64
+ await copyTree({
65
+ sourceRoot: source.root,
66
+ sourceRel: 'templates',
67
+ destRoot: join(repoPath, '.cofoundr', 'templates'),
68
+ dryRun,
69
+ force,
70
+ written,
71
+ skipped,
72
+ });
73
+
74
+ return { written, skipped };
75
+ }
76
+
77
+ function renderToolShims(selectedTools, manifest) {
78
+ const map = new Map(manifest.targets.map((t) => [t.id, t]));
79
+ return selectedTools
80
+ .map((id) => {
81
+ const t = map.get(id);
82
+ if (!t) return `- \`${id}\``;
83
+ const paths = t.config.map((p) => `\`${p}\``).join(', ');
84
+ return `- **${t.name}** — ${paths}`;
85
+ })
86
+ .join('\n');
87
+ }
88
+
89
+ function applySubs(text, subs) {
90
+ let out = text;
91
+ for (const [k, v] of Object.entries(subs)) {
92
+ out = out.replaceAll(`{{${k}}}`, v);
93
+ }
94
+ return out;
95
+ }
96
+
97
+ function bump(result, written, skipped) {
98
+ if (result.wrote) written.push(result.path);
99
+ else skipped.push({ path: result.path, reason: result.reason });
100
+ }
101
+
102
+ async function copyTreeWithSubs({ sourceRoot, sourceRel, destRoot, subs, dryRun, force, written, skipped }) {
103
+ const root = join(sourceRoot, sourceRel);
104
+ const items = await walk(root);
105
+ for (const rel of items) {
106
+ const srcAbs = join(root, rel);
107
+ const isTpl = rel.endsWith('.tmpl');
108
+ const destRel = isTpl ? rel.replace(/\.tmpl$/, '') : rel;
109
+ const destAbs = join(destRoot, destRel);
110
+ const raw = await readFileText(srcAbs);
111
+ const contents = isTpl ? applySubs(raw, subs) : raw;
112
+ const r = await writeIfChanged(destAbs, contents, { dryRun, force });
113
+ bump(r, written, skipped);
114
+ }
115
+ }
116
+
117
+ async function copyTree({ sourceRoot, sourceRel, destRoot, dryRun, force, written, skipped }) {
118
+ const root = join(sourceRoot, sourceRel);
119
+ const items = await walk(root);
120
+ for (const rel of items) {
121
+ const srcAbs = join(root, rel);
122
+ const destAbs = join(destRoot, rel);
123
+ const raw = await readFileText(srcAbs);
124
+ const r = await writeIfChanged(destAbs, raw, { dryRun, force });
125
+ bump(r, written, skipped);
126
+ }
127
+ }
128
+
129
+ async function walk(root) {
130
+ const out = [];
131
+ await walkInto(root, '', out);
132
+ return out;
133
+ }
134
+
135
+ async function walkInto(root, rel, out) {
136
+ let entries;
137
+ try {
138
+ entries = await readdir(join(root, rel), { withFileTypes: true });
139
+ } catch {
140
+ return;
141
+ }
142
+ for (const e of entries) {
143
+ const sub = rel ? `${rel}/${e.name}` : e.name;
144
+ if (e.isDirectory()) {
145
+ await walkInto(root, sub, out);
146
+ } else if (e.isFile()) {
147
+ out.push(sub);
148
+ }
149
+ }
150
+ }
151
+
152
+ async function readFileText(p) {
153
+ const { readFile } = await import('node:fs/promises');
154
+ return readFile(p, 'utf8');
155
+ }
156
+
157
+ export async function writeProjectManifest({ repoPath, manifest, selectedTools, dryRun }) {
158
+ const today = new Date().toISOString().slice(0, 10);
159
+ const body = JSON.stringify(
160
+ {
161
+ $schema: 'https://cofoundr.ai/schemas/project-manifest.schema.json',
162
+ cofoundrVersion: manifest.version,
163
+ initializedAt: today,
164
+ tools: selectedTools,
165
+ layout: {
166
+ constitution: '.cofoundr/constitution.md',
167
+ memory: '.cofoundr/memory/',
168
+ specs: '.cofoundr/specs/',
169
+ commands: '.cofoundr/commands/',
170
+ agents: '.cofoundr/agents/',
171
+ docs: '.cofoundr/docs/',
172
+ reports: '.cofoundr/reports/',
173
+ },
174
+ },
175
+ null,
176
+ 2
177
+ ) + '\n';
178
+ return writeAlways(join(repoPath, '.cofoundr', 'manifest.json'), body, { dryRun });
179
+ }
@@ -0,0 +1,54 @@
1
+ // Locates the canonical CoFoundr content (commands, agents, templates, scaffold)
2
+ // in two modes:
3
+ // 1) Bundled — when the package was published to npm, content is at packages/cofoundr-cli/content/
4
+ // 2) Dev — when running from a checkout, content lives at <repo>/cofoundr-system/
5
+
6
+ import { readFile, stat } from 'node:fs/promises';
7
+ import { existsSync } from 'node:fs';
8
+ import { dirname, join, resolve } from 'node:path';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ const here = dirname(fileURLToPath(import.meta.url));
12
+ const pkgRoot = resolve(here, '..', '..');
13
+
14
+ export async function locateSource() {
15
+ const bundled = join(pkgRoot, 'content');
16
+ if (existsSync(join(bundled, 'manifest.json'))) {
17
+ return { root: bundled, mode: 'bundled' };
18
+ }
19
+
20
+ // Dev: walk up looking for cofoundr-system/manifest.json
21
+ let dir = pkgRoot;
22
+ for (let i = 0; i < 6; i++) {
23
+ const candidate = join(dir, 'cofoundr-system');
24
+ if (existsSync(join(candidate, 'manifest.json'))) {
25
+ return { root: candidate, mode: 'dev' };
26
+ }
27
+ const parent = dirname(dir);
28
+ if (parent === dir) break;
29
+ dir = parent;
30
+ }
31
+
32
+ throw new Error(
33
+ 'Could not locate the CoFoundr canonical content. Expected either ' +
34
+ `${join(pkgRoot, 'content')} (bundled) or a sibling cofoundr-system/ directory.`
35
+ );
36
+ }
37
+
38
+ export async function loadManifest(source) {
39
+ const raw = await readFile(join(source.root, 'manifest.json'), 'utf8');
40
+ return JSON.parse(raw);
41
+ }
42
+
43
+ export async function readSourceFile(source, relPath) {
44
+ return readFile(join(source.root, relPath), 'utf8');
45
+ }
46
+
47
+ export async function sourceFileExists(source, relPath) {
48
+ try {
49
+ await stat(join(source.root, relPath));
50
+ return true;
51
+ } catch {
52
+ return false;
53
+ }
54
+ }
@@ -0,0 +1,10 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { fileURLToPath } from 'node:url';
3
+ import { dirname, join } from 'node:path';
4
+
5
+ const here = dirname(fileURLToPath(import.meta.url));
6
+
7
+ export async function version() {
8
+ const pkg = JSON.parse(await readFile(join(here, '..', '..', 'package.json'), 'utf8'));
9
+ return pkg.version;
10
+ }