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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "buildflow-dev",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
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",
@@ -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
- installGlobal(commandFiles) {
105
- const dir = join(homedir(), '.codex', 'instructions')
106
- const skillsDir = join(homedir(), '.codex', 'skills')
107
- mkdirSync(dir, { recursive: true })
108
- for (const [name, content] of Object.entries(commandFiles)) {
109
- writeFileSync(join(dir, `buildflow-${name}.md`), content)
110
- writeCodexSkill(skillsDir, name, content)
111
- }
112
- patchAgentsMd(join(homedir(), '.codex', 'AGENTS.md'), 'global')
113
- return `${dir} + ${skillsDir}`
114
- },
115
-
116
- installLocal(commandFiles) {
117
- const dir = join(process.cwd(), '.codex', 'instructions')
118
- const skillsDir = join(process.cwd(), '.codex', 'skills')
119
- mkdirSync(dir, { recursive: true })
120
- for (const [name, content] of Object.entries(commandFiles)) {
121
- writeFileSync(join(dir, `buildflow-${name}.md`), content)
122
- writeCodexSkill(skillsDir, name, content)
123
- }
124
- patchAgentsMd(join(process.cwd(), 'AGENTS.md'), 'local')
125
- return `${dir} + ${skillsDir}`
126
- },
127
-
128
- triggerNote: 'In Codex CLI, use $buildflow-start or say "use buildflow-start". Slash menu commands are not exposed by Codex.',
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\n\nWhen the user types a /buildflow-* command, load and execute the corresponding file from .gemini/commands/.\n\n${commandList}`
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 patchAgentsMd(filePath, scope) {
233
- const existing = existsSync(filePath) ? readFileSync(filePath, 'utf8') : ''
234
- if (existing.includes('BuildFlow')) return
235
- const dir = scope === 'global' ? '~/.codex/instructions/' : '.codex/instructions/'
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`
237
- writeFileSync(filePath, existing + block)
238
- }
239
-
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))
245
- }
246
-
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`
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
- console.log(chalk.green(` ${t.icon} ${t.name}`) + chalk.dim(` ${t.description}`))
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
- name: id,
458
- message: `${tool.icon} ${tool.name}${detected[id] ? chalk.green(' ✓') : ''}`,
459
- hint: tool.description,
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
- initial: detectedList.map(([id]) => id),
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
+ }
@@ -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('[None]')
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 = ' ') => {
@@ -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 pkgPath = join(__dirname, '../../package.json')
21
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
22
- console.log(chalk.dim(` Local version: ${pkg.version}`))
23
- console.log(chalk.dim(' Run: npx buildflow-dev update to update commands\n'))
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
+ }
@@ -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'))
@@ -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: