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 +2 -2
- package/dist/detector.js +23 -11
- package/dist/index.js +78 -5
- package/dist/scanner.d.ts +12 -0
- package/dist/scanner.js +501 -0
- package/dist/setup.js +46 -1
- package/dist/types.d.ts +24 -0
- package/dist/ui.js +6 -5
- package/package.json +1 -1
- package/stacks/_shared/config/security-rules.json +27 -5
- package/stacks/_shared/hooks/user-prompt-submit.ts +26 -2
- package/stacks/frontend/react/skills/preline-ui/SKILL.md +31 -35
- package/stacks/frontend/react/skills/react-standards/SKILL.md +20 -20
- package/stacks/frontend/react/skills/react-ui-patterns/SKILL.md +78 -42
- package/stacks/frontend/react/skills/tailwind-patterns/SKILL.md +1 -1
- package/stacks/frontend/react/skills/zod-validation/SKILL.md +84 -18
- package/stacks/frontend/react-inertia/skills/inertia-react/SKILL.md +342 -0
- package/stacks/frontend/react-inertia/skills/react-standards/SKILL.md +267 -0
- package/stacks/nodejs/skills/nextjs-app-router/SKILL.md +101 -0
- package/stacks/nodejs/stack.json +43 -121
- package/stacks/php/skills/laravel-octane/SKILL.md +155 -53
- package/stacks/php/skills/laravel-patterns/SKILL.md +244 -39
- package/stacks/php/skills/php-patterns/SKILL.md +113 -53
- package/stacks/php/skills/security-scan-php/SKILL.md +161 -43
- package/stacks/php/stack.json +19 -6
- package/templates/CLAUDE-nodejs.md +323 -0
- package/templates/CLAUDE-php.md +233 -33
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
|
|
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
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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
|
-
|
|
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: [
|
|
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;
|