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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buildflow-dev",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Adaptive AI-powered development orchestration. Works with Claude Code, Gemini CLI, Codex CLI, Cursor, and more.",
5
5
  "keywords": [
6
6
  "ai",
@@ -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() { return existsSync(join(process.cwd(), '.gemini', 'commands', 'start.md')) },
66
- isInstalledGlobal() { return existsSync(join(homedir(), '.gemini', 'commands', 'start.md')) },
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
- const contextPath = join(homedir(), '.gemini', 'GEMINI.md')
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
- const contextPath = join(process.cwd(), 'GEMINI.md')
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 @buildflow to use commands',
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
- const p = join(process.cwd(), '.clinerules')
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 = existsSync(filePath) ? readFileSync(filePath, 'utf8') : ''
271
- if (existing.includes('BuildFlow')) return
276
+ const existing = readFileSafe(filePath)
272
277
  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)
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,
@@ -1,21 +1,14 @@
1
1
  import chalk from 'chalk'
2
2
  import ora from 'ora'
3
- import { existsSync, readFileSync } from 'fs'
3
+ import { readFileSync } from 'fs'
4
4
  import { join, dirname } from 'path'
5
5
  import { fileURLToPath } from 'url'
6
- import { run as runInstall } from './install.js'
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
- const sp = ora('Updating command files...').start()
39
- await new Promise(r => setTimeout(r, 400))
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
  }
@@ -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(' Update BuildFlow commands'))
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)