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.
- package/README.md +483 -157
- package/bin/buildflow.js +91 -80
- package/package.json +60 -60
- package/src/commands/fix.js +368 -0
- package/src/commands/init.js +614 -239
- package/src/commands/install.js +539 -498
- package/src/index.js +1 -0
package/src/commands/install.js
CHANGED
|
@@ -1,537 +1,578 @@
|
|
|
1
|
-
import chalk from 'chalk'
|
|
2
|
-
import ora from 'ora'
|
|
3
|
-
import
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
.
|
|
224
|
-
|
|
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
|
|
236
|
-
const
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
|
281
|
-
const
|
|
282
|
-
|
|
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
|
|
310
|
-
const
|
|
311
|
-
|
|
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
|
|
349
|
-
|
|
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
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
}
|