buildflow-dev 1.0.0 → 1.0.2

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.
@@ -1,537 +1,578 @@
1
- import chalk from 'chalk'
2
- import ora from 'ora'
3
- import { prompt } from 'enquirer'
4
- import which from 'which'
5
- import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'fs'
6
- import { join, dirname } from 'path'
7
- import { homedir } from 'os'
8
- import { fileURLToPath } from 'url'
9
- import { execSync } from 'child_process'
10
-
11
- const __dirname = dirname(fileURLToPath(import.meta.url))
12
-
13
- const TOOLS = {
14
-
15
- claude: {
16
- id: 'claude',
17
- name: 'Claude Code',
18
- description: "Anthropic's agentic coding assistant",
19
- icon: '🟣',
20
- docsUrl: 'https://docs.anthropic.com/claude-code',
21
-
22
- detect() {
23
- const hasCli = (() => { try { which.sync('claude'); return true } catch { return false } })()
24
- const hasClaudeDir = existsSync(join(homedir(), '.claude'))
25
- return hasCli || hasClaudeDir
26
- },
27
-
28
- installGlobal(commandFiles) {
29
- const dir = join(homedir(), '.claude', 'commands')
30
- mkdirSync(dir, { recursive: true })
31
- for (const [name, content] of Object.entries(commandFiles)) {
32
- writeFileSync(join(dir, `buildflow-${name}.md`), content)
33
- }
34
- return dir
35
- },
36
-
37
- installLocal(commandFiles) {
38
- const dir = join(process.cwd(), '.claude', 'commands')
39
- mkdirSync(dir, { recursive: true })
40
- for (const [name, content] of Object.entries(commandFiles)) {
41
- writeFileSync(join(dir, `buildflow-${name}.md`), content)
42
- }
43
- writeFileSync(join(process.cwd(), 'CLAUDE.md'), claudeMdContent())
44
- return dir
45
- },
46
-
47
- triggerNote: 'Type "/" in Claude Code to see /buildflow-* commands',
48
- },
49
-
50
- gemini: {
51
- id: 'gemini',
52
- name: 'Gemini CLI',
53
- description: "Google's Gemini command-line AI assistant",
54
- icon: '🔵',
55
- docsUrl: 'https://github.com/google-gemini/gemini-cli',
56
-
57
- detect() {
58
- try { which.sync('gemini'); return true } catch { return false }
59
- },
60
-
61
- installGlobal(commandFiles) {
62
- const dir = join(homedir(), '.gemini', 'commands')
63
- mkdirSync(dir, { recursive: true })
64
- const contextPath = join(homedir(), '.gemini', 'GEMINI.md')
65
- const existingContent = existsSync(contextPath) ? readFileSync(contextPath, 'utf8') : ''
66
- if (!existingContent.includes('## BuildFlow Commands')) {
67
- writeFileSync(contextPath, existingContent + '\n\n' + geminiContextBlock(commandFiles))
68
- }
69
- for (const [name, content] of Object.entries(commandFiles)) {
70
- writeFileSync(join(dir, `${name}.md`), content)
71
- }
72
- return dir
73
- },
74
-
75
- installLocal(commandFiles) {
76
- const dir = join(process.cwd(), '.gemini', 'commands')
77
- mkdirSync(dir, { recursive: true })
78
- const contextPath = join(process.cwd(), 'GEMINI.md')
79
- const existingContent = existsSync(contextPath) ? readFileSync(contextPath, 'utf8') : ''
80
- if (!existingContent.includes('## BuildFlow Commands')) {
81
- writeFileSync(contextPath, existingContent + '\n\n' + geminiContextBlock(commandFiles))
82
- }
83
- for (const [name, content] of Object.entries(commandFiles)) {
84
- writeFileSync(join(dir, `${name}.md`), content)
85
- }
86
- return dir
87
- },
88
-
89
- triggerNote: 'In Gemini CLI, type "/" or @buildflow to use commands',
90
- },
91
-
92
- codex: {
93
- id: 'codex',
94
- name: 'Codex CLI',
95
- description: "OpenAI's Codex command-line coding agent",
96
- icon: '🟢',
97
- docsUrl: 'https://github.com/openai/codex',
98
-
99
- detect() {
100
- try { which.sync('codex'); return true } catch { return false }
101
- },
102
-
1
+ import chalk from 'chalk'
2
+ import ora from 'ora'
3
+ import enquirer from 'enquirer'
4
+ import which from 'which'
5
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'fs'
6
+ import { join, dirname } from 'path'
7
+ import { homedir } from 'os'
8
+ import { fileURLToPath } from 'url'
9
+ import { execSync } from 'child_process'
10
+
11
+ const __dirname = dirname(fileURLToPath(import.meta.url))
12
+ const { prompt } = enquirer
13
+
14
+ const TOOLS = {
15
+
16
+ claude: {
17
+ id: 'claude',
18
+ name: 'Claude Code',
19
+ description: "Anthropic's agentic coding assistant",
20
+ icon: '🟣',
21
+ docsUrl: 'https://docs.anthropic.com/claude-code',
22
+
23
+ detect() {
24
+ const hasCli = (() => { try { which.sync('claude'); return true } catch { return false } })()
25
+ const hasClaudeDir = existsSync(join(homedir(), '.claude'))
26
+ return hasCli || hasClaudeDir
27
+ },
28
+
29
+ installGlobal(commandFiles) {
30
+ const dir = join(homedir(), '.claude', 'commands')
31
+ mkdirSync(dir, { recursive: true })
32
+ for (const [name, content] of Object.entries(commandFiles)) {
33
+ writeFileSync(join(dir, `buildflow-${name}.md`), content)
34
+ }
35
+ return dir
36
+ },
37
+
38
+ installLocal(commandFiles) {
39
+ const dir = join(process.cwd(), '.claude', 'commands')
40
+ mkdirSync(dir, { recursive: true })
41
+ for (const [name, content] of Object.entries(commandFiles)) {
42
+ writeFileSync(join(dir, `buildflow-${name}.md`), content)
43
+ }
44
+ writeFileSync(join(process.cwd(), 'CLAUDE.md'), claudeMdContent())
45
+ return dir
46
+ },
47
+
48
+ triggerNote: 'Type "/" in Claude Code to see /buildflow-* commands',
49
+ },
50
+
51
+ gemini: {
52
+ id: 'gemini',
53
+ name: 'Gemini CLI',
54
+ description: "Google's Gemini command-line AI assistant",
55
+ icon: '🔵',
56
+ docsUrl: 'https://github.com/google-gemini/gemini-cli',
57
+
58
+ detect() {
59
+ try { which.sync('gemini'); return true } catch { return false }
60
+ },
61
+
62
+ installGlobal(commandFiles) {
63
+ const dir = join(homedir(), '.gemini', 'commands')
64
+ mkdirSync(dir, { recursive: true })
65
+ const contextPath = join(homedir(), '.gemini', 'GEMINI.md')
66
+ const existingContent = existsSync(contextPath) ? readFileSync(contextPath, 'utf8') : ''
67
+ if (!existingContent.includes('## BuildFlow Commands')) {
68
+ writeFileSync(contextPath, existingContent + '\n\n' + geminiContextBlock(commandFiles))
69
+ }
70
+ for (const [name, content] of Object.entries(commandFiles)) {
71
+ writeFileSync(join(dir, `${name}.md`), content)
72
+ }
73
+ return dir
74
+ },
75
+
76
+ installLocal(commandFiles) {
77
+ const dir = join(process.cwd(), '.gemini', 'commands')
78
+ mkdirSync(dir, { recursive: true })
79
+ const contextPath = join(process.cwd(), 'GEMINI.md')
80
+ const existingContent = existsSync(contextPath) ? readFileSync(contextPath, 'utf8') : ''
81
+ if (!existingContent.includes('## BuildFlow Commands')) {
82
+ writeFileSync(contextPath, existingContent + '\n\n' + geminiContextBlock(commandFiles))
83
+ }
84
+ for (const [name, content] of Object.entries(commandFiles)) {
85
+ writeFileSync(join(dir, `${name}.md`), content)
86
+ }
87
+ return dir
88
+ },
89
+
90
+ triggerNote: 'In Gemini CLI, type "/" or @buildflow to use commands',
91
+ },
92
+
93
+ codex: {
94
+ id: 'codex',
95
+ name: 'Codex CLI',
96
+ description: "OpenAI's Codex command-line coding agent",
97
+ icon: '🟢',
98
+ docsUrl: 'https://github.com/openai/codex',
99
+
100
+ detect() {
101
+ try { which.sync('codex'); return true } catch { return false }
102
+ },
103
+
103
104
  installGlobal(commandFiles) {
104
105
  const dir = join(homedir(), '.codex', 'instructions')
106
+ const skillsDir = join(homedir(), '.codex', 'skills')
105
107
  mkdirSync(dir, { recursive: true })
106
108
  for (const [name, content] of Object.entries(commandFiles)) {
107
109
  writeFileSync(join(dir, `buildflow-${name}.md`), content)
110
+ writeCodexSkill(skillsDir, name, content)
108
111
  }
109
112
  patchAgentsMd(join(homedir(), '.codex', 'AGENTS.md'), 'global')
110
- return dir
113
+ return `${dir} + ${skillsDir}`
111
114
  },
112
115
 
113
116
  installLocal(commandFiles) {
114
117
  const dir = join(process.cwd(), '.codex', 'instructions')
118
+ const skillsDir = join(process.cwd(), '.codex', 'skills')
115
119
  mkdirSync(dir, { recursive: true })
116
120
  for (const [name, content] of Object.entries(commandFiles)) {
117
121
  writeFileSync(join(dir, `buildflow-${name}.md`), content)
122
+ writeCodexSkill(skillsDir, name, content)
118
123
  }
119
124
  patchAgentsMd(join(process.cwd(), 'AGENTS.md'), 'local')
120
- return dir
121
- },
122
-
123
- triggerNote: 'In Codex CLI, say "use the buildflow-start instructions" or type /buildflow-start',
124
- },
125
-
126
- cursor: {
127
- id: 'cursor',
128
- name: 'Cursor',
129
- description: 'AI-powered code editor with built-in LLM',
130
- icon: '⚫',
131
- docsUrl: 'https://cursor.sh',
132
-
133
- detect() {
134
- const hasCursorApp = existsSync('/Applications/Cursor.app')
135
- || existsSync('C:\\Users\\Public\\Desktop\\Cursor.lnk')
136
- || (() => { try { which.sync('cursor'); return true } catch { return false } })()
137
- const hasCursorDir = existsSync(join(homedir(), '.cursor'))
138
- return hasCursorApp || hasCursorDir
139
- },
140
-
141
- installGlobal(commandFiles) {
142
- return this.installLocal(commandFiles)
143
- },
144
-
145
- installLocal(commandFiles) {
146
- const rulesDir = join(process.cwd(), '.cursor', 'rules')
147
- mkdirSync(rulesDir, { recursive: true })
148
- writeFileSync(join(rulesDir, 'buildflow.mdc'), cursorRulesContent(commandFiles))
149
- return rulesDir
150
- },
151
-
152
- triggerNote: 'In Cursor Chat, type @BuildFlow or reference rules using # in composer',
153
- },
154
-
155
- cline: {
156
- id: 'cline',
157
- name: 'Cline (VS Code extension)',
158
- description: 'Autonomous coding agent for VS Code',
159
- icon: '🔷',
160
- docsUrl: 'https://github.com/cline/cline',
161
-
162
- detect() {
163
- const extDirs = [
164
- join(homedir(), '.vscode', 'extensions'),
165
- join(homedir(), '.vscode-server', 'extensions'),
166
- ]
167
- return extDirs.some(d =>
168
- existsSync(d) &&
169
- readdirSafe(d).some(f => f.startsWith('saoudrizwan.claude-dev'))
170
- )
171
- },
172
-
173
- installGlobal(commandFiles) {
174
- return this.installLocal(commandFiles)
175
- },
176
-
177
- installLocal(commandFiles) {
178
- writeFileSync(join(process.cwd(), '.clinerules'), clineRulesContent(commandFiles))
179
- return process.cwd()
180
- },
181
-
182
- triggerNote: 'Cline reads .clinerules automatically. Type "use /buildflow-start" in Cline chat.',
183
- },
184
-
185
- continue: {
186
- id: 'continue',
187
- name: 'Continue (VS Code / JetBrains)',
188
- description: 'Open-source AI code assistant extension',
189
- icon: '🟡',
190
- docsUrl: 'https://continue.dev',
191
-
192
- detect() {
193
- return existsSync(join(homedir(), '.continue', 'config.json'))
194
- },
195
-
196
- installGlobal(commandFiles) {
197
- const dir = join(homedir(), '.continue', 'buildflow')
198
- mkdirSync(dir, { recursive: true })
199
- for (const [name, content] of Object.entries(commandFiles)) {
200
- writeFileSync(join(dir, `${name}.md`), content)
201
- }
202
- patchContinueConfig(commandFiles)
203
- return dir
204
- },
205
-
206
- installLocal(commandFiles) {
207
- const dir = join(process.cwd(), '.continue', 'buildflow')
208
- mkdirSync(dir, { recursive: true })
209
- for (const [name, content] of Object.entries(commandFiles)) {
210
- writeFileSync(join(dir, `${name}.md`), content)
211
- }
212
- return dir
125
+ return `${dir} + ${skillsDir}`
213
126
  },
214
127
 
215
- triggerNote: 'In Continue, use @BuildFlow in chat or trigger custom slash commands.',
128
+ triggerNote: 'In Codex CLI, use $buildflow-start or say "use buildflow-start". Slash menu commands are not exposed by Codex.',
216
129
  },
217
-
218
- }
219
-
220
- function geminiContextBlock(commandFiles) {
221
- const commandList = Object.keys(commandFiles)
222
- .map(name => `- \`/buildflow-${name}\`: see .gemini/commands/${name}.md`)
223
- .join('\n')
224
- return `## BuildFlow Commands\n\nWhen the user types a /buildflow-* command, load and execute the corresponding file from .gemini/commands/.\n\n${commandList}`
225
- }
226
-
130
+
131
+ cursor: {
132
+ id: 'cursor',
133
+ name: 'Cursor',
134
+ description: 'AI-powered code editor with built-in LLM',
135
+ icon: '⚫',
136
+ docsUrl: 'https://cursor.sh',
137
+
138
+ detect() {
139
+ const hasCursorApp = existsSync('/Applications/Cursor.app')
140
+ || existsSync('C:\\Users\\Public\\Desktop\\Cursor.lnk')
141
+ || (() => { try { which.sync('cursor'); return true } catch { return false } })()
142
+ const hasCursorDir = existsSync(join(homedir(), '.cursor'))
143
+ return hasCursorApp || hasCursorDir
144
+ },
145
+
146
+ installGlobal(commandFiles) {
147
+ return this.installLocal(commandFiles)
148
+ },
149
+
150
+ installLocal(commandFiles) {
151
+ const rulesDir = join(process.cwd(), '.cursor', 'rules')
152
+ mkdirSync(rulesDir, { recursive: true })
153
+ writeFileSync(join(rulesDir, 'buildflow.mdc'), cursorRulesContent(commandFiles))
154
+ return rulesDir
155
+ },
156
+
157
+ triggerNote: 'In Cursor Chat, type @BuildFlow or reference rules using # in composer',
158
+ },
159
+
160
+ cline: {
161
+ id: 'cline',
162
+ name: 'Cline (VS Code extension)',
163
+ description: 'Autonomous coding agent for VS Code',
164
+ icon: '🔷',
165
+ docsUrl: 'https://github.com/cline/cline',
166
+
167
+ detect() {
168
+ const extDirs = [
169
+ join(homedir(), '.vscode', 'extensions'),
170
+ join(homedir(), '.vscode-server', 'extensions'),
171
+ ]
172
+ return extDirs.some(d =>
173
+ existsSync(d) &&
174
+ readdirSafe(d).some(f => f.startsWith('saoudrizwan.claude-dev'))
175
+ )
176
+ },
177
+
178
+ installGlobal(commandFiles) {
179
+ return this.installLocal(commandFiles)
180
+ },
181
+
182
+ installLocal(commandFiles) {
183
+ writeFileSync(join(process.cwd(), '.clinerules'), clineRulesContent(commandFiles))
184
+ return process.cwd()
185
+ },
186
+
187
+ triggerNote: 'Cline reads .clinerules automatically. Type "use /buildflow-start" in Cline chat.',
188
+ },
189
+
190
+ continue: {
191
+ id: 'continue',
192
+ name: 'Continue (VS Code / JetBrains)',
193
+ description: 'Open-source AI code assistant extension',
194
+ icon: '🟡',
195
+ docsUrl: 'https://continue.dev',
196
+
197
+ detect() {
198
+ return existsSync(join(homedir(), '.continue', 'config.json'))
199
+ },
200
+
201
+ installGlobal(commandFiles) {
202
+ const dir = join(homedir(), '.continue', 'buildflow')
203
+ mkdirSync(dir, { recursive: true })
204
+ for (const [name, content] of Object.entries(commandFiles)) {
205
+ writeFileSync(join(dir, `${name}.md`), content)
206
+ }
207
+ patchContinueConfig(commandFiles)
208
+ return dir
209
+ },
210
+
211
+ installLocal(commandFiles) {
212
+ const dir = join(process.cwd(), '.continue', 'buildflow')
213
+ mkdirSync(dir, { recursive: true })
214
+ for (const [name, content] of Object.entries(commandFiles)) {
215
+ writeFileSync(join(dir, `${name}.md`), content)
216
+ }
217
+ return dir
218
+ },
219
+
220
+ triggerNote: 'In Continue, use @BuildFlow in chat or trigger custom slash commands.',
221
+ },
222
+
223
+ }
224
+
225
+ function geminiContextBlock(commandFiles) {
226
+ const commandList = Object.keys(commandFiles)
227
+ .map(name => `- \`/buildflow-${name}\`: see .gemini/commands/${name}.md`)
228
+ .join('\n')
229
+ return `## BuildFlow Commands\n\nWhen the user types a /buildflow-* command, load and execute the corresponding file from .gemini/commands/.\n\n${commandList}`
230
+ }
231
+
227
232
  function patchAgentsMd(filePath, scope) {
228
233
  const existing = existsSync(filePath) ? readFileSync(filePath, 'utf8') : ''
229
234
  if (existing.includes('BuildFlow')) return
230
235
  const dir = scope === 'global' ? '~/.codex/instructions/' : '.codex/instructions/'
231
- const block = `\n\n## BuildFlow Instructions\n\nWhen the user types /buildflow-<command>, load the matching file from ${dir} and follow those instructions.\n\nAvailable commands: start, think, plan, build, check, ship, onboard, modify, refactor, audit, status, explain, back, help\n`
236
+ const block = `\n\n## BuildFlow Instructions\n\nWhen the user types $buildflow-<command> or /buildflow-<command>, load the matching file from ${dir} and follow those instructions.\n\nAvailable commands: start, think, plan, build, check, ship, onboard, modify, refactor, audit, status, explain, back, help\n`
232
237
  writeFileSync(filePath, existing + block)
233
238
  }
234
239
 
235
- function cursorRulesContent(commandFiles) {
236
- const commandDescriptions = Object.keys(commandFiles)
237
- .map(name => `- @buildflow-${name}`)
238
- .join('\n')
239
- return `---
240
- description: BuildFlow development orchestration commands
241
- globs: ["**/*"]
242
- alwaysApply: false
243
- ---
244
-
245
- # BuildFlow v3.0
246
-
247
- You are integrated with BuildFlow, an adaptive development orchestration system.
248
-
249
- ## Available Commands
250
-
251
- When the user types @buildflow-<command> or references a buildflow command, execute the corresponding workflow:
252
-
253
- ${commandDescriptions}
254
-
255
- ## Core Rules
256
-
257
- 1. Load .buildflow/memory/light.md at session start
258
- 2. Ask confidence (1-5) on major decisions
259
- 3. Show alternatives before locking choices
260
- 4. Add LEARN: comments for new concepts
261
- 5. Create restore points before destructive changes
262
- 6. Run security checks before shipping
263
- 7. Cite sources with trust scores
264
-
265
- ## Agents
266
-
267
- Use these specialized agents based on context:
268
- - Strategist: vision and discussion
269
- - Researcher: parallel web research with sources
270
- - Synthesizer: combine parallel research
271
- - Architect: dependency-aware planning
272
- - Builder: code matching user's style
273
- - Reviewer: quality checks
274
- - Cartographer: map existing codebases (onboarding)
275
- - Surgeon: precise modifications to existing code
276
- - Security Auditor: OWASP Top 10 security scanning
277
- `
240
+ function writeCodexSkill(skillsDir, name, commandContent) {
241
+ const skillName = `buildflow-${name}`
242
+ const skillDir = join(skillsDir, skillName)
243
+ mkdirSync(skillDir, { recursive: true })
244
+ writeFileSync(join(skillDir, 'SKILL.md'), codexSkillContent(skillName, commandContent))
278
245
  }
279
246
 
280
- function clineRulesContent(commandFiles) {
281
- const commandList = Object.keys(commandFiles)
282
- .map(name => `- /buildflow-${name}`)
283
- .join('\n')
284
- return `# BuildFlow v3.0 Rules for Cline
285
-
286
- ## Slash Commands
287
-
288
- When the user types any of the following commands, load the corresponding instruction file from .buildflow/commands/:
289
-
290
- ${commandList}
291
-
292
- ## Core Behavior
293
-
294
- - Always load .buildflow/memory/light.md first
295
- - Ask confidence (1-5) on major architectural decisions
296
- - Show alternatives before making choices
297
- - Generate LEARN: comments for unfamiliar concepts
298
- - Run /buildflow-audit before shipping
299
- - Cite all research sources with trust scores (1-5)
300
- - Create git restore points before destructive operations
301
-
302
- ## Memory
303
-
304
- Light memory is stored in .buildflow/memory/light.md.
305
- Keep it under 5K tokens. Distill insights, don't log events.
306
- `
247
+ function codexSkillContent(skillName, commandContent) {
248
+ const description = extractFrontmatterValue(commandContent, 'description') || `Run ${skillName}`
249
+ return `---\nname: "${skillName}"\ndescription: "${escapeYamlString(description)}"\nmetadata:\n short-description: "${escapeYamlString(description)}"\n---\n\n<objective>\nExecute the BuildFlow workflow below end-to-end.\nTreat any user text after $${skillName} as arguments for this workflow.\n</objective>\n\n<workflow>\n${commandContent}\n</workflow>\n`
307
250
  }
308
251
 
309
- function patchContinueConfig(commandFiles) {
310
- const configPath = join(homedir(), '.continue', 'config.json')
311
- if (!existsSync(configPath)) return
312
-
313
- let config
314
- try {
315
- config = JSON.parse(readFileSync(configPath, 'utf8'))
316
- } catch {
317
- return
318
- }
319
-
320
- if (!config.slashCommands) config.slashCommands = []
321
-
322
- const existing = config.slashCommands.map(c => c.name)
323
- const toAdd = Object.keys(commandFiles)
324
- .filter(name => !existing.includes(`buildflow-${name}`))
325
- .map(name => ({
326
- name: `buildflow-${name}`,
327
- description: `BuildFlow: ${name}`,
328
- prompt: `Execute the BuildFlow ${name} workflow from .continue/buildflow/${name}.md`,
329
- }))
330
-
331
- config.slashCommands.push(...toAdd)
332
- writeFileSync(configPath, JSON.stringify(config, null, 2))
333
- }
334
-
335
- function claudeMdContent() {
336
- const templatePath = join(__dirname, '../../templates/CLAUDE.md')
337
- return readFileSync(templatePath, 'utf8').replace('{{APP_NAME}}', detectAppName())
338
- }
339
-
340
- function readdirSafe(dir) {
341
- try {
342
- return readdirSync(dir)
343
- } catch {
344
- return []
345
- }
252
+ function extractFrontmatterValue(content, key) {
253
+ const match = content.match(new RegExp(`^${key}:\\s*(.+)$`, 'm'))
254
+ return match?.[1]?.trim().replace(/^["']|["']$/g, '')
346
255
  }
347
256
 
348
- function detectAppName() {
349
- const pkgPath = join(process.cwd(), 'package.json')
350
- if (existsSync(pkgPath)) {
351
- try {
352
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
353
- return pkg.name || 'my-project'
354
- } catch {
355
- return 'my-project'
356
- }
357
- }
358
- return process.cwd().split(/[/\\]/).pop() || 'my-project'
359
- }
360
-
361
- function loadCommandTemplates() {
362
- const templatesDir = join(__dirname, '../../templates/commands')
363
- const commands = {}
364
- const commandNames = [
365
- 'start', 'think', 'plan', 'build', 'check', 'ship',
366
- 'onboard', 'modify', 'refactor', 'audit',
367
- 'status', 'explain', 'back', 'help',
368
- ]
369
- for (const name of commandNames) {
370
- const filePath = join(templatesDir, `${name}.md`)
371
- if (existsSync(filePath)) {
372
- commands[name] = readFileSync(filePath, 'utf8')
373
- }
374
- }
375
- return commands
257
+ function escapeYamlString(value) {
258
+ return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
376
259
  }
377
-
378
- export async function run(opts = {}) {
379
- console.log('\n' + chalk.bold.white(' BuildFlow — AI Tool Integration'))
380
- console.log(chalk.dim(' Install slash commands into your AI coding tools\n'))
381
-
382
- const spinner = ora('Detecting installed AI tools...').start()
383
- await new Promise(r => setTimeout(r, 600))
384
-
385
- const detected = {}
386
- for (const [id, tool] of Object.entries(TOOLS)) {
387
- detected[id] = tool.detect()
388
- }
389
-
390
- spinner.stop()
391
-
392
- const detectedList = Object.entries(detected).filter(([, found]) => found)
393
- const notFoundList = Object.entries(detected).filter(([, found]) => !found)
394
-
395
- if (detectedList.length > 0) {
396
- console.log(chalk.green(' ✓ Detected on your system:'))
397
- for (const [id] of detectedList) {
398
- const t = TOOLS[id]
399
- console.log(chalk.green(` ${t.icon} ${t.name}`) + chalk.dim(` — ${t.description}`))
400
- }
401
- } else {
402
- console.log(chalk.yellow(' ⚠ No AI tools detected automatically.'))
403
- console.log(chalk.dim(' You can still install for tools not on this list.\n'))
404
- }
405
-
406
- if (notFoundList.length > 0) {
407
- console.log(chalk.dim('\n Not detected (can still install):'))
408
- for (const [id] of notFoundList) {
409
- const t = TOOLS[id]
410
- console.log(chalk.dim(` ${t.icon} ${t.name}`))
411
- }
412
- }
413
-
414
- console.log('')
415
-
416
- let toolsToInstall
417
-
418
- if (opts.tool === 'all') {
419
- toolsToInstall = Object.keys(TOOLS)
420
- } else if (opts.tool) {
421
- toolsToInstall = [opts.tool]
422
- } else {
423
- const choices = Object.entries(TOOLS).map(([id, tool]) => ({
424
- name: id,
425
- message: `${tool.icon} ${tool.name}${detected[id] ? chalk.green(' ✓') : ''}`,
426
- hint: tool.description,
427
- }))
428
-
429
- const { tools } = await prompt({
430
- type: 'multiselect',
431
- name: 'tools',
432
- message: 'Which AI tools do you want to install BuildFlow into?',
433
- hint: '(Space to select, Enter to confirm)',
434
- choices,
435
- initial: detectedList.map(([id]) => id),
436
- })
437
-
438
- if (!tools || tools.length === 0) {
439
- console.log(chalk.yellow('\n Nothing selected. Exiting.\n'))
440
- return
441
- }
442
-
443
- toolsToInstall = tools
444
- }
445
-
446
- let scope
447
- if (opts.global) {
448
- scope = 'global'
449
- } else if (opts.local) {
450
- scope = 'local'
451
- } else {
452
- const { installScope } = await prompt({
453
- type: 'select',
454
- name: 'installScope',
455
- message: 'Install scope:',
456
- choices: [
457
- {
458
- name: 'local',
459
- message: 'Local — This project only',
460
- hint: 'Writes to .claude/commands/, .cursor/rules/, etc. in current directory',
461
- },
462
- {
463
- name: 'global',
464
- message: 'Global — All projects',
465
- hint: 'Writes to ~/.claude/commands/, ~/.gemini/, etc.',
466
- },
467
- {
468
- name: 'both',
469
- message: 'Both',
470
- hint: 'Global baseline + local overrides',
471
- },
472
- ],
473
- })
474
- scope = installScope
475
- }
476
-
477
- const commandFiles = loadCommandTemplates()
478
- const commandCount = Object.keys(commandFiles).length
479
-
480
- console.log(chalk.dim(`\n Installing ${commandCount} commands into ${toolsToInstall.length} tool(s)...\n`))
481
-
482
- const results = []
483
-
484
- for (const toolId of toolsToInstall) {
485
- const tool = TOOLS[toolId]
486
- if (!tool) {
487
- console.log(chalk.red(` ✗ Unknown tool: ${toolId}`))
488
- continue
489
- }
490
-
491
- const sp = ora(` ${tool.icon} Installing into ${tool.name}...`).start()
492
-
493
- try {
494
- let installDir
495
-
496
- if (scope === 'global' || scope === 'both') {
497
- installDir = tool.installGlobal(commandFiles)
498
- }
499
- if (scope === 'local' || scope === 'both') {
500
- installDir = tool.installLocal(commandFiles)
501
- }
502
-
503
- sp.succeed(chalk.green(` ${tool.icon} ${tool.name}`) + chalk.dim(` → ${installDir}`))
504
- results.push({ tool, success: true })
505
- } catch (err) {
506
- sp.fail(chalk.red(` ${tool.icon} ${tool.name} ${err.message}`))
507
- results.push({ tool, success: false, error: err })
508
- }
509
- }
510
-
511
- const succeeded = results.filter(r => r.success)
512
- const failed = results.filter(r => !r.success)
513
-
514
- console.log('\n' + chalk.bold.white(' ─── Installation Complete ───\n'))
515
-
516
- for (const { tool } of succeeded) {
517
- console.log(chalk.green(` ✓ ${tool.icon} ${tool.name}`))
518
- console.log(chalk.dim(` ${tool.triggerNote}`))
519
- console.log('')
520
- }
521
-
522
- if (failed.length > 0) {
523
- console.log(chalk.red('\n Failed:'))
524
- for (const { tool, error } of failed) {
525
- console.log(chalk.red(` ${tool.name}: ${error.message}`))
526
- }
527
- }
528
-
260
+
261
+ function cursorRulesContent(commandFiles) {
262
+ const commandDescriptions = Object.keys(commandFiles)
263
+ .map(name => `- @buildflow-${name}`)
264
+ .join('\n')
265
+ return `---
266
+ description: BuildFlow development orchestration commands
267
+ globs: ["**/*"]
268
+ alwaysApply: false
269
+ ---
270
+
271
+ # BuildFlow v3.0
272
+
273
+ You are integrated with BuildFlow, an adaptive development orchestration system.
274
+
275
+ ## Available Commands
276
+
277
+ When the user types @buildflow-<command> or references a buildflow command, execute the corresponding workflow:
278
+
279
+ ${commandDescriptions}
280
+
281
+ ## Core Rules
282
+
283
+ 1. Load .buildflow/memory/light.md at session start
284
+ 2. Ask confidence (1-5) on major decisions
285
+ 3. Show alternatives before locking choices
286
+ 4. Add LEARN: comments for new concepts
287
+ 5. Create restore points before destructive changes
288
+ 6. Run security checks before shipping
289
+ 7. Cite sources with trust scores
290
+
291
+ ## Agents
292
+
293
+ Use these specialized agents based on context:
294
+ - Strategist: vision and discussion
295
+ - Researcher: parallel web research with sources
296
+ - Synthesizer: combine parallel research
297
+ - Architect: dependency-aware planning
298
+ - Builder: code matching user's style
299
+ - Reviewer: quality checks
300
+ - Cartographer: map existing codebases (onboarding)
301
+ - Surgeon: precise modifications to existing code
302
+ - Security Auditor: OWASP Top 10 security scanning
303
+ `
304
+ }
305
+
306
+ function clineRulesContent(commandFiles) {
307
+ const commandList = Object.keys(commandFiles)
308
+ .map(name => `- /buildflow-${name}`)
309
+ .join('\n')
310
+ return `# BuildFlow v3.0 Rules for Cline
311
+
312
+ ## Slash Commands
313
+
314
+ When the user types any of the following commands, load the corresponding instruction file from .buildflow/commands/:
315
+
316
+ ${commandList}
317
+
318
+ ## Core Behavior
319
+
320
+ - Always load .buildflow/memory/light.md first
321
+ - Ask confidence (1-5) on major architectural decisions
322
+ - Show alternatives before making choices
323
+ - Generate LEARN: comments for unfamiliar concepts
324
+ - Run /buildflow-audit before shipping
325
+ - Cite all research sources with trust scores (1-5)
326
+ - Create git restore points before destructive operations
327
+
328
+ ## Memory
329
+
330
+ Light memory is stored in .buildflow/memory/light.md.
331
+ Keep it under 5K tokens. Distill insights, don't log events.
332
+ `
333
+ }
334
+
335
+ function patchContinueConfig(commandFiles) {
336
+ const configPath = join(homedir(), '.continue', 'config.json')
337
+ if (!existsSync(configPath)) return
338
+
339
+ let config
340
+ try {
341
+ config = JSON.parse(readFileSync(configPath, 'utf8'))
342
+ } catch {
343
+ return
344
+ }
345
+
346
+ if (!config.slashCommands) config.slashCommands = []
347
+
348
+ const existing = config.slashCommands.map(c => c.name)
349
+ const toAdd = Object.keys(commandFiles)
350
+ .filter(name => !existing.includes(`buildflow-${name}`))
351
+ .map(name => ({
352
+ name: `buildflow-${name}`,
353
+ description: `BuildFlow: ${name}`,
354
+ prompt: `Execute the BuildFlow ${name} workflow from .continue/buildflow/${name}.md`,
355
+ }))
356
+
357
+ config.slashCommands.push(...toAdd)
358
+ writeFileSync(configPath, JSON.stringify(config, null, 2))
359
+ }
360
+
361
+ function claudeMdContent() {
362
+ const templatePath = join(__dirname, '../../templates/CLAUDE.md')
363
+ return readFileSync(templatePath, 'utf8').replace('{{APP_NAME}}', detectAppName())
364
+ }
365
+
366
+ function readdirSafe(dir) {
367
+ try {
368
+ return readdirSync(dir)
369
+ } catch {
370
+ return []
371
+ }
372
+ }
373
+
374
+ function detectAppName() {
375
+ const pkgPath = join(process.cwd(), 'package.json')
376
+ if (existsSync(pkgPath)) {
377
+ try {
378
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
379
+ return pkg.name || 'my-project'
380
+ } catch {
381
+ return 'my-project'
382
+ }
383
+ }
384
+ return process.cwd().split(/[/\\]/).pop() || 'my-project'
385
+ }
386
+
387
+ function loadCommandTemplates() {
388
+ const templatesDir = join(__dirname, '../../templates/commands')
389
+ const commands = {}
390
+ const commandNames = [
391
+ 'start', 'think', 'plan', 'build', 'check', 'ship',
392
+ 'onboard', 'modify', 'refactor', 'audit',
393
+ 'status', 'explain', 'back', 'help',
394
+ ]
395
+ for (const name of commandNames) {
396
+ const filePath = join(templatesDir, `${name}.md`)
397
+ if (existsSync(filePath)) {
398
+ commands[name] = readFileSync(filePath, 'utf8')
399
+ }
400
+ }
401
+ return commands
402
+ }
403
+
404
+ export async function run(opts = {}) {
405
+ console.log('\n' + chalk.bold.white(' BuildFlow AI Tool Integration'))
406
+ console.log(chalk.dim(' Install slash commands into your AI coding tools\n'))
407
+
408
+ const spinner = ora('Detecting installed AI tools...').start()
409
+ await new Promise(r => setTimeout(r, 600))
410
+
411
+ const detected = {}
412
+ for (const [id, tool] of Object.entries(TOOLS)) {
413
+ detected[id] = tool.detect()
414
+ }
415
+
416
+ spinner.stop()
417
+
418
+ const detectedList = Object.entries(detected).filter(([, found]) => found)
419
+ const notFoundList = Object.entries(detected).filter(([, found]) => !found)
420
+
421
+ if (detectedList.length > 0) {
422
+ console.log(chalk.green(' ✓ Detected on your system:'))
423
+ for (const [id] of detectedList) {
424
+ const t = TOOLS[id]
425
+ console.log(chalk.green(` ${t.icon} ${t.name}`) + chalk.dim(` — ${t.description}`))
426
+ }
427
+ } else {
428
+ console.log(chalk.yellow(' ⚠ No AI tools detected automatically.'))
429
+ console.log(chalk.dim(' You can still install for tools not on this list.\n'))
430
+ }
431
+
432
+ if (notFoundList.length > 0) {
433
+ console.log(chalk.dim('\n Not detected (can still install):'))
434
+ for (const [id] of notFoundList) {
435
+ const t = TOOLS[id]
436
+ console.log(chalk.dim(` ${t.icon} ${t.name}`))
437
+ }
438
+ }
439
+
440
+ console.log('')
441
+
442
+ let toolsToInstall
443
+
444
+ if (opts.tool === 'all') {
445
+ toolsToInstall = Object.keys(TOOLS)
446
+ } else if (opts.tool) {
447
+ toolsToInstall = [opts.tool]
448
+ } else if (opts.yes) {
449
+ toolsToInstall = detectedList.map(([id]) => id)
450
+
451
+ if (toolsToInstall.length === 0) {
452
+ console.log(chalk.yellow('\n No AI tools detected automatically. Skipping tool installation.\n'))
453
+ return
454
+ }
455
+ } else {
456
+ const choices = Object.entries(TOOLS).map(([id, tool]) => ({
457
+ name: id,
458
+ message: `${tool.icon} ${tool.name}${detected[id] ? chalk.green(' ✓') : ''}`,
459
+ hint: tool.description,
460
+ }))
461
+
462
+ const { tools } = await prompt({
463
+ type: 'multiselect',
464
+ name: 'tools',
465
+ message: 'Which AI tools do you want to install BuildFlow into?',
466
+ hint: '(Space to select, Enter to confirm)',
467
+ choices,
468
+ initial: detectedList.map(([id]) => id),
469
+ })
470
+
471
+ if (!tools || tools.length === 0) {
472
+ console.log(chalk.yellow('\n Nothing selected. Exiting.\n'))
473
+ return
474
+ }
475
+
476
+ toolsToInstall = tools
477
+ }
478
+
479
+ let scope
480
+ if (opts.global) {
481
+ scope = 'global'
482
+ } else if (opts.local) {
483
+ scope = 'local'
484
+ } else if (opts.yes) {
485
+ scope = 'local'
486
+ } else {
487
+ const { installScope } = await prompt({
488
+ type: 'select',
489
+ name: 'installScope',
490
+ message: 'Install scope:',
491
+ choices: [
492
+ {
493
+ name: 'local',
494
+ message: 'Local — This project only',
495
+ hint: 'Writes to .claude/commands/, .cursor/rules/, etc. in current directory',
496
+ },
497
+ {
498
+ name: 'global',
499
+ message: 'Global — All projects',
500
+ hint: 'Writes to ~/.claude/commands/, ~/.gemini/, etc.',
501
+ },
502
+ {
503
+ name: 'both',
504
+ message: 'Both',
505
+ hint: 'Global baseline + local overrides',
506
+ },
507
+ ],
508
+ })
509
+ scope = installScope
510
+ }
511
+
512
+ const commandFiles = loadCommandTemplates()
513
+ const commandCount = Object.keys(commandFiles).length
514
+
515
+ console.log(chalk.dim(`\n Installing ${commandCount} commands into ${toolsToInstall.length} tool(s)...\n`))
516
+
517
+ const results = []
518
+
519
+ for (const toolId of toolsToInstall) {
520
+ const tool = TOOLS[toolId]
521
+ if (!tool) {
522
+ console.log(chalk.red(` ✗ Unknown tool: ${toolId}`))
523
+ continue
524
+ }
525
+
526
+ const sp = ora(` ${tool.icon} Installing into ${tool.name}...`).start()
527
+
528
+ try {
529
+ let installDir
530
+
531
+ if (scope === 'global' || scope === 'both') {
532
+ installDir = tool.installGlobal(commandFiles)
533
+ }
534
+ if (scope === 'local' || scope === 'both') {
535
+ installDir = tool.installLocal(commandFiles)
536
+ }
537
+
538
+ sp.succeed(chalk.green(` ${tool.icon} ${tool.name}`) + chalk.dim(` → ${installDir}`))
539
+ results.push({ tool, success: true })
540
+ } catch (err) {
541
+ sp.fail(chalk.red(` ${tool.icon} ${tool.name} — ${err.message}`))
542
+ results.push({ tool, success: false, error: err })
543
+ }
544
+ }
545
+
546
+ const succeeded = results.filter(r => r.success)
547
+ const failed = results.filter(r => !r.success)
548
+
549
+ console.log('\n' + chalk.bold.white(' ─── Installation Complete ───\n'))
550
+
551
+ for (const { tool } of succeeded) {
552
+ console.log(chalk.green(` ✓ ${tool.icon} ${tool.name}`))
553
+ console.log(chalk.dim(` ${tool.triggerNote}`))
554
+ console.log('')
555
+ }
556
+
557
+ if (failed.length > 0) {
558
+ console.log(chalk.red('\n Failed:'))
559
+ for (const { tool, error } of failed) {
560
+ console.log(chalk.red(` ✗ ${tool.name}: ${error.message}`))
561
+ }
562
+ }
563
+
529
564
  if (succeeded.length > 0) {
530
565
  console.log(chalk.bold('\n Next steps:\n'))
531
566
  console.log(chalk.white(' 1. Open your AI tool in this project'))
532
- console.log(chalk.white(' 2. Type "/" to see BuildFlow commands'))
533
- console.log(chalk.white(' 3. Start with: ') + chalk.cyan('/buildflow-start'))
534
- console.log(chalk.white(' Or for existing projects: ') + chalk.cyan('/buildflow-onboard'))
567
+ if (succeeded.every(({ tool }) => tool.id === 'codex')) {
568
+ console.log(chalk.white(' 2. Restart Codex CLI so new skills are loaded'))
569
+ console.log(chalk.white(' 3. Start with: ') + chalk.cyan('$buildflow-start'))
570
+ console.log(chalk.white(' Or for existing projects: ') + chalk.cyan('$buildflow-onboard'))
571
+ } else {
572
+ console.log(chalk.white(' 2. Type "/" to see BuildFlow commands'))
573
+ console.log(chalk.white(' 3. Start with: ') + chalk.cyan('/buildflow-start'))
574
+ console.log(chalk.white(' Or for existing projects: ') + chalk.cyan('/buildflow-onboard'))
575
+ }
535
576
  console.log('')
536
577
  }
537
578
  }