buildflow-dev 1.0.0 → 1.0.1

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