baldart 3.14.1 → 3.16.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/CHANGELOG.md +79 -0
- package/VERSION +1 -1
- package/framework/.claude/agents/REGISTRY.md +10 -0
- package/framework/.claude/agents/api-perf-cost-auditor.md +47 -10
- package/framework/.claude/agents/coder.md +61 -10
- package/framework/.claude/agents/prd-card-writer.md +92 -19
- package/framework/.claude/agents/security-reviewer.md +1 -1
- package/framework/.claude/skills/bug/references/logging-patterns.md +45 -8
- package/framework/.claude/skills/new/SKILL.md +319 -87
- package/framework/.claude/skills/prd/SKILL.md +30 -2
- package/framework/.claude/skills/prd/assets/card-template.yml +59 -27
- package/framework/.claude/skills/prd/assets/epic-template.yml +11 -3
- package/framework/.claude/skills/prd/assets/prd-template.md +90 -14
- package/framework/.claude/skills/prd/assets/state-template.md +18 -0
- package/framework/.claude/skills/prd/references/api-perf-gate.md +102 -52
- package/framework/.claude/skills/prd/references/audit-phase.md +13 -13
- package/framework/.claude/skills/prd/references/backlog-phase.md +81 -0
- package/framework/.claude/skills/prd/references/discovery-phase.md +214 -28
- package/framework/.claude/skills/prd/references/prd-writing-phase.md +65 -23
- package/framework/.claude/skills/prd/references/research-phase.md +22 -4
- package/framework/.claude/skills/prd/references/ui-design-phase.md +115 -3
- package/framework/.claude/skills/prd/references/validation-phase.md +22 -2
- package/framework/docs/PROJECT-CONFIGURATION.md +41 -1
- package/framework/templates/baldart.config.template.yml +25 -0
- package/package.json +1 -1
- package/src/commands/configure.js +72 -0
- package/src/commands/update.js +13 -1
|
@@ -131,9 +131,49 @@ stack:
|
|
|
131
131
|
forbidden: []
|
|
132
132
|
testing:
|
|
133
133
|
e2e: "playwright" # or "cypress", or ""
|
|
134
|
+
|
|
135
|
+
# Stack-cardinal scalars (since v3.15.0). Used by /prd, coder,
|
|
136
|
+
# api-perf-cost-auditor, /new, code-reviewer, security-reviewer to branch
|
|
137
|
+
# vocabulary, gate sections, anti-pattern checklists, deploy commands.
|
|
138
|
+
database: "firestore" # firestore|supabase|postgres|mysql|mongodb|dynamodb|sqlite|none|""
|
|
139
|
+
auth_provider: "firebase-auth" # firebase-auth|supabase-auth|clerk|auth0|cognito|nextauth|lucia|custom|none|""
|
|
140
|
+
framework: "nextjs" # nextjs|remix|sveltekit|astro|nuxt|rails|django|fastapi|express|none|""
|
|
141
|
+
deployment: "vercel" # vercel|firebase|aws|gcp|cloudflare|render|fly|self-hosted|none|""
|
|
134
142
|
```
|
|
135
143
|
|
|
136
|
-
Skills propose only `canonical` libraries and refuse `forbidden` ones. Empty
|
|
144
|
+
Skills propose only `canonical` libraries and refuse `forbidden` ones. Empty
|
|
145
|
+
`canonical` = ask the user before proposing a library.
|
|
146
|
+
|
|
147
|
+
**Stack-cardinal scalars (v3.15.0+).** The four scalar keys (`database`,
|
|
148
|
+
`auth_provider`, `framework`, `deployment`) drive stack-aware branching across
|
|
149
|
+
the skill/agent set:
|
|
150
|
+
|
|
151
|
+
- `/prd` Schema Verification Gate picks the registry path matching
|
|
152
|
+
`stack.database` (Firestore field-registry, Prisma schema, SQL migrations,
|
|
153
|
+
Mongo validator).
|
|
154
|
+
- `/prd` Section 5 of the PRD template renders the index variant matching
|
|
155
|
+
`stack.database` (Firestore composite, SQL B-tree, Mongo compound,
|
|
156
|
+
DynamoDB GSI/LSI).
|
|
157
|
+
- `api-perf-gate` keyword scan adds DB-specific anti-patterns only when the
|
|
158
|
+
matching `stack.database` is set; universal patterns always run.
|
|
159
|
+
- `coder.md` Database Index Invariant ships the right migration kind for
|
|
160
|
+
the resolved `stack.database`.
|
|
161
|
+
- `api-perf-cost-auditor` reads the four scalars at session start and cites
|
|
162
|
+
them in every audit header, so the user knows which variant applied.
|
|
163
|
+
- `/new` Production Readiness Checklist picks deploy commands matching
|
|
164
|
+
`stack.deployment` + `stack.database` (firebase deploy, vercel deploy,
|
|
165
|
+
supabase db push, CDK/Terraform apply).
|
|
166
|
+
- `security-reviewer` evaluates access rules per `stack.database`
|
|
167
|
+
(Firestore rules, Supabase RLS, Mongo validators, DynamoDB IAM,
|
|
168
|
+
Postgres RLS+GRANT).
|
|
169
|
+
- `bug/logging-patterns.md` picks the framework-native debug switches
|
|
170
|
+
matching `stack.framework`.
|
|
171
|
+
|
|
172
|
+
**Always-ask contract on empty.** When a scalar is `""` and a skill needs it,
|
|
173
|
+
the skill asks the user once and suggests running `npx baldart configure` to
|
|
174
|
+
persist the answer. Skills NEVER assume a default — empty means "ask".
|
|
175
|
+
`baldart update` flags newly-introduced scalar keys via the schema-drift
|
|
176
|
+
detector and auto-offers `baldart configure`.
|
|
137
177
|
|
|
138
178
|
### 4.5 `features` — explicit booleans gating BLOCKING reads
|
|
139
179
|
|
|
@@ -85,6 +85,31 @@ stack:
|
|
|
85
85
|
# E2E framework — drives playwright-skill behaviour.
|
|
86
86
|
e2e: "" # e.g. "playwright" | "cypress" | ""
|
|
87
87
|
|
|
88
|
+
# Persistence layer. Drives /prd Schema Verification Gate, index strategies
|
|
89
|
+
# in prd-template, coder Database Index Invariant, api-perf-cost-auditor
|
|
90
|
+
# pricing model, audit-phase security-rules detection.
|
|
91
|
+
# Recognized values: "firestore" | "supabase" | "postgres" | "mysql" |
|
|
92
|
+
# "mongodb" | "dynamodb" | "sqlite" | "none" | "" (unset → skill asks).
|
|
93
|
+
database: ""
|
|
94
|
+
|
|
95
|
+
# Authentication provider. Drives security-reviewer rule sets and discovery
|
|
96
|
+
# E2E credential gathering defaults.
|
|
97
|
+
# Recognized values: "firebase-auth" | "supabase-auth" | "clerk" | "auth0" |
|
|
98
|
+
# "cognito" | "nextauth" | "lucia" | "custom" | "none" | "".
|
|
99
|
+
auth_provider: ""
|
|
100
|
+
|
|
101
|
+
# Application framework. Drives api-perf-gate caching defaults, bug
|
|
102
|
+
# logging-patterns, code-reviewer convention checks, coder file layout.
|
|
103
|
+
# Recognized values: "nextjs" | "remix" | "sveltekit" | "astro" | "nuxt" |
|
|
104
|
+
# "rails" | "django" | "fastapi" | "express" | "none" | "".
|
|
105
|
+
framework: ""
|
|
106
|
+
|
|
107
|
+
# Deployment target. Drives /new deploy commands, api-perf-cost-auditor
|
|
108
|
+
# cost model, framework-edit-gate path checks.
|
|
109
|
+
# Recognized values: "vercel" | "firebase" | "aws" | "gcp" | "cloudflare" |
|
|
110
|
+
# "render" | "fly" | "self-hosted" | "none" | "".
|
|
111
|
+
deployment: ""
|
|
112
|
+
|
|
88
113
|
# ─── FEATURES ────────────────────────────────────────────────────────────
|
|
89
114
|
# Explicit booleans. ALL keys must be present (true | false) once `baldart
|
|
90
115
|
# configure` has run. An absent flag means the user hasn't been asked yet —
|
package/package.json
CHANGED
|
@@ -284,6 +284,50 @@ function detect(cwd = process.cwd()) {
|
|
|
284
284
|
|
|
285
285
|
const mergeStrategyProbe = detectMergeStrategy(cwd);
|
|
286
286
|
|
|
287
|
+
// ---- Persistence layer (stack.database) --------------------------------
|
|
288
|
+
let detectedDatabase = '';
|
|
289
|
+
if (hasAnyDep('firebase', 'firebase-admin', '@google-cloud/firestore', '@firebase/firestore')) detectedDatabase = 'firestore';
|
|
290
|
+
else if (hasAnyDep('@supabase/supabase-js', '@supabase/ssr', '@supabase/auth-helpers-nextjs')) detectedDatabase = 'supabase';
|
|
291
|
+
else if (hasAnyDep('mongoose', 'mongodb')) detectedDatabase = 'mongodb';
|
|
292
|
+
else if (hasAnyDep('pg', 'postgres', '@neondatabase/serverless', '@vercel/postgres')) detectedDatabase = 'postgres';
|
|
293
|
+
else if (hasAnyDep('mysql', 'mysql2', '@planetscale/database')) detectedDatabase = 'mysql';
|
|
294
|
+
else if (hasAnyDep('@aws-sdk/client-dynamodb', 'dynamodb-toolbox')) detectedDatabase = 'dynamodb';
|
|
295
|
+
else if (hasAnyDep('better-sqlite3', 'sqlite3', '@libsql/client')) detectedDatabase = 'sqlite';
|
|
296
|
+
|
|
297
|
+
// ---- Auth provider (stack.auth_provider) -------------------------------
|
|
298
|
+
// Order matters: more specific deps win over the catch-all firebase/supabase.
|
|
299
|
+
let detectedAuthProvider = '';
|
|
300
|
+
if (hasAnyDep('@clerk/nextjs', '@clerk/clerk-sdk-node', '@clerk/clerk-js', '@clerk/backend')) detectedAuthProvider = 'clerk';
|
|
301
|
+
else if (hasAnyDep('@auth0/nextjs-auth0', '@auth0/auth0-react', 'auth0')) detectedAuthProvider = 'auth0';
|
|
302
|
+
else if (hasAnyDep('next-auth', '@auth/core')) detectedAuthProvider = 'nextauth';
|
|
303
|
+
else if (hasAnyDep('lucia', '@lucia-auth/core', '@lucia-auth/adapter-prisma')) detectedAuthProvider = 'lucia';
|
|
304
|
+
else if (hasAnyDep('amazon-cognito-identity-js', '@aws-sdk/client-cognito-identity-provider')) detectedAuthProvider = 'cognito';
|
|
305
|
+
else if (hasAnyDep('@supabase/auth-helpers-nextjs', '@supabase/ssr')) detectedAuthProvider = 'supabase-auth';
|
|
306
|
+
else if (hasAnyDep('firebase', '@firebase/auth', 'firebase-admin')) detectedAuthProvider = 'firebase-auth';
|
|
307
|
+
|
|
308
|
+
// ---- Application framework (stack.framework) ---------------------------
|
|
309
|
+
let detectedFramework = '';
|
|
310
|
+
if (hasDep('next')) detectedFramework = 'nextjs';
|
|
311
|
+
else if (hasAnyDep('@remix-run/react', '@remix-run/node', '@remix-run/server-runtime')) detectedFramework = 'remix';
|
|
312
|
+
else if (hasAnyDep('@sveltejs/kit', 'svelte')) detectedFramework = 'sveltekit';
|
|
313
|
+
else if (hasDep('astro')) detectedFramework = 'astro';
|
|
314
|
+
else if (hasDep('nuxt')) detectedFramework = 'nuxt';
|
|
315
|
+
else if (hasDep('express')) detectedFramework = 'express';
|
|
316
|
+
else if (exists('Gemfile') && /\bgem\s+['"]rails['"]/.test((readSafe('Gemfile') || ''))) detectedFramework = 'rails';
|
|
317
|
+
else if (exists('manage.py') || (exists('requirements.txt') && /\bdjango\b/i.test(readSafe('requirements.txt') || ''))) detectedFramework = 'django';
|
|
318
|
+
else if (exists('requirements.txt') && /\bfastapi\b/i.test(readSafe('requirements.txt') || '')) detectedFramework = 'fastapi';
|
|
319
|
+
|
|
320
|
+
// ---- Deployment target (stack.deployment) ------------------------------
|
|
321
|
+
let detectedDeployment = '';
|
|
322
|
+
if (exists('vercel.json') || hasDep('@vercel/node')) detectedDeployment = 'vercel';
|
|
323
|
+
else if (exists('firebase.json')) detectedDeployment = 'firebase';
|
|
324
|
+
else if (exists('wrangler.toml') || exists('wrangler.jsonc') || exists('wrangler.json')) detectedDeployment = 'cloudflare';
|
|
325
|
+
else if (exists('fly.toml')) detectedDeployment = 'fly';
|
|
326
|
+
else if (exists('render.yaml')) detectedDeployment = 'render';
|
|
327
|
+
else if (exists('amplify.yml') || exists('serverless.yml')) detectedDeployment = 'aws';
|
|
328
|
+
else if (exists('app.yaml') || exists('cloudbuild.yaml')) detectedDeployment = 'gcp';
|
|
329
|
+
// Note: bare Dockerfile → leave empty; user picks "self-hosted" or actual target.
|
|
330
|
+
|
|
287
331
|
const detected = {
|
|
288
332
|
paths: {
|
|
289
333
|
design_system: designSystemPath,
|
|
@@ -323,6 +367,10 @@ function detect(cwd = process.cwd()) {
|
|
|
323
367
|
forbidden: [],
|
|
324
368
|
},
|
|
325
369
|
testing: { e2e: e2eFramework },
|
|
370
|
+
database: detectedDatabase,
|
|
371
|
+
auth_provider: detectedAuthProvider,
|
|
372
|
+
framework: detectedFramework,
|
|
373
|
+
deployment: detectedDeployment,
|
|
326
374
|
// New: surface the detected monorepo + DS signals so skills can read them.
|
|
327
375
|
monorepo: isMonorepo ? {
|
|
328
376
|
detected: true,
|
|
@@ -692,6 +740,26 @@ async function interactivePrompts(merged, detected) {
|
|
|
692
740
|
merged.stack.testing.e2e || detected.stack.testing.e2e || ''
|
|
693
741
|
);
|
|
694
742
|
|
|
743
|
+
merged.stack.database = await promptForKey(
|
|
744
|
+
'Persistence layer ("firestore" / "supabase" / "postgres" / "mysql" / "mongodb" / "dynamodb" / "sqlite" / "none" — empty to leave unset)',
|
|
745
|
+
merged.stack.database || detected.stack.database || ''
|
|
746
|
+
);
|
|
747
|
+
|
|
748
|
+
merged.stack.auth_provider = await promptForKey(
|
|
749
|
+
'Auth provider ("firebase-auth" / "supabase-auth" / "clerk" / "auth0" / "cognito" / "nextauth" / "lucia" / "custom" / "none" — empty to leave unset)',
|
|
750
|
+
merged.stack.auth_provider || detected.stack.auth_provider || ''
|
|
751
|
+
);
|
|
752
|
+
|
|
753
|
+
merged.stack.framework = await promptForKey(
|
|
754
|
+
'Application framework ("nextjs" / "remix" / "sveltekit" / "astro" / "nuxt" / "rails" / "django" / "fastapi" / "express" / "none" — empty to leave unset)',
|
|
755
|
+
merged.stack.framework || detected.stack.framework || ''
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
merged.stack.deployment = await promptForKey(
|
|
759
|
+
'Deployment target ("vercel" / "firebase" / "aws" / "gcp" / "cloudflare" / "render" / "fly" / "self-hosted" / "none" — empty to leave unset)',
|
|
760
|
+
merged.stack.deployment || detected.stack.deployment || ''
|
|
761
|
+
);
|
|
762
|
+
|
|
695
763
|
return merged;
|
|
696
764
|
}
|
|
697
765
|
|
|
@@ -771,6 +839,10 @@ async function configure(opts = {}) {
|
|
|
771
839
|
`Charting libs: ${detected.stack.charting.canonical.join(', ') || '—'}`,
|
|
772
840
|
`Animation libs: ${detected.stack.animation.canonical.join(', ') || '—'}`,
|
|
773
841
|
`E2E framework: ${detected.stack.testing.e2e || '—'}`,
|
|
842
|
+
`Database: ${detected.stack.database || '—'}`,
|
|
843
|
+
`Auth provider: ${detected.stack.auth_provider || '—'}`,
|
|
844
|
+
`Framework: ${detected.stack.framework || '—'}`,
|
|
845
|
+
`Deployment: ${detected.stack.deployment || '—'}`,
|
|
774
846
|
]);
|
|
775
847
|
|
|
776
848
|
if (opts.nonInteractive) {
|
package/src/commands/update.js
CHANGED
|
@@ -452,7 +452,19 @@ async function update(options = {}) {
|
|
|
452
452
|
.filter((k) => !(k in (cur2.paths || {})));
|
|
453
453
|
const missingGit = Object.keys(tpl.git || {})
|
|
454
454
|
.filter((k) => !(k in (cur2.git || {})));
|
|
455
|
-
|
|
455
|
+
// Scalar (string) top-level stack.* keys — e.g. stack.database,
|
|
456
|
+
// stack.auth_provider, stack.framework, stack.deployment. Sub-object
|
|
457
|
+
// keys (charting, animation, testing) are intentionally skipped:
|
|
458
|
+
// their drift is handled by individual sub-prompts in configure.
|
|
459
|
+
const missingStack = Object.keys(tpl.stack || {})
|
|
460
|
+
.filter((k) => typeof tpl.stack[k] === 'string')
|
|
461
|
+
.filter((k) => !(k in (cur2.stack || {})));
|
|
462
|
+
const allMissing = [
|
|
463
|
+
...missingPaths,
|
|
464
|
+
...missingFeatures,
|
|
465
|
+
...missingGit.map((k) => `git.${k}`),
|
|
466
|
+
...missingStack.map((k) => `stack.${k}`),
|
|
467
|
+
];
|
|
456
468
|
if (allMissing.length) {
|
|
457
469
|
UI.newline();
|
|
458
470
|
UI.warning(`New config keys in this version: ${allMissing.join(', ')}.`);
|