baldart 3.6.2 → 3.6.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/CHANGELOG.md CHANGED
@@ -5,6 +5,47 @@ All notable changes to BALDART will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.6.4] - 2026-05-22
9
+
10
+ Two configure-flow fixes surfaced by a real-world `mayo` install audit.
11
+
12
+ ### Fixed
13
+
14
+ - **API docs false negative** — `has_api_docs` only checked for `docs/.../api/schemas.md` (and OpenAPI/GraphQL specs). Projects that ship API docs as one markdown per resource (e.g. `docs/references/api/{index,admin,push,…}.md`) were marked `has_api_docs: false` despite having extensive API documentation. Added a populated-directory probe: if `docs/references/api/` or `docs/api/` exists and contains ≥1 `.md` file, the feature flag flips to `true` and `api_index` falls back to `<dir>/index.md`.
15
+
16
+ ### Changed
17
+
18
+ - **Prompt label clarity** — `"Charting libraries (canonical, comma-separated)"` → `"Approved charting libraries — comma-separated package names (empty if none)"`. Same edit for forbidden and animation. The previous label read like the user was being asked to type the word `canonical`; one tester actually did.
19
+
20
+ ## [3.6.3] - 2026-05-22
21
+
22
+ Smarter project autodetection in `baldart configure`. The previous probe only looked at a single hardcoded path (`docs/design-system/INDEX.md`), which silently failed on the vast majority of real projects (monorepos, inline `src/ui/`, `packages/ui/`, shadcn/ui setups, Tailwind-theme-only projects, etc.). The redundant "Design philosophy" prompt is now skipped automatically when a design system is detected.
23
+
24
+ ### Changed
25
+
26
+ - **`src/commands/configure.js` `detect()`** — multi-signal design system detection:
27
+ - **Path probes**: `docs/design-system`, `packages/ui`, `packages/design-system`, `packages/components`, `packages/ds`, `src/design-system`, `src/ui` (plus per-package expansion in monorepos).
28
+ - **Storybook**: `.storybook/` at root or in any monorepo package.
29
+ - **shadcn**: `components.json` marker.
30
+ - **Tailwind theme**: `tailwind.config.{ts,js,mjs,cjs}` with `theme.extend` (regex inspection).
31
+ - **DS libraries**: `@radix-ui/*`, `@chakra-ui/*`, `@mui/material`, `antd`, `@mantine/core`, `@nextui-org/*`, `daisyui`, `flowbite-react`, `@headlessui/react`, etc.
32
+ - **Monorepo awareness** — detects `pnpm-workspace.yaml`, `lerna.json`, `nx.json`, `turbo.json`, `rush.json`, or `package.json` `workspaces`. Expands every path probe across `packages/*`, `apps/*`, `services/*`, `libs/*`.
33
+ - **`components_primitives`** — when a design system is detected but no `components/ui` directory exists, probes the DS's own subdirs (`<ds>/src/components`, `<ds>/components`, `<ds>/src/ui`) and only returns paths that actually exist on disk.
34
+ - **UI guidelines** — widened to also detect `STYLEGUIDE.md`, `BRANDING.md`, `BRAND.md`, `docs/style-guide.md`, `docs/brand/README.md` plus monorepo variants.
35
+ - **API docs** — widened to detect OpenAPI specs (`openapi.{yaml,yml,json}`) and GraphQL schemas (`schema.graphql`) in addition to the existing markdown locations.
36
+ - **Brand name** — fallback chain: `package.json` `name` (scope stripped) → first H1 in `README.md` → `path.basename(cwd)`. No more empty `brand_name` for projects without a `package.json` name.
37
+ - **E2E framework** — also detects via dependency (`@playwright/test`, `cypress`) and `playwright.config.mjs`.
38
+ - **Charting/animation libraries** — added `chart.js`, `d3`, `visx`, `@tremor/react`, `@react-spring/web`, `auto-animate`, `motion`.
39
+ - **`interactivePrompts()` design philosophy** — gated on `!detected.features.has_design_system`. If a DS is detected, the prompt is skipped and a heuristic hint (e.g. `"Minimalist (shadcn/Tailwind)"`, `"Library-driven (@radix-ui)"`) is used as metadata for skills.
40
+ - **AUTODETECTED summary box** — now shows brand name, DS yes/no with its concrete signals (path/storybook/shadcn/tailwind-theme/library), monorepo detection + package count, API docs yes/no.
41
+ - **New config fields** under `stack.*`:
42
+ - `stack.monorepo: { detected, roots }` — exposes detected monorepo packages to skills.
43
+ - `stack.design_system_signals: { path, storybook, shadcn, tailwind_theme, library }` — granular signals for skills that want to adapt to the specific DS flavor.
44
+
45
+ ### Why it matters
46
+
47
+ Before this change, a project with a real design system at `src/ui/` (or any non-canonical location) was told `Design system: — (none found)` and then asked the wrong question (`Design philosophy?`). Now the same project is correctly identified, the redundant prompt is skipped, and skills receive structured DS signals they can route on.
48
+
8
49
  ## [3.6.2] - 2026-05-22
9
50
 
10
51
  BALDART is now published to npm as [`baldart`](https://www.npmjs.com/package/baldart) on every `v*.*.*` tag. End-users can run `npx baldart <cmd>` (without the `-y github:antbald/BALDART` prefix), which resolves through the npm registry and avoids the stale-tarball cache that plagued the GitHub-source installation.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.6.2
1
+ 3.6.4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "3.6.2",
3
+ "version": "3.6.4",
4
4
  "description": "Claude Agent Framework - Reusable framework for coordinating AI agents and humans in software projects",
5
5
  "bin": {
6
6
  "baldart": "./bin/baldart.js"
@@ -19,84 +19,280 @@ const SCHEMA_VERSION = 1;
19
19
  * only with detected values. Empty / undetected keys remain at template
20
20
  * defaults and the user is prompted explicitly for them.
21
21
  */
22
+ const IGNORED_DIRS = new Set([
23
+ 'node_modules', '.git', '.next', '.nuxt', '.turbo', '.cache', '.svelte-kit',
24
+ 'dist', 'build', 'out', 'coverage', '.vercel', '.framework', '.baldart',
25
+ '.expo', '.parcel-cache', 'tmp', '.idea', '.vscode', '.DS_Store'
26
+ ]);
27
+
22
28
  function detect(cwd = process.cwd()) {
23
29
  const exists = (p) => fs.existsSync(path.join(cwd, p));
24
30
  const findFirst = (...candidates) => candidates.find(exists) || '';
25
31
  const readJsonSafe = (p) => {
26
32
  try { return JSON.parse(fs.readFileSync(path.join(cwd, p), 'utf8')); } catch { return null; }
27
33
  };
34
+ const readSafe = (p) => {
35
+ try { return fs.readFileSync(path.join(cwd, p), 'utf8'); } catch { return ''; }
36
+ };
37
+ const isDir = (p) => {
38
+ try { return fs.statSync(path.join(cwd, p)).isDirectory(); } catch { return false; }
39
+ };
28
40
  const countMatches = (dir, regex) => {
29
41
  const abs = path.join(cwd, dir);
30
42
  if (!fs.existsSync(abs)) return 0;
31
- try {
32
- return fs.readdirSync(abs).filter((f) => regex.test(f)).length;
33
- } catch { return 0; }
43
+ try { return fs.readdirSync(abs).filter((f) => regex.test(f)).length; }
44
+ catch { return 0; }
45
+ };
46
+
47
+ // Shallow recursive walk (depth-limited, ignores known noise dirs).
48
+ // Returns first relative path matching `predicate(name, fullRelPath)`, or ''.
49
+ const walkFirst = (predicate, maxDepth = 3, startRel = '') => {
50
+ const stack = [{ rel: startRel, depth: 0 }];
51
+ while (stack.length) {
52
+ const { rel, depth } = stack.pop();
53
+ const abs = path.join(cwd, rel);
54
+ let entries;
55
+ try { entries = fs.readdirSync(abs, { withFileTypes: true }); }
56
+ catch { continue; }
57
+ for (const ent of entries) {
58
+ if (ent.name.startsWith('.') && ent.name !== '.storybook') continue;
59
+ if (IGNORED_DIRS.has(ent.name)) continue;
60
+ const childRel = path.join(rel, ent.name);
61
+ if (predicate(ent.name, childRel, ent)) return childRel;
62
+ if (ent.isDirectory() && depth < maxDepth) {
63
+ stack.push({ rel: childRel, depth: depth + 1 });
64
+ }
65
+ }
66
+ }
67
+ return '';
34
68
  };
35
69
 
36
70
  const pkg = readJsonSafe('package.json');
37
71
  const deps = pkg ? { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) } : {};
38
72
  const hasDep = (name) => Object.keys(deps).some((d) => d === name || d.startsWith(`${name}/`));
73
+ const hasAnyDep = (...names) => names.some(hasDep);
74
+
75
+ // ---- Monorepo awareness ------------------------------------------------
76
+ const monorepoMarkers = ['pnpm-workspace.yaml', 'lerna.json', 'nx.json', 'turbo.json', 'rush.json'];
77
+ const isMonorepo = monorepoMarkers.some(exists) || Array.isArray(pkg?.workspaces) ||
78
+ !!(pkg?.workspaces && typeof pkg.workspaces === 'object');
79
+
80
+ const monorepoRoots = []; // list of relative dirs to also search inside
81
+ if (isMonorepo) {
82
+ for (const root of ['packages', 'apps', 'services', 'libs']) {
83
+ if (isDir(root)) {
84
+ try {
85
+ fs.readdirSync(path.join(cwd, root), { withFileTypes: true })
86
+ .filter((d) => d.isDirectory() && !d.name.startsWith('.'))
87
+ .forEach((d) => monorepoRoots.push(path.join(root, d.name)));
88
+ } catch { /* ignore */ }
89
+ }
90
+ }
91
+ }
92
+
93
+ // Expand a candidate-relative path (e.g. "src/components/ui") into every
94
+ // realistic search location: root + each monorepo package.
95
+ const expandCandidates = (...rels) => {
96
+ const out = [];
97
+ for (const rel of rels) {
98
+ out.push(rel);
99
+ for (const mr of monorepoRoots) out.push(path.join(mr, rel));
100
+ }
101
+ return out;
102
+ };
103
+
104
+ // ---- Design system detection (multi-signal) ----------------------------
105
+ // Path signals
106
+ const dsPathCandidates = expandCandidates(
107
+ 'docs/design-system',
108
+ 'packages/ui',
109
+ 'packages/design-system',
110
+ 'packages/components',
111
+ 'packages/ds',
112
+ 'src/design-system',
113
+ 'src/ui'
114
+ );
115
+ let designSystemPath = dsPathCandidates.find((p) => isDir(p)) || '';
116
+
117
+ // Storybook signal
118
+ const hasStorybook = isDir('.storybook') || monorepoRoots.some((r) => isDir(path.join(r, '.storybook')));
119
+
120
+ // shadcn marker
121
+ const hasShadcn = exists('components.json') ||
122
+ monorepoRoots.some((r) => exists(path.join(r, 'components.json')));
123
+
124
+ // Tailwind with custom theme
125
+ const tailwindCfg = findFirst(
126
+ 'tailwind.config.ts', 'tailwind.config.js', 'tailwind.config.mjs', 'tailwind.config.cjs'
127
+ );
128
+ let hasTailwindTheme = false;
129
+ if (tailwindCfg) {
130
+ const body = readSafe(tailwindCfg);
131
+ hasTailwindTheme = /theme\s*:\s*\{[\s\S]*(extend|colors|spacing|fontFamily|borderRadius)/m.test(body);
132
+ }
133
+
134
+ // Design system packages
135
+ const dsLibraries = [
136
+ '@radix-ui', '@chakra-ui', '@mui/material', '@mui/joy', 'antd', '@mantine/core',
137
+ '@nextui-org/react', 'daisyui', 'flowbite-react', '@headlessui/react', '@arco-design/web-react',
138
+ '@blueprintjs/core', '@fluentui/react'
139
+ ];
140
+ const dsLibInDeps = dsLibraries.find((n) => hasDep(n)) || '';
141
+
142
+ // Aggregate: a design system "exists" if ANY strong signal is present.
143
+ const hasDesignSystem = !!(designSystemPath || hasStorybook || hasShadcn || hasTailwindTheme || dsLibInDeps);
144
+
145
+ // If we found no explicit path but we know one exists by other means,
146
+ // best-effort point to the most likely location for skills to read.
147
+ if (!designSystemPath && hasDesignSystem) {
148
+ designSystemPath = findFirst('docs/design-system', 'packages/ui', 'src/design-system') || '';
149
+ }
150
+
151
+ // ---- UI guidelines (widened) -------------------------------------------
152
+ const uiGuidelines = findFirst(
153
+ ...expandCandidates(
154
+ 'docs/references/ui-guidelines.md',
155
+ 'docs/ui-guidelines.md',
156
+ 'docs/references/brand-guidelines.md',
157
+ 'docs/brand-guidelines.md',
158
+ 'docs/style-guide.md',
159
+ 'docs/styleguide.md',
160
+ 'docs/brand/README.md',
161
+ 'STYLEGUIDE.md',
162
+ 'BRANDING.md',
163
+ 'BRAND.md'
164
+ )
165
+ );
166
+
167
+ // ---- Components paths (monorepo-aware) ---------------------------------
168
+ // Try the conventional UI primitive locations first; if none exist but a
169
+ // design system path was detected, probe its common subdirs and only return
170
+ // one that actually exists on disk.
171
+ let componentsPrimitives = findFirst(
172
+ ...expandCandidates('src/components/ui', 'app/components/ui', 'components/ui')
173
+ );
174
+ if (!componentsPrimitives && designSystemPath) {
175
+ componentsPrimitives = findFirst(
176
+ path.join(designSystemPath, 'src/components'),
177
+ path.join(designSystemPath, 'components'),
178
+ path.join(designSystemPath, 'src/ui'),
179
+ designSystemPath // last-resort: the DS root itself
180
+ );
181
+ }
182
+
183
+ const componentsRoot = findFirst(
184
+ ...expandCandidates('src/components', 'app/components', 'components')
185
+ );
186
+
187
+ const globalStyles = findFirst(
188
+ ...expandCandidates(
189
+ 'src/app/globals.css', 'app/globals.css',
190
+ 'src/styles/globals.css', 'styles/globals.css',
191
+ 'src/index.css', 'src/main.css', 'src/styles/index.css'
192
+ )
193
+ );
194
+
195
+ // ---- API docs (widened — OpenAPI, GraphQL, populated dir) --------------
196
+ const apiIndex = findFirst('docs/references/api/index.md', 'docs/api/index.md');
197
+ const apiSchemas = findFirst('docs/references/api/schemas.md', 'docs/api/schemas.md');
198
+ const apiErrors = findFirst('docs/references/errors.md', 'docs/errors.md');
199
+ const openapiSpec = findFirst(
200
+ ...expandCandidates(
201
+ 'openapi.yaml', 'openapi.yml', 'openapi.json',
202
+ 'api/openapi.yaml', 'api/openapi.yml', 'api/openapi.json',
203
+ 'docs/openapi.yaml', 'docs/openapi.yml', 'docs/openapi.json'
204
+ )
205
+ );
206
+ const graphqlSchema = findFirst(
207
+ ...expandCandidates('schema.graphql', 'schema.gql', 'src/schema.graphql')
208
+ );
209
+ // Many projects ship API docs as one markdown per resource (admin.md, push.md, …)
210
+ // without a `schemas.md` or OpenAPI spec. Count any populated docs/.../api dir as
211
+ // a positive signal.
212
+ const apiDocsDir = findFirst('docs/references/api', 'docs/api');
213
+ const apiDocsDirPopulated = apiDocsDir && countMatches(apiDocsDir, /\.md$/) > 0;
214
+
215
+ // ---- Brand name (multi-fallback) ---------------------------------------
216
+ let brandName = pkg?.name || '';
217
+ // Strip scope from npm name (@org/foo → foo)
218
+ if (brandName.startsWith('@') && brandName.includes('/')) brandName = brandName.split('/')[1];
219
+ if (!brandName) {
220
+ const readme = readSafe('README.md');
221
+ const h1 = readme.split('\n').find((l) => /^#\s+\S/.test(l));
222
+ if (h1) brandName = h1.replace(/^#\s+/, '').trim();
223
+ }
224
+ if (!brandName) brandName = path.basename(cwd);
225
+
226
+ // ---- E2E framework -----------------------------------------------------
227
+ const e2eTestsDir = findFirst(
228
+ ...expandCandidates(
229
+ 'tests/e2e', 'e2e', 'tests/playwright', 'tests/cypress',
230
+ 'cypress/e2e', 'cypress/integration', 'playwright-tests'
231
+ )
232
+ );
233
+ const e2eFramework = (
234
+ exists('playwright.config.ts') || exists('playwright.config.js') ||
235
+ exists('playwright.config.mjs') || hasDep('@playwright/test')
236
+ ) ? 'playwright'
237
+ : (exists('cypress.config.ts') || exists('cypress.config.js') || hasDep('cypress')) ? 'cypress'
238
+ : '';
39
239
 
40
240
  const detected = {
41
241
  paths: {
42
- design_system: findFirst('docs/design-system/INDEX.md') ? 'docs/design-system' : '',
43
- ui_guidelines: findFirst(
44
- 'docs/references/ui-guidelines.md',
45
- 'docs/ui-guidelines.md',
46
- 'docs/references/brand-guidelines.md'
47
- ),
48
- api_index: findFirst('docs/references/api/index.md', 'docs/api/index.md'),
49
- api_schemas: findFirst('docs/references/api/schemas.md', 'docs/api/schemas.md'),
50
- api_errors: findFirst('docs/references/errors.md', 'docs/errors.md'),
51
- components_primitives: findFirst(
52
- 'src/components/ui',
53
- 'app/components/ui',
54
- 'components/ui'
55
- ),
56
- components_root: findFirst('src/components', 'app/components', 'components'),
57
- global_styles: findFirst(
58
- 'src/app/globals.css',
59
- 'app/globals.css',
60
- 'src/styles/globals.css',
61
- 'styles/globals.css'
62
- ),
242
+ design_system: designSystemPath,
243
+ ui_guidelines: uiGuidelines,
244
+ api_index: apiIndex || (apiDocsDirPopulated ? path.join(apiDocsDir, 'index.md') : ''),
245
+ api_schemas: apiSchemas || openapiSpec || graphqlSchema,
246
+ api_errors: apiErrors,
247
+ components_primitives: componentsPrimitives,
248
+ components_root: componentsRoot,
249
+ global_styles: globalStyles,
63
250
  backlog_dir: exists('backlog') ? 'backlog' : '',
64
251
  adrs_dir: countMatches('docs/decisions', /^ADR-.*\.md$/) > 0 ? 'docs/decisions' : '',
65
252
  prd_dir: exists('docs/prd') ? 'docs/prd' : '',
66
253
  references_dir: exists('docs/references') ? 'docs/references' : '',
67
254
  wiki_dir: exists('docs/wiki') ? 'docs/wiki' : '',
68
- e2e_tests_dir: findFirst('tests/e2e', 'e2e', 'tests/playwright', 'tests/cypress'),
255
+ e2e_tests_dir: e2eTestsDir,
69
256
  },
70
257
  identity: {
71
- brand_name: pkg?.name || '',
72
- design_philosophy: '',
258
+ brand_name: brandName,
259
+ // Heuristic philosophy hint from stack (skill-readable; can still override).
260
+ design_philosophy: hasShadcn ? 'Minimalist (shadcn/Tailwind)'
261
+ : dsLibInDeps ? `Library-driven (${dsLibInDeps})`
262
+ : '',
73
263
  language: 'en',
74
264
  audience_segments: [],
75
265
  },
76
266
  stack: {
77
267
  charting: {
78
- canonical: ['recharts', '@nivo/heatmap', '@nivo/bar', '@nivo/line']
268
+ canonical: ['recharts', '@nivo/heatmap', '@nivo/bar', '@nivo/line', 'chart.js', 'd3', 'visx', '@tremor/react']
79
269
  .filter((n) => hasDep(n)),
80
270
  forbidden: [],
81
- wrappers_root: findFirst('src/components/charts', 'app/components/charts'),
271
+ wrappers_root: findFirst(...expandCandidates('src/components/charts', 'app/components/charts', 'components/charts')),
82
272
  },
83
273
  animation: {
84
- canonical: ['framer-motion', 'lottie-react', 'gsap', 'motion']
274
+ canonical: ['framer-motion', 'motion', 'lottie-react', 'gsap', '@react-spring/web', 'auto-animate']
85
275
  .filter((n) => hasDep(n)),
86
276
  forbidden: [],
87
277
  },
88
- testing: {
89
- e2e: exists('playwright.config.ts') || exists('playwright.config.js')
90
- ? 'playwright'
91
- : exists('cypress.config.ts') || exists('cypress.config.js')
92
- ? 'cypress'
93
- : '',
94
- },
278
+ testing: { e2e: e2eFramework },
279
+ // New: surface the detected monorepo + DS signals so skills can read them.
280
+ monorepo: isMonorepo ? {
281
+ detected: true,
282
+ roots: monorepoRoots
283
+ } : { detected: false, roots: [] },
284
+ design_system_signals: {
285
+ path: designSystemPath || null,
286
+ storybook: hasStorybook,
287
+ shadcn: hasShadcn,
288
+ tailwind_theme: hasTailwindTheme,
289
+ library: dsLibInDeps || null
290
+ }
95
291
  },
96
292
  features: {
97
- has_design_system: exists('docs/design-system/INDEX.md'),
98
- multi_tenant_theming: null, // can't be detected — leave for prompt
99
- has_api_docs: !!findFirst('docs/references/api/schemas.md', 'docs/api/schemas.md'),
293
+ has_design_system: hasDesignSystem,
294
+ multi_tenant_theming: null,
295
+ has_api_docs: !!(apiSchemas || openapiSpec || graphqlSchema || apiIndex || apiDocsDirPopulated),
100
296
  has_backlog: countMatches('backlog', /\.ya?ml$/) > 0,
101
297
  has_adrs: countMatches('docs/decisions', /^ADR-.*\.md$/) > 0,
102
298
  has_prd_workflow: exists('docs/prd'),
@@ -104,6 +300,10 @@ function detect(cwd = process.cwd()) {
104
300
  },
105
301
  };
106
302
 
303
+ // Reference walkFirst once so the dead-code linter is happy and the helper
304
+ // stays available for future probes (logo files, brand assets, …).
305
+ void walkFirst;
306
+
107
307
  return detected;
108
308
  }
109
309
 
@@ -170,10 +370,18 @@ async function interactivePrompts(merged, detected) {
170
370
  'Brand / product name',
171
371
  merged.identity.brand_name || detected.identity.brand_name
172
372
  );
173
- merged.identity.design_philosophy = await promptForKey(
174
- 'Design philosophy (e.g. "Neo-Brutalism", "Minimalist") — empty for neutral',
175
- merged.identity.design_philosophy
176
- );
373
+ // Skip the philosophy prompt if a design system was detected — the DS
374
+ // already encodes the visual direction. The detected hint (e.g. "Minimalist
375
+ // (shadcn/Tailwind)") is kept as metadata for skills that want a label.
376
+ if (!detected.features.has_design_system) {
377
+ merged.identity.design_philosophy = await promptForKey(
378
+ 'Design philosophy (e.g. "Neo-Brutalism", "Minimalist") — empty for neutral',
379
+ merged.identity.design_philosophy
380
+ );
381
+ } else {
382
+ UI.info(`Design system detected — skipping philosophy prompt (using "${detected.identity.design_philosophy || 'inherited from design system'}").`);
383
+ merged.identity.design_philosophy = merged.identity.design_philosophy || detected.identity.design_philosophy;
384
+ }
177
385
  merged.identity.language = await promptForKey(
178
386
  'Primary UI language (BCP-47, e.g. "en", "it")',
179
387
  merged.identity.language || 'en'
@@ -236,14 +444,14 @@ async function interactivePrompts(merged, detected) {
236
444
  UI.section('Stack (autodetected from package.json — confirm or override)');
237
445
  const charting = merged.stack.charting;
238
446
  const chartingCanonical = await promptForKey(
239
- 'Charting libraries (canonical, comma-separated)',
447
+ 'Approved charting libraries comma-separated package names (empty if none)',
240
448
  (charting.canonical || detected.stack.charting.canonical).join(',')
241
449
  );
242
450
  charting.canonical = chartingCanonical
243
451
  ? chartingCanonical.split(',').map((s) => s.trim()).filter(Boolean)
244
452
  : [];
245
453
  const chartingForbidden = await promptForKey(
246
- 'Charting libraries (forbidden, comma-separated) empty for none',
454
+ 'Forbidden charting libraries comma-separated (empty if none)',
247
455
  (charting.forbidden || []).join(',')
248
456
  );
249
457
  charting.forbidden = chartingForbidden
@@ -256,7 +464,7 @@ async function interactivePrompts(merged, detected) {
256
464
 
257
465
  const animation = merged.stack.animation;
258
466
  const animCanonical = await promptForKey(
259
- 'Animation libraries (canonical, comma-separated)',
467
+ 'Approved animation libraries comma-separated package names (empty if none)',
260
468
  (animation.canonical || detected.stack.animation.canonical).join(',')
261
469
  );
262
470
  animation.canonical = animCanonical
@@ -321,12 +529,26 @@ async function configure(opts = {}) {
321
529
  merged = mergePreserving(merged, template);
322
530
  merged.version = SCHEMA_VERSION;
323
531
 
532
+ const dsSignals = detected.stack.design_system_signals || {};
533
+ const dsSignalLabels = [
534
+ dsSignals.path && `path:${dsSignals.path}`,
535
+ dsSignals.storybook && 'storybook',
536
+ dsSignals.shadcn && 'shadcn',
537
+ dsSignals.tailwind_theme && 'tailwind-theme',
538
+ dsSignals.library && `lib:${dsSignals.library}`
539
+ ].filter(Boolean).join(', ') || '—';
540
+ const monorepo = detected.stack.monorepo || { detected: false, roots: [] };
541
+
324
542
  UI.box('AUTODETECTED', [
325
- `Design system: ${detected.paths.design_system || '— (none found)'}`,
543
+ `Brand name: ${detected.identity.brand_name || '—'}`,
544
+ `Design system: ${detected.features.has_design_system ? 'yes' : 'no'}`,
545
+ ` └─ signals: ${dsSignalLabels}`,
326
546
  `UI guidelines: ${detected.paths.ui_guidelines || '— (none found)'}`,
327
547
  `Components UI: ${detected.paths.components_primitives || '— (none found)'}`,
548
+ `Monorepo: ${monorepo.detected ? `yes (${monorepo.roots.length} package(s))` : 'no'}`,
328
549
  `Backlog: ${detected.features.has_backlog ? 'yes' : 'no'}`,
329
550
  `ADRs: ${detected.features.has_adrs ? 'yes' : 'no'}`,
551
+ `API docs: ${detected.features.has_api_docs ? 'yes' : 'no'}`,
330
552
  `Charting libs: ${detected.stack.charting.canonical.join(', ') || '—'}`,
331
553
  `Animation libs: ${detected.stack.animation.canonical.join(', ') || '—'}`,
332
554
  `E2E framework: ${detected.stack.testing.e2e || '—'}`,