buildflow-dev 1.0.2 → 1.0.3
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/package.json +1 -1
- package/src/commands/install.js +151 -79
- package/src/commands/status.js +36 -1
- package/src/commands/update.js +16 -4
- package/src/utils/checkVersion.js +72 -0
- package/src/utils/welcome.js +24 -1
- package/templates/CLAUDE.md +14 -0
package/package.json
CHANGED
package/src/commands/install.js
CHANGED
|
@@ -26,6 +26,9 @@ const TOOLS = {
|
|
|
26
26
|
return hasCli || hasClaudeDir
|
|
27
27
|
},
|
|
28
28
|
|
|
29
|
+
isInstalledLocal() { return existsSync(join(process.cwd(), '.claude', 'commands', 'buildflow-start.md')) },
|
|
30
|
+
isInstalledGlobal() { return existsSync(join(homedir(), '.claude', 'commands', 'buildflow-start.md')) },
|
|
31
|
+
|
|
29
32
|
installGlobal(commandFiles) {
|
|
30
33
|
const dir = join(homedir(), '.claude', 'commands')
|
|
31
34
|
mkdirSync(dir, { recursive: true })
|
|
@@ -59,6 +62,9 @@ const TOOLS = {
|
|
|
59
62
|
try { which.sync('gemini'); return true } catch { return false }
|
|
60
63
|
},
|
|
61
64
|
|
|
65
|
+
isInstalledLocal() { return existsSync(join(process.cwd(), '.gemini', 'commands', 'start.md')) },
|
|
66
|
+
isInstalledGlobal() { return existsSync(join(homedir(), '.gemini', 'commands', 'start.md')) },
|
|
67
|
+
|
|
62
68
|
installGlobal(commandFiles) {
|
|
63
69
|
const dir = join(homedir(), '.gemini', 'commands')
|
|
64
70
|
mkdirSync(dir, { recursive: true })
|
|
@@ -101,32 +107,35 @@ const TOOLS = {
|
|
|
101
107
|
try { which.sync('codex'); return true } catch { return false }
|
|
102
108
|
},
|
|
103
109
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
110
|
+
isInstalledLocal() { return existsSync(join(process.cwd(), '.codex', 'instructions', 'buildflow-start.md')) },
|
|
111
|
+
isInstalledGlobal() { return existsSync(join(homedir(), '.codex', 'instructions', 'buildflow-start.md')) },
|
|
112
|
+
|
|
113
|
+
installGlobal(commandFiles) {
|
|
114
|
+
const dir = join(homedir(), '.codex', 'instructions')
|
|
115
|
+
const skillsDir = join(homedir(), '.codex', 'skills')
|
|
116
|
+
mkdirSync(dir, { recursive: true })
|
|
117
|
+
for (const [name, content] of Object.entries(commandFiles)) {
|
|
118
|
+
writeFileSync(join(dir, `buildflow-${name}.md`), content)
|
|
119
|
+
writeCodexSkill(skillsDir, name, content)
|
|
120
|
+
}
|
|
121
|
+
patchAgentsMd(join(homedir(), '.codex', 'AGENTS.md'), 'global')
|
|
122
|
+
return `${dir} + ${skillsDir}`
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
installLocal(commandFiles) {
|
|
126
|
+
const dir = join(process.cwd(), '.codex', 'instructions')
|
|
127
|
+
const skillsDir = join(process.cwd(), '.codex', 'skills')
|
|
128
|
+
mkdirSync(dir, { recursive: true })
|
|
129
|
+
for (const [name, content] of Object.entries(commandFiles)) {
|
|
130
|
+
writeFileSync(join(dir, `buildflow-${name}.md`), content)
|
|
131
|
+
writeCodexSkill(skillsDir, name, content)
|
|
132
|
+
}
|
|
133
|
+
patchAgentsMd(join(process.cwd(), 'AGENTS.md'), 'local')
|
|
134
|
+
return `${dir} + ${skillsDir}`
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
triggerNote: 'In Codex CLI, use $buildflow-start or say "use buildflow-start". Slash menu commands are not exposed by Codex.',
|
|
138
|
+
},
|
|
130
139
|
|
|
131
140
|
cursor: {
|
|
132
141
|
id: 'cursor',
|
|
@@ -143,6 +152,9 @@ const TOOLS = {
|
|
|
143
152
|
return hasCursorApp || hasCursorDir
|
|
144
153
|
},
|
|
145
154
|
|
|
155
|
+
isInstalledLocal() { return existsSync(join(process.cwd(), '.cursor', 'rules', 'buildflow.mdc')) },
|
|
156
|
+
isInstalledGlobal() { return this.isInstalledLocal() },
|
|
157
|
+
|
|
146
158
|
installGlobal(commandFiles) {
|
|
147
159
|
return this.installLocal(commandFiles)
|
|
148
160
|
},
|
|
@@ -175,6 +187,12 @@ const TOOLS = {
|
|
|
175
187
|
)
|
|
176
188
|
},
|
|
177
189
|
|
|
190
|
+
isInstalledLocal() {
|
|
191
|
+
const p = join(process.cwd(), '.clinerules')
|
|
192
|
+
return existsSync(p) && readFileSync(p, 'utf8').includes('BuildFlow')
|
|
193
|
+
},
|
|
194
|
+
isInstalledGlobal() { return this.isInstalledLocal() },
|
|
195
|
+
|
|
178
196
|
installGlobal(commandFiles) {
|
|
179
197
|
return this.installLocal(commandFiles)
|
|
180
198
|
},
|
|
@@ -198,6 +216,9 @@ const TOOLS = {
|
|
|
198
216
|
return existsSync(join(homedir(), '.continue', 'config.json'))
|
|
199
217
|
},
|
|
200
218
|
|
|
219
|
+
isInstalledLocal() { return existsSync(join(process.cwd(), '.continue', 'buildflow', 'start.md')) },
|
|
220
|
+
isInstalledGlobal() { return existsSync(join(homedir(), '.continue', 'buildflow', 'start.md')) },
|
|
221
|
+
|
|
201
222
|
installGlobal(commandFiles) {
|
|
202
223
|
const dir = join(homedir(), '.continue', 'buildflow')
|
|
203
224
|
mkdirSync(dir, { recursive: true })
|
|
@@ -222,41 +243,57 @@ const TOOLS = {
|
|
|
222
243
|
|
|
223
244
|
}
|
|
224
245
|
|
|
246
|
+
const UPDATE_CHECK_INSTRUCTION = `
|
|
247
|
+
## BuildFlow Update Check (Run Every Session)
|
|
248
|
+
|
|
249
|
+
At the very start of every session, before anything else:
|
|
250
|
+
1. Check if \`.buildflow/core/UPDATE.md\` exists
|
|
251
|
+
2. If it does, show the user this notice:
|
|
252
|
+
> "BuildFlow update available — run \`npx buildflow-dev update\` in your terminal to upgrade."
|
|
253
|
+
Then show the contents of UPDATE.md so the user sees the version details.
|
|
254
|
+
3. If the file does not exist, proceed silently.
|
|
255
|
+
`
|
|
256
|
+
|
|
225
257
|
function geminiContextBlock(commandFiles) {
|
|
226
258
|
const commandList = Object.keys(commandFiles)
|
|
227
259
|
.map(name => `- \`/buildflow-${name}\`: see .gemini/commands/${name}.md`)
|
|
228
260
|
.join('\n')
|
|
229
|
-
return `## BuildFlow Commands
|
|
261
|
+
return `## BuildFlow Commands
|
|
262
|
+
|
|
263
|
+
When the user types a /buildflow-* command, load and execute the corresponding file from .gemini/commands/.
|
|
264
|
+
|
|
265
|
+
${commandList}
|
|
266
|
+
${UPDATE_CHECK_INSTRUCTION}`
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function patchAgentsMd(filePath, scope) {
|
|
270
|
+
const existing = existsSync(filePath) ? readFileSync(filePath, 'utf8') : ''
|
|
271
|
+
if (existing.includes('BuildFlow')) return
|
|
272
|
+
const dir = scope === 'global' ? '~/.codex/instructions/' : '.codex/instructions/'
|
|
273
|
+
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${UPDATE_CHECK_INSTRUCTION}`
|
|
274
|
+
writeFileSync(filePath, existing + block)
|
|
230
275
|
}
|
|
231
276
|
|
|
232
|
-
function
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
function extractFrontmatterValue(content, key) {
|
|
253
|
-
const match = content.match(new RegExp(`^${key}:\\s*(.+)$`, 'm'))
|
|
254
|
-
return match?.[1]?.trim().replace(/^["']|["']$/g, '')
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function escapeYamlString(value) {
|
|
258
|
-
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
|
|
259
|
-
}
|
|
277
|
+
function writeCodexSkill(skillsDir, name, commandContent) {
|
|
278
|
+
const skillName = `buildflow-${name}`
|
|
279
|
+
const skillDir = join(skillsDir, skillName)
|
|
280
|
+
mkdirSync(skillDir, { recursive: true })
|
|
281
|
+
writeFileSync(join(skillDir, 'SKILL.md'), codexSkillContent(skillName, commandContent))
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function codexSkillContent(skillName, commandContent) {
|
|
285
|
+
const description = extractFrontmatterValue(commandContent, 'description') || `Run ${skillName}`
|
|
286
|
+
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`
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function extractFrontmatterValue(content, key) {
|
|
290
|
+
const match = content.match(new RegExp(`^${key}:\\s*(.+)$`, 'm'))
|
|
291
|
+
return match?.[1]?.trim().replace(/^["']|["']$/g, '')
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function escapeYamlString(value) {
|
|
295
|
+
return value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')
|
|
296
|
+
}
|
|
260
297
|
|
|
261
298
|
function cursorRulesContent(commandFiles) {
|
|
262
299
|
const commandDescriptions = Object.keys(commandFiles)
|
|
@@ -271,7 +308,7 @@ alwaysApply: false
|
|
|
271
308
|
# BuildFlow v3.0
|
|
272
309
|
|
|
273
310
|
You are integrated with BuildFlow, an adaptive development orchestration system.
|
|
274
|
-
|
|
311
|
+
${UPDATE_CHECK_INSTRUCTION}
|
|
275
312
|
## Available Commands
|
|
276
313
|
|
|
277
314
|
When the user types @buildflow-<command> or references a buildflow command, execute the corresponding workflow:
|
|
@@ -308,7 +345,7 @@ function clineRulesContent(commandFiles) {
|
|
|
308
345
|
.map(name => `- /buildflow-${name}`)
|
|
309
346
|
.join('\n')
|
|
310
347
|
return `# BuildFlow v3.0 Rules for Cline
|
|
311
|
-
|
|
348
|
+
${UPDATE_CHECK_INSTRUCTION}
|
|
312
349
|
## Slash Commands
|
|
313
350
|
|
|
314
351
|
When the user types any of the following commands, load the corresponding instruction file from .buildflow/commands/:
|
|
@@ -384,6 +421,17 @@ function detectAppName() {
|
|
|
384
421
|
return process.cwd().split(/[/\\]/).pop() || 'my-project'
|
|
385
422
|
}
|
|
386
423
|
|
|
424
|
+
export function getToolStatus() {
|
|
425
|
+
return Object.values(TOOLS).map(tool => ({
|
|
426
|
+
id: tool.id,
|
|
427
|
+
name: tool.name,
|
|
428
|
+
icon: tool.icon,
|
|
429
|
+
detected: tool.detect(),
|
|
430
|
+
installedLocal: tool.isInstalledLocal(),
|
|
431
|
+
installedGlobal: tool.isInstalledGlobal(),
|
|
432
|
+
}))
|
|
433
|
+
}
|
|
434
|
+
|
|
387
435
|
function loadCommandTemplates() {
|
|
388
436
|
const templatesDir = join(__dirname, '../../templates/commands')
|
|
389
437
|
const commands = {}
|
|
@@ -418,17 +466,30 @@ export async function run(opts = {}) {
|
|
|
418
466
|
const detectedList = Object.entries(detected).filter(([, found]) => found)
|
|
419
467
|
const notFoundList = Object.entries(detected).filter(([, found]) => !found)
|
|
420
468
|
|
|
469
|
+
// Tools detected on the system but BuildFlow not yet installed into them
|
|
470
|
+
const newlyDetected = detectedList.filter(([id]) => {
|
|
471
|
+
const t = TOOLS[id]
|
|
472
|
+
return !t.isInstalledLocal() && !t.isInstalledGlobal()
|
|
473
|
+
})
|
|
474
|
+
|
|
421
475
|
if (detectedList.length > 0) {
|
|
422
476
|
console.log(chalk.green(' ✓ Detected on your system:'))
|
|
423
477
|
for (const [id] of detectedList) {
|
|
424
478
|
const t = TOOLS[id]
|
|
425
|
-
|
|
479
|
+
const installed = t.isInstalledLocal() || t.isInstalledGlobal()
|
|
480
|
+
const badge = installed ? chalk.dim(' (already installed)') : chalk.yellow(' (not yet installed)')
|
|
481
|
+
console.log(chalk.green(` ${t.icon} ${t.name}`) + badge)
|
|
426
482
|
}
|
|
427
483
|
} else {
|
|
428
484
|
console.log(chalk.yellow(' ⚠ No AI tools detected automatically.'))
|
|
429
485
|
console.log(chalk.dim(' You can still install for tools not on this list.\n'))
|
|
430
486
|
}
|
|
431
487
|
|
|
488
|
+
if (newlyDetected.length > 0) {
|
|
489
|
+
console.log(chalk.yellow(`\n ${newlyDetected.length} tool(s) detected but BuildFlow not installed yet.`))
|
|
490
|
+
console.log(chalk.dim(' They are pre-selected below.\n'))
|
|
491
|
+
}
|
|
492
|
+
|
|
432
493
|
if (notFoundList.length > 0) {
|
|
433
494
|
console.log(chalk.dim('\n Not detected (can still install):'))
|
|
434
495
|
for (const [id] of notFoundList) {
|
|
@@ -453,11 +514,19 @@ export async function run(opts = {}) {
|
|
|
453
514
|
return
|
|
454
515
|
}
|
|
455
516
|
} else {
|
|
456
|
-
const choices = Object.entries(TOOLS).map(([id, tool]) =>
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
517
|
+
const choices = Object.entries(TOOLS).map(([id, tool]) => {
|
|
518
|
+
const isInstalled = tool.isInstalledLocal() || tool.isInstalledGlobal()
|
|
519
|
+
const statusBadge = !detected[id]
|
|
520
|
+
? ''
|
|
521
|
+
: isInstalled
|
|
522
|
+
? chalk.dim(' (reinstall)')
|
|
523
|
+
: chalk.yellow(' ← new')
|
|
524
|
+
return {
|
|
525
|
+
name: id,
|
|
526
|
+
message: `${tool.icon} ${tool.name}${statusBadge}`,
|
|
527
|
+
hint: tool.description,
|
|
528
|
+
}
|
|
529
|
+
})
|
|
461
530
|
|
|
462
531
|
const { tools } = await prompt({
|
|
463
532
|
type: 'multiselect',
|
|
@@ -465,7 +534,10 @@ export async function run(opts = {}) {
|
|
|
465
534
|
message: 'Which AI tools do you want to install BuildFlow into?',
|
|
466
535
|
hint: '(Space to select, Enter to confirm)',
|
|
467
536
|
choices,
|
|
468
|
-
|
|
537
|
+
// Pre-select newly detected tools (detected but not yet installed)
|
|
538
|
+
initial: newlyDetected.length > 0
|
|
539
|
+
? newlyDetected.map(([id]) => id)
|
|
540
|
+
: detectedList.map(([id]) => id),
|
|
469
541
|
})
|
|
470
542
|
|
|
471
543
|
if (!tools || tools.length === 0) {
|
|
@@ -561,18 +633,18 @@ export async function run(opts = {}) {
|
|
|
561
633
|
}
|
|
562
634
|
}
|
|
563
635
|
|
|
564
|
-
if (succeeded.length > 0) {
|
|
565
|
-
console.log(chalk.bold('\n Next steps:\n'))
|
|
566
|
-
console.log(chalk.white(' 1. Open your AI tool in this project'))
|
|
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
|
-
}
|
|
576
|
-
console.log('')
|
|
577
|
-
}
|
|
578
|
-
}
|
|
636
|
+
if (succeeded.length > 0) {
|
|
637
|
+
console.log(chalk.bold('\n Next steps:\n'))
|
|
638
|
+
console.log(chalk.white(' 1. Open your AI tool in this project'))
|
|
639
|
+
if (succeeded.every(({ tool }) => tool.id === 'codex')) {
|
|
640
|
+
console.log(chalk.white(' 2. Restart Codex CLI so new skills are loaded'))
|
|
641
|
+
console.log(chalk.white(' 3. Start with: ') + chalk.cyan('$buildflow-start'))
|
|
642
|
+
console.log(chalk.white(' Or for existing projects: ') + chalk.cyan('$buildflow-onboard'))
|
|
643
|
+
} else {
|
|
644
|
+
console.log(chalk.white(' 2. Type "/" to see BuildFlow commands'))
|
|
645
|
+
console.log(chalk.white(' 3. Start with: ') + chalk.cyan('/buildflow-start'))
|
|
646
|
+
console.log(chalk.white(' Or for existing projects: ') + chalk.cyan('/buildflow-onboard'))
|
|
647
|
+
}
|
|
648
|
+
console.log('')
|
|
649
|
+
}
|
|
650
|
+
}
|
package/src/commands/status.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
import { existsSync, readFileSync, readdirSync, statSync } from 'fs'
|
|
3
3
|
import { join } from 'path'
|
|
4
|
+
import { getToolStatus } from './install.js'
|
|
4
5
|
|
|
5
6
|
export async function run(opts = {}) {
|
|
6
7
|
const base = join(process.cwd(), '.buildflow')
|
|
@@ -35,10 +36,44 @@ export async function run(opts = {}) {
|
|
|
35
36
|
onboarded === 'n/a' ? chalk.dim('N/A (greenfield)') :
|
|
36
37
|
chalk.yellow('No — run /buildflow-onboard')))
|
|
37
38
|
|
|
38
|
-
const hasDebt = debt.includes('## Active') && !debt.includes('
|
|
39
|
+
const hasDebt = debt.includes('## Active') && !debt.includes('*None')
|
|
39
40
|
console.log(chalk.dim(' Security debt: ') +
|
|
40
41
|
(hasDebt ? chalk.red('⚠ Issues pending — see .buildflow/security/DEBT.md') : chalk.green('Clean')))
|
|
41
42
|
|
|
43
|
+
// ── AI Tool Status ──────────────────────────────────────────────────────────
|
|
44
|
+
console.log(chalk.dim('\n AI Tools:\n'))
|
|
45
|
+
|
|
46
|
+
const tools = getToolStatus()
|
|
47
|
+
const uninstalled = []
|
|
48
|
+
|
|
49
|
+
for (const t of tools) {
|
|
50
|
+
if (!t.detected) {
|
|
51
|
+
console.log(chalk.dim(` ${t.icon} ${t.name}`) + chalk.dim(' — not installed on this machine'))
|
|
52
|
+
continue
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const local = t.installedLocal
|
|
56
|
+
const global = t.installedGlobal
|
|
57
|
+
|
|
58
|
+
if (local || global) {
|
|
59
|
+
const scope = local && global ? 'local + global' : local ? 'local' : 'global'
|
|
60
|
+
console.log(chalk.green(` ${t.icon} ${t.name}`) + chalk.dim(` ✓ BuildFlow installed (${scope})`))
|
|
61
|
+
} else {
|
|
62
|
+
console.log(chalk.yellow(` ${t.icon} ${t.name}`) + chalk.yellow(' ⚠ Detected but BuildFlow not installed'))
|
|
63
|
+
uninstalled.push(t)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (uninstalled.length > 0) {
|
|
68
|
+
console.log('')
|
|
69
|
+
console.log(chalk.yellow(' Some tools are missing BuildFlow commands.'))
|
|
70
|
+
console.log(chalk.dim(' Run one of:'))
|
|
71
|
+
for (const t of uninstalled) {
|
|
72
|
+
console.log(chalk.white(` buildflow install --tool ${t.id}`))
|
|
73
|
+
}
|
|
74
|
+
console.log(chalk.dim(' Or: buildflow install (interactive)'))
|
|
75
|
+
}
|
|
76
|
+
|
|
42
77
|
if (opts.verbose) {
|
|
43
78
|
console.log(chalk.dim('\n .buildflow/ structure:'))
|
|
44
79
|
const walk = (dir, prefix = ' ') => {
|
package/src/commands/update.js
CHANGED
|
@@ -4,6 +4,7 @@ import { existsSync, readFileSync } from 'fs'
|
|
|
4
4
|
import { join, dirname } from 'path'
|
|
5
5
|
import { fileURLToPath } from 'url'
|
|
6
6
|
import { run as runInstall } from './install.js'
|
|
7
|
+
import { checkVersion, clearUpdateNotice } from '../utils/checkVersion.js'
|
|
7
8
|
|
|
8
9
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
9
10
|
|
|
@@ -15,12 +16,20 @@ export async function run(opts = {}) {
|
|
|
15
16
|
return
|
|
16
17
|
}
|
|
17
18
|
|
|
19
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8'))
|
|
20
|
+
|
|
18
21
|
if (opts.check) {
|
|
19
22
|
console.log('\n' + chalk.bold.white(' BuildFlow Update Check\n'))
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
const sp = ora('Checking npm registry...').start()
|
|
24
|
+
const update = await checkVersion(pkg.version)
|
|
25
|
+
sp.stop()
|
|
26
|
+
|
|
27
|
+
if (update) {
|
|
28
|
+
console.log(chalk.yellow(` Update available: ${update.current} → ${update.latest}`))
|
|
29
|
+
console.log(chalk.white(' Run: npx buildflow-dev update\n'))
|
|
30
|
+
} else {
|
|
31
|
+
console.log(chalk.green(` ✓ Up to date (v${pkg.version})\n`))
|
|
32
|
+
}
|
|
24
33
|
return
|
|
25
34
|
}
|
|
26
35
|
|
|
@@ -30,6 +39,9 @@ export async function run(opts = {}) {
|
|
|
30
39
|
await new Promise(r => setTimeout(r, 400))
|
|
31
40
|
sp.succeed(chalk.green(' ✓ Command files updated'))
|
|
32
41
|
|
|
42
|
+
// Clear the update notice now that we've updated
|
|
43
|
+
clearUpdateNotice()
|
|
44
|
+
|
|
33
45
|
console.log('')
|
|
34
46
|
await runInstall({ yes: opts.yes })
|
|
35
47
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { existsSync, writeFileSync, unlinkSync, mkdirSync } from 'fs'
|
|
2
|
+
import { join } from 'path'
|
|
3
|
+
|
|
4
|
+
const REGISTRY_URL = 'https://registry.npmjs.org/buildflow-dev/latest'
|
|
5
|
+
const UPDATE_FILE = '.buildflow/core/UPDATE.md'
|
|
6
|
+
|
|
7
|
+
export async function checkVersion(currentVersion) {
|
|
8
|
+
try {
|
|
9
|
+
const res = await fetch(REGISTRY_URL, {
|
|
10
|
+
signal: AbortSignal.timeout(3000),
|
|
11
|
+
headers: { Accept: 'application/json' },
|
|
12
|
+
})
|
|
13
|
+
if (!res.ok) return null
|
|
14
|
+
|
|
15
|
+
const { version: latest } = await res.json()
|
|
16
|
+
const updatePath = join(process.cwd(), UPDATE_FILE)
|
|
17
|
+
|
|
18
|
+
if (latest === currentVersion) {
|
|
19
|
+
// Remove stale notice if we're already up to date
|
|
20
|
+
if (existsSync(updatePath)) unlinkSync(updatePath)
|
|
21
|
+
return null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Only write the file if .buildflow/ exists (i.e. project is initialized)
|
|
25
|
+
const coreDir = join(process.cwd(), '.buildflow', 'core')
|
|
26
|
+
if (!existsSync(coreDir)) return { current: currentVersion, latest }
|
|
27
|
+
|
|
28
|
+
mkdirSync(coreDir, { recursive: true })
|
|
29
|
+
writeFileSync(updatePath, buildUpdateNotice(currentVersion, latest))
|
|
30
|
+
|
|
31
|
+
return { current: currentVersion, latest }
|
|
32
|
+
} catch {
|
|
33
|
+
return null // Network timeout or offline — silently skip
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function clearUpdateNotice() {
|
|
38
|
+
const updatePath = join(process.cwd(), UPDATE_FILE)
|
|
39
|
+
if (existsSync(updatePath)) unlinkSync(updatePath)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function buildUpdateNotice(current, latest) {
|
|
43
|
+
return `# BuildFlow Update Available
|
|
44
|
+
|
|
45
|
+
> A new version of BuildFlow is available.
|
|
46
|
+
> Your AI tool detected this file at session start.
|
|
47
|
+
|
|
48
|
+
| | Version |
|
|
49
|
+
|---|---|
|
|
50
|
+
| **Installed** | ${current} |
|
|
51
|
+
| **Latest** | ${latest} |
|
|
52
|
+
|
|
53
|
+
## How to Update
|
|
54
|
+
|
|
55
|
+
Run this in your terminal:
|
|
56
|
+
|
|
57
|
+
\`\`\`bash
|
|
58
|
+
npx buildflow-dev update
|
|
59
|
+
\`\`\`
|
|
60
|
+
|
|
61
|
+
Or if installed globally:
|
|
62
|
+
|
|
63
|
+
\`\`\`bash
|
|
64
|
+
npm install -g buildflow-dev@latest
|
|
65
|
+
\`\`\`
|
|
66
|
+
|
|
67
|
+
After updating, this notice will disappear automatically.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
*Generated by BuildFlow CLI · ${new Date().toISOString().split('T')[0]}*
|
|
71
|
+
`
|
|
72
|
+
}
|
package/src/utils/welcome.js
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
import { existsSync, readFileSync } from 'fs'
|
|
3
|
-
import { join } from 'path'
|
|
3
|
+
import { join, dirname } from 'path'
|
|
4
|
+
import { fileURLToPath } from 'url'
|
|
5
|
+
import { checkVersion } from './checkVersion.js'
|
|
6
|
+
import { getToolStatus } from '../commands/install.js'
|
|
7
|
+
|
|
8
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
4
9
|
|
|
5
10
|
export async function showWelcome() {
|
|
11
|
+
const pkg = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8'))
|
|
12
|
+
// Fire-and-forget version check — writes UPDATE.md if behind, doesn't block output
|
|
13
|
+
checkVersion(pkg.version).then(update => {
|
|
14
|
+
if (update) {
|
|
15
|
+
console.log(chalk.yellow(`\n Update available: ${update.current} → ${update.latest}`))
|
|
16
|
+
console.log(chalk.dim(' Run: npx buildflow-dev update\n'))
|
|
17
|
+
}
|
|
18
|
+
}).catch(() => {})
|
|
6
19
|
const isInitialized = existsSync(join(process.cwd(), '.buildflow'))
|
|
7
20
|
|
|
8
21
|
console.log('\n' + chalk.bold.white(' BuildFlow v3.0'))
|
|
@@ -56,6 +69,16 @@ export async function showWelcome() {
|
|
|
56
69
|
console.log(chalk.white(' buildflow status ') + chalk.dim(' Show project status'))
|
|
57
70
|
console.log(chalk.white(' buildflow audit ') + chalk.dim(' Run security audit from terminal'))
|
|
58
71
|
console.log(chalk.white(' buildflow update ') + chalk.dim(' Update BuildFlow commands'))
|
|
72
|
+
|
|
73
|
+
// Surface any newly installed AI tools that don't have BuildFlow yet
|
|
74
|
+
const uninstalled = getToolStatus().filter(t => t.detected && !t.installedLocal && !t.installedGlobal)
|
|
75
|
+
if (uninstalled.length > 0) {
|
|
76
|
+
console.log('')
|
|
77
|
+
console.log(chalk.yellow(` New AI tool(s) detected — BuildFlow not installed yet:`))
|
|
78
|
+
for (const t of uninstalled) {
|
|
79
|
+
console.log(chalk.yellow(` ${t.icon} ${t.name}`) + chalk.dim(` → buildflow install --tool ${t.id}`))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
59
82
|
} else {
|
|
60
83
|
console.log(chalk.yellow(' Not initialized in this directory.\n'))
|
|
61
84
|
console.log(chalk.bold(' Get started:\n'))
|
package/templates/CLAUDE.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
This project uses **BuildFlow v3.0** for adaptive AI-powered development orchestration.
|
|
4
4
|
|
|
5
|
+
## Session Start Checklist (Run Every Time)
|
|
6
|
+
|
|
7
|
+
Before doing anything else at the start of every session:
|
|
8
|
+
|
|
9
|
+
1. **Check for updates** — if `.buildflow/core/UPDATE.md` exists, show the user this notice:
|
|
10
|
+
> "BuildFlow update available — run `npx buildflow-dev update` in your terminal."
|
|
11
|
+
Then display the contents of UPDATE.md so the user sees the version details.
|
|
12
|
+
If the file does not exist, proceed silently.
|
|
13
|
+
|
|
14
|
+
2. **Load memory** — read `.buildflow/memory/light.md` for project context
|
|
15
|
+
3. **Load state** — read `.buildflow/core/state.md` for current phase and status
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
5
19
|
## Quick Start
|
|
6
20
|
|
|
7
21
|
Type `/` in Claude Code to see available commands:
|