create-sdd-project 0.2.1 → 0.2.4
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 +117 -107
- package/lib/config.js +1 -1
- package/lib/init-generator.js +43 -14
- package/lib/init-wizard.js +1 -0
- package/lib/prompts.js +9 -9
- package/lib/scanner.js +61 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,18 +1,67 @@
|
|
|
1
1
|
# SDD DevFlow
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/create-sdd-project)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](package.json)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
**Spec-Driven Development workflow for AI-assisted coding.**
|
|
8
|
+
|
|
9
|
+
A complete development methodology for Claude Code and Gemini that combines specialized AI agents, workflow orchestration with human checkpoints, and institutional memory. Works with new and existing TypeScript/JavaScript projects.
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### New Project
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx create-sdd-project my-app
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The interactive wizard asks about your stack, AI tools, and autonomy level. For defaults (fullstack Express+Next.js):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx create-sdd-project my-app --yes
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Existing Project
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
cd your-existing-project
|
|
29
|
+
npx create-sdd-project --init
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Scans your project, detects your stack and architecture, and installs SDD files adapted to your project. Never modifies existing code or overwrites existing files.
|
|
33
|
+
|
|
34
|
+
### After Setup
|
|
35
|
+
|
|
36
|
+
Open in your AI coding tool and run:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
init sprint 0
|
|
40
|
+
start task B0.1
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The workflow skill guides you through each step with checkpoints based on your autonomy level.
|
|
44
|
+
|
|
45
|
+
---
|
|
6
46
|
|
|
7
47
|
## What is SDD?
|
|
8
48
|
|
|
9
49
|
SDD DevFlow combines three proven practices:
|
|
10
50
|
|
|
11
51
|
1. **Spec-Driven Development** — Write specifications before code. Specs are the contract between planning and implementation.
|
|
12
|
-
2. **Test-Driven Development** — Red-Green-Refactor cycle for every feature.
|
|
13
|
-
3. **Human-in-the-Loop** — Strategic checkpoints
|
|
52
|
+
2. **Test-Driven Development** — Red-Green-Refactor cycle for every feature.
|
|
53
|
+
3. **Human-in-the-Loop** — Strategic checkpoints with configurable autonomy levels that reduce human intervention as trust increases.
|
|
54
|
+
|
|
55
|
+
### Why use SDD DevFlow?
|
|
56
|
+
|
|
57
|
+
- **AI agents work better with structure.** Without guardrails, AI coding assistants produce inconsistent results. SDD provides the methodology, standards, and workflow that make AI output predictable and high-quality.
|
|
58
|
+
- **Institutional memory across sessions.** Sprint trackers, bug logs, and decision records survive context compaction and session boundaries.
|
|
59
|
+
- **Scales from solo to team.** Start at L1 (full control) while learning, scale to L4 (full auto) for repetitive tasks.
|
|
60
|
+
- **Works with your stack.** Not opinionated about frameworks — detects and adapts to Express, Fastify, NestJS, Next.js, Nuxt, Vue, Angular, and many more.
|
|
61
|
+
|
|
62
|
+
---
|
|
14
63
|
|
|
15
|
-
## What's
|
|
64
|
+
## What's Included
|
|
16
65
|
|
|
17
66
|
### 9 Specialized Agents
|
|
18
67
|
|
|
@@ -26,35 +75,33 @@ SDD DevFlow combines three proven practices:
|
|
|
26
75
|
| `qa-engineer` | Edge cases, spec verification | 5 |
|
|
27
76
|
| `database-architect` | Schema design, optimization | Any |
|
|
28
77
|
|
|
78
|
+
### 3 Skills (Slash Commands)
|
|
79
|
+
|
|
80
|
+
| Skill | Trigger | What it does |
|
|
81
|
+
|-------|---------|-------------|
|
|
82
|
+
| `development-workflow` | `start task B0.1`, `next task`, `init sprint N` | Orchestrates the complete 7-step workflow |
|
|
83
|
+
| `bug-workflow` | `report bug`, `fix bug`, `hotfix needed` | Bug triage, investigation, and resolution |
|
|
84
|
+
| `project-memory` | `set up project memory`, `log a bug fix` | Maintains institutional knowledge |
|
|
85
|
+
|
|
29
86
|
### Workflow (Steps 0–6)
|
|
30
87
|
|
|
31
88
|
```
|
|
32
|
-
0. SPEC → spec-creator drafts specs → Spec Approval
|
|
33
|
-
1. SETUP → Branch, ticket, sprint tracker → Ticket Approval
|
|
34
|
-
2. PLAN → Planner creates implementation plan → Plan Approval
|
|
89
|
+
0. SPEC → spec-creator drafts specs → Spec Approval
|
|
90
|
+
1. SETUP → Branch, ticket, sprint tracker → Ticket Approval
|
|
91
|
+
2. PLAN → Planner creates implementation plan → Plan Approval
|
|
35
92
|
3. IMPLEMENT → Developer agent, TDD
|
|
36
93
|
4. FINALIZE → Tests/lint/build, validator → Commit Approval
|
|
37
94
|
5. REVIEW → PR, code review, QA → Merge Approval
|
|
38
95
|
6. COMPLETE → Clean up, update tracker
|
|
39
96
|
```
|
|
40
97
|
|
|
41
|
-
**
|
|
42
|
-
- **Simple
|
|
43
|
-
- **Standard
|
|
44
|
-
- **Complex
|
|
45
|
-
|
|
46
|
-
### 3 Complexity Tiers
|
|
47
|
-
|
|
48
|
-
| Tier | Spec | Ticket | Plan | QA |
|
|
49
|
-
|------|:----:|:------:|:----:|:--:|
|
|
50
|
-
| Simple | Skip | Skip | Skip | Skip |
|
|
51
|
-
| Standard | Yes | Yes | Yes | Yes |
|
|
52
|
-
| Complex | Yes | Yes + ADR | Yes | Yes |
|
|
98
|
+
**By complexity:**
|
|
99
|
+
- **Simple** (bug fixes, small tweaks): 1 → 3 → 4 → 5 → 6
|
|
100
|
+
- **Standard** (features): 0 → 1 → 2 → 3 → 4 → 5 (+QA) → 6
|
|
101
|
+
- **Complex** (architectural changes): 0 → 1 (+ADR) → 2 → 3 → 4 → 5 (+QA) → 6
|
|
53
102
|
|
|
54
103
|
### 4 Autonomy Levels
|
|
55
104
|
|
|
56
|
-
Control how many human approval checkpoints are active:
|
|
57
|
-
|
|
58
105
|
| Level | Name | Human Checkpoints | Best For |
|
|
59
106
|
|-------|------|-------------------|----------|
|
|
60
107
|
| L1 | Full Control | All 5 | First sprint, learning SDD |
|
|
@@ -64,13 +111,6 @@ Control how many human approval checkpoints are active:
|
|
|
64
111
|
|
|
65
112
|
Quality gates (tests, lint, build, validators) **always run** regardless of level.
|
|
66
113
|
|
|
67
|
-
### Branching Strategy
|
|
68
|
-
|
|
69
|
-
Configurable per-project in `key_facts.md`:
|
|
70
|
-
|
|
71
|
-
- **GitHub Flow** (default): `main` + `feature/*` + `hotfix/*`. Best for MVPs.
|
|
72
|
-
- **GitFlow** (scaled): `main` + `develop` + `feature/*` + `release/*` + `hotfix/*`. For larger projects.
|
|
73
|
-
|
|
74
114
|
### Project Memory
|
|
75
115
|
|
|
76
116
|
Tracks institutional knowledge across sessions in `docs/project_notes/`:
|
|
@@ -82,70 +122,39 @@ Tracks institutional knowledge across sessions in `docs/project_notes/`:
|
|
|
82
122
|
|
|
83
123
|
### Automated Hooks (Claude Code)
|
|
84
124
|
|
|
85
|
-
- **Quick Scan** — After developer agents finish, a fast grep-based scan (~2s, no API calls) checks for debug code, secrets, and TODOs
|
|
86
|
-
- **Compaction Recovery** — After context compaction, injects a reminder to read the sprint tracker for context recovery
|
|
87
|
-
- **Notifications** — Personal notification hooks (macOS/Linux) in `.claude/settings.local.json`.
|
|
125
|
+
- **Quick Scan** — After developer agents finish, a fast grep-based scan (~2s, no API calls) checks for debug code, secrets, and TODOs
|
|
126
|
+
- **Compaction Recovery** — After context compaction, injects a reminder to read the sprint tracker for context recovery
|
|
88
127
|
|
|
89
128
|
### Multi-Tool Support
|
|
90
129
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
130
|
+
| Tool | Support Level |
|
|
131
|
+
|------|--------------|
|
|
132
|
+
| **Claude Code** | Full — agents, skills, hooks, settings (`.claude/`) |
|
|
133
|
+
| **Gemini** | Full — agents, skills, commands, settings (`.gemini/`) |
|
|
134
|
+
| **Other AI tools** | `AGENTS.md` provides universal instructions (Cursor, Copilot, Windsurf, etc.) |
|
|
94
135
|
|
|
95
|
-
|
|
136
|
+
---
|
|
96
137
|
|
|
97
|
-
|
|
138
|
+
## `--init` Stack Detection
|
|
98
139
|
|
|
99
|
-
|
|
100
|
-
npx create-sdd-project my-app
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
The interactive wizard guides you through:
|
|
140
|
+
When running `--init` on an existing project, the scanner automatically detects:
|
|
104
141
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
```bash
|
|
119
|
-
cd your-existing-project
|
|
120
|
-
npx create-sdd-project --init
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
The `--init` flag:
|
|
124
|
-
- **Scans** your project: detects stack (Express, Next.js, Prisma, etc.), architecture (MVC, DDD, feature-based), tests, and existing docs
|
|
125
|
-
- **Adapts** standards files to match your real architecture (not generic DDD defaults)
|
|
126
|
-
- **Imports** existing OpenAPI/Swagger specs and references Prisma schemas
|
|
127
|
-
- **Audits** test coverage and suggests retrofit testing tasks if coverage is low
|
|
128
|
-
- **Never** modifies your existing code or overwrites existing files
|
|
129
|
-
|
|
130
|
-
### After setup
|
|
131
|
-
|
|
132
|
-
```
|
|
133
|
-
# Open in your AI coding tool and run:
|
|
134
|
-
init sprint 0
|
|
135
|
-
start task B0.1
|
|
136
|
-
```
|
|
142
|
+
| Category | Detected |
|
|
143
|
+
|----------|----------|
|
|
144
|
+
| **Backend frameworks** | Express, Fastify, Koa, NestJS, Hapi, AdonisJS |
|
|
145
|
+
| **ORMs** | Prisma, Mongoose, TypeORM, Sequelize, Drizzle, Knex, MikroORM, Objection.js |
|
|
146
|
+
| **Databases** | PostgreSQL, MySQL, SQLite, MongoDB, SQL Server, CockroachDB (from Prisma schema, `.env`, or dependencies) |
|
|
147
|
+
| **Frontend frameworks** | Next.js, Nuxt, Remix, Astro, SolidJS, React, Vue, Angular, Svelte |
|
|
148
|
+
| **Styling** | Tailwind CSS, styled-components, Emotion, Sass |
|
|
149
|
+
| **Component libraries** | Radix UI, Headless UI, Material UI, Chakra UI, Ant Design |
|
|
150
|
+
| **State management** | Zustand, Redux, Jotai, TanStack Query, Recoil, Pinia, MobX |
|
|
151
|
+
| **Testing** | Jest, Vitest, Mocha (unit) + Playwright, Cypress (e2e) |
|
|
152
|
+
| **Architecture** | MVC, DDD, feature-based, handler-based, flat |
|
|
153
|
+
| **Project type** | Monorepo (workspaces, Lerna, Turbo, pnpm) or single-package |
|
|
137
154
|
|
|
138
|
-
|
|
155
|
+
Standards files are adapted to match your actual architecture — not generic defaults.
|
|
139
156
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
If you prefer manual configuration, copy the template directly:
|
|
143
|
-
|
|
144
|
-
```bash
|
|
145
|
-
cp -r template/ /path/to/your-project/
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Then look for `<!-- CONFIG: ... -->` comments in the files to customize.
|
|
157
|
+
---
|
|
149
158
|
|
|
150
159
|
## Template Structure
|
|
151
160
|
|
|
@@ -155,7 +164,6 @@ project/
|
|
|
155
164
|
├── CLAUDE.md # Claude Code config (autonomy, recovery)
|
|
156
165
|
├── GEMINI.md # Gemini config (autonomy)
|
|
157
166
|
├── .env.example # Environment variables template
|
|
158
|
-
├── .gitignore # Git ignore with secrets protection
|
|
159
167
|
│
|
|
160
168
|
├── .claude/
|
|
161
169
|
│ ├── agents/ # 9 specialized agents
|
|
@@ -165,45 +173,37 @@ project/
|
|
|
165
173
|
│ │ ├── bug-workflow/ # Bug triage and resolution
|
|
166
174
|
│ │ └── project-memory/ # Memory system setup
|
|
167
175
|
│ ├── hooks/quick-scan.sh # Post-developer quality scan
|
|
168
|
-
│
|
|
169
|
-
│ └── settings.local.json # Personal hooks (gitignored)
|
|
176
|
+
│ └── settings.json # Shared hooks (git-tracked)
|
|
170
177
|
│
|
|
171
178
|
├── .gemini/
|
|
172
179
|
│ ├── agents/ # 9 agents (Gemini format)
|
|
173
|
-
│ ├── skills/
|
|
174
|
-
│ │ ├── development-workflow/ # Main task workflow (Steps 0-6)
|
|
175
|
-
│ │ │ └── references/ # Templates, guides, examples
|
|
176
|
-
│ │ ├── bug-workflow/ # Bug triage and resolution
|
|
177
|
-
│ │ └── project-memory/ # Memory system setup
|
|
180
|
+
│ ├── skills/ # Same 3 skills
|
|
178
181
|
│ ├── commands/ # Slash command shortcuts
|
|
179
|
-
│
|
|
180
|
-
│ └── styles/default.md # Response style
|
|
182
|
+
│ └── settings.json # Gemini configuration
|
|
181
183
|
│
|
|
182
184
|
├── ai-specs/specs/
|
|
183
185
|
│ ├── base-standards.mdc # Constitution + methodology
|
|
184
|
-
│ ├── backend-standards.mdc # Backend patterns
|
|
185
|
-
│ ├── frontend-standards.mdc # Frontend patterns
|
|
186
|
-
│ └── documentation-standards.mdc # Documentation
|
|
186
|
+
│ ├── backend-standards.mdc # Backend patterns
|
|
187
|
+
│ ├── frontend-standards.mdc # Frontend patterns
|
|
188
|
+
│ └── documentation-standards.mdc # Documentation rules
|
|
187
189
|
│
|
|
188
190
|
└── docs/
|
|
189
191
|
├── project_notes/ # Project memory
|
|
190
|
-
│ ├── sprint-0-tracker.md
|
|
191
|
-
│ ├── key_facts.md
|
|
192
|
-
│ ├── bugs.md
|
|
193
|
-
│ └── decisions.md
|
|
194
|
-
├── specs/
|
|
195
|
-
|
|
196
|
-
│ └── ui-components.md # Component spec (frontend)
|
|
197
|
-
└── tickets/ # Task tickets (generated by workflow)
|
|
192
|
+
│ ├── sprint-0-tracker.md
|
|
193
|
+
│ ├── key_facts.md
|
|
194
|
+
│ ├── bugs.md
|
|
195
|
+
│ └── decisions.md
|
|
196
|
+
├── specs/ # API and UI specs
|
|
197
|
+
└── tickets/ # Task tickets (workflow-generated)
|
|
198
198
|
```
|
|
199
199
|
|
|
200
200
|
## Default Tech Stack
|
|
201
201
|
|
|
202
|
-
|
|
202
|
+
Configurable via the wizard or `<!-- CONFIG -->` comments in template files:
|
|
203
203
|
|
|
204
204
|
- **Backend**: Node.js + Express + Prisma + PostgreSQL
|
|
205
205
|
- **Frontend**: Next.js (App Router) + Tailwind CSS + Radix UI + Zustand
|
|
206
|
-
- **Shared Types**: Zod schemas
|
|
206
|
+
- **Shared Types**: Zod schemas with `z.infer<>` for TypeScript types
|
|
207
207
|
- **Testing**: Jest (unit) + Playwright (e2e)
|
|
208
208
|
- **Methodology**: TDD + DDD + Spec-Driven Development
|
|
209
209
|
|
|
@@ -224,6 +224,16 @@ These 6 principles apply to ALL tasks, ALL agents, ALL complexity levels:
|
|
|
224
224
|
- Node.js 18+
|
|
225
225
|
- `jq` (for quick-scan hook): `brew install jq` (macOS) or `apt install jq` (Linux)
|
|
226
226
|
|
|
227
|
+
## Manual Setup (Alternative)
|
|
228
|
+
|
|
229
|
+
If you prefer manual configuration over the CLI wizard:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
cp -r template/ /path/to/your-project/
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Then look for `<!-- CONFIG: ... -->` comments in the files to customize.
|
|
236
|
+
|
|
227
237
|
## Roadmap
|
|
228
238
|
|
|
229
239
|
- **Agent Teams**: Parallel execution of independent tasks (waiting for Claude Code Agent Teams to stabilize)
|
package/lib/config.js
CHANGED
|
@@ -79,7 +79,7 @@ const AI_TOOLS = [
|
|
|
79
79
|
|
|
80
80
|
const AUTONOMY_LEVELS = [
|
|
81
81
|
{ level: 1, name: 'Full Control', desc: 'Human approves every checkpoint (first sprint, learning SDD)' },
|
|
82
|
-
{ level: 2, name: 'Trusted', desc: 'Human reviews plans + merges only (
|
|
82
|
+
{ level: 2, name: 'Trusted', desc: 'Human reviews plans + merges only (normal development)', default: true },
|
|
83
83
|
{ level: 3, name: 'Autopilot', desc: 'Human only approves merges (well-defined, repetitive tasks)' },
|
|
84
84
|
{ level: 4, name: 'Full Auto', desc: 'No human checkpoints, CI/CD gates only (bulk simple tasks)' },
|
|
85
85
|
];
|
package/lib/init-generator.js
CHANGED
|
@@ -305,16 +305,16 @@ function adaptBackendStandards(template, scan) {
|
|
|
305
305
|
'<!-- TODO: Review and adjust the sections below to match your project\'s conventions. -->\n<!-- This file was generated from project analysis by create-sdd-project --init. -->'
|
|
306
306
|
);
|
|
307
307
|
|
|
308
|
-
// Update globs in frontmatter
|
|
308
|
+
// Update globs in frontmatter — only include tsx/jsx if frontend detected
|
|
309
309
|
const srcRoot = findSrcRootName(scan);
|
|
310
|
-
const
|
|
310
|
+
const exts = scan.frontend.detected ? 'ts,js,tsx,jsx' : 'ts,js';
|
|
311
|
+
const globPattern = srcRoot ? `${srcRoot}/**/*.{${exts}}` : `**/*.{${exts}}`;
|
|
311
312
|
content = content.replace(
|
|
312
313
|
/globs: \[.*?\]/,
|
|
313
314
|
`globs: ["${globPattern}"]`
|
|
314
315
|
);
|
|
315
316
|
|
|
316
317
|
// Update Technology Stack
|
|
317
|
-
const framework = scan.backend.framework || 'Unknown';
|
|
318
318
|
const orm = scan.backend.orm;
|
|
319
319
|
const db = scan.backend.db;
|
|
320
320
|
const lang = scan.language === 'typescript' ? 'TypeScript' : 'JavaScript';
|
|
@@ -325,8 +325,10 @@ function adaptBackendStandards(template, scan) {
|
|
|
325
325
|
|
|
326
326
|
let stackLines = [
|
|
327
327
|
`- **Runtime**: Node.js with ${lang}`,
|
|
328
|
-
`- **Framework**: ${framework}`,
|
|
329
328
|
];
|
|
329
|
+
if (scan.backend.framework) {
|
|
330
|
+
stackLines.push(`- **Framework**: ${scan.backend.framework}`);
|
|
331
|
+
}
|
|
330
332
|
if (orm) {
|
|
331
333
|
stackLines.push(`- **ORM**: ${orm}${db ? ` (${db})` : ''}`);
|
|
332
334
|
} else if (db) {
|
|
@@ -352,21 +354,38 @@ function adaptBackendStandards(template, scan) {
|
|
|
352
354
|
const patternLabel = patternLabels[scan.srcStructure.pattern] || 'Custom';
|
|
353
355
|
|
|
354
356
|
const archStructure = buildArchitectureTree(scan);
|
|
357
|
+
// Robust: match any "## Architecture — <anything>" heading up to "## Naming"
|
|
355
358
|
content = content.replace(
|
|
356
|
-
/## Architecture —
|
|
359
|
+
/## Architecture — [^\n]+\n\n```\n[\s\S]*?```\n\n(?:### Layer Rules\n\n[\s\S]*?)?(?=\n## Naming)/,
|
|
357
360
|
`## Architecture — ${patternLabel}\n\n\`\`\`\n${archStructure}\`\`\`\n\n<!-- TODO: Add layer rules that match your project's architecture. -->\n\n`
|
|
358
361
|
);
|
|
359
362
|
|
|
360
363
|
// Update Database Patterns section if not Prisma
|
|
364
|
+
// Robust: match from "## Database Patterns" to next "## " heading
|
|
361
365
|
if (!scan.backend.orm) {
|
|
362
366
|
content = content.replace(
|
|
363
|
-
/## Database Patterns\n\n
|
|
364
|
-
`## Database Patterns\n\n<!-- TODO: Add database access patterns for your project. -->\n
|
|
367
|
+
/## Database Patterns\n\n[\s\S]*?(?=\n## )/,
|
|
368
|
+
`## Database Patterns\n\n<!-- TODO: Add database access patterns for your project. -->\n`
|
|
365
369
|
);
|
|
366
370
|
} else if (scan.backend.orm !== 'Prisma') {
|
|
367
371
|
content = content.replace(
|
|
368
|
-
/## Database Patterns\n\n
|
|
369
|
-
`## Database Patterns\n\n<!-- TODO: Add ${scan.backend.orm} best practices and patterns for your project. -->\n
|
|
372
|
+
/## Database Patterns\n\n[\s\S]*?(?=\n## )/,
|
|
373
|
+
`## Database Patterns\n\n<!-- TODO: Add ${scan.backend.orm} best practices and patterns for your project. -->\n`
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Adapt Validation section based on detected validation library
|
|
378
|
+
if (scan.backend.validation && scan.backend.validation !== 'Zod') {
|
|
379
|
+
// Has a validator, but not Zod — replace with detected library name
|
|
380
|
+
content = content.replace(
|
|
381
|
+
/## Validation\n\n[\s\S]*?(?=\n## Database Patterns)/,
|
|
382
|
+
`## Validation\n\n- Validate all inputs at the application layer using ${scan.backend.validation}\n- Validate before executing business logic\n- Return descriptive validation errors\n\n<!-- TODO: Add ${scan.backend.validation} validation patterns for your project. -->\n\n`
|
|
383
|
+
);
|
|
384
|
+
} else if (!scan.backend.validation) {
|
|
385
|
+
// No validation library detected — generic guidance
|
|
386
|
+
content = content.replace(
|
|
387
|
+
/## Validation\n\n[\s\S]*?(?=\n## Database Patterns)/,
|
|
388
|
+
`## Validation\n\n- Validate all inputs at the application layer\n- Validate before executing business logic\n- Return descriptive validation errors\n\n<!-- TODO: Add validation patterns for your project (e.g., Zod, Joi, class-validator). -->\n\n`
|
|
370
389
|
);
|
|
371
390
|
}
|
|
372
391
|
|
|
@@ -444,7 +463,7 @@ function buildArchitectureTree(scan) {
|
|
|
444
463
|
}
|
|
445
464
|
|
|
446
465
|
function capitalizeFramework(name) {
|
|
447
|
-
const map = { jest: 'Jest', vitest: 'Vitest', mocha: 'Mocha' };
|
|
466
|
+
const map = { jest: 'Jest', vitest: 'Vitest', mocha: 'Mocha', playwright: 'Playwright', cypress: 'Cypress' };
|
|
448
467
|
return map[name] || name;
|
|
449
468
|
}
|
|
450
469
|
|
|
@@ -467,15 +486,17 @@ function adaptAgentsMd(template, config, scan) {
|
|
|
467
486
|
const tree = rootDirs.map((d) => `├── ${d.replace(/\/$/, '/')} `).join('\n');
|
|
468
487
|
const treeBlock = `\`\`\`\nproject/\n${tree}\n└── docs/ ← Documentation\n\`\`\``;
|
|
469
488
|
|
|
489
|
+
// Robust: flexible whitespace between CONFIG comment and code block
|
|
470
490
|
content = content.replace(
|
|
471
|
-
/<!-- CONFIG: Adjust directories
|
|
491
|
+
/<!-- CONFIG: Adjust directories[^>]*-->\n+```\nproject\/\n[\s\S]*?```/,
|
|
472
492
|
treeBlock
|
|
473
493
|
);
|
|
474
494
|
|
|
475
495
|
// If not monorepo, simplify the install instructions
|
|
496
|
+
// Robust: match any number of table rows (not hardcoded count)
|
|
476
497
|
if (!scan.isMonorepo) {
|
|
477
498
|
content = content.replace(
|
|
478
|
-
/\*\*Critical\*\*: NEVER install dependencies in the root directory\.\n\n
|
|
499
|
+
/\*\*Critical\*\*: NEVER install dependencies in the root directory\.\n\n(\|.*\n)+/,
|
|
479
500
|
''
|
|
480
501
|
);
|
|
481
502
|
}
|
|
@@ -494,6 +515,12 @@ function adaptAgentsMd(template, config, scan) {
|
|
|
494
515
|
'Frontend patterns (Next.js, Tailwind, Radix)',
|
|
495
516
|
`Frontend patterns (${parts.join(', ')})`
|
|
496
517
|
);
|
|
518
|
+
} else {
|
|
519
|
+
// Remove frontend-standards reference for backend-only projects
|
|
520
|
+
content = content.replace(
|
|
521
|
+
/- \[Frontend Standards\].*\n/,
|
|
522
|
+
''
|
|
523
|
+
);
|
|
497
524
|
}
|
|
498
525
|
|
|
499
526
|
return content;
|
|
@@ -528,9 +555,11 @@ function configureKeyFacts(template, config, scan) {
|
|
|
528
555
|
|
|
529
556
|
// Technology Stack from scan
|
|
530
557
|
if (scan.backend.detected) {
|
|
531
|
-
const fw = scan.backend.framework || 'Unknown';
|
|
532
558
|
const runtime = scan.language === 'typescript' ? 'Node.js (TypeScript)' : 'Node.js';
|
|
533
|
-
|
|
559
|
+
const backendLabel = scan.backend.framework
|
|
560
|
+
? `${scan.backend.framework}, ${runtime}`
|
|
561
|
+
: runtime;
|
|
562
|
+
content = content.replace('[Framework, runtime, version]', backendLabel);
|
|
534
563
|
|
|
535
564
|
if (scan.backend.db) {
|
|
536
565
|
content = content.replace('[Type, host, port]', scan.backend.db);
|
package/lib/init-wizard.js
CHANGED
|
@@ -33,6 +33,7 @@ function formatScanSummary(scanResult) {
|
|
|
33
33
|
const patternLabels = {
|
|
34
34
|
mvc: 'MVC',
|
|
35
35
|
ddd: 'DDD (Domain-Driven Design)',
|
|
36
|
+
layered: 'Layered (controllers + handlers + managers)',
|
|
36
37
|
'feature-based': 'Feature-based',
|
|
37
38
|
'handler-based': 'Handler-based',
|
|
38
39
|
flat: 'Flat structure',
|
package/lib/prompts.js
CHANGED
|
@@ -47,29 +47,29 @@ function askMultiline(rl, question) {
|
|
|
47
47
|
console.log(' (Enter text below. Empty line to finish, or press Enter to skip)');
|
|
48
48
|
const lines = [];
|
|
49
49
|
let firstLine = true;
|
|
50
|
+
let done = false;
|
|
50
51
|
|
|
51
|
-
const
|
|
52
|
+
const handler = (line) => {
|
|
53
|
+
if (done) return;
|
|
52
54
|
if (firstLine && line.trim() === '') {
|
|
53
|
-
|
|
55
|
+
done = true;
|
|
56
|
+
rl.removeListener('line', handler);
|
|
54
57
|
resolve('');
|
|
55
58
|
return;
|
|
56
59
|
}
|
|
57
60
|
firstLine = false;
|
|
58
61
|
if (line.trim() === '') {
|
|
59
|
-
|
|
62
|
+
done = true;
|
|
63
|
+
rl.removeListener('line', handler);
|
|
60
64
|
resolve(lines.join('\n'));
|
|
61
65
|
} else {
|
|
62
66
|
lines.push(line);
|
|
67
|
+
process.stdout.write(' > ');
|
|
63
68
|
}
|
|
64
69
|
};
|
|
65
70
|
|
|
66
71
|
process.stdout.write(' > ');
|
|
67
|
-
rl.on('line',
|
|
68
|
-
if (lines.length > 0 || line.trim() !== '') {
|
|
69
|
-
process.stdout.write(' > ');
|
|
70
|
-
}
|
|
71
|
-
onLine(line);
|
|
72
|
-
});
|
|
72
|
+
rl.on('line', handler);
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
|
package/lib/scanner.js
CHANGED
|
@@ -67,11 +67,12 @@ function detectBackend(dir, pkg) {
|
|
|
67
67
|
|
|
68
68
|
// Framework detection
|
|
69
69
|
const frameworks = [
|
|
70
|
-
{
|
|
71
|
-
{
|
|
72
|
-
{
|
|
73
|
-
{
|
|
74
|
-
{
|
|
70
|
+
{ dep: 'express', label: 'Express' },
|
|
71
|
+
{ dep: 'fastify', label: 'Fastify' },
|
|
72
|
+
{ dep: 'koa', label: 'Koa' },
|
|
73
|
+
{ dep: '@nestjs/core', label: 'NestJS' },
|
|
74
|
+
{ dep: '@hapi/hapi', label: 'Hapi' },
|
|
75
|
+
{ dep: '@adonisjs/core', label: 'AdonisJS' },
|
|
75
76
|
];
|
|
76
77
|
|
|
77
78
|
for (const fw of frameworks) {
|
|
@@ -84,11 +85,14 @@ function detectBackend(dir, pkg) {
|
|
|
84
85
|
|
|
85
86
|
// ORM detection
|
|
86
87
|
const orms = [
|
|
87
|
-
{
|
|
88
|
-
{
|
|
89
|
-
{
|
|
90
|
-
{
|
|
91
|
-
{
|
|
88
|
+
{ dep: '@prisma/client', label: 'Prisma' },
|
|
89
|
+
{ dep: 'mongoose', label: 'Mongoose' },
|
|
90
|
+
{ dep: 'typeorm', label: 'TypeORM' },
|
|
91
|
+
{ dep: 'sequelize', label: 'Sequelize' },
|
|
92
|
+
{ dep: 'drizzle-orm', label: 'Drizzle' },
|
|
93
|
+
{ dep: 'knex', label: 'Knex' },
|
|
94
|
+
{ dep: '@mikro-orm/core', label: 'MikroORM' },
|
|
95
|
+
{ dep: 'objection', label: 'Objection.js' },
|
|
92
96
|
];
|
|
93
97
|
|
|
94
98
|
for (const orm of orms) {
|
|
@@ -117,6 +121,22 @@ function detectBackend(dir, pkg) {
|
|
|
117
121
|
// Port detection
|
|
118
122
|
result.port = detectPort(dir, pkg);
|
|
119
123
|
|
|
124
|
+
// Validation library detection
|
|
125
|
+
const validators = [
|
|
126
|
+
{ dep: 'zod', label: 'Zod' },
|
|
127
|
+
{ dep: 'joi', label: 'Joi' },
|
|
128
|
+
{ dep: 'class-validator', label: 'class-validator' },
|
|
129
|
+
{ dep: 'yup', label: 'Yup' },
|
|
130
|
+
{ dep: 'ajv', label: 'Ajv' },
|
|
131
|
+
];
|
|
132
|
+
result.validation = null;
|
|
133
|
+
for (const v of validators) {
|
|
134
|
+
if (deps[v.dep]) {
|
|
135
|
+
result.validation = v.label;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
120
140
|
// If no framework but has a server-like structure, still mark as detected
|
|
121
141
|
if (!result.detected && (result.orm || result.db)) {
|
|
122
142
|
result.detected = true;
|
|
@@ -129,9 +149,13 @@ function detectFrontend(dir, pkg) {
|
|
|
129
149
|
const deps = getAllDeps(pkg);
|
|
130
150
|
const result = { detected: false, framework: null, styling: null, components: null, state: null };
|
|
131
151
|
|
|
132
|
-
// Framework detection
|
|
152
|
+
// Framework detection (order matters — more specific first)
|
|
133
153
|
const frameworks = [
|
|
134
154
|
{ dep: 'next', label: 'Next.js' },
|
|
155
|
+
{ dep: 'nuxt', label: 'Nuxt' },
|
|
156
|
+
{ dep: '@remix-run/react', label: 'Remix' },
|
|
157
|
+
{ dep: 'astro', label: 'Astro' },
|
|
158
|
+
{ dep: 'solid-js', label: 'SolidJS' },
|
|
135
159
|
{ dep: 'react', label: 'React' },
|
|
136
160
|
{ dep: 'vue', label: 'Vue' },
|
|
137
161
|
{ dep: '@angular/core', label: 'Angular' },
|
|
@@ -155,6 +179,8 @@ function detectFrontend(dir, pkg) {
|
|
|
155
179
|
// Component libraries
|
|
156
180
|
if (deps['@radix-ui/react-dialog'] || deps['@radix-ui/react-select'] || hasRadixDep(deps)) {
|
|
157
181
|
result.components = 'Radix UI';
|
|
182
|
+
} else if (deps['@headlessui/react']) {
|
|
183
|
+
result.components = 'Headless UI';
|
|
158
184
|
} else if (deps['@mui/material']) {
|
|
159
185
|
result.components = 'Material UI';
|
|
160
186
|
} else if (deps['@chakra-ui/react']) {
|
|
@@ -166,6 +192,8 @@ function detectFrontend(dir, pkg) {
|
|
|
166
192
|
// State management
|
|
167
193
|
if (deps['zustand']) result.state = 'Zustand';
|
|
168
194
|
else if (deps['@reduxjs/toolkit'] || deps['redux']) result.state = 'Redux';
|
|
195
|
+
else if (deps['jotai']) result.state = 'Jotai';
|
|
196
|
+
else if (deps['@tanstack/react-query']) result.state = 'TanStack Query';
|
|
169
197
|
else if (deps['recoil']) result.state = 'Recoil';
|
|
170
198
|
else if (deps['pinia']) result.state = 'Pinia';
|
|
171
199
|
else if (deps['mobx']) result.state = 'MobX';
|
|
@@ -280,13 +308,14 @@ function detectTests(dir, pkg) {
|
|
|
280
308
|
const deps = getAllDeps(pkg);
|
|
281
309
|
const result = {
|
|
282
310
|
framework: 'none',
|
|
311
|
+
e2eFramework: null,
|
|
283
312
|
hasConfig: false,
|
|
284
313
|
testFiles: 0,
|
|
285
314
|
testDirs: [],
|
|
286
315
|
estimatedCoverage: 'none',
|
|
287
316
|
};
|
|
288
317
|
|
|
289
|
-
//
|
|
318
|
+
// Unit test framework detection
|
|
290
319
|
if (deps['jest'] || deps['@jest/core'] || deps['ts-jest'] || deps['@types/jest']) {
|
|
291
320
|
result.framework = 'jest';
|
|
292
321
|
} else if (deps['vitest']) {
|
|
@@ -295,11 +324,21 @@ function detectTests(dir, pkg) {
|
|
|
295
324
|
result.framework = 'mocha';
|
|
296
325
|
}
|
|
297
326
|
|
|
327
|
+
// E2E test framework detection (supplement, doesn't override unit framework)
|
|
328
|
+
result.e2eFramework = null;
|
|
329
|
+
if (deps['@playwright/test'] || deps['playwright']) {
|
|
330
|
+
result.e2eFramework = 'playwright';
|
|
331
|
+
} else if (deps['cypress']) {
|
|
332
|
+
result.e2eFramework = 'cypress';
|
|
333
|
+
}
|
|
334
|
+
|
|
298
335
|
// Config files
|
|
299
336
|
const configFiles = [
|
|
300
337
|
'jest.config.js', 'jest.config.ts', 'jest.config.mjs', 'jest.config.cjs',
|
|
301
338
|
'vitest.config.js', 'vitest.config.ts', 'vitest.config.mjs',
|
|
302
339
|
'.mocharc.yml', '.mocharc.json', '.mocharc.js',
|
|
340
|
+
'playwright.config.ts', 'playwright.config.js',
|
|
341
|
+
'cypress.config.ts', 'cypress.config.js',
|
|
303
342
|
];
|
|
304
343
|
result.hasConfig = configFiles.some((f) => fs.existsSync(path.join(dir, f)));
|
|
305
344
|
|
|
@@ -417,7 +456,10 @@ function detectDatabaseFromPrisma(dir) {
|
|
|
417
456
|
if (fs.existsSync(schemaPath)) {
|
|
418
457
|
try {
|
|
419
458
|
const content = fs.readFileSync(schemaPath, 'utf8');
|
|
420
|
-
|
|
459
|
+
// Match provider only within a datasource block (skip generator blocks)
|
|
460
|
+
const dsBlock = content.match(/datasource\s+\w+\s*\{[^}]*\}/);
|
|
461
|
+
const providerSource = dsBlock ? dsBlock[0] : content;
|
|
462
|
+
const match = providerSource.match(/provider\s*=\s*"(\w+)"/);
|
|
421
463
|
if (match) {
|
|
422
464
|
const provider = match[1];
|
|
423
465
|
const dbMap = {
|
|
@@ -444,6 +486,7 @@ function detectDatabaseFromEnv(dir) {
|
|
|
444
486
|
if (fs.existsSync(envPath)) {
|
|
445
487
|
try {
|
|
446
488
|
const content = fs.readFileSync(envPath, 'utf8');
|
|
489
|
+
// Check DATABASE_URL
|
|
447
490
|
const match = content.match(/DATABASE_URL\s*=\s*(\S+)/);
|
|
448
491
|
if (match) {
|
|
449
492
|
const url = match[1].replace(/["']/g, '');
|
|
@@ -452,6 +495,10 @@ function detectDatabaseFromEnv(dir) {
|
|
|
452
495
|
if (url.startsWith('mysql://')) return 'MySQL';
|
|
453
496
|
if (url.includes('sqlite')) return 'SQLite';
|
|
454
497
|
}
|
|
498
|
+
// Check MONGODB_URI / MONGO_URI
|
|
499
|
+
if (/^MONGO(?:DB)?_URI\s*=/m.test(content)) return 'MongoDB';
|
|
500
|
+
// Check REDIS_URL
|
|
501
|
+
if (/^REDIS_URL\s*=/m.test(content)) return 'Redis';
|
|
455
502
|
} catch { /* ignore */ }
|
|
456
503
|
}
|
|
457
504
|
}
|
|
@@ -466,7 +513,7 @@ function detectPort(dir, pkg) {
|
|
|
466
513
|
if (fs.existsSync(envPath)) {
|
|
467
514
|
try {
|
|
468
515
|
const content = fs.readFileSync(envPath, 'utf8');
|
|
469
|
-
const match = content.match(/^PORT\s*=\s*(\d+)/m);
|
|
516
|
+
const match = content.match(/^PORT\s*=\s*["']?(\d+)/m);
|
|
470
517
|
if (match) return parseInt(match[1], 10);
|
|
471
518
|
} catch { /* ignore */ }
|
|
472
519
|
}
|