buildflow-dev 1.0.3 → 1.0.5
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 +93 -21
- package/src/commands/update.js +6 -15
- package/src/utils/welcome.js +4 -4
package/package.json
CHANGED
package/src/commands/install.js
CHANGED
|
@@ -59,20 +59,29 @@ const TOOLS = {
|
|
|
59
59
|
docsUrl: 'https://github.com/google-gemini/gemini-cli',
|
|
60
60
|
|
|
61
61
|
detect() {
|
|
62
|
-
try { which.sync('gemini'); return true } catch { return false }
|
|
62
|
+
const hasCli = (() => { try { which.sync('gemini'); return true } catch { return false } })()
|
|
63
|
+
// Also check config dir — binary may not be on PATH in all shells
|
|
64
|
+
const hasGeminiDir = existsSync(join(homedir(), '.gemini'))
|
|
65
|
+
return hasCli || hasGeminiDir
|
|
63
66
|
},
|
|
64
67
|
|
|
65
|
-
isInstalledLocal()
|
|
66
|
-
|
|
68
|
+
isInstalledLocal() {
|
|
69
|
+
const cmdFile = existsSync(join(process.cwd(), '.gemini', 'commands', 'start.md'))
|
|
70
|
+
const ctxFile = existsSync(join(process.cwd(), 'GEMINI.md')) &&
|
|
71
|
+
readFileSafe(join(process.cwd(), 'GEMINI.md')).includes('BuildFlow')
|
|
72
|
+
return cmdFile || ctxFile
|
|
73
|
+
},
|
|
74
|
+
isInstalledGlobal() {
|
|
75
|
+
const cmdFile = existsSync(join(homedir(), '.gemini', 'commands', 'start.md'))
|
|
76
|
+
const ctxFile = existsSync(join(homedir(), '.gemini', 'GEMINI.md')) &&
|
|
77
|
+
readFileSafe(join(homedir(), '.gemini', 'GEMINI.md')).includes('BuildFlow')
|
|
78
|
+
return cmdFile || ctxFile
|
|
79
|
+
},
|
|
67
80
|
|
|
68
81
|
installGlobal(commandFiles) {
|
|
69
82
|
const dir = join(homedir(), '.gemini', 'commands')
|
|
70
83
|
mkdirSync(dir, { recursive: true })
|
|
71
|
-
|
|
72
|
-
const existingContent = existsSync(contextPath) ? readFileSync(contextPath, 'utf8') : ''
|
|
73
|
-
if (!existingContent.includes('## BuildFlow Commands')) {
|
|
74
|
-
writeFileSync(contextPath, existingContent + '\n\n' + geminiContextBlock(commandFiles))
|
|
75
|
-
}
|
|
84
|
+
patchGeminiContext(join(homedir(), '.gemini', 'GEMINI.md'), commandFiles)
|
|
76
85
|
for (const [name, content] of Object.entries(commandFiles)) {
|
|
77
86
|
writeFileSync(join(dir, `${name}.md`), content)
|
|
78
87
|
}
|
|
@@ -82,18 +91,14 @@ const TOOLS = {
|
|
|
82
91
|
installLocal(commandFiles) {
|
|
83
92
|
const dir = join(process.cwd(), '.gemini', 'commands')
|
|
84
93
|
mkdirSync(dir, { recursive: true })
|
|
85
|
-
|
|
86
|
-
const existingContent = existsSync(contextPath) ? readFileSync(contextPath, 'utf8') : ''
|
|
87
|
-
if (!existingContent.includes('## BuildFlow Commands')) {
|
|
88
|
-
writeFileSync(contextPath, existingContent + '\n\n' + geminiContextBlock(commandFiles))
|
|
89
|
-
}
|
|
94
|
+
patchGeminiContext(join(process.cwd(), 'GEMINI.md'), commandFiles)
|
|
90
95
|
for (const [name, content] of Object.entries(commandFiles)) {
|
|
91
96
|
writeFileSync(join(dir, `${name}.md`), content)
|
|
92
97
|
}
|
|
93
98
|
return dir
|
|
94
99
|
},
|
|
95
100
|
|
|
96
|
-
triggerNote: 'In Gemini CLI, type "/" or
|
|
101
|
+
triggerNote: 'In Gemini CLI, type "/buildflow-start" or ask Gemini to run a buildflow command',
|
|
97
102
|
},
|
|
98
103
|
|
|
99
104
|
codex: {
|
|
@@ -104,7 +109,9 @@ const TOOLS = {
|
|
|
104
109
|
docsUrl: 'https://github.com/openai/codex',
|
|
105
110
|
|
|
106
111
|
detect() {
|
|
107
|
-
try { which.sync('codex'); return true } catch { return false }
|
|
112
|
+
const hasCli = (() => { try { which.sync('codex'); return true } catch { return false } })()
|
|
113
|
+
const hasCodexDir = existsSync(join(homedir(), '.codex'))
|
|
114
|
+
return hasCli || hasCodexDir
|
|
108
115
|
},
|
|
109
116
|
|
|
110
117
|
isInstalledLocal() { return existsSync(join(process.cwd(), '.codex', 'instructions', 'buildflow-start.md')) },
|
|
@@ -188,8 +195,7 @@ const TOOLS = {
|
|
|
188
195
|
},
|
|
189
196
|
|
|
190
197
|
isInstalledLocal() {
|
|
191
|
-
|
|
192
|
-
return existsSync(p) && readFileSync(p, 'utf8').includes('BuildFlow')
|
|
198
|
+
return readFileSafe(join(process.cwd(), '.clinerules')).includes('BuildFlow')
|
|
193
199
|
},
|
|
194
200
|
isInstalledGlobal() { return this.isInstalledLocal() },
|
|
195
201
|
|
|
@@ -267,11 +273,18 @@ ${UPDATE_CHECK_INSTRUCTION}`
|
|
|
267
273
|
}
|
|
268
274
|
|
|
269
275
|
function patchAgentsMd(filePath, scope) {
|
|
270
|
-
const existing =
|
|
271
|
-
if (existing.includes('BuildFlow')) return
|
|
276
|
+
const existing = readFileSafe(filePath)
|
|
272
277
|
const dir = scope === 'global' ? '~/.codex/instructions/' : '.codex/instructions/'
|
|
273
|
-
const block =
|
|
274
|
-
|
|
278
|
+
const block = `## 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}`
|
|
279
|
+
if (existing.includes('## BuildFlow Instructions')) {
|
|
280
|
+
const updated = existing.replace(
|
|
281
|
+
/## BuildFlow Instructions[\s\S]*?(?=\n## |\n# |$)/,
|
|
282
|
+
block
|
|
283
|
+
)
|
|
284
|
+
writeFileSync(filePath, updated)
|
|
285
|
+
} else {
|
|
286
|
+
writeFileSync(filePath, existing + (existing ? '\n\n' : '') + block)
|
|
287
|
+
}
|
|
275
288
|
}
|
|
276
289
|
|
|
277
290
|
function writeCodexSkill(skillsDir, name, commandContent) {
|
|
@@ -408,6 +421,27 @@ function readdirSafe(dir) {
|
|
|
408
421
|
}
|
|
409
422
|
}
|
|
410
423
|
|
|
424
|
+
function readFileSafe(filePath) {
|
|
425
|
+
try { return readFileSync(filePath, 'utf8') } catch { return '' }
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Writes or replaces the BuildFlow block in a GEMINI.md context file.
|
|
429
|
+
// Always overwrites the block so updates pick up new commands.
|
|
430
|
+
function patchGeminiContext(contextPath, commandFiles) {
|
|
431
|
+
const existing = readFileSafe(contextPath)
|
|
432
|
+
const block = geminiContextBlock(commandFiles)
|
|
433
|
+
if (existing.includes('## BuildFlow Commands')) {
|
|
434
|
+
// Replace old block — from the marker to the next top-level ## or end of file
|
|
435
|
+
const updated = existing.replace(
|
|
436
|
+
/## BuildFlow Commands[\s\S]*?(?=\n## |\n# |$)/,
|
|
437
|
+
block.trimStart()
|
|
438
|
+
)
|
|
439
|
+
writeFileSync(contextPath, updated)
|
|
440
|
+
} else {
|
|
441
|
+
writeFileSync(contextPath, existing + (existing ? '\n\n' : '') + block)
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
411
445
|
function detectAppName() {
|
|
412
446
|
const pkgPath = join(process.cwd(), 'package.json')
|
|
413
447
|
if (existsSync(pkgPath)) {
|
|
@@ -421,6 +455,44 @@ function detectAppName() {
|
|
|
421
455
|
return process.cwd().split(/[/\\]/).pop() || 'my-project'
|
|
422
456
|
}
|
|
423
457
|
|
|
458
|
+
export async function refreshInstalledTools(opts = {}) {
|
|
459
|
+
const commandFiles = loadCommandTemplates()
|
|
460
|
+
const commandCount = Object.keys(commandFiles).length
|
|
461
|
+
const results = []
|
|
462
|
+
|
|
463
|
+
for (const tool of Object.values(TOOLS)) {
|
|
464
|
+
const local = tool.isInstalledLocal()
|
|
465
|
+
const global = tool.isInstalledGlobal()
|
|
466
|
+
if (!local && !global) continue
|
|
467
|
+
|
|
468
|
+
const sp = ora(` ${tool.icon} Refreshing ${tool.name}...`).start()
|
|
469
|
+
try {
|
|
470
|
+
if (local) tool.installLocal(commandFiles)
|
|
471
|
+
if (global && !local) tool.installGlobal(commandFiles)
|
|
472
|
+
sp.succeed(chalk.green(` ${tool.icon} ${tool.name}`) + chalk.dim(` — ${commandCount} commands refreshed`))
|
|
473
|
+
results.push({ tool, success: true })
|
|
474
|
+
} catch (err) {
|
|
475
|
+
sp.fail(chalk.red(` ${tool.icon} ${tool.name} — ${err.message}`))
|
|
476
|
+
results.push({ tool, success: false, error: err })
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (results.length === 0) {
|
|
481
|
+
console.log(chalk.yellow(' No previously installed tools found.'))
|
|
482
|
+
console.log(chalk.dim(' Run: buildflow install\n'))
|
|
483
|
+
} else {
|
|
484
|
+
const failed = results.filter(r => !r.success)
|
|
485
|
+
if (failed.length > 0) {
|
|
486
|
+
console.log(chalk.red('\n Some tools failed to update:'))
|
|
487
|
+
for (const { tool, error } of failed) {
|
|
488
|
+
console.log(chalk.red(` ✗ ${tool.name}: ${error.message}`))
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return results
|
|
494
|
+
}
|
|
495
|
+
|
|
424
496
|
export function getToolStatus() {
|
|
425
497
|
return Object.values(TOOLS).map(tool => ({
|
|
426
498
|
id: tool.id,
|
package/src/commands/update.js
CHANGED
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
import ora from 'ora'
|
|
3
|
-
import {
|
|
3
|
+
import { readFileSync } from 'fs'
|
|
4
4
|
import { join, dirname } from 'path'
|
|
5
5
|
import { fileURLToPath } from 'url'
|
|
6
|
-
import {
|
|
6
|
+
import { refreshInstalledTools } from './install.js'
|
|
7
7
|
import { checkVersion, clearUpdateNotice } from '../utils/checkVersion.js'
|
|
8
8
|
|
|
9
9
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
10
10
|
|
|
11
11
|
export async function run(opts = {}) {
|
|
12
|
-
const base = join(process.cwd(), '.buildflow')
|
|
13
|
-
|
|
14
|
-
if (!existsSync(base)) {
|
|
15
|
-
console.log(chalk.yellow('\n BuildFlow not initialized. Run: npx buildflow-dev init\n'))
|
|
16
|
-
return
|
|
17
|
-
}
|
|
18
|
-
|
|
19
12
|
const pkg = JSON.parse(readFileSync(join(__dirname, '../../package.json'), 'utf8'))
|
|
20
13
|
|
|
21
14
|
if (opts.check) {
|
|
@@ -26,7 +19,7 @@ export async function run(opts = {}) {
|
|
|
26
19
|
|
|
27
20
|
if (update) {
|
|
28
21
|
console.log(chalk.yellow(` Update available: ${update.current} → ${update.latest}`))
|
|
29
|
-
console.log(chalk.white(' Run: npx buildflow-dev update\n'))
|
|
22
|
+
console.log(chalk.white(' Run: npx buildflow-dev@latest update\n'))
|
|
30
23
|
} else {
|
|
31
24
|
console.log(chalk.green(` ✓ Up to date (v${pkg.version})\n`))
|
|
32
25
|
}
|
|
@@ -35,13 +28,11 @@ export async function run(opts = {}) {
|
|
|
35
28
|
|
|
36
29
|
console.log('\n' + chalk.bold.white(' Updating BuildFlow...\n'))
|
|
37
30
|
|
|
38
|
-
|
|
39
|
-
await
|
|
40
|
-
sp.succeed(chalk.green(' ✓ Command files updated'))
|
|
31
|
+
// Re-push latest command templates to all previously installed tools
|
|
32
|
+
await refreshInstalledTools()
|
|
41
33
|
|
|
42
34
|
// Clear the update notice now that we've updated
|
|
43
35
|
clearUpdateNotice()
|
|
44
36
|
|
|
45
|
-
console.log(
|
|
46
|
-
await runInstall({ yes: opts.yes })
|
|
37
|
+
console.log(chalk.green(`\n ✓ BuildFlow updated to v${pkg.version}\n`))
|
|
47
38
|
}
|
package/src/utils/welcome.js
CHANGED
|
@@ -13,7 +13,7 @@ export async function showWelcome() {
|
|
|
13
13
|
checkVersion(pkg.version).then(update => {
|
|
14
14
|
if (update) {
|
|
15
15
|
console.log(chalk.yellow(`\n Update available: ${update.current} → ${update.latest}`))
|
|
16
|
-
console.log(chalk.dim(' Run: npx buildflow-dev update\n'))
|
|
16
|
+
console.log(chalk.dim(' Run: npx buildflow-dev@latest update\n'))
|
|
17
17
|
}
|
|
18
18
|
}).catch(() => {})
|
|
19
19
|
const isInitialized = existsSync(join(process.cwd(), '.buildflow'))
|
|
@@ -66,9 +66,9 @@ export async function showWelcome() {
|
|
|
66
66
|
console.log(chalk.cyan(' /buildflow-status ') + chalk.dim(' Where am I?'))
|
|
67
67
|
console.log('')
|
|
68
68
|
console.log(chalk.dim(' CLI commands:'))
|
|
69
|
-
console.log(chalk.white(' buildflow status ') + chalk.dim(' Show project status'))
|
|
70
|
-
console.log(chalk.white(' buildflow audit ') + chalk.dim(' Run security audit from terminal'))
|
|
71
|
-
console.log(chalk.white(' buildflow update ') + chalk.dim('
|
|
69
|
+
console.log(chalk.white(' npx buildflow-dev status ') + chalk.dim(' Show project status'))
|
|
70
|
+
console.log(chalk.white(' npx buildflow-dev audit ') + chalk.dim(' Run security audit from terminal'))
|
|
71
|
+
console.log(chalk.white(' npx buildflow-dev update ') + chalk.dim(' Refresh AI tool command files'))
|
|
72
72
|
|
|
73
73
|
// Surface any newly installed AI tools that don't have BuildFlow yet
|
|
74
74
|
const uninstalled = getToolStatus().filter(t => t.detected && !t.installedLocal && !t.installedGlobal)
|