buildflow-dev 1.0.0

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.
@@ -0,0 +1,239 @@
1
+ import chalk from 'chalk'
2
+ import ora from 'ora'
3
+ import { prompt } from 'enquirer'
4
+ import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs'
5
+ import { join } from 'path'
6
+ import { execSync } from 'child_process'
7
+ import { run as runInstall } from './install.js'
8
+
9
+ function detectProjectInfo() {
10
+ const cwd = process.cwd()
11
+ const info = {
12
+ appName: 'my-project',
13
+ projectType: 'greenfield',
14
+ framework: 'none',
15
+ hasGit: existsSync(join(cwd, '.git')),
16
+ hasTests: false,
17
+ hasSrc: existsSync(join(cwd, 'src')),
18
+ language: 'unknown',
19
+ }
20
+
21
+ const pkgPath = join(cwd, 'package.json')
22
+ if (existsSync(pkgPath)) {
23
+ try {
24
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'))
25
+ info.appName = pkg.name || cwd.split('/').pop()
26
+ info.language = 'javascript'
27
+ info.projectType = 'existing'
28
+
29
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies }
30
+ if (deps?.next) info.framework = 'Next.js'
31
+ else if (deps?.react) info.framework = 'React'
32
+ else if (deps?.vue) info.framework = 'Vue'
33
+ else if (deps?.svelte) info.framework = 'Svelte'
34
+ else if (deps?.express) info.framework = 'Express'
35
+ else if (deps?.fastify) info.framework = 'Fastify'
36
+ else if (deps?.nestjs) info.framework = 'NestJS'
37
+ else info.framework = 'Node.js'
38
+
39
+ info.hasTests = !!(deps?.jest || deps?.vitest || deps?.mocha || deps?.['@playwright/test'])
40
+ } catch {}
41
+ }
42
+
43
+ if (existsSync(join(cwd, 'requirements.txt')) || existsSync(join(cwd, 'pyproject.toml'))) {
44
+ info.language = 'python'
45
+ info.projectType = 'existing'
46
+ try {
47
+ const req = readFileSync(join(cwd, 'requirements.txt'), 'utf8').toLowerCase()
48
+ if (req.includes('django')) info.framework = 'Django'
49
+ else if (req.includes('fastapi')) info.framework = 'FastAPI'
50
+ else if (req.includes('flask')) info.framework = 'Flask'
51
+ else info.framework = 'Python'
52
+ } catch {}
53
+ info.hasTests = existsSync(join(cwd, 'tests')) || existsSync(join(cwd, 'test'))
54
+ }
55
+
56
+ if (existsSync(join(cwd, 'Cargo.toml'))) {
57
+ info.language = 'rust'
58
+ info.framework = 'Rust'
59
+ info.projectType = 'existing'
60
+ }
61
+
62
+ if (existsSync(join(cwd, 'go.mod'))) {
63
+ info.language = 'go'
64
+ info.framework = 'Go'
65
+ info.projectType = 'existing'
66
+ }
67
+
68
+ if (!existsSync(join(cwd, 'src')) && !existsSync(pkgPath) && !existsSync(join(cwd, 'requirements.txt'))) {
69
+ info.projectType = 'greenfield'
70
+ }
71
+
72
+ return info
73
+ }
74
+
75
+ function scaffoldBuildflow(appName, projectInfo) {
76
+ const base = join(process.cwd(), '.buildflow')
77
+
78
+ const dirs = [
79
+ 'core', 'you', 'memory', 'phases',
80
+ 'learnings', 'research', 'codebase',
81
+ 'security/reports', 'security/rules',
82
+ 'security/suppressions',
83
+ ]
84
+ for (const d of dirs) mkdirSync(join(base, d), { recursive: true })
85
+
86
+ const today = new Date().toISOString().split('T')[0]
87
+
88
+ writeFileSync(join(base, 'core', 'vision.md'),
89
+ projectInfo.projectType === 'existing'
90
+ ? `# Project Vision: ${appName}\n\n## Type\nExisting ${projectInfo.framework} project\n\n## Goals\n[Fill during /buildflow-start]\n\n---\nInitialized: ${today}\n`
91
+ : `# Project Vision: ${appName}\n\n## What I'm Building\n[Fill during /buildflow-start]\n\n---\nInitialized: ${today}\n`
92
+ )
93
+
94
+ writeFileSync(join(base, 'core', 'state.md'),
95
+ `# State\n\nProject: ${appName}\nType: ${projectInfo.projectType}\nFramework: ${projectInfo.framework}\nPhase: 0\nStatus: Initialized\nBuildFlow: 3.0\nDate: ${today}\n`
96
+ )
97
+
98
+ writeFileSync(join(base, 'you', 'preferences.md'),
99
+ `# Preferences\n\nexperience: junior\nproject_type: ${projectInfo.projectType}\nframework: ${projectInfo.framework}\n\nlearning:\n show_explanations: true\n confidence_calibration: true\n source_citations: true\n\nsafety:\n enable_undo: true\n restore_points: true\n\nmemory:\n type: light\n retention_days: 30\n\nparallel:\n enabled: true\n max_researchers: 3\n\nsecurity:\n pre_ship_gate: true\n auto_suggest_on_sensitive: true\n`
100
+ )
101
+
102
+ writeFileSync(join(base, 'memory', 'light.md'),
103
+ `# Light Memory\n\napp: ${appName}\ntype: ${projectInfo.projectType}\nframework: ${projectInfo.framework}\nphase: 0\nlast_session: ${today}\nbuildflow: 3.0\nonboarded: ${projectInfo.projectType === 'greenfield' ? 'n/a' : 'false'}\n\n## Style Fingerprint\n[Auto-populated after first build]\n\n## Recent Decisions\n[Auto-populated]\n`
104
+ )
105
+
106
+ writeFileSync(join(base, 'learnings', 'glossary.md'),
107
+ `# Glossary\n\n## context-rot\nAI quality degrades as conversation grows. Avoided by fresh agents.\n\n## confidence-calibration\nAsking 1-5 before locking decisions. Triggers explanations when low.\n\n## cartographer\nAgent that maps existing codebases for other agents to use.\n\n## surgeon\nAgent that makes precise modifications to existing code.\n\n## security-auditor\nAgent that runs OWASP Top 10 checks before shipping.\n`
108
+ )
109
+
110
+ writeFileSync(join(base, 'learnings', 'decisions.md'),
111
+ `# Decision Log\n\n## ${today} — Initial Setup\nDecision: Use BuildFlow v3.0\nType: ${projectInfo.projectType} (${projectInfo.framework})\nConfidence: 5/5\n`
112
+ )
113
+
114
+ writeFileSync(join(base, 'security', 'DEBT.md'),
115
+ `# Security Debt\n\nTrack deferred security issues.\n\n## Active\n[None]\n`
116
+ )
117
+
118
+ return base
119
+ }
120
+
121
+ function patchGitignore() {
122
+ const gitignorePath = join(process.cwd(), '.gitignore')
123
+ const entry = '\n# BuildFlow security reports (may contain sensitive findings)\n.buildflow/security/reports/\n'
124
+
125
+ if (existsSync(gitignorePath)) {
126
+ const existing = readFileSync(gitignorePath, 'utf8')
127
+ if (!existing.includes('.buildflow/security/reports')) {
128
+ writeFileSync(gitignorePath, existing + entry)
129
+ }
130
+ } else {
131
+ writeFileSync(gitignorePath, entry)
132
+ }
133
+ }
134
+
135
+ function ensureGit() {
136
+ if (!existsSync(join(process.cwd(), '.git'))) {
137
+ try {
138
+ execSync('git init -q', { cwd: process.cwd() })
139
+ return true
140
+ } catch {
141
+ return false
142
+ }
143
+ }
144
+ return false
145
+ }
146
+
147
+ export async function run(opts = {}) {
148
+ console.log('\n' + chalk.bold.white(' BuildFlow v3.0 — Project Setup') + '\n')
149
+
150
+ const spinner = ora('Analyzing project...').start()
151
+ const projectInfo = detectProjectInfo()
152
+ await new Promise(r => setTimeout(r, 500))
153
+ spinner.stop()
154
+
155
+ if (projectInfo.projectType === 'existing') {
156
+ console.log(chalk.green(` ✓ Detected: ${projectInfo.framework} project`))
157
+ console.log(chalk.dim(` Language: ${projectInfo.language}`))
158
+ console.log(chalk.dim(` Tests: ${projectInfo.hasTests ? 'Yes' : 'Not found'}`))
159
+ console.log(chalk.dim(` Git: ${projectInfo.hasGit ? 'Initialized' : 'Not found'}`))
160
+ } else {
161
+ console.log(chalk.cyan(' Starting fresh (greenfield project)'))
162
+ }
163
+ console.log('')
164
+
165
+ let appName = projectInfo.appName
166
+
167
+ if (!opts.yes) {
168
+ const { confirmedName } = await prompt({
169
+ type: 'input',
170
+ name: 'confirmedName',
171
+ message: 'App name:',
172
+ initial: appName,
173
+ })
174
+ appName = confirmedName || appName
175
+ }
176
+
177
+ let projectType = projectInfo.projectType
178
+ if (!opts.yes && !opts.greenfield && !opts.existing) {
179
+ const { type } = await prompt({
180
+ type: 'select',
181
+ name: 'type',
182
+ message: 'Project mode:',
183
+ choices: [
184
+ {
185
+ name: 'existing',
186
+ message: 'Existing codebase — Add BuildFlow to current code',
187
+ hint: 'Enables /buildflow-onboard, /buildflow-modify, /buildflow-refactor',
188
+ },
189
+ {
190
+ name: 'greenfield',
191
+ message: 'Greenfield — Starting from scratch',
192
+ hint: 'Enables /buildflow-start, full new project workflow',
193
+ },
194
+ ],
195
+ initial: projectType === 'existing' ? 0 : 1,
196
+ })
197
+ projectType = type
198
+ }
199
+
200
+ let wantSecurity = true
201
+ if (!opts.yes) {
202
+ const { security } = await prompt({
203
+ type: 'confirm',
204
+ name: 'security',
205
+ message: 'Enable security layer? (Recommended — OWASP Top 10 + pre-ship gate)',
206
+ initial: true,
207
+ })
208
+ wantSecurity = security
209
+ }
210
+
211
+ const sp2 = ora('Setting up .buildflow/ folder...').start()
212
+ scaffoldBuildflow(appName, { ...projectInfo, projectType })
213
+ patchGitignore()
214
+ ensureGit()
215
+ await new Promise(r => setTimeout(r, 300))
216
+ sp2.succeed(chalk.green(' ✓ .buildflow/ scaffold created'))
217
+
218
+ console.log('')
219
+ await runInstall({ ...opts })
220
+
221
+ console.log(chalk.bold.green('\n ✓ BuildFlow initialized!\n'))
222
+
223
+ if (projectType === 'existing') {
224
+ console.log(chalk.white(' Start here:'))
225
+ console.log(chalk.cyan(' /buildflow-onboard') + chalk.dim(' ← analyze your codebase (one-time)'))
226
+ console.log(chalk.cyan(' /buildflow-modify') + chalk.dim(' ← change existing code safely'))
227
+ } else {
228
+ console.log(chalk.white(' Start here:'))
229
+ console.log(chalk.cyan(' /buildflow-start') + chalk.dim(' ← begin your project'))
230
+ console.log(chalk.cyan(' /buildflow-think') + chalk.dim(' ← research and discuss'))
231
+ }
232
+
233
+ if (wantSecurity) {
234
+ console.log(chalk.dim('\n Security: Pre-ship gate enabled (/buildflow-ship auto-runs audit)'))
235
+ console.log(chalk.dim(' Manual audit: /buildflow-audit'))
236
+ }
237
+
238
+ console.log('')
239
+ }