create-baton 1.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 ADDED
@@ -0,0 +1,58 @@
1
+ # create-baton
2
+
3
+ Set up the [Baton](https://github.com/darwesh88/baton) AI orchestration protocol in any project with one command.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npm create baton
9
+ ```
10
+
11
+ Or with npx:
12
+
13
+ ```bash
14
+ npx create-baton
15
+ ```
16
+
17
+ ## What It Does
18
+
19
+ Baton turns any AI coding assistant into a self-managing project partner. This CLI sets up:
20
+
21
+ - **BATON_v3.1.md** — The core protocol
22
+ - **skills/** — Proven patterns library (security, testing, stack guides)
23
+ - **.ai-rules/** — Stub files for AI to fill during discovery
24
+ - **handoff/** — Session handoff directory
25
+ - **IDE config** — CLAUDE.md, .cursorrules, or .windsurfrules based on your tool
26
+
27
+ ## How It Works
28
+
29
+ ```
30
+ $ npx create-baton
31
+
32
+ Baton — AI Orchestration Protocol v3.1
33
+
34
+ ? What AI coding tool are you using? → Claude Code
35
+ ? What's your primary stack? → Next.js + Supabase
36
+ ? Project name? → my-app
37
+
38
+ Setting up Baton...
39
+
40
+ ✓ Copied BATON_v3.1.md
41
+ ✓ Copied skills/ (8 core + 2 stack + 1 pattern)
42
+ ✓ Created .ai-rules/ (4 stub files)
43
+ ✓ Created handoff/
44
+ ✓ Created CLAUDE.md
45
+ ✓ Created PROGRESS.md, BACKLOG.md, FEATURES.md
46
+
47
+ Done! Next steps:
48
+ 1. Open this folder in your AI coding tool
49
+ 2. Tell the AI: "Read BATON_v3.1.md and begin"
50
+ ```
51
+
52
+ ## Requirements
53
+
54
+ - Node.js 18+
55
+
56
+ ## License
57
+
58
+ MIT
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env node
2
+
3
+ const [major] = process.versions.node.split('.').map(Number);
4
+ if (major < 18) {
5
+ console.error('Baton requires Node.js 18 or higher. You are running ' + process.version);
6
+ process.exit(1);
7
+ }
8
+
9
+ require('../src/index.js');
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "create-baton",
3
+ "version": "1.0.0",
4
+ "description": "Set up Baton AI orchestration protocol in any project",
5
+ "bin": {
6
+ "create-baton": "./bin/create-baton.js"
7
+ },
8
+ "scripts": {
9
+ "prep": "node scripts/prep-templates.js",
10
+ "prepublishOnly": "node scripts/prep-templates.js"
11
+ },
12
+ "keywords": [
13
+ "ai",
14
+ "orchestration",
15
+ "protocol",
16
+ "claude",
17
+ "cursor",
18
+ "windsurf",
19
+ "baton",
20
+ "scaffolding"
21
+ ],
22
+ "author": "darwesh88",
23
+ "license": "MIT",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/darwesh88/baton.git",
27
+ "directory": "cli"
28
+ },
29
+ "homepage": "https://github.com/darwesh88/baton",
30
+ "engines": {
31
+ "node": ">=18"
32
+ },
33
+ "dependencies": {
34
+ "prompts": "^2.4.2"
35
+ }
36
+ }
@@ -0,0 +1,30 @@
1
+ // IDE tool → config file mapping
2
+ const IDE_MAP = {
3
+ 'Claude Code': { file: 'CLAUDE.md', template: 'CLAUDE.md.template' },
4
+ 'Cursor': { file: '.cursorrules', template: 'cursorrules.template' },
5
+ 'Windsurf': { file: '.windsurfrules', template: 'cursorrules.template' },
6
+ 'Codex': { file: 'AGENTS.md', template: null }, // AGENTS.md generated directly
7
+ 'Kiro': { file: 'CLAUDE.md', template: 'CLAUDE.md.template' },
8
+ 'Warp': { file: 'CLAUDE.md', template: 'CLAUDE.md.template' },
9
+ 'Other': { file: 'CLAUDE.md', template: 'CLAUDE.md.template' },
10
+ };
11
+
12
+ // Stack → which skill directories to copy into stacks/
13
+ const STACK_MAP = {
14
+ 'Next.js + Supabase': ['nextjs', 'supabase'],
15
+ 'Next.js + other': ['nextjs'],
16
+ 'React + Node': [],
17
+ 'Python': [],
18
+ 'Other': [],
19
+ };
20
+
21
+ // Stack → display name for templates
22
+ const STACK_LABEL = {
23
+ 'Next.js + Supabase': 'Next.js, Supabase',
24
+ 'Next.js + other': 'Next.js',
25
+ 'React + Node': 'React, Node.js',
26
+ 'Python': 'Python',
27
+ 'Other': '',
28
+ };
29
+
30
+ module.exports = { IDE_MAP, STACK_MAP, STACK_LABEL };
package/src/index.js ADDED
@@ -0,0 +1,35 @@
1
+ const { askQuestions } = require('./prompts');
2
+ const { scaffold } = require('./scaffold');
3
+
4
+ async function main() {
5
+ // Banner
6
+ console.log('');
7
+ console.log(' Baton — AI Orchestration Protocol v3.1');
8
+ console.log('');
9
+
10
+ // Prompts
11
+ const answers = await askQuestions();
12
+ console.log('');
13
+ console.log(' Setting up Baton...');
14
+ console.log('');
15
+
16
+ // Scaffold
17
+ const dest = process.cwd();
18
+ const results = scaffold(dest, answers);
19
+
20
+ // Success
21
+ for (const r of results) {
22
+ console.log(` \u2713 ${r}`);
23
+ }
24
+
25
+ console.log('');
26
+ console.log(' Done! Next steps:');
27
+ console.log(' 1. Open this folder in your AI coding tool');
28
+ console.log(' 2. Tell the AI: "Read BATON_v3.1.md and begin"');
29
+ console.log('');
30
+ }
31
+
32
+ main().catch((err) => {
33
+ console.error('Error:', err.message);
34
+ process.exit(1);
35
+ });
package/src/prompts.js ADDED
@@ -0,0 +1,54 @@
1
+ const prompts = require('prompts');
2
+ const path = require('path');
3
+
4
+ async function askQuestions() {
5
+ // Exit gracefully on Ctrl+C
6
+ const onCancel = () => {
7
+ console.log('\nAborted.');
8
+ process.exit(0);
9
+ };
10
+
11
+ const answers = await prompts([
12
+ {
13
+ type: 'select',
14
+ name: 'tool',
15
+ message: 'What AI coding tool are you using?',
16
+ choices: [
17
+ { title: 'Claude Code', value: 'Claude Code' },
18
+ { title: 'Cursor', value: 'Cursor' },
19
+ { title: 'Windsurf', value: 'Windsurf' },
20
+ { title: 'Codex (OpenAI)', value: 'Codex' },
21
+ { title: 'Kiro', value: 'Kiro' },
22
+ { title: 'Warp', value: 'Warp' },
23
+ { title: 'Other', value: 'Other' },
24
+ ],
25
+ },
26
+ {
27
+ type: 'select',
28
+ name: 'stack',
29
+ message: "What's your primary stack?",
30
+ choices: [
31
+ { title: 'Next.js + Supabase', value: 'Next.js + Supabase' },
32
+ { title: 'Next.js + other', value: 'Next.js + other' },
33
+ { title: 'React + Node', value: 'React + Node' },
34
+ { title: 'Python', value: 'Python' },
35
+ { title: 'Other', value: 'Other' },
36
+ ],
37
+ },
38
+ {
39
+ type: 'text',
40
+ name: 'projectName',
41
+ message: 'Project name?',
42
+ initial: path.basename(process.cwd()),
43
+ },
44
+ ], { onCancel });
45
+
46
+ // If prompts were cancelled (empty object), exit
47
+ if (!answers.tool && answers.tool !== 0) {
48
+ process.exit(0);
49
+ }
50
+
51
+ return answers;
52
+ }
53
+
54
+ module.exports = { askQuestions };
@@ -0,0 +1,193 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { IDE_MAP, STACK_MAP, STACK_LABEL } = require('./constants');
4
+
5
+ const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
6
+
7
+ function scaffold(dest, { tool, stack, projectName }) {
8
+ const results = [];
9
+
10
+ // 1. Check for existing BATON_v3.1.md
11
+ const batonDest = path.join(dest, 'BATON_v3.1.md');
12
+ if (fs.existsSync(batonDest)) {
13
+ console.log('\n ! BATON_v3.1.md already exists — skipping (delete it first to overwrite)');
14
+ } else {
15
+ fs.copyFileSync(path.join(TEMPLATES_DIR, 'BATON_v3.1.md'), batonDest);
16
+ results.push('Copied BATON_v3.1.md');
17
+ }
18
+
19
+ // 2. Copy skills/
20
+ const skillsSrc = path.join(TEMPLATES_DIR, 'skills');
21
+ const skillsDest = path.join(dest, 'skills');
22
+
23
+ // Always copy core/ and patterns/
24
+ copyDir(path.join(skillsSrc, 'core'), path.join(skillsDest, 'core'));
25
+ copyDir(path.join(skillsSrc, 'patterns'), path.join(skillsDest, 'patterns'));
26
+
27
+ // Create domains/ (empty, for user to add)
28
+ fs.mkdirSync(path.join(skillsDest, 'domains'), { recursive: true });
29
+
30
+ // Copy stack-specific skills (each is a directory with SKILL.md)
31
+ const stackSkills = STACK_MAP[stack] || [];
32
+ fs.mkdirSync(path.join(skillsDest, 'stacks'), { recursive: true });
33
+ for (const skillDir of stackSkills) {
34
+ const src = path.join(skillsSrc, 'stacks', skillDir);
35
+ if (fs.existsSync(src)) {
36
+ copyDir(src, path.join(skillsDest, 'stacks', skillDir));
37
+ }
38
+ }
39
+
40
+ const coreCount = fs.readdirSync(path.join(skillsDest, 'core')).filter(
41
+ f => fs.statSync(path.join(skillsDest, 'core', f)).isDirectory()
42
+ ).length;
43
+ const stackCount = stackSkills.length;
44
+ const patternCount = fs.readdirSync(path.join(skillsDest, 'patterns')).filter(
45
+ f => fs.statSync(path.join(skillsDest, 'patterns', f)).isDirectory()
46
+ ).length;
47
+ results.push(`Copied skills/ (${coreCount} core + ${stackCount} stack + ${patternCount} pattern)`);
48
+
49
+ // 3. Create .ai-rules/ with stub files
50
+ const aiRulesDir = path.join(dest, '.ai-rules');
51
+ fs.mkdirSync(aiRulesDir, { recursive: true });
52
+ const stubs = {
53
+ 'project.md': `# Project Rules — ${projectName}\n\n> AI will fill this during discovery session.\n`,
54
+ 'tech-stack.md': `# Tech Stack — ${projectName}\n\n> AI will fill this during discovery session.\n`,
55
+ 'patterns.md': `# Patterns & Quirks — ${projectName}\n\n> AI adds entries here as it discovers gotchas and solutions.\n`,
56
+ 'structure.md': `# Project Structure — ${projectName}\n\n> AI will fill this once the project scaffolding is set up.\n`,
57
+ };
58
+ for (const [file, content] of Object.entries(stubs)) {
59
+ fs.writeFileSync(path.join(aiRulesDir, file), content);
60
+ }
61
+ results.push('Created .ai-rules/ (4 stub files)');
62
+
63
+ // 4. Create handoff/ with .gitkeep
64
+ const handoffDir = path.join(dest, 'handoff');
65
+ fs.mkdirSync(handoffDir, { recursive: true });
66
+ fs.writeFileSync(path.join(handoffDir, '.gitkeep'), '');
67
+ results.push('Created handoff/');
68
+
69
+ // 5. Create IDE config file from template
70
+ const ide = IDE_MAP[tool] || IDE_MAP['Other'];
71
+ const stackLabel = STACK_LABEL[stack] || '';
72
+
73
+ if (ide.template) {
74
+ // Template-based IDE config (Claude Code, Cursor, Windsurf, etc.)
75
+ const templatePath = path.join(TEMPLATES_DIR, 'ide', ide.template);
76
+ let templateContent = fs.readFileSync(templatePath, 'utf-8');
77
+
78
+ templateContent = templateContent
79
+ .replace(/\{\{PROJECT_NAME\}\}/g, projectName)
80
+ .replace(/\{\{STACK\}\}/g, stackLabel)
81
+ .replace(/\{\{CURRENT_SESSION\}\}/g, '1')
82
+ .replace(/\{\{NEXT_SESSION\}\}/g, '2')
83
+ .replace(/\{\{BUILD_COMMAND\}\}/g, 'npm run build')
84
+ .replace(/\{\{DEV_COMMAND\}\}/g, 'npm run dev')
85
+ .replace(/\{\{TYPECHECK_COMMAND\}\}/g, 'npx tsc --noEmit')
86
+ .replace(/\{\{PROJECT_RULES\}\}/g, '<!-- AI will add project-specific rules here -->');
87
+
88
+ fs.writeFileSync(path.join(dest, ide.file), templateContent);
89
+ results.push(`Created ${ide.file}`);
90
+ } else {
91
+ // Codex — uses AGENTS.md directly (already generated in step 6)
92
+ results.push('Using AGENTS.md as IDE config (Codex)');
93
+ }
94
+
95
+ // 6. Generate AGENTS.md (universal standard for AI coding agents)
96
+ const agentsMd = generateAgentsMd(projectName, stackLabel);
97
+ fs.writeFileSync(path.join(dest, 'AGENTS.md'), agentsMd);
98
+ results.push('Generated AGENTS.md');
99
+
100
+ // 7. Create PROGRESS.md, BACKLOG.md, FEATURES.md
101
+ fs.writeFileSync(path.join(dest, 'PROGRESS.md'),
102
+ `# Progress — ${projectName}\n\n## Sessions\n\n_No sessions yet. AI will log progress here._\n`);
103
+ fs.writeFileSync(path.join(dest, 'BACKLOG.md'),
104
+ `# Backlog — ${projectName}\n\n_Deferred items go here. AI adds items during sessions._\n`);
105
+ fs.writeFileSync(path.join(dest, 'FEATURES.md'),
106
+ `# Features — ${projectName}\n\n_User-facing feature documentation. AI updates this as features ship._\n`);
107
+ results.push('Created PROGRESS.md, BACKLOG.md, FEATURES.md');
108
+
109
+ return results;
110
+ }
111
+
112
+ function generateAgentsMd(projectName, stack) {
113
+ const stackLine = stack ? `- **Stack:** ${stack}` : '- **Stack:** (to be determined during Session Zero)';
114
+ return `# AGENTS.md
115
+
116
+ This file provides guidance to AI coding agents working with this repository.
117
+
118
+ ## Project Overview
119
+
120
+ ${projectName} — bootstrapped with the Baton protocol.
121
+
122
+ ${stackLine}
123
+ - **Protocol:** Baton v3.1 (AI orchestration protocol)
124
+ - **Architecture:** See \`.ai-rules/\` for project context (generated during Session Zero)
125
+
126
+ ## Getting Started
127
+
128
+ This project uses the Baton protocol for AI-assisted development.
129
+
130
+ 1. Read \`BATON_v3.1.md\` — the orchestration protocol
131
+ 2. Read \`.ai-rules/\` — project context files (AI-generated)
132
+ 3. Read \`skills/\` — curated best practices for this stack
133
+ 4. Check \`handoff/\` — session handoff files for continuity
134
+
135
+ ## Build & Development Commands
136
+
137
+ \`\`\`bash
138
+ # Install dependencies
139
+ npm install
140
+
141
+ # Development
142
+ npm run dev
143
+
144
+ # Build
145
+ npm run build
146
+ \`\`\`
147
+
148
+ ## Code Style & Conventions
149
+
150
+ See \`.ai-rules/patterns.md\` for project-specific patterns.
151
+ See \`skills/core/\` for universal rules (security, testing, anti-overengineering).
152
+
153
+ ## Project Structure
154
+
155
+ \`\`\`
156
+ BATON_v3.1.md # AI orchestration protocol
157
+ .ai-rules/ # Project context (AI-generated)
158
+ skills/ # Best practice skills
159
+ core/ # Universal rules
160
+ stacks/ # Stack-specific patterns
161
+ patterns/ # Implementation patterns
162
+ handoff/ # Session handoff files
163
+ PROGRESS.md # Session progress log
164
+ BACKLOG.md # Deferred items
165
+ \`\`\`
166
+
167
+ ## Agent Boundaries
168
+
169
+ This project follows the Baton protocol. Key rules:
170
+ - Read BATON_v3.1.md before starting work
171
+ - Check skills/ before web searching
172
+ - Document discoveries in .ai-rules/patterns.md
173
+ - Create handoff files at session end
174
+ - Update PROGRESS.md after each session
175
+ `;
176
+ }
177
+
178
+ function copyDir(src, dest) {
179
+ fs.mkdirSync(dest, { recursive: true });
180
+ if (!fs.existsSync(src)) return;
181
+ const entries = fs.readdirSync(src, { withFileTypes: true });
182
+ for (const entry of entries) {
183
+ const srcPath = path.join(src, entry.name);
184
+ const destPath = path.join(dest, entry.name);
185
+ if (entry.isDirectory()) {
186
+ copyDir(srcPath, destPath);
187
+ } else {
188
+ fs.copyFileSync(srcPath, destPath);
189
+ }
190
+ }
191
+ }
192
+
193
+ module.exports = { scaffold };