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.
- package/CHANGELOG.md +599 -0
- package/README.md +566 -0
- package/VERSION +1 -0
- package/bin/baldart.js +143 -0
- package/framework/.claude/agents/REGISTRY.md +169 -0
- package/framework/.claude/agents/api-perf-cost-auditor.md +291 -0
- package/framework/.claude/agents/code-reviewer.md +350 -0
- package/framework/.claude/agents/codebase-architect.md +391 -0
- package/framework/.claude/agents/coder.md +291 -0
- package/framework/.claude/agents/deep-human-insight.md +198 -0
- package/framework/.claude/agents/doc-reviewer.md +440 -0
- package/framework/.claude/agents/email-deliverability-architect.md +193 -0
- package/framework/.claude/agents/hybrid-ml-architect.md +285 -0
- package/framework/.claude/agents/hyper-gamification-designer.md +149 -0
- package/framework/.claude/agents/legal-counsel-gdpr.md +179 -0
- package/framework/.claude/agents/marketing-conversion-strategist.md +162 -0
- package/framework/.claude/agents/motion-expert.md +108 -0
- package/framework/.claude/agents/onboarding-architect-lead.md +230 -0
- package/framework/.claude/agents/plan-auditor.md +546 -0
- package/framework/.claude/agents/prd-card-writer.md +372 -0
- package/framework/.claude/agents/prd.md +744 -0
- package/framework/.claude/agents/qa-sentinel.md +305 -0
- package/framework/.claude/agents/remotion-animator-orchestrator.md +218 -0
- package/framework/.claude/agents/security-reviewer.md +276 -0
- package/framework/.claude/agents/senior-researcher.md +175 -0
- package/framework/.claude/agents/seo-analytics-strategist.md +156 -0
- package/framework/.claude/agents/skill-improver.md +61 -0
- package/framework/.claude/agents/ui-expert.md +191 -0
- package/framework/.claude/agents/visual-designer.md +190 -0
- package/framework/.claude/agents/website-orchestrator.md +118 -0
- package/framework/.claude/agents/wiki-curator.md +145 -0
- package/framework/.claude/commands/baldart-push.md +15 -0
- package/framework/.claude/commands/check.md +237 -0
- package/framework/.claude/commands/codexreview.md +203 -0
- package/framework/.claude/commands/design-review.md +11 -0
- package/framework/.claude/commands/issue-review.md +34 -0
- package/framework/.claude/commands/new.md +331 -0
- package/framework/.claude/commands/qa.md +257 -0
- package/framework/.claude/hooks/framework-edit-gate.js +208 -0
- package/framework/.claude/hooks/lint-before-commit.sh.template +66 -0
- package/framework/.claude/settings.local.json.example +32 -0
- package/framework/.claude/skills/api-design-principles/SKILL.md +567 -0
- package/framework/.claude/skills/api-design-principles/assets/api-design-checklist.md +155 -0
- package/framework/.claude/skills/api-design-principles/assets/rest-api-template.py +182 -0
- package/framework/.claude/skills/api-design-principles/references/graphql-schema-design.md +583 -0
- package/framework/.claude/skills/api-design-principles/references/rest-best-practices.md +408 -0
- package/framework/.claude/skills/baldart-push/SKILL.md +222 -0
- package/framework/.claude/skills/bug/SKILL.md +200 -0
- package/framework/.claude/skills/bug/references/logging-patterns.md +174 -0
- package/framework/.claude/skills/capture/SKILL.md +125 -0
- package/framework/.claude/skills/capture/references/synthesis-template.md +42 -0
- package/framework/.claude/skills/context-primer/SKILL.md +189 -0
- package/framework/.claude/skills/copywriting/SKILL.md +273 -0
- package/framework/.claude/skills/copywriting/references/copy-frameworks.md +338 -0
- package/framework/.claude/skills/copywriting/references/natural-transitions.md +252 -0
- package/framework/.claude/skills/doc-writing-for-rag/SKILL.md +119 -0
- package/framework/.claude/skills/doc-writing-for-rag/references/before-after-examples.md +291 -0
- package/framework/.claude/skills/doc-writing-for-rag/references/compact-templates.md +183 -0
- package/framework/.claude/skills/doc-writing-for-rag/references/frontmatter-minimal.md +112 -0
- package/framework/.claude/skills/doc-writing-for-rag/references/line-count-targets.md +110 -0
- package/framework/.claude/skills/doc-writing-for-rag/references/schemas-and-errors.md +129 -0
- package/framework/.claude/skills/find-skills/SKILL.md +133 -0
- package/framework/.claude/skills/frontend-design/LICENSE.txt +177 -0
- package/framework/.claude/skills/frontend-design/SKILL.md +84 -0
- package/framework/.claude/skills/gamification-design/SKILL.md +130 -0
- package/framework/.claude/skills/issue-review/SKILL.md +45 -0
- package/framework/.claude/skills/kie-ai/SKILL.md +262 -0
- package/framework/.claude/skills/kie-ai/references/models-catalog.md +272 -0
- package/framework/.claude/skills/kie-ai/scripts/kie_api.sh +209 -0
- package/framework/.claude/skills/kie-ai/scripts/remove_greenscreen.py +69 -0
- package/framework/.claude/skills/kie-ai/scripts/setup_api_key.sh +77 -0
- package/framework/.claude/skills/motion-design/LICENSE +21 -0
- package/framework/.claude/skills/motion-design/README.md +82 -0
- package/framework/.claude/skills/motion-design/SKILL.md +336 -0
- package/framework/.claude/skills/motion-design/director/choreography.md +93 -0
- package/framework/.claude/skills/motion-design/director/context-adaptation.md +83 -0
- package/framework/.claude/skills/motion-design/director/core-philosophy.md +53 -0
- package/framework/.claude/skills/motion-design/director/decision-framework.md +91 -0
- package/framework/.claude/skills/motion-design/director/disney-principles.md +102 -0
- package/framework/.claude/skills/motion-design/director/emotion-mapping.md +71 -0
- package/framework/.claude/skills/motion-design/director/motion-personality.md +89 -0
- package/framework/.claude/skills/motion-design/director/narrative-structure.md +62 -0
- package/framework/.claude/skills/motion-design/patterns/ambient-continuous.md +81 -0
- package/framework/.claude/skills/motion-design/patterns/entrance-exit.md +82 -0
- package/framework/.claude/skills/motion-design/patterns/multi-element.md +69 -0
- package/framework/.claude/skills/motion-design/patterns/state-feedback.md +96 -0
- package/framework/.claude/skills/motion-design/reference/property-selection.md +95 -0
- package/framework/.claude/skills/motion-design/reference/quality-checklist.md +67 -0
- package/framework/.claude/skills/motion-design/reference/timing-easing-tables.md +106 -0
- package/framework/.claude/skills/motion-design/reference/troubleshooting.md +73 -0
- package/framework/.claude/skills/new/SKILL.md +1687 -0
- package/framework/.claude/skills/playwright-skill/API_REFERENCE.md +652 -0
- package/framework/.claude/skills/playwright-skill/SKILL.md +157 -0
- package/framework/.claude/skills/playwright-skill/package.json +26 -0
- package/framework/.claude/skills/prd/SKILL.md +228 -0
- package/framework/.claude/skills/prd/assets/card-template.yml +232 -0
- package/framework/.claude/skills/prd/assets/epic-template.yml +190 -0
- package/framework/.claude/skills/prd/assets/prd-template.md +230 -0
- package/framework/.claude/skills/prd/assets/state-template.md +78 -0
- package/framework/.claude/skills/prd/references/api-perf-gate.md +152 -0
- package/framework/.claude/skills/prd/references/audit-phase.md +478 -0
- package/framework/.claude/skills/prd/references/backlog-phase.md +145 -0
- package/framework/.claude/skills/prd/references/discovery-phase.md +359 -0
- package/framework/.claude/skills/prd/references/impact-analysis.md +233 -0
- package/framework/.claude/skills/prd/references/prd-add-phase.md +214 -0
- package/framework/.claude/skills/prd/references/prd-writing-phase.md +145 -0
- package/framework/.claude/skills/prd/references/research-phase.md +216 -0
- package/framework/.claude/skills/prd/references/ui-design-phase.md +61 -0
- package/framework/.claude/skills/prd/references/validation-phase.md +72 -0
- package/framework/.claude/skills/prd-add/SKILL.md +222 -0
- package/framework/.claude/skills/prd-add/references/impact-analysis.md +233 -0
- package/framework/.claude/skills/remotion-best-practices/SKILL.md +48 -0
- package/framework/.claude/skills/remotion-best-practices/rules/3d.md +86 -0
- package/framework/.claude/skills/remotion-best-practices/rules/animations.md +29 -0
- package/framework/.claude/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/framework/.claude/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/framework/.claude/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/framework/.claude/skills/remotion-best-practices/rules/assets.md +78 -0
- package/framework/.claude/skills/remotion-best-practices/rules/audio.md +169 -0
- package/framework/.claude/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
- package/framework/.claude/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/framework/.claude/skills/remotion-best-practices/rules/charts.md +58 -0
- package/framework/.claude/skills/remotion-best-practices/rules/compositions.md +141 -0
- package/framework/.claude/skills/remotion-best-practices/rules/display-captions.md +184 -0
- package/framework/.claude/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/framework/.claude/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/framework/.claude/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/framework/.claude/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/framework/.claude/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/framework/.claude/skills/remotion-best-practices/rules/gifs.md +141 -0
- package/framework/.claude/skills/remotion-best-practices/rules/images.md +130 -0
- package/framework/.claude/skills/remotion-best-practices/rules/import-srt-captions.md +69 -0
- package/framework/.claude/skills/remotion-best-practices/rules/light-leaks.md +73 -0
- package/framework/.claude/skills/remotion-best-practices/rules/lottie.md +67 -0
- package/framework/.claude/skills/remotion-best-practices/rules/maps.md +401 -0
- package/framework/.claude/skills/remotion-best-practices/rules/measuring-dom-nodes.md +34 -0
- package/framework/.claude/skills/remotion-best-practices/rules/measuring-text.md +143 -0
- package/framework/.claude/skills/remotion-best-practices/rules/parameters.md +98 -0
- package/framework/.claude/skills/remotion-best-practices/rules/sequencing.md +118 -0
- package/framework/.claude/skills/remotion-best-practices/rules/subtitles.md +36 -0
- package/framework/.claude/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/framework/.claude/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/framework/.claude/skills/remotion-best-practices/rules/timing.md +179 -0
- package/framework/.claude/skills/remotion-best-practices/rules/transcribe-captions.md +70 -0
- package/framework/.claude/skills/remotion-best-practices/rules/transitions.md +197 -0
- package/framework/.claude/skills/remotion-best-practices/rules/transparent-videos.md +106 -0
- package/framework/.claude/skills/remotion-best-practices/rules/trimming.md +52 -0
- package/framework/.claude/skills/remotion-best-practices/rules/videos.md +171 -0
- package/framework/.claude/skills/seo-audit/SKILL.md +394 -0
- package/framework/.claude/skills/seo-audit/references/aeo-geo-patterns.md +279 -0
- package/framework/.claude/skills/seo-audit/references/ai-writing-detection.md +190 -0
- package/framework/.claude/skills/simplify/SKILL.md +137 -0
- package/framework/.claude/skills/skill-creator/LICENSE.txt +202 -0
- package/framework/.claude/skills/skill-creator/SKILL.md +356 -0
- package/framework/.claude/skills/skill-creator/references/output-patterns.md +82 -0
- package/framework/.claude/skills/skill-creator/references/workflows.md +28 -0
- package/framework/.claude/skills/skill-creator/scripts/init_skill.py +303 -0
- package/framework/.claude/skills/skill-creator/scripts/package_skill.py +110 -0
- package/framework/.claude/skills/skill-creator/scripts/quick_validate.py +95 -0
- package/framework/.claude/skills/ui-design/SKILL.md +199 -0
- package/framework/.claude/skills/ui-design/references/component-discovery.md +54 -0
- package/framework/.claude/skills/ui-design/references/evaluation.md +171 -0
- package/framework/.claude/skills/ui-design/references/generation.md +109 -0
- package/framework/.claude/skills/ui-design/references/inventory.md +59 -0
- package/framework/.claude/skills/webapp-testing/LICENSE.txt +202 -0
- package/framework/.claude/skills/webapp-testing/SKILL.md +123 -0
- package/framework/.claude/skills/webapp-testing/examples/console_logging.py +35 -0
- package/framework/.claude/skills/webapp-testing/examples/element_discovery.py +40 -0
- package/framework/.claude/skills/webapp-testing/examples/static_html_automation.py +33 -0
- package/framework/.claude/skills/webapp-testing/scripts/with_server.py +106 -0
- package/framework/.claude/skills/worktree-manager/SKILL.md +680 -0
- package/framework/AGENTS.md +240 -0
- package/framework/agents/api-contracts.md +137 -0
- package/framework/agents/architecture.md +145 -0
- package/framework/agents/coding-standards.md +148 -0
- package/framework/agents/data-model.md +110 -0
- package/framework/agents/deployment-protocol.md +232 -0
- package/framework/agents/design-review.md +172 -0
- package/framework/agents/env-reference.md +171 -0
- package/framework/agents/github-issue-subagent.md +252 -0
- package/framework/agents/index.md +261 -0
- package/framework/agents/llm-wiki-methodology.md +216 -0
- package/framework/agents/maintenance-protocol.md +305 -0
- package/framework/agents/observability.md +162 -0
- package/framework/agents/performance.md +155 -0
- package/framework/agents/project-context.md +145 -0
- package/framework/agents/runbook.md +208 -0
- package/framework/agents/security.md +168 -0
- package/framework/agents/skills-mapping.md +286 -0
- package/framework/agents/testing.md +111 -0
- package/framework/agents/workflows.md +215 -0
- package/framework/docs/PROJECT-CONFIGURATION.md +336 -0
- package/framework/docs/references/brand-guidelines.md +170 -0
- package/framework/docs/references/ui-guidelines.template.md +182 -0
- package/framework/routines/code-review.routine.yml +46 -0
- package/framework/routines/doc-review.routine.yml +45 -0
- package/framework/routines/ds-drift.routine.yml +52 -0
- package/framework/routines/full-sweep.routine.yml +51 -0
- package/framework/routines/index.yml +70 -0
- package/framework/routines/skill-improve.routine.yml +50 -0
- package/framework/routines/wiki-review.routine.yml +45 -0
- package/framework/templates/baldart.config.template.yml +113 -0
- package/framework/templates/breaking-change-checklist.md +484 -0
- package/framework/templates/feature-card.template.yml +125 -0
- package/framework/templates/overlays/README.md +44 -0
- package/framework/templates/overlays/copywriting.fidelity-example.md +62 -0
- package/framework/templates/overlays/ui-design.fidelity-example.md +75 -0
- package/framework/templates/skill-project-context.snippet.md +19 -0
- package/framework/templates/spec.template.md +208 -0
- package/package.json +51 -0
- package/src/commands/add.js +229 -0
- package/src/commands/configure.js +385 -0
- package/src/commands/doctor.js +486 -0
- package/src/commands/migrate.js +185 -0
- package/src/commands/push.js +0 -0
- package/src/commands/routines.js +269 -0
- package/src/commands/status.js +130 -0
- package/src/commands/update.js +419 -0
- package/src/commands/version.js +88 -0
- package/src/utils/contamination.js +400 -0
- package/src/utils/git.js +181 -0
- package/src/utils/hooks.js +152 -0
- package/src/utils/routine-adapters/claude-code-cloud.js +78 -0
- package/src/utils/routine-adapters/cron.js +138 -0
- package/src/utils/routine-adapters/github-actions.js +141 -0
- package/src/utils/routine-adapters/index.js +21 -0
- package/src/utils/routines.js +166 -0
- package/src/utils/state.js +143 -0
- package/src/utils/symlinks.js +425 -0
- 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;
|