buildflow-dev 1.0.5 → 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 +1 -1
- package/src/commands/init.js +2 -1
- package/src/commands/install.js +118 -6
- package/src/commands/update.js +7 -1
- package/src/utils/checkVersion.js +38 -14
- package/templates/CLAUDE.md +8 -4
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -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
|
|
package/src/commands/install.js
CHANGED
|
@@ -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.
|
|
257
|
-
2. If
|
|
258
|
-
> "BuildFlow update available — run \`npx buildflow-dev update\`
|
|
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
|
|
472
|
-
|
|
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
|
|
package/src/commands/update.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk'
|
|
2
2
|
import ora from 'ora'
|
|
3
|
-
import { readFileSync } from 'fs'
|
|
3
|
+
import { existsSync, readFileSync } from 'fs'
|
|
4
4
|
import { join, dirname } from 'path'
|
|
5
5
|
import { fileURLToPath } from 'url'
|
|
6
6
|
import { refreshInstalledTools } from './install.js'
|
|
@@ -28,6 +28,12 @@ export async function run(opts = {}) {
|
|
|
28
28
|
|
|
29
29
|
console.log('\n' + chalk.bold.white(' Updating BuildFlow...\n'))
|
|
30
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
|
+
|
|
31
37
|
// Re-push latest command templates to all previously installed tools
|
|
32
38
|
await refreshInstalledTools()
|
|
33
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
|
|
5
|
-
const UPDATE_FILE
|
|
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
|
-
//
|
|
20
|
-
|
|
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
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
39
|
-
|
|
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) {
|
package/templates/CLAUDE.md
CHANGED
|
@@ -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** —
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|