start-vibing-stacks 2.2.0 → 2.4.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.
package/README.md CHANGED
@@ -20,7 +20,7 @@ svs # shortcut
20
20
  ## ✨ Features
21
21
 
22
22
  - **🔍 Auto-detection** — Scans your project files to suggest the right stack
23
- - **🐘 PHP 8.3+** — PHPStan, PHPUnit, Composer, Laravel/Octane, Symfony, Vanilla
23
+ - **🐘 PHP 8.3+** — PHPStan, PHPUnit, Composer, Laravel/Octane
24
24
  - **📦 Node.js/TS** — Vitest, TypeScript, Bun, Next.js, Express
25
25
  - **🎯 Multi-framework** — Choose your framework, database, frontend, deploy target
26
26
  - **🤖 6 Universal Agents** — research, documenter, domain-updater, commit-manager, tester, compactor
@@ -33,7 +33,7 @@ svs # shortcut
33
33
 
34
34
  | Stack | Status | Frameworks |
35
35
  |-------|--------|------------|
36
- | 🐘 PHP 8.3+ | ✅ Ready | Laravel, Laravel+Octane, Symfony, CodeIgniter, Vanilla |
36
+ | 🐘 PHP 8.3+ | ✅ Ready | Laravel, Laravel+Octane |
37
37
  | 📦 Node.js/TS | ✅ Ready | Next.js, Nuxt, Astro, Express, Fastify |
38
38
  | 🐍 Python | 🔜 Soon | Django, FastAPI, Flask |
39
39
  | 🦀 Rust | 🔜 Soon | Actix, Axum |
package/dist/detector.js CHANGED
@@ -11,7 +11,6 @@ const STACK_SIGNATURES = [
11
11
  { id: 'php', files: ['composer.json'], weight: 90, reason: 'composer.json detected' },
12
12
  { id: 'php', files: ['index.php'], weight: 60, reason: 'index.php detected' },
13
13
  { id: 'php', files: ['artisan'], weight: 95, reason: 'Laravel artisan detected' },
14
- { id: 'php', files: ['symfony.lock'], weight: 95, reason: 'Symfony lock detected' },
15
14
  { id: 'php', files: ['public/index.php'], weight: 70, reason: 'public/index.php detected' },
16
15
  { id: 'php', files: ['.php-version'], weight: 80, reason: '.php-version detected' },
17
16
  // Node.js / TypeScript
@@ -26,10 +25,6 @@ const STACK_SIGNATURES = [
26
25
  { id: 'python', files: ['pyproject.toml'], weight: 90, reason: 'pyproject.toml detected' },
27
26
  { id: 'python', files: ['Pipfile'], weight: 85, reason: 'Pipfile detected' },
28
27
  { id: 'python', files: ['setup.py'], weight: 75, reason: 'setup.py detected' },
29
- // Rust
30
- { id: 'rust', files: ['Cargo.toml'], weight: 95, reason: 'Cargo.toml detected' },
31
- // Go
32
- { id: 'go', files: ['go.mod'], weight: 95, reason: 'go.mod detected' },
33
28
  ];
34
29
  export function detectProject(projectDir) {
35
30
  const result = {
@@ -79,12 +74,11 @@ export function detectProject(projectDir) {
79
74
  * Detect framework within a PHP project
80
75
  */
81
76
  export function detectPhpFramework(projectDir) {
82
- if (existsSync(join(projectDir, 'artisan')))
77
+ if (existsSync(join(projectDir, 'artisan'))) {
78
+ if (existsSync(join(projectDir, 'rr.yaml')))
79
+ return 'laravel-octane';
83
80
  return 'laravel';
84
- if (existsSync(join(projectDir, 'symfony.lock')))
85
- return 'symfony';
86
- if (existsSync(join(projectDir, 'spark')))
87
- return 'codeigniter';
81
+ }
88
82
  return null;
89
83
  }
90
84
  /**
@@ -97,8 +91,26 @@ export function detectNodeFramework(projectDir) {
97
91
  return 'nextjs';
98
92
  if (existsSync(join(projectDir, 'nuxt.config.ts')))
99
93
  return 'nuxt';
100
- if (existsSync(join(projectDir, 'astro.config.mjs')))
94
+ if (existsSync(join(projectDir, 'astro.config.mjs')) || existsSync(join(projectDir, 'astro.config.ts')))
101
95
  return 'astro';
96
+ if (existsSync(join(projectDir, 'svelte.config.js')) || existsSync(join(projectDir, 'svelte.config.ts')))
97
+ return 'svelte';
98
+ const pkgPath = join(projectDir, 'package.json');
99
+ if (existsSync(pkgPath)) {
100
+ try {
101
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
102
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
103
+ if (deps['express'])
104
+ return 'express';
105
+ if (deps['fastify'])
106
+ return 'fastify';
107
+ if (deps['hono'])
108
+ return 'hono';
109
+ }
110
+ catch {
111
+ // ignore parse errors
112
+ }
113
+ }
102
114
  return null;
103
115
  }
104
116
  export function detectPythonFramework(projectDir) {
package/dist/index.js CHANGED
@@ -6,7 +6,8 @@
6
6
  * Detects project stack, validates requirements, and configures agents.
7
7
  */
8
8
  import { existsSync, readFileSync } from 'fs';
9
- import { join, basename } from 'path';
9
+ import { join, basename, dirname, resolve } from 'path';
10
+ import { fileURLToPath } from 'url';
10
11
  import inquirer from 'inquirer';
11
12
  import chalk from 'chalk';
12
13
  import * as ui from './ui.js';
@@ -14,6 +15,11 @@ import { detectProject, detectPhpFramework, detectNodeFramework, detectPythonFra
14
15
  import { autoInstall, installComposer, installClaudeCode } from './installer.js';
15
16
  import { loadStackConfig, setupProject } from './setup.js';
16
17
  import { selectMcpServers, installMcpServers } from './mcp.js';
18
+ import { scanProjectStandards } from './scanner.js';
19
+ const __cli_filename = fileURLToPath(import.meta.url);
20
+ const __cli_dirname = dirname(__cli_filename);
21
+ const CLI_ROOT = resolve(__cli_dirname, '..');
22
+ const PKG_VERSION = JSON.parse(readFileSync(join(CLI_ROOT, 'package.json'), 'utf8')).version;
17
23
  // =============================================================================
18
24
  // CLI Arguments
19
25
  // =============================================================================
@@ -27,7 +33,7 @@ const FLAGS = {
27
33
  version: args.includes('--version') || args.includes('-v'),
28
34
  };
29
35
  if (FLAGS.version) {
30
- console.log('1.0.0');
36
+ console.log(PKG_VERSION);
31
37
  process.exit(0);
32
38
  }
33
39
  if (FLAGS.help) {
@@ -157,18 +163,22 @@ async function main() {
157
163
  : stackId === 'python'
158
164
  ? detectPythonFramework(projectDir)
159
165
  : null;
166
+ const defaultFramework = detectedFramework ||
167
+ stackConfig.frameworks.find((f) => f.default)?.id ||
168
+ undefined;
160
169
  const { framework } = await inquirer.prompt([
161
170
  {
162
171
  type: 'list',
163
172
  name: 'framework',
164
173
  message: 'Select framework:',
165
- default: detectedFramework || undefined,
174
+ default: defaultFramework,
166
175
  choices: stackConfig.frameworks.map((f) => ({
167
176
  name: `${f.icon} ${f.name}`,
168
177
  value: f.id,
169
178
  })),
170
179
  },
171
180
  ]);
181
+ const selectedFramework = stackConfig.frameworks.find((f) => f.id === framework);
172
182
  // ─── Step 4: Select Database ───────────────────────────────────────────
173
183
  const { database } = await inquirer.prompt([
174
184
  {
@@ -182,12 +192,15 @@ async function main() {
182
192
  },
183
193
  ]);
184
194
  // ─── Step 5: Frontend ──────────────────────────────────────────────────
195
+ const compatibleFrontends = stackConfig.frontendOptions.filter((f) => !f.frameworks || f.frameworks.includes(framework));
196
+ const defaultFrontend = compatibleFrontends.find((f) => f.default)?.id || undefined;
185
197
  const { frontend } = await inquirer.prompt([
186
198
  {
187
199
  type: 'list',
188
200
  name: 'frontend',
189
201
  message: 'Frontend included?',
190
- choices: stackConfig.frontendOptions.map((f) => ({
202
+ default: defaultFrontend,
203
+ choices: compatibleFrontends.map((f) => ({
191
204
  name: `${f.icon} ${f.name}`,
192
205
  value: f.id,
193
206
  })),
@@ -197,27 +210,87 @@ async function main() {
197
210
  const deploy = 'github';
198
211
  // ─── Step 6b: MCP Servers ────────────────────────────────────────────
199
212
  const selectedMcps = FLAGS.noMcp ? [] : await selectMcpServers(stackId, database);
213
+ // ─── Step 6c: Standards Review ──────────────────────────────────────────
214
+ let standardsReview;
215
+ const scanResults = scanProjectStandards(projectDir);
216
+ if (scanResults.patterns.length > 0) {
217
+ console.log('');
218
+ ui.header('📋 Standards Review');
219
+ ui.info(`Found ${scanResults.patterns.length} patterns from: ${scanResults.sources.join(', ')}`);
220
+ console.log('');
221
+ const categories = new Map();
222
+ for (const p of scanResults.patterns) {
223
+ const list = categories.get(p.category) || [];
224
+ list.push(p.name);
225
+ categories.set(p.category, list);
226
+ }
227
+ for (const [category, items] of categories) {
228
+ console.log(chalk.cyan(` ${category}:`));
229
+ for (const item of items.slice(0, 3)) {
230
+ console.log(chalk.dim(` • ${item}`));
231
+ }
232
+ if (items.length > 3) {
233
+ console.log(chalk.dim(` ... and ${items.length - 3} more`));
234
+ }
235
+ }
236
+ console.log('');
237
+ const { adaptStandards } = await inquirer.prompt([
238
+ {
239
+ type: 'list',
240
+ name: 'adaptStandards',
241
+ message: 'Adapt AI skills to match your project standards?',
242
+ choices: [
243
+ {
244
+ name: `🎯 Use my standards ${chalk.dim('— import patterns into AI context')}`,
245
+ value: 'adapt',
246
+ },
247
+ {
248
+ name: `📦 Use defaults ${chalk.dim('— start with plugin defaults')}`,
249
+ value: 'defaults',
250
+ },
251
+ ],
252
+ },
253
+ ]);
254
+ scanResults.status = adaptStandards === 'adapt' ? 'adapted' : 'defaults';
255
+ scanResults.userChoice = adaptStandards;
256
+ standardsReview = scanResults;
257
+ if (adaptStandards === 'adapt') {
258
+ ui.success(`${scanResults.patterns.length} patterns will be imported into AI context`);
259
+ }
260
+ }
200
261
  // ─── Step 7: Show Summary & Confirm ────────────────────────────────────
262
+ const selectedFrontend = compatibleFrontends.find((f) => f.id === frontend);
201
263
  const config = {
202
264
  name: projectName,
203
265
  stack: stackId,
204
266
  framework,
205
267
  database,
206
268
  frontend,
269
+ frontendSkillsDir: selectedFrontend?.skillsDir,
207
270
  deploy,
208
271
  path: projectDir,
209
272
  createdAt: new Date().toISOString(),
210
- skills: [...stackConfig.skills],
273
+ skills: [
274
+ ...stackConfig.skills,
275
+ ...(selectedFramework?.skills ?? []),
276
+ ],
211
277
  qualityGates: `stacks/${stackId}/config/quality-gates.json`,
212
278
  cursorRules: detection.hasCursorRules,
213
279
  domains: {},
280
+ standardsReview,
214
281
  };
282
+ const standardsLabel = standardsReview
283
+ ? standardsReview.status === 'adapted'
284
+ ? `${standardsReview.patterns.length} patterns imported`
285
+ : 'Using defaults'
286
+ : 'No patterns found';
215
287
  ui.configSummary({
216
288
  'Stack': `${stackConfig.icon} ${stackConfig.name} (${stackConfig.runtime})`,
217
289
  'Framework': framework,
218
290
  'Database': database,
219
291
  'Frontend': frontend,
220
292
  'Deploy': '🐙 GitHub (git push)',
293
+ 'Standards': standardsLabel,
221
294
  'Agents': '6 universal',
222
295
  'Skills': `${stackConfig.skills.length} stack + shared`,
223
296
  'Hooks': 'stop-validator + prompt-inject',
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Start Vibing Stacks — Project Standards Scanner
3
+ *
4
+ * Scans existing project files to extract coding patterns
5
+ * and standards already in use.
6
+ */
7
+ import type { StandardsReview } from './types.js';
8
+ /**
9
+ * Run all scanners against a project directory.
10
+ * Returns combined results from all sources.
11
+ */
12
+ export declare function scanProjectStandards(projectDir: string): StandardsReview;