baldart 3.6.2 → 3.6.3
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 +29 -0
- package/VERSION +1 -1
- package/package.json +1 -1
- package/src/commands/configure.js +262 -45
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,35 @@ 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.3] - 2026-05-22
|
|
9
|
+
|
|
10
|
+
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.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **`src/commands/configure.js` `detect()`** — multi-signal design system detection:
|
|
15
|
+
- **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).
|
|
16
|
+
- **Storybook**: `.storybook/` at root or in any monorepo package.
|
|
17
|
+
- **shadcn**: `components.json` marker.
|
|
18
|
+
- **Tailwind theme**: `tailwind.config.{ts,js,mjs,cjs}` with `theme.extend` (regex inspection).
|
|
19
|
+
- **DS libraries**: `@radix-ui/*`, `@chakra-ui/*`, `@mui/material`, `antd`, `@mantine/core`, `@nextui-org/*`, `daisyui`, `flowbite-react`, `@headlessui/react`, etc.
|
|
20
|
+
- **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/*`.
|
|
21
|
+
- **`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.
|
|
22
|
+
- **UI guidelines** — widened to also detect `STYLEGUIDE.md`, `BRANDING.md`, `BRAND.md`, `docs/style-guide.md`, `docs/brand/README.md` plus monorepo variants.
|
|
23
|
+
- **API docs** — widened to detect OpenAPI specs (`openapi.{yaml,yml,json}`) and GraphQL schemas (`schema.graphql`) in addition to the existing markdown locations.
|
|
24
|
+
- **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.
|
|
25
|
+
- **E2E framework** — also detects via dependency (`@playwright/test`, `cypress`) and `playwright.config.mjs`.
|
|
26
|
+
- **Charting/animation libraries** — added `chart.js`, `d3`, `visx`, `@tremor/react`, `@react-spring/web`, `auto-animate`, `motion`.
|
|
27
|
+
- **`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.
|
|
28
|
+
- **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.
|
|
29
|
+
- **New config fields** under `stack.*`:
|
|
30
|
+
- `stack.monorepo: { detected, roots }` — exposes detected monorepo packages to skills.
|
|
31
|
+
- `stack.design_system_signals: { path, storybook, shadcn, tailwind_theme, library }` — granular signals for skills that want to adapt to the specific DS flavor.
|
|
32
|
+
|
|
33
|
+
### Why it matters
|
|
34
|
+
|
|
35
|
+
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.
|
|
36
|
+
|
|
8
37
|
## [3.6.2] - 2026-05-22
|
|
9
38
|
|
|
10
39
|
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.
|
|
1
|
+
3.6.3
|
package/package.json
CHANGED
|
@@ -19,84 +19,275 @@ 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
|
-
|
|
33
|
-
|
|
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) -----------------------------
|
|
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
|
+
|
|
210
|
+
// ---- Brand name (multi-fallback) ---------------------------------------
|
|
211
|
+
let brandName = pkg?.name || '';
|
|
212
|
+
// Strip scope from npm name (@org/foo → foo)
|
|
213
|
+
if (brandName.startsWith('@') && brandName.includes('/')) brandName = brandName.split('/')[1];
|
|
214
|
+
if (!brandName) {
|
|
215
|
+
const readme = readSafe('README.md');
|
|
216
|
+
const h1 = readme.split('\n').find((l) => /^#\s+\S/.test(l));
|
|
217
|
+
if (h1) brandName = h1.replace(/^#\s+/, '').trim();
|
|
218
|
+
}
|
|
219
|
+
if (!brandName) brandName = path.basename(cwd);
|
|
220
|
+
|
|
221
|
+
// ---- E2E framework -----------------------------------------------------
|
|
222
|
+
const e2eTestsDir = findFirst(
|
|
223
|
+
...expandCandidates(
|
|
224
|
+
'tests/e2e', 'e2e', 'tests/playwright', 'tests/cypress',
|
|
225
|
+
'cypress/e2e', 'cypress/integration', 'playwright-tests'
|
|
226
|
+
)
|
|
227
|
+
);
|
|
228
|
+
const e2eFramework = (
|
|
229
|
+
exists('playwright.config.ts') || exists('playwright.config.js') ||
|
|
230
|
+
exists('playwright.config.mjs') || hasDep('@playwright/test')
|
|
231
|
+
) ? 'playwright'
|
|
232
|
+
: (exists('cypress.config.ts') || exists('cypress.config.js') || hasDep('cypress')) ? 'cypress'
|
|
233
|
+
: '';
|
|
39
234
|
|
|
40
235
|
const detected = {
|
|
41
236
|
paths: {
|
|
42
|
-
design_system:
|
|
43
|
-
ui_guidelines:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
),
|
|
237
|
+
design_system: designSystemPath,
|
|
238
|
+
ui_guidelines: uiGuidelines,
|
|
239
|
+
api_index: apiIndex,
|
|
240
|
+
api_schemas: apiSchemas || openapiSpec || graphqlSchema,
|
|
241
|
+
api_errors: apiErrors,
|
|
242
|
+
components_primitives: componentsPrimitives,
|
|
243
|
+
components_root: componentsRoot,
|
|
244
|
+
global_styles: globalStyles,
|
|
63
245
|
backlog_dir: exists('backlog') ? 'backlog' : '',
|
|
64
246
|
adrs_dir: countMatches('docs/decisions', /^ADR-.*\.md$/) > 0 ? 'docs/decisions' : '',
|
|
65
247
|
prd_dir: exists('docs/prd') ? 'docs/prd' : '',
|
|
66
248
|
references_dir: exists('docs/references') ? 'docs/references' : '',
|
|
67
249
|
wiki_dir: exists('docs/wiki') ? 'docs/wiki' : '',
|
|
68
|
-
e2e_tests_dir:
|
|
250
|
+
e2e_tests_dir: e2eTestsDir,
|
|
69
251
|
},
|
|
70
252
|
identity: {
|
|
71
|
-
brand_name:
|
|
72
|
-
|
|
253
|
+
brand_name: brandName,
|
|
254
|
+
// Heuristic philosophy hint from stack (skill-readable; can still override).
|
|
255
|
+
design_philosophy: hasShadcn ? 'Minimalist (shadcn/Tailwind)'
|
|
256
|
+
: dsLibInDeps ? `Library-driven (${dsLibInDeps})`
|
|
257
|
+
: '',
|
|
73
258
|
language: 'en',
|
|
74
259
|
audience_segments: [],
|
|
75
260
|
},
|
|
76
261
|
stack: {
|
|
77
262
|
charting: {
|
|
78
|
-
canonical: ['recharts', '@nivo/heatmap', '@nivo/bar', '@nivo/line']
|
|
263
|
+
canonical: ['recharts', '@nivo/heatmap', '@nivo/bar', '@nivo/line', 'chart.js', 'd3', 'visx', '@tremor/react']
|
|
79
264
|
.filter((n) => hasDep(n)),
|
|
80
265
|
forbidden: [],
|
|
81
|
-
wrappers_root: findFirst('src/components/charts', 'app/components/charts'),
|
|
266
|
+
wrappers_root: findFirst(...expandCandidates('src/components/charts', 'app/components/charts', 'components/charts')),
|
|
82
267
|
},
|
|
83
268
|
animation: {
|
|
84
|
-
canonical: ['framer-motion', 'lottie-react', 'gsap', '
|
|
269
|
+
canonical: ['framer-motion', 'motion', 'lottie-react', 'gsap', '@react-spring/web', 'auto-animate']
|
|
85
270
|
.filter((n) => hasDep(n)),
|
|
86
271
|
forbidden: [],
|
|
87
272
|
},
|
|
88
|
-
testing: {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
273
|
+
testing: { e2e: e2eFramework },
|
|
274
|
+
// New: surface the detected monorepo + DS signals so skills can read them.
|
|
275
|
+
monorepo: isMonorepo ? {
|
|
276
|
+
detected: true,
|
|
277
|
+
roots: monorepoRoots
|
|
278
|
+
} : { detected: false, roots: [] },
|
|
279
|
+
design_system_signals: {
|
|
280
|
+
path: designSystemPath || null,
|
|
281
|
+
storybook: hasStorybook,
|
|
282
|
+
shadcn: hasShadcn,
|
|
283
|
+
tailwind_theme: hasTailwindTheme,
|
|
284
|
+
library: dsLibInDeps || null
|
|
285
|
+
}
|
|
95
286
|
},
|
|
96
287
|
features: {
|
|
97
|
-
has_design_system:
|
|
98
|
-
multi_tenant_theming: null,
|
|
99
|
-
has_api_docs: !!
|
|
288
|
+
has_design_system: hasDesignSystem,
|
|
289
|
+
multi_tenant_theming: null,
|
|
290
|
+
has_api_docs: !!(apiSchemas || openapiSpec || graphqlSchema),
|
|
100
291
|
has_backlog: countMatches('backlog', /\.ya?ml$/) > 0,
|
|
101
292
|
has_adrs: countMatches('docs/decisions', /^ADR-.*\.md$/) > 0,
|
|
102
293
|
has_prd_workflow: exists('docs/prd'),
|
|
@@ -104,6 +295,10 @@ function detect(cwd = process.cwd()) {
|
|
|
104
295
|
},
|
|
105
296
|
};
|
|
106
297
|
|
|
298
|
+
// Reference walkFirst once so the dead-code linter is happy and the helper
|
|
299
|
+
// stays available for future probes (logo files, brand assets, …).
|
|
300
|
+
void walkFirst;
|
|
301
|
+
|
|
107
302
|
return detected;
|
|
108
303
|
}
|
|
109
304
|
|
|
@@ -170,10 +365,18 @@ async function interactivePrompts(merged, detected) {
|
|
|
170
365
|
'Brand / product name',
|
|
171
366
|
merged.identity.brand_name || detected.identity.brand_name
|
|
172
367
|
);
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
)
|
|
368
|
+
// Skip the philosophy prompt if a design system was detected — the DS
|
|
369
|
+
// already encodes the visual direction. The detected hint (e.g. "Minimalist
|
|
370
|
+
// (shadcn/Tailwind)") is kept as metadata for skills that want a label.
|
|
371
|
+
if (!detected.features.has_design_system) {
|
|
372
|
+
merged.identity.design_philosophy = await promptForKey(
|
|
373
|
+
'Design philosophy (e.g. "Neo-Brutalism", "Minimalist") — empty for neutral',
|
|
374
|
+
merged.identity.design_philosophy
|
|
375
|
+
);
|
|
376
|
+
} else {
|
|
377
|
+
UI.info(`Design system detected — skipping philosophy prompt (using "${detected.identity.design_philosophy || 'inherited from design system'}").`);
|
|
378
|
+
merged.identity.design_philosophy = merged.identity.design_philosophy || detected.identity.design_philosophy;
|
|
379
|
+
}
|
|
177
380
|
merged.identity.language = await promptForKey(
|
|
178
381
|
'Primary UI language (BCP-47, e.g. "en", "it")',
|
|
179
382
|
merged.identity.language || 'en'
|
|
@@ -321,12 +524,26 @@ async function configure(opts = {}) {
|
|
|
321
524
|
merged = mergePreserving(merged, template);
|
|
322
525
|
merged.version = SCHEMA_VERSION;
|
|
323
526
|
|
|
527
|
+
const dsSignals = detected.stack.design_system_signals || {};
|
|
528
|
+
const dsSignalLabels = [
|
|
529
|
+
dsSignals.path && `path:${dsSignals.path}`,
|
|
530
|
+
dsSignals.storybook && 'storybook',
|
|
531
|
+
dsSignals.shadcn && 'shadcn',
|
|
532
|
+
dsSignals.tailwind_theme && 'tailwind-theme',
|
|
533
|
+
dsSignals.library && `lib:${dsSignals.library}`
|
|
534
|
+
].filter(Boolean).join(', ') || '—';
|
|
535
|
+
const monorepo = detected.stack.monorepo || { detected: false, roots: [] };
|
|
536
|
+
|
|
324
537
|
UI.box('AUTODETECTED', [
|
|
325
|
-
`
|
|
538
|
+
`Brand name: ${detected.identity.brand_name || '—'}`,
|
|
539
|
+
`Design system: ${detected.features.has_design_system ? 'yes' : 'no'}`,
|
|
540
|
+
` └─ signals: ${dsSignalLabels}`,
|
|
326
541
|
`UI guidelines: ${detected.paths.ui_guidelines || '— (none found)'}`,
|
|
327
542
|
`Components UI: ${detected.paths.components_primitives || '— (none found)'}`,
|
|
543
|
+
`Monorepo: ${monorepo.detected ? `yes (${monorepo.roots.length} package(s))` : 'no'}`,
|
|
328
544
|
`Backlog: ${detected.features.has_backlog ? 'yes' : 'no'}`,
|
|
329
545
|
`ADRs: ${detected.features.has_adrs ? 'yes' : 'no'}`,
|
|
546
|
+
`API docs: ${detected.features.has_api_docs ? 'yes' : 'no'}`,
|
|
330
547
|
`Charting libs: ${detected.stack.charting.canonical.join(', ') || '—'}`,
|
|
331
548
|
`Animation libs: ${detected.stack.animation.canonical.join(', ') || '—'}`,
|
|
332
549
|
`E2E framework: ${detected.stack.testing.e2e || '—'}`,
|