buildflow-dev 1.0.1 → 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.
@@ -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,27 +107,34 @@ const TOOLS = {
101
107
  try { which.sync('codex'); return true } catch { return false }
102
108
  },
103
109
 
110
+ isInstalledLocal() { return existsSync(join(process.cwd(), '.codex', 'instructions', 'buildflow-start.md')) },
111
+ isInstalledGlobal() { return existsSync(join(homedir(), '.codex', 'instructions', 'buildflow-start.md')) },
112
+
104
113
  installGlobal(commandFiles) {
105
114
  const dir = join(homedir(), '.codex', 'instructions')
115
+ const skillsDir = join(homedir(), '.codex', 'skills')
106
116
  mkdirSync(dir, { recursive: true })
107
117
  for (const [name, content] of Object.entries(commandFiles)) {
108
118
  writeFileSync(join(dir, `buildflow-${name}.md`), content)
119
+ writeCodexSkill(skillsDir, name, content)
109
120
  }
110
121
  patchAgentsMd(join(homedir(), '.codex', 'AGENTS.md'), 'global')
111
- return dir
122
+ return `${dir} + ${skillsDir}`
112
123
  },
113
124
 
114
125
  installLocal(commandFiles) {
115
126
  const dir = join(process.cwd(), '.codex', 'instructions')
127
+ const skillsDir = join(process.cwd(), '.codex', 'skills')
116
128
  mkdirSync(dir, { recursive: true })
117
129
  for (const [name, content] of Object.entries(commandFiles)) {
118
130
  writeFileSync(join(dir, `buildflow-${name}.md`), content)
131
+ writeCodexSkill(skillsDir, name, content)
119
132
  }
120
133
  patchAgentsMd(join(process.cwd(), 'AGENTS.md'), 'local')
121
- return dir
134
+ return `${dir} + ${skillsDir}`
122
135
  },
123
136
 
124
- triggerNote: 'In Codex CLI, say "use the buildflow-start instructions" or type /buildflow-start',
137
+ triggerNote: 'In Codex CLI, use $buildflow-start or say "use buildflow-start". Slash menu commands are not exposed by Codex.',
125
138
  },
126
139
 
127
140
  cursor: {
@@ -139,6 +152,9 @@ const TOOLS = {
139
152
  return hasCursorApp || hasCursorDir
140
153
  },
141
154
 
155
+ isInstalledLocal() { return existsSync(join(process.cwd(), '.cursor', 'rules', 'buildflow.mdc')) },
156
+ isInstalledGlobal() { return this.isInstalledLocal() },
157
+
142
158
  installGlobal(commandFiles) {
143
159
  return this.installLocal(commandFiles)
144
160
  },
@@ -171,6 +187,12 @@ const TOOLS = {
171
187
  )
172
188
  },
173
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
+
174
196
  installGlobal(commandFiles) {
175
197
  return this.installLocal(commandFiles)
176
198
  },
@@ -194,6 +216,9 @@ const TOOLS = {
194
216
  return existsSync(join(homedir(), '.continue', 'config.json'))
195
217
  },
196
218
 
219
+ isInstalledLocal() { return existsSync(join(process.cwd(), '.continue', 'buildflow', 'start.md')) },
220
+ isInstalledGlobal() { return existsSync(join(homedir(), '.continue', 'buildflow', 'start.md')) },
221
+
197
222
  installGlobal(commandFiles) {
198
223
  const dir = join(homedir(), '.continue', 'buildflow')
199
224
  mkdirSync(dir, { recursive: true })
@@ -218,21 +243,58 @@ const TOOLS = {
218
243
 
219
244
  }
220
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
+
221
257
  function geminiContextBlock(commandFiles) {
222
258
  const commandList = Object.keys(commandFiles)
223
259
  .map(name => `- \`/buildflow-${name}\`: see .gemini/commands/${name}.md`)
224
260
  .join('\n')
225
- 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}`
226
267
  }
227
268
 
228
269
  function patchAgentsMd(filePath, scope) {
229
270
  const existing = existsSync(filePath) ? readFileSync(filePath, 'utf8') : ''
230
271
  if (existing.includes('BuildFlow')) return
231
272
  const dir = scope === 'global' ? '~/.codex/instructions/' : '.codex/instructions/'
232
- const block = `\n\n## BuildFlow Instructions\n\nWhen the user types /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`
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}`
233
274
  writeFileSync(filePath, existing + block)
234
275
  }
235
276
 
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
+ }
297
+
236
298
  function cursorRulesContent(commandFiles) {
237
299
  const commandDescriptions = Object.keys(commandFiles)
238
300
  .map(name => `- @buildflow-${name}`)
@@ -246,7 +308,7 @@ alwaysApply: false
246
308
  # BuildFlow v3.0
247
309
 
248
310
  You are integrated with BuildFlow, an adaptive development orchestration system.
249
-
311
+ ${UPDATE_CHECK_INSTRUCTION}
250
312
  ## Available Commands
251
313
 
252
314
  When the user types @buildflow-<command> or references a buildflow command, execute the corresponding workflow:
@@ -283,7 +345,7 @@ function clineRulesContent(commandFiles) {
283
345
  .map(name => `- /buildflow-${name}`)
284
346
  .join('\n')
285
347
  return `# BuildFlow v3.0 Rules for Cline
286
-
348
+ ${UPDATE_CHECK_INSTRUCTION}
287
349
  ## Slash Commands
288
350
 
289
351
  When the user types any of the following commands, load the corresponding instruction file from .buildflow/commands/:
@@ -359,6 +421,17 @@ function detectAppName() {
359
421
  return process.cwd().split(/[/\\]/).pop() || 'my-project'
360
422
  }
361
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
+
362
435
  function loadCommandTemplates() {
363
436
  const templatesDir = join(__dirname, '../../templates/commands')
364
437
  const commands = {}
@@ -393,17 +466,30 @@ export async function run(opts = {}) {
393
466
  const detectedList = Object.entries(detected).filter(([, found]) => found)
394
467
  const notFoundList = Object.entries(detected).filter(([, found]) => !found)
395
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
+
396
475
  if (detectedList.length > 0) {
397
476
  console.log(chalk.green(' ✓ Detected on your system:'))
398
477
  for (const [id] of detectedList) {
399
478
  const t = TOOLS[id]
400
- 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)
401
482
  }
402
483
  } else {
403
484
  console.log(chalk.yellow(' ⚠ No AI tools detected automatically.'))
404
485
  console.log(chalk.dim(' You can still install for tools not on this list.\n'))
405
486
  }
406
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
+
407
493
  if (notFoundList.length > 0) {
408
494
  console.log(chalk.dim('\n Not detected (can still install):'))
409
495
  for (const [id] of notFoundList) {
@@ -428,11 +514,19 @@ export async function run(opts = {}) {
428
514
  return
429
515
  }
430
516
  } else {
431
- const choices = Object.entries(TOOLS).map(([id, tool]) => ({
432
- name: id,
433
- message: `${tool.icon} ${tool.name}${detected[id] ? chalk.green(' ✓') : ''}`,
434
- hint: tool.description,
435
- }))
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
+ })
436
530
 
437
531
  const { tools } = await prompt({
438
532
  type: 'multiselect',
@@ -440,7 +534,10 @@ export async function run(opts = {}) {
440
534
  message: 'Which AI tools do you want to install BuildFlow into?',
441
535
  hint: '(Space to select, Enter to confirm)',
442
536
  choices,
443
- 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),
444
541
  })
445
542
 
446
543
  if (!tools || tools.length === 0) {
@@ -539,9 +636,15 @@ export async function run(opts = {}) {
539
636
  if (succeeded.length > 0) {
540
637
  console.log(chalk.bold('\n Next steps:\n'))
541
638
  console.log(chalk.white(' 1. Open your AI tool in this project'))
542
- console.log(chalk.white(' 2. Type "/" to see BuildFlow commands'))
543
- console.log(chalk.white(' 3. Start with: ') + chalk.cyan('/buildflow-start'))
544
- console.log(chalk.white(' Or for existing projects: ') + chalk.cyan('/buildflow-onboard'))
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
+ }
545
648
  console.log('')
546
649
  }
547
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
  }
package/src/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export { run as init } from './commands/init.js'
2
2
  export { run as install } from './commands/install.js'
3
3
  export { run as audit } from './commands/audit.js'
4
+ export { run as fix } from './commands/fix.js'
4
5
  export { run as status } from './commands/status.js'
5
6
  export { run as update } from './commands/update.js'
@@ -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: