buildflow-dev 1.0.4 → 1.0.6

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.4",
3
+ "version": "1.0.6",
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",
@@ -4,7 +4,7 @@ import enquirer from 'enquirer'
4
4
  import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs'
5
5
  import { join } from 'path'
6
6
  import { execSync } from 'child_process'
7
- import { run as runInstall } from './install.js'
7
+ import { run as runInstall, registerProject } from './install.js'
8
8
 
9
9
  const { prompt } = enquirer
10
10
 
@@ -587,6 +587,7 @@ export async function run(opts = {}) {
587
587
  scaffoldBuildflow(appName, { ...projectInfo, projectType })
588
588
  patchGitignore()
589
589
  ensureGit()
590
+ registerProject(process.cwd())
590
591
  await new Promise(r => setTimeout(r, 300))
591
592
  sp2.succeed(chalk.green(' ✓ .buildflow/ scaffold created'))
592
593
 
@@ -253,9 +253,9 @@ const UPDATE_CHECK_INSTRUCTION = `
253
253
  ## BuildFlow Update Check (Run Every Session)
254
254
 
255
255
  At the very start of every session, before anything else:
256
- 1. Check if \`.buildflow/core/UPDATE.md\` exists
257
- 2. If it does, show the user this notice:
258
- > "BuildFlow update available — run \`npx buildflow-dev update\` in your terminal to upgrade."
256
+ 1. Run this shell command silently: \`npx buildflow-dev@latest update --check\`
257
+ 2. If \`.buildflow/core/UPDATE.md\` exists after the check, notify the user:
258
+ > "BuildFlow update available — run \`npx buildflow-dev@latest update\` to upgrade."
259
259
  Then show the contents of UPDATE.md so the user sees the version details.
260
260
  3. If the file does not exist, proceed silently.
261
261
  `
@@ -413,6 +413,86 @@ function claudeMdContent() {
413
413
  return readFileSync(templatePath, 'utf8').replace('{{APP_NAME}}', detectAppName())
414
414
  }
415
415
 
416
+ // ── Global project registry ──────────────────────────────────────────────────
417
+ // Tracks all directories where `buildflow init` has been run so that
418
+ // `buildflow update` can refresh local command files in every project,
419
+ // not just the current working directory.
420
+
421
+ const REGISTRY_PATH = join(homedir(), '.buildflow', 'projects.json')
422
+
423
+ export function getProjectRegistry() {
424
+ try { return JSON.parse(readFileSync(REGISTRY_PATH, 'utf8')) } catch { return [] }
425
+ }
426
+
427
+ export function registerProject(projectPath) {
428
+ mkdirSync(join(homedir(), '.buildflow'), { recursive: true })
429
+ const projects = getProjectRegistry()
430
+ if (!projects.includes(projectPath)) {
431
+ projects.push(projectPath)
432
+ writeFileSync(REGISTRY_PATH, JSON.stringify(projects, null, 2))
433
+ }
434
+ }
435
+
436
+ // Refresh local command files inside a specific project directory.
437
+ // Called by refreshInstalledTools for every registered project.
438
+ function refreshProjectLocal(projectPath, commandFiles) {
439
+ let refreshed = false
440
+
441
+ const claudeDir = join(projectPath, '.claude', 'commands')
442
+ if (existsSync(claudeDir)) {
443
+ for (const [name, content] of Object.entries(commandFiles)) {
444
+ writeFileSync(join(claudeDir, `buildflow-${name}.md`), content)
445
+ }
446
+ const claudeMd = join(projectPath, 'CLAUDE.md')
447
+ if (existsSync(claudeMd)) {
448
+ writeFileSync(claudeMd, readFileSync(join(__dirname, '../../templates/CLAUDE.md'), 'utf8')
449
+ .replace('{{APP_NAME}}', projectPath.split(/[/\\]/).pop()))
450
+ }
451
+ refreshed = true
452
+ }
453
+
454
+ const geminiDir = join(projectPath, '.gemini', 'commands')
455
+ if (existsSync(geminiDir)) {
456
+ patchGeminiContext(join(projectPath, 'GEMINI.md'), commandFiles)
457
+ for (const [name, content] of Object.entries(commandFiles)) {
458
+ writeFileSync(join(geminiDir, `${name}.md`), content)
459
+ }
460
+ refreshed = true
461
+ }
462
+
463
+ const codexDir = join(projectPath, '.codex', 'instructions')
464
+ if (existsSync(codexDir)) {
465
+ for (const [name, content] of Object.entries(commandFiles)) {
466
+ writeFileSync(join(codexDir, `buildflow-${name}.md`), content)
467
+ writeCodexSkill(join(projectPath, '.codex', 'skills'), name, content)
468
+ }
469
+ patchAgentsMd(join(projectPath, 'AGENTS.md'), 'local')
470
+ refreshed = true
471
+ }
472
+
473
+ const cursorDir = join(projectPath, '.cursor', 'rules')
474
+ if (existsSync(join(cursorDir, 'buildflow.mdc'))) {
475
+ writeFileSync(join(cursorDir, 'buildflow.mdc'), cursorRulesContent(commandFiles))
476
+ refreshed = true
477
+ }
478
+
479
+ const clineRules = join(projectPath, '.clinerules')
480
+ if (readFileSafe(clineRules).includes('BuildFlow')) {
481
+ writeFileSync(clineRules, clineRulesContent(commandFiles))
482
+ refreshed = true
483
+ }
484
+
485
+ const continueDir = join(projectPath, '.continue', 'buildflow')
486
+ if (existsSync(continueDir)) {
487
+ for (const [name, content] of Object.entries(commandFiles)) {
488
+ writeFileSync(join(continueDir, `${name}.md`), content)
489
+ }
490
+ refreshed = true
491
+ }
492
+
493
+ return refreshed
494
+ }
495
+
416
496
  function readdirSafe(dir) {
417
497
  try {
418
498
  return readdirSync(dir)
@@ -467,9 +547,12 @@ export async function refreshInstalledTools(opts = {}) {
467
547
 
468
548
  const sp = ora(` ${tool.icon} Refreshing ${tool.name}...`).start()
469
549
  try {
550
+ // Always refresh every scope that has BuildFlow installed —
551
+ // local and global can both be stale after a version bump.
470
552
  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`))
553
+ if (global) tool.installGlobal(commandFiles)
554
+ const scope = local && global ? 'local + global' : local ? 'local' : 'global'
555
+ sp.succeed(chalk.green(` ${tool.icon} ${tool.name}`) + chalk.dim(` — ${commandCount} commands refreshed (${scope})`))
473
556
  results.push({ tool, success: true })
474
557
  } catch (err) {
475
558
  sp.fail(chalk.red(` ${tool.icon} ${tool.name} — ${err.message}`))
@@ -479,7 +562,7 @@ export async function refreshInstalledTools(opts = {}) {
479
562
 
480
563
  if (results.length === 0) {
481
564
  console.log(chalk.yellow(' No previously installed tools found.'))
482
- console.log(chalk.dim(' Run: buildflow install\n'))
565
+ console.log(chalk.dim(' Run: npx buildflow-dev install\n'))
483
566
  } else {
484
567
  const failed = results.filter(r => !r.success)
485
568
  if (failed.length > 0) {
@@ -490,6 +573,35 @@ export async function refreshInstalledTools(opts = {}) {
490
573
  }
491
574
  }
492
575
 
576
+ // Refresh local command files in every registered project
577
+ const projects = getProjectRegistry()
578
+ // Always include cwd if it's an initialized buildflow project
579
+ if (existsSync(join(process.cwd(), '.buildflow')) && !projects.includes(process.cwd())) {
580
+ projects.push(process.cwd())
581
+ }
582
+
583
+ const validProjects = projects.filter(p => existsSync(join(p, '.buildflow')))
584
+ if (validProjects.length > 0) {
585
+ console.log(chalk.dim(`\n Refreshing ${validProjects.length} registered project(s)...\n`))
586
+ for (const projectPath of validProjects) {
587
+ const name = projectPath.split(/[/\\]/).pop()
588
+ try {
589
+ const touched = refreshProjectLocal(projectPath, commandFiles)
590
+ if (touched) {
591
+ console.log(chalk.green(` ✓ ${name}`) + chalk.dim(` ${projectPath}`))
592
+ }
593
+ } catch (err) {
594
+ console.log(chalk.red(` ✗ ${name} — ${err.message}`))
595
+ }
596
+ }
597
+
598
+ // Prune stale entries from registry
599
+ const stale = projects.filter(p => !existsSync(join(p, '.buildflow')))
600
+ if (stale.length > 0) {
601
+ writeFileSync(REGISTRY_PATH, JSON.stringify(validProjects, null, 2))
602
+ }
603
+ }
604
+
493
605
  return results
494
606
  }
495
607
 
@@ -9,13 +9,6 @@ import { checkVersion, clearUpdateNotice } from '../utils/checkVersion.js'
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,6 +28,12 @@ export async function run(opts = {}) {
35
28
 
36
29
  console.log('\n' + chalk.bold.white(' Updating BuildFlow...\n'))
37
30
 
31
+ const inProject = existsSync(join(process.cwd(), '.buildflow'))
32
+ if (!inProject) {
33
+ console.log(chalk.dim(' Tip: run this from inside your project directory to also'))
34
+ console.log(chalk.dim(' refresh local command files (e.g. .claude/commands/).\n'))
35
+ }
36
+
38
37
  // Re-push latest command templates to all previously installed tools
39
38
  await refreshInstalledTools()
40
39
 
@@ -1,8 +1,23 @@
1
- import { existsSync, writeFileSync, unlinkSync, mkdirSync } from 'fs'
1
+ import { existsSync, writeFileSync, unlinkSync, mkdirSync, readFileSync } from 'fs'
2
2
  import { join } from 'path'
3
+ import { homedir } from 'os'
3
4
 
4
- const REGISTRY_URL = 'https://registry.npmjs.org/buildflow-dev/latest'
5
- const UPDATE_FILE = '.buildflow/core/UPDATE.md'
5
+ const REGISTRY_URL = 'https://registry.npmjs.org/buildflow-dev/latest'
6
+ const UPDATE_FILE = '.buildflow/core/UPDATE.md'
7
+ const REGISTRY_PATH = join(homedir(), '.buildflow', 'projects.json')
8
+
9
+ function getProjectRegistry() {
10
+ try { return JSON.parse(readFileSync(REGISTRY_PATH, 'utf8')) } catch { return [] }
11
+ }
12
+
13
+ function allProjectPaths() {
14
+ const projects = getProjectRegistry()
15
+ const cwd = process.cwd()
16
+ if (!projects.includes(cwd) && existsSync(join(cwd, '.buildflow'))) {
17
+ projects.push(cwd)
18
+ }
19
+ return projects.filter(p => existsSync(join(p, '.buildflow', 'core')))
20
+ }
6
21
 
7
22
  export async function checkVersion(currentVersion) {
8
23
  try {
@@ -13,20 +28,24 @@ export async function checkVersion(currentVersion) {
13
28
  if (!res.ok) return null
14
29
 
15
30
  const { version: latest } = await res.json()
16
- const updatePath = join(process.cwd(), UPDATE_FILE)
17
31
 
18
32
  if (latest === currentVersion) {
19
- // Remove stale notice if we're already up to date
20
- if (existsSync(updatePath)) unlinkSync(updatePath)
33
+ // Clear stale notices from all registered projects
34
+ for (const p of allProjectPaths()) {
35
+ const f = join(p, UPDATE_FILE)
36
+ if (existsSync(f)) unlinkSync(f)
37
+ }
21
38
  return null
22
39
  }
23
40
 
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))
41
+ // Write UPDATE.md to every registered project so their next AI session
42
+ // shows the update notice regardless of which project is opened.
43
+ const notice = buildUpdateNotice(currentVersion, latest)
44
+ for (const p of allProjectPaths()) {
45
+ try {
46
+ writeFileSync(join(p, UPDATE_FILE), notice)
47
+ } catch {}
48
+ }
30
49
 
31
50
  return { current: currentVersion, latest }
32
51
  } catch {
@@ -35,8 +54,13 @@ export async function checkVersion(currentVersion) {
35
54
  }
36
55
 
37
56
  export function clearUpdateNotice() {
38
- const updatePath = join(process.cwd(), UPDATE_FILE)
39
- if (existsSync(updatePath)) unlinkSync(updatePath)
57
+ // Clear from all registered projects, not just cwd
58
+ for (const p of allProjectPaths()) {
59
+ try {
60
+ const f = join(p, UPDATE_FILE)
61
+ if (existsSync(f)) unlinkSync(f)
62
+ } catch {}
63
+ }
40
64
  }
41
65
 
42
66
  function buildUpdateNotice(current, latest) {
@@ -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)
@@ -6,10 +6,14 @@ This project uses **BuildFlow v3.0** for adaptive AI-powered development orchest
6
6
 
7
7
  Before doing anything else at the start of every session:
8
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.
9
+ 1. **Check for updates** — run this shell command silently:
10
+ ```
11
+ npx buildflow-dev@latest update --check
12
+ ```
13
+ - If `.buildflow/core/UPDATE.md` exists after the check, show the user:
14
+ > "BuildFlow update available — run `npx buildflow-dev@latest update` in your terminal."
15
+ Then display the contents of UPDATE.md.
16
+ - If the file does not exist, proceed silently.
13
17
 
14
18
  2. **Load memory** — read `.buildflow/memory/light.md` for project context
15
19
  3. **Load state** — read `.buildflow/core/state.md` for current phase and status