baldart 3.6.2

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.
Files changed (230) hide show
  1. package/CHANGELOG.md +599 -0
  2. package/README.md +566 -0
  3. package/VERSION +1 -0
  4. package/bin/baldart.js +143 -0
  5. package/framework/.claude/agents/REGISTRY.md +169 -0
  6. package/framework/.claude/agents/api-perf-cost-auditor.md +291 -0
  7. package/framework/.claude/agents/code-reviewer.md +350 -0
  8. package/framework/.claude/agents/codebase-architect.md +391 -0
  9. package/framework/.claude/agents/coder.md +291 -0
  10. package/framework/.claude/agents/deep-human-insight.md +198 -0
  11. package/framework/.claude/agents/doc-reviewer.md +440 -0
  12. package/framework/.claude/agents/email-deliverability-architect.md +193 -0
  13. package/framework/.claude/agents/hybrid-ml-architect.md +285 -0
  14. package/framework/.claude/agents/hyper-gamification-designer.md +149 -0
  15. package/framework/.claude/agents/legal-counsel-gdpr.md +179 -0
  16. package/framework/.claude/agents/marketing-conversion-strategist.md +162 -0
  17. package/framework/.claude/agents/motion-expert.md +108 -0
  18. package/framework/.claude/agents/onboarding-architect-lead.md +230 -0
  19. package/framework/.claude/agents/plan-auditor.md +546 -0
  20. package/framework/.claude/agents/prd-card-writer.md +372 -0
  21. package/framework/.claude/agents/prd.md +744 -0
  22. package/framework/.claude/agents/qa-sentinel.md +305 -0
  23. package/framework/.claude/agents/remotion-animator-orchestrator.md +218 -0
  24. package/framework/.claude/agents/security-reviewer.md +276 -0
  25. package/framework/.claude/agents/senior-researcher.md +175 -0
  26. package/framework/.claude/agents/seo-analytics-strategist.md +156 -0
  27. package/framework/.claude/agents/skill-improver.md +61 -0
  28. package/framework/.claude/agents/ui-expert.md +191 -0
  29. package/framework/.claude/agents/visual-designer.md +190 -0
  30. package/framework/.claude/agents/website-orchestrator.md +118 -0
  31. package/framework/.claude/agents/wiki-curator.md +145 -0
  32. package/framework/.claude/commands/baldart-push.md +15 -0
  33. package/framework/.claude/commands/check.md +237 -0
  34. package/framework/.claude/commands/codexreview.md +203 -0
  35. package/framework/.claude/commands/design-review.md +11 -0
  36. package/framework/.claude/commands/issue-review.md +34 -0
  37. package/framework/.claude/commands/new.md +331 -0
  38. package/framework/.claude/commands/qa.md +257 -0
  39. package/framework/.claude/hooks/framework-edit-gate.js +208 -0
  40. package/framework/.claude/hooks/lint-before-commit.sh.template +66 -0
  41. package/framework/.claude/settings.local.json.example +32 -0
  42. package/framework/.claude/skills/api-design-principles/SKILL.md +567 -0
  43. package/framework/.claude/skills/api-design-principles/assets/api-design-checklist.md +155 -0
  44. package/framework/.claude/skills/api-design-principles/assets/rest-api-template.py +182 -0
  45. package/framework/.claude/skills/api-design-principles/references/graphql-schema-design.md +583 -0
  46. package/framework/.claude/skills/api-design-principles/references/rest-best-practices.md +408 -0
  47. package/framework/.claude/skills/baldart-push/SKILL.md +222 -0
  48. package/framework/.claude/skills/bug/SKILL.md +200 -0
  49. package/framework/.claude/skills/bug/references/logging-patterns.md +174 -0
  50. package/framework/.claude/skills/capture/SKILL.md +125 -0
  51. package/framework/.claude/skills/capture/references/synthesis-template.md +42 -0
  52. package/framework/.claude/skills/context-primer/SKILL.md +189 -0
  53. package/framework/.claude/skills/copywriting/SKILL.md +273 -0
  54. package/framework/.claude/skills/copywriting/references/copy-frameworks.md +338 -0
  55. package/framework/.claude/skills/copywriting/references/natural-transitions.md +252 -0
  56. package/framework/.claude/skills/doc-writing-for-rag/SKILL.md +119 -0
  57. package/framework/.claude/skills/doc-writing-for-rag/references/before-after-examples.md +291 -0
  58. package/framework/.claude/skills/doc-writing-for-rag/references/compact-templates.md +183 -0
  59. package/framework/.claude/skills/doc-writing-for-rag/references/frontmatter-minimal.md +112 -0
  60. package/framework/.claude/skills/doc-writing-for-rag/references/line-count-targets.md +110 -0
  61. package/framework/.claude/skills/doc-writing-for-rag/references/schemas-and-errors.md +129 -0
  62. package/framework/.claude/skills/find-skills/SKILL.md +133 -0
  63. package/framework/.claude/skills/frontend-design/LICENSE.txt +177 -0
  64. package/framework/.claude/skills/frontend-design/SKILL.md +84 -0
  65. package/framework/.claude/skills/gamification-design/SKILL.md +130 -0
  66. package/framework/.claude/skills/issue-review/SKILL.md +45 -0
  67. package/framework/.claude/skills/kie-ai/SKILL.md +262 -0
  68. package/framework/.claude/skills/kie-ai/references/models-catalog.md +272 -0
  69. package/framework/.claude/skills/kie-ai/scripts/kie_api.sh +209 -0
  70. package/framework/.claude/skills/kie-ai/scripts/remove_greenscreen.py +69 -0
  71. package/framework/.claude/skills/kie-ai/scripts/setup_api_key.sh +77 -0
  72. package/framework/.claude/skills/motion-design/LICENSE +21 -0
  73. package/framework/.claude/skills/motion-design/README.md +82 -0
  74. package/framework/.claude/skills/motion-design/SKILL.md +336 -0
  75. package/framework/.claude/skills/motion-design/director/choreography.md +93 -0
  76. package/framework/.claude/skills/motion-design/director/context-adaptation.md +83 -0
  77. package/framework/.claude/skills/motion-design/director/core-philosophy.md +53 -0
  78. package/framework/.claude/skills/motion-design/director/decision-framework.md +91 -0
  79. package/framework/.claude/skills/motion-design/director/disney-principles.md +102 -0
  80. package/framework/.claude/skills/motion-design/director/emotion-mapping.md +71 -0
  81. package/framework/.claude/skills/motion-design/director/motion-personality.md +89 -0
  82. package/framework/.claude/skills/motion-design/director/narrative-structure.md +62 -0
  83. package/framework/.claude/skills/motion-design/patterns/ambient-continuous.md +81 -0
  84. package/framework/.claude/skills/motion-design/patterns/entrance-exit.md +82 -0
  85. package/framework/.claude/skills/motion-design/patterns/multi-element.md +69 -0
  86. package/framework/.claude/skills/motion-design/patterns/state-feedback.md +96 -0
  87. package/framework/.claude/skills/motion-design/reference/property-selection.md +95 -0
  88. package/framework/.claude/skills/motion-design/reference/quality-checklist.md +67 -0
  89. package/framework/.claude/skills/motion-design/reference/timing-easing-tables.md +106 -0
  90. package/framework/.claude/skills/motion-design/reference/troubleshooting.md +73 -0
  91. package/framework/.claude/skills/new/SKILL.md +1687 -0
  92. package/framework/.claude/skills/playwright-skill/API_REFERENCE.md +652 -0
  93. package/framework/.claude/skills/playwright-skill/SKILL.md +157 -0
  94. package/framework/.claude/skills/playwright-skill/package.json +26 -0
  95. package/framework/.claude/skills/prd/SKILL.md +228 -0
  96. package/framework/.claude/skills/prd/assets/card-template.yml +232 -0
  97. package/framework/.claude/skills/prd/assets/epic-template.yml +190 -0
  98. package/framework/.claude/skills/prd/assets/prd-template.md +230 -0
  99. package/framework/.claude/skills/prd/assets/state-template.md +78 -0
  100. package/framework/.claude/skills/prd/references/api-perf-gate.md +152 -0
  101. package/framework/.claude/skills/prd/references/audit-phase.md +478 -0
  102. package/framework/.claude/skills/prd/references/backlog-phase.md +145 -0
  103. package/framework/.claude/skills/prd/references/discovery-phase.md +359 -0
  104. package/framework/.claude/skills/prd/references/impact-analysis.md +233 -0
  105. package/framework/.claude/skills/prd/references/prd-add-phase.md +214 -0
  106. package/framework/.claude/skills/prd/references/prd-writing-phase.md +145 -0
  107. package/framework/.claude/skills/prd/references/research-phase.md +216 -0
  108. package/framework/.claude/skills/prd/references/ui-design-phase.md +61 -0
  109. package/framework/.claude/skills/prd/references/validation-phase.md +72 -0
  110. package/framework/.claude/skills/prd-add/SKILL.md +222 -0
  111. package/framework/.claude/skills/prd-add/references/impact-analysis.md +233 -0
  112. package/framework/.claude/skills/remotion-best-practices/SKILL.md +48 -0
  113. package/framework/.claude/skills/remotion-best-practices/rules/3d.md +86 -0
  114. package/framework/.claude/skills/remotion-best-practices/rules/animations.md +29 -0
  115. package/framework/.claude/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
  116. package/framework/.claude/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
  117. package/framework/.claude/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
  118. package/framework/.claude/skills/remotion-best-practices/rules/assets.md +78 -0
  119. package/framework/.claude/skills/remotion-best-practices/rules/audio.md +169 -0
  120. package/framework/.claude/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
  121. package/framework/.claude/skills/remotion-best-practices/rules/can-decode.md +75 -0
  122. package/framework/.claude/skills/remotion-best-practices/rules/charts.md +58 -0
  123. package/framework/.claude/skills/remotion-best-practices/rules/compositions.md +141 -0
  124. package/framework/.claude/skills/remotion-best-practices/rules/display-captions.md +184 -0
  125. package/framework/.claude/skills/remotion-best-practices/rules/extract-frames.md +229 -0
  126. package/framework/.claude/skills/remotion-best-practices/rules/fonts.md +152 -0
  127. package/framework/.claude/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
  128. package/framework/.claude/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
  129. package/framework/.claude/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
  130. package/framework/.claude/skills/remotion-best-practices/rules/gifs.md +141 -0
  131. package/framework/.claude/skills/remotion-best-practices/rules/images.md +130 -0
  132. package/framework/.claude/skills/remotion-best-practices/rules/import-srt-captions.md +69 -0
  133. package/framework/.claude/skills/remotion-best-practices/rules/light-leaks.md +73 -0
  134. package/framework/.claude/skills/remotion-best-practices/rules/lottie.md +67 -0
  135. package/framework/.claude/skills/remotion-best-practices/rules/maps.md +401 -0
  136. package/framework/.claude/skills/remotion-best-practices/rules/measuring-dom-nodes.md +34 -0
  137. package/framework/.claude/skills/remotion-best-practices/rules/measuring-text.md +143 -0
  138. package/framework/.claude/skills/remotion-best-practices/rules/parameters.md +98 -0
  139. package/framework/.claude/skills/remotion-best-practices/rules/sequencing.md +118 -0
  140. package/framework/.claude/skills/remotion-best-practices/rules/subtitles.md +36 -0
  141. package/framework/.claude/skills/remotion-best-practices/rules/tailwind.md +11 -0
  142. package/framework/.claude/skills/remotion-best-practices/rules/text-animations.md +20 -0
  143. package/framework/.claude/skills/remotion-best-practices/rules/timing.md +179 -0
  144. package/framework/.claude/skills/remotion-best-practices/rules/transcribe-captions.md +70 -0
  145. package/framework/.claude/skills/remotion-best-practices/rules/transitions.md +197 -0
  146. package/framework/.claude/skills/remotion-best-practices/rules/transparent-videos.md +106 -0
  147. package/framework/.claude/skills/remotion-best-practices/rules/trimming.md +52 -0
  148. package/framework/.claude/skills/remotion-best-practices/rules/videos.md +171 -0
  149. package/framework/.claude/skills/seo-audit/SKILL.md +394 -0
  150. package/framework/.claude/skills/seo-audit/references/aeo-geo-patterns.md +279 -0
  151. package/framework/.claude/skills/seo-audit/references/ai-writing-detection.md +190 -0
  152. package/framework/.claude/skills/simplify/SKILL.md +137 -0
  153. package/framework/.claude/skills/skill-creator/LICENSE.txt +202 -0
  154. package/framework/.claude/skills/skill-creator/SKILL.md +356 -0
  155. package/framework/.claude/skills/skill-creator/references/output-patterns.md +82 -0
  156. package/framework/.claude/skills/skill-creator/references/workflows.md +28 -0
  157. package/framework/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
  158. package/framework/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
  159. package/framework/.claude/skills/skill-creator/scripts/quick_validate.py +95 -0
  160. package/framework/.claude/skills/ui-design/SKILL.md +199 -0
  161. package/framework/.claude/skills/ui-design/references/component-discovery.md +54 -0
  162. package/framework/.claude/skills/ui-design/references/evaluation.md +171 -0
  163. package/framework/.claude/skills/ui-design/references/generation.md +109 -0
  164. package/framework/.claude/skills/ui-design/references/inventory.md +59 -0
  165. package/framework/.claude/skills/webapp-testing/LICENSE.txt +202 -0
  166. package/framework/.claude/skills/webapp-testing/SKILL.md +123 -0
  167. package/framework/.claude/skills/webapp-testing/examples/console_logging.py +35 -0
  168. package/framework/.claude/skills/webapp-testing/examples/element_discovery.py +40 -0
  169. package/framework/.claude/skills/webapp-testing/examples/static_html_automation.py +33 -0
  170. package/framework/.claude/skills/webapp-testing/scripts/with_server.py +106 -0
  171. package/framework/.claude/skills/worktree-manager/SKILL.md +680 -0
  172. package/framework/AGENTS.md +240 -0
  173. package/framework/agents/api-contracts.md +137 -0
  174. package/framework/agents/architecture.md +145 -0
  175. package/framework/agents/coding-standards.md +148 -0
  176. package/framework/agents/data-model.md +110 -0
  177. package/framework/agents/deployment-protocol.md +232 -0
  178. package/framework/agents/design-review.md +172 -0
  179. package/framework/agents/env-reference.md +171 -0
  180. package/framework/agents/github-issue-subagent.md +252 -0
  181. package/framework/agents/index.md +261 -0
  182. package/framework/agents/llm-wiki-methodology.md +216 -0
  183. package/framework/agents/maintenance-protocol.md +305 -0
  184. package/framework/agents/observability.md +162 -0
  185. package/framework/agents/performance.md +155 -0
  186. package/framework/agents/project-context.md +145 -0
  187. package/framework/agents/runbook.md +208 -0
  188. package/framework/agents/security.md +168 -0
  189. package/framework/agents/skills-mapping.md +286 -0
  190. package/framework/agents/testing.md +111 -0
  191. package/framework/agents/workflows.md +215 -0
  192. package/framework/docs/PROJECT-CONFIGURATION.md +336 -0
  193. package/framework/docs/references/brand-guidelines.md +170 -0
  194. package/framework/docs/references/ui-guidelines.template.md +182 -0
  195. package/framework/routines/code-review.routine.yml +46 -0
  196. package/framework/routines/doc-review.routine.yml +45 -0
  197. package/framework/routines/ds-drift.routine.yml +52 -0
  198. package/framework/routines/full-sweep.routine.yml +51 -0
  199. package/framework/routines/index.yml +70 -0
  200. package/framework/routines/skill-improve.routine.yml +50 -0
  201. package/framework/routines/wiki-review.routine.yml +45 -0
  202. package/framework/templates/baldart.config.template.yml +113 -0
  203. package/framework/templates/breaking-change-checklist.md +484 -0
  204. package/framework/templates/feature-card.template.yml +125 -0
  205. package/framework/templates/overlays/README.md +44 -0
  206. package/framework/templates/overlays/copywriting.fidelity-example.md +62 -0
  207. package/framework/templates/overlays/ui-design.fidelity-example.md +75 -0
  208. package/framework/templates/skill-project-context.snippet.md +19 -0
  209. package/framework/templates/spec.template.md +208 -0
  210. package/package.json +51 -0
  211. package/src/commands/add.js +229 -0
  212. package/src/commands/configure.js +385 -0
  213. package/src/commands/doctor.js +486 -0
  214. package/src/commands/migrate.js +185 -0
  215. package/src/commands/push.js +0 -0
  216. package/src/commands/routines.js +269 -0
  217. package/src/commands/status.js +130 -0
  218. package/src/commands/update.js +419 -0
  219. package/src/commands/version.js +88 -0
  220. package/src/utils/contamination.js +400 -0
  221. package/src/utils/git.js +181 -0
  222. package/src/utils/hooks.js +152 -0
  223. package/src/utils/routine-adapters/claude-code-cloud.js +78 -0
  224. package/src/utils/routine-adapters/cron.js +138 -0
  225. package/src/utils/routine-adapters/github-actions.js +141 -0
  226. package/src/utils/routine-adapters/index.js +21 -0
  227. package/src/utils/routines.js +166 -0
  228. package/src/utils/state.js +143 -0
  229. package/src/utils/symlinks.js +425 -0
  230. package/src/utils/ui.js +133 -0
@@ -0,0 +1,385 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const yaml = require('js-yaml');
4
+ const UI = require('../utils/ui');
5
+
6
+ const CONFIG_FILE = 'baldart.config.yml';
7
+ // The subtree pull copies the entire BALDART repo (which itself has a
8
+ // top-level `framework/` directory) into `.framework/`. So the in-consumer
9
+ // path is `.framework/framework/templates/...` with the doubled segment.
10
+ const TEMPLATE_PATH = path.join('.framework', 'framework', 'templates', 'baldart.config.template.yml');
11
+ const OVERLAYS_DIR = path.join('.baldart', 'overlays');
12
+ const SCHEMA_VERSION = 1;
13
+
14
+ /**
15
+ * Filesystem probes — autodetection heuristics aligned with
16
+ * framework/agents/project-context.md § 6.
17
+ *
18
+ * Returns an object with the same shape as baldart.config.yml, populated
19
+ * only with detected values. Empty / undetected keys remain at template
20
+ * defaults and the user is prompted explicitly for them.
21
+ */
22
+ function detect(cwd = process.cwd()) {
23
+ const exists = (p) => fs.existsSync(path.join(cwd, p));
24
+ const findFirst = (...candidates) => candidates.find(exists) || '';
25
+ const readJsonSafe = (p) => {
26
+ try { return JSON.parse(fs.readFileSync(path.join(cwd, p), 'utf8')); } catch { return null; }
27
+ };
28
+ const countMatches = (dir, regex) => {
29
+ const abs = path.join(cwd, dir);
30
+ if (!fs.existsSync(abs)) return 0;
31
+ try {
32
+ return fs.readdirSync(abs).filter((f) => regex.test(f)).length;
33
+ } catch { return 0; }
34
+ };
35
+
36
+ const pkg = readJsonSafe('package.json');
37
+ const deps = pkg ? { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) } : {};
38
+ const hasDep = (name) => Object.keys(deps).some((d) => d === name || d.startsWith(`${name}/`));
39
+
40
+ const detected = {
41
+ 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
+ ),
63
+ backlog_dir: exists('backlog') ? 'backlog' : '',
64
+ adrs_dir: countMatches('docs/decisions', /^ADR-.*\.md$/) > 0 ? 'docs/decisions' : '',
65
+ prd_dir: exists('docs/prd') ? 'docs/prd' : '',
66
+ references_dir: exists('docs/references') ? 'docs/references' : '',
67
+ wiki_dir: exists('docs/wiki') ? 'docs/wiki' : '',
68
+ e2e_tests_dir: findFirst('tests/e2e', 'e2e', 'tests/playwright', 'tests/cypress'),
69
+ },
70
+ identity: {
71
+ brand_name: pkg?.name || '',
72
+ design_philosophy: '',
73
+ language: 'en',
74
+ audience_segments: [],
75
+ },
76
+ stack: {
77
+ charting: {
78
+ canonical: ['recharts', '@nivo/heatmap', '@nivo/bar', '@nivo/line']
79
+ .filter((n) => hasDep(n)),
80
+ forbidden: [],
81
+ wrappers_root: findFirst('src/components/charts', 'app/components/charts'),
82
+ },
83
+ animation: {
84
+ canonical: ['framer-motion', 'lottie-react', 'gsap', 'motion']
85
+ .filter((n) => hasDep(n)),
86
+ forbidden: [],
87
+ },
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
+ },
95
+ },
96
+ 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'),
100
+ has_backlog: countMatches('backlog', /\.ya?ml$/) > 0,
101
+ has_adrs: countMatches('docs/decisions', /^ADR-.*\.md$/) > 0,
102
+ has_prd_workflow: exists('docs/prd'),
103
+ has_wiki_overlay: exists('docs/wiki'),
104
+ },
105
+ };
106
+
107
+ return detected;
108
+ }
109
+
110
+ /**
111
+ * Deep merge: existing user values WIN over detected/proposed values.
112
+ * Used by `baldart configure` to re-run idempotently without clobbering
113
+ * customizations.
114
+ *
115
+ * IMPORTANT: empty string `''` is treated as a DELIBERATE user value
116
+ * (the user said "this concept does not exist in my project"). Only
117
+ * `null` / `undefined` / missing-key fall back to the source.
118
+ */
119
+ function mergePreserving(target, source) {
120
+ if (target === null || target === undefined) return source;
121
+ if (Array.isArray(target)) return target.length ? target : source;
122
+ if (typeof target !== 'object') return target;
123
+
124
+ const out = { ...target };
125
+ for (const key of Object.keys(source || {})) {
126
+ if (!(key in out)) {
127
+ out[key] = source[key];
128
+ } else {
129
+ out[key] = mergePreserving(out[key], source[key]);
130
+ }
131
+ }
132
+ return out;
133
+ }
134
+
135
+ function loadExisting(cwd) {
136
+ const full = path.join(cwd, CONFIG_FILE);
137
+ if (!fs.existsSync(full)) return null;
138
+ try {
139
+ return yaml.load(fs.readFileSync(full, 'utf8'));
140
+ } catch (err) {
141
+ UI.warning(`Existing ${CONFIG_FILE} is not valid YAML: ${err.message}`);
142
+ return null;
143
+ }
144
+ }
145
+
146
+ function loadTemplateSkeleton(cwd) {
147
+ const full = path.join(cwd, TEMPLATE_PATH);
148
+ if (!fs.existsSync(full)) {
149
+ throw new Error(
150
+ `Template not found at ${TEMPLATE_PATH}. Run \`npx baldart add\` first.`
151
+ );
152
+ }
153
+ return yaml.load(fs.readFileSync(full, 'utf8'));
154
+ }
155
+
156
+ async function promptForKey(label, defaultValue, kind = 'input') {
157
+ if (kind === 'confirm') {
158
+ return UI.confirm(label, defaultValue === true);
159
+ }
160
+ if (kind === 'select') {
161
+ return UI.select(label, defaultValue);
162
+ }
163
+ const answer = await UI.input(label, defaultValue || '');
164
+ return answer.trim();
165
+ }
166
+
167
+ async function interactivePrompts(merged, detected) {
168
+ UI.section('Identity');
169
+ merged.identity.brand_name = await promptForKey(
170
+ 'Brand / product name',
171
+ merged.identity.brand_name || detected.identity.brand_name
172
+ );
173
+ merged.identity.design_philosophy = await promptForKey(
174
+ 'Design philosophy (e.g. "Neo-Brutalism", "Minimalist") — empty for neutral',
175
+ merged.identity.design_philosophy
176
+ );
177
+ merged.identity.language = await promptForKey(
178
+ 'Primary UI language (BCP-47, e.g. "en", "it")',
179
+ merged.identity.language || 'en'
180
+ );
181
+ const segments = await promptForKey(
182
+ 'Audience segments (comma-separated, e.g. "merchant,customer") — empty for none',
183
+ (merged.identity.audience_segments || []).join(',')
184
+ );
185
+ merged.identity.audience_segments = segments
186
+ ? segments.split(',').map((s) => s.trim()).filter(Boolean)
187
+ : [];
188
+
189
+ UI.section('Features (explicit yes/no — option A: always ask)');
190
+ for (const flag of [
191
+ ['has_design_system', 'Project has a documented design system?'],
192
+ ['multi_tenant_theming', 'Project uses multi-tenant theming (per-tenant overrides)?'],
193
+ ['has_api_docs', 'Project has canonical API docs?'],
194
+ ['has_backlog', 'Project uses backlog-card workflow?'],
195
+ ['has_adrs', 'Project uses ADR workflow?'],
196
+ ['has_prd_workflow', 'Project uses PRD workflow (docs/prd/)?'],
197
+ ['has_wiki_overlay', 'Project has LLM-wiki overlay (docs/wiki/)?'],
198
+ ]) {
199
+ const [key, question] = flag;
200
+ const currentVal = merged.features[key];
201
+ const detectedVal = detected.features[key];
202
+ const proposedDefault = currentVal !== null && currentVal !== undefined
203
+ ? currentVal
204
+ : (detectedVal === true);
205
+ merged.features[key] = await promptForKey(question, proposedDefault, 'confirm');
206
+ }
207
+
208
+ // Path prompts — only ask for paths whose feature is enabled and value missing
209
+ UI.section('Paths (autodetected — confirm or override)');
210
+ const pathPrompts = [
211
+ ['design_system', 'Design-system root', () => merged.features.has_design_system],
212
+ ['ui_guidelines', 'UI guidelines doc', () => true],
213
+ ['api_index', 'API index doc', () => merged.features.has_api_docs],
214
+ ['api_schemas', 'API schemas doc', () => merged.features.has_api_docs],
215
+ ['api_errors', 'API errors doc', () => merged.features.has_api_docs],
216
+ ['components_primitives', 'UI primitives directory (e.g. src/components/ui)', () => true],
217
+ ['components_root', 'Components root directory', () => true],
218
+ ['global_styles', 'Global stylesheet path', () => true],
219
+ ['backlog_dir', 'Backlog directory', () => merged.features.has_backlog],
220
+ ['adrs_dir', 'ADR directory', () => merged.features.has_adrs],
221
+ ['prd_dir', 'PRD directory', () => merged.features.has_prd_workflow],
222
+ ['references_dir', 'References docs root', () => true],
223
+ ['wiki_dir', 'Wiki overlay directory', () => merged.features.has_wiki_overlay],
224
+ ['e2e_tests_dir', 'E2E tests directory', () => true],
225
+ ];
226
+
227
+ for (const [key, label, gate] of pathPrompts) {
228
+ if (!gate()) {
229
+ merged.paths[key] = '';
230
+ continue;
231
+ }
232
+ const proposed = merged.paths[key] || detected.paths[key] || '';
233
+ merged.paths[key] = await promptForKey(`${label}`, proposed);
234
+ }
235
+
236
+ UI.section('Stack (autodetected from package.json — confirm or override)');
237
+ const charting = merged.stack.charting;
238
+ const chartingCanonical = await promptForKey(
239
+ 'Charting libraries (canonical, comma-separated)',
240
+ (charting.canonical || detected.stack.charting.canonical).join(',')
241
+ );
242
+ charting.canonical = chartingCanonical
243
+ ? chartingCanonical.split(',').map((s) => s.trim()).filter(Boolean)
244
+ : [];
245
+ const chartingForbidden = await promptForKey(
246
+ 'Charting libraries (forbidden, comma-separated) — empty for none',
247
+ (charting.forbidden || []).join(',')
248
+ );
249
+ charting.forbidden = chartingForbidden
250
+ ? chartingForbidden.split(',').map((s) => s.trim()).filter(Boolean)
251
+ : [];
252
+ charting.wrappers_root = await promptForKey(
253
+ 'Custom chart wrappers directory (empty if none)',
254
+ charting.wrappers_root || detected.stack.charting.wrappers_root || ''
255
+ );
256
+
257
+ const animation = merged.stack.animation;
258
+ const animCanonical = await promptForKey(
259
+ 'Animation libraries (canonical, comma-separated)',
260
+ (animation.canonical || detected.stack.animation.canonical).join(',')
261
+ );
262
+ animation.canonical = animCanonical
263
+ ? animCanonical.split(',').map((s) => s.trim()).filter(Boolean)
264
+ : [];
265
+
266
+ merged.stack.testing.e2e = await promptForKey(
267
+ 'E2E framework ("playwright", "cypress", or empty)',
268
+ merged.stack.testing.e2e || detected.stack.testing.e2e || ''
269
+ );
270
+
271
+ return merged;
272
+ }
273
+
274
+ /**
275
+ * Strip features.* flags whose value is null. We use null to signal
276
+ * "detection inconclusive — never written, ask user on first use".
277
+ * Honouring the always-ask contract (project-context.md § 3).
278
+ */
279
+ function stripNullFeatures(config) {
280
+ if (!config || !config.features) return config;
281
+ const out = { ...config, features: { ...config.features } };
282
+ for (const k of Object.keys(out.features)) {
283
+ if (out.features[k] === null) {
284
+ delete out.features[k];
285
+ }
286
+ }
287
+ return out;
288
+ }
289
+
290
+ function serialize(config) {
291
+ const header = `# BALDART Project Configuration
292
+ # Generated / updated by \`npx baldart configure\` — safe to edit by hand.
293
+ # Schema docs: framework/docs/PROJECT-CONFIGURATION.md
294
+ # Protocol: framework/agents/project-context.md
295
+ # This file is a CUSTOMIZABLE COPY and is never overwritten by \`baldart update\`.
296
+ #
297
+ # NOTE: features.* flags MUST be explicit (true | false). Any flag absent
298
+ # from this file will prompt the user on first skill invocation.
299
+ `;
300
+ return header + yaml.dump(stripNullFeatures(config), { lineWidth: 100, noRefs: true });
301
+ }
302
+
303
+ async function configure(opts = {}) {
304
+ const cwd = process.cwd();
305
+ UI.header('BALDART CONFIGURE — project context');
306
+
307
+ if (!fs.existsSync(path.join(cwd, '.framework'))) {
308
+ UI.error('Framework not installed. Run `npx baldart add` first.');
309
+ process.exit(1);
310
+ }
311
+
312
+ const probeSpinner = UI.spinner('Probing project structure...').start();
313
+ const detected = detect(cwd);
314
+ probeSpinner.succeed('Project structure probed');
315
+
316
+ const existing = loadExisting(cwd);
317
+ const template = loadTemplateSkeleton(cwd);
318
+ // Priority (highest wins): existing user values > detected > template defaults.
319
+ // mergePreserving(target, source) keeps target where set, fills from source.
320
+ let merged = mergePreserving(existing || {}, detected);
321
+ merged = mergePreserving(merged, template);
322
+ merged.version = SCHEMA_VERSION;
323
+
324
+ UI.box('AUTODETECTED', [
325
+ `Design system: ${detected.paths.design_system || '— (none found)'}`,
326
+ `UI guidelines: ${detected.paths.ui_guidelines || '— (none found)'}`,
327
+ `Components UI: ${detected.paths.components_primitives || '— (none found)'}`,
328
+ `Backlog: ${detected.features.has_backlog ? 'yes' : 'no'}`,
329
+ `ADRs: ${detected.features.has_adrs ? 'yes' : 'no'}`,
330
+ `Charting libs: ${detected.stack.charting.canonical.join(', ') || '—'}`,
331
+ `Animation libs: ${detected.stack.animation.canonical.join(', ') || '—'}`,
332
+ `E2E framework: ${detected.stack.testing.e2e || '—'}`,
333
+ ]);
334
+
335
+ if (opts.nonInteractive) {
336
+ UI.info('Non-interactive mode — writing autodetected values.');
337
+ // For features.* keys where detection returned null (inconclusive), strip
338
+ // them so the YAML omits the flag and the always-ask contract kicks in
339
+ // on first skill invocation. Honors project-context.md § 3.
340
+ if (merged.features && detected.features) {
341
+ for (const k of Object.keys(detected.features)) {
342
+ if (detected.features[k] === null && k in merged.features) {
343
+ delete merged.features[k];
344
+ }
345
+ }
346
+ }
347
+ } else {
348
+ const proceed = await UI.confirm(
349
+ existing ? 'Re-run prompts and merge into existing config?' : 'Start configuration prompts?',
350
+ true
351
+ );
352
+ if (!proceed) {
353
+ UI.info('Configure cancelled.');
354
+ return;
355
+ }
356
+ merged = await interactivePrompts(merged, detected);
357
+ }
358
+
359
+ const outPath = path.join(cwd, CONFIG_FILE);
360
+ fs.writeFileSync(outPath, serialize(merged), 'utf8');
361
+ UI.success(`Wrote ${CONFIG_FILE}`);
362
+
363
+ // Ensure overlays dir exists (user-owned)
364
+ const overlaysAbs = path.join(cwd, OVERLAYS_DIR);
365
+ if (!fs.existsSync(overlaysAbs)) {
366
+ fs.mkdirSync(overlaysAbs, { recursive: true });
367
+ fs.writeFileSync(
368
+ path.join(overlaysAbs, '.gitkeep'),
369
+ '# Skill overlays — see framework/agents/project-context.md § 5\n',
370
+ 'utf8'
371
+ );
372
+ UI.success(`Created ${OVERLAYS_DIR}/`);
373
+ }
374
+
375
+ UI.box('NEXT STEPS', [
376
+ '• Review baldart.config.yml — values are committed to your repo',
377
+ '• Author skill overlays in .baldart/overlays/<skill-name>.md',
378
+ ' Examples: framework/templates/overlays/',
379
+ '• Re-run `npx baldart configure` any time to refresh',
380
+ ]);
381
+ }
382
+
383
+ module.exports = configure;
384
+ module.exports.detect = detect;
385
+ module.exports.mergePreserving = mergePreserving;