baldart 4.82.0 → 4.84.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/README.md +4 -2
- package/VERSION +1 -1
- package/framework/.claude/hooks/agent-discovery-gate.js +4 -2
- package/framework/.claude/skills/ds-handoff/CHANGELOG.md +20 -0
- package/framework/.claude/skills/ds-handoff/SKILL.md +22 -6
- package/framework/.claude/skills/ds-handoff/assets/claude-design-handoff-prompt.template.md +23 -2
- package/framework/.claude/skills/ds-handoff/references/decision-pass.md +94 -0
- package/framework/.claude/skills/ds-handoff/references/field-extraction.md +21 -1
- package/framework/.claude/skills/prd/references/ui-design-phase.md +9 -4
- package/framework/docs/ROOT-PRIMITIVES.md +98 -0
- package/framework/templates/primitives/AGENTS.CHANGELOG.md +18 -0
- package/framework/templates/primitives/AGENTS.md +111 -0
- package/framework/templates/primitives/CLAUDE.CHANGELOG.md +15 -0
- package/framework/templates/primitives/CLAUDE.md +41 -0
- package/package.json +1 -1
- package/src/commands/add.js +15 -0
- package/src/commands/doctor.js +65 -0
- package/src/commands/migrate.js +30 -0
- package/src/commands/update.js +31 -0
- package/src/utils/root-primitives.js +418 -0
- package/src/utils/symlinks.js +24 -10
- package/framework/AGENTS.md +0 -255
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<!-- baldart-primitive: name=CLAUDE primitive_version=1.0.0
|
|
2
|
+
SHIPPED SKELETON. The writer strips this banner, resolves {{ slot }}/{{#flag}} from
|
|
3
|
+
baldart.config.yml, merges .baldart/overlays/CLAUDE.md, stamps a baldart-generated
|
|
4
|
+
marker, and writes the result to the repo root.
|
|
5
|
+
Claude Code loads BOTH CLAUDE.md and AGENTS.md automatically, so this file does NOT
|
|
6
|
+
import AGENTS.md (that would double-load the protocol). It holds ONLY the Claude-native
|
|
7
|
+
mechanics that would be noise in the cross-tool AGENTS.md. Edit the OVERLAY, never the
|
|
8
|
+
generated root CLAUDE.md. -->
|
|
9
|
+
# {{ identity.name }} — Claude Code entry
|
|
10
|
+
|
|
11
|
+
The full cross-tool coordination protocol (rules, MUST list, git workflow, gates, routing)
|
|
12
|
+
lives in `AGENTS.md`, which Claude Code loads automatically alongside this file — it is the
|
|
13
|
+
SSOT, not restated here. Below are the **Claude-native mechanics only**.
|
|
14
|
+
|
|
15
|
+
## Plan Mode (Claude-native)
|
|
16
|
+
|
|
17
|
+
- Enter plan mode (`EnterPlanMode`) for any non-trivial task before writing code.
|
|
18
|
+
Trivial = single-line fix / typo / obvious bug.
|
|
19
|
+
- Invoke `codebase-architect` before writing the plan — never plan blind.
|
|
20
|
+
- Launch `plan-auditor` + `doc-reviewer` in parallel to review the plan, incorporate their
|
|
21
|
+
feedback, and state inside the plan that the review gate was honored, BEFORE `ExitPlanMode`.
|
|
22
|
+
- Wait for explicit user approval, then delegate implementation to the `coder` agent (via the
|
|
23
|
+
Task tool). The `coder` writes code + minimal doc stubs only.
|
|
24
|
+
|
|
25
|
+
## User Interaction (Claude-native, ABSOLUTE)
|
|
26
|
+
|
|
27
|
+
- `AskUserQuestion` = STOP and wait. Never answer your own question.
|
|
28
|
+
- It MUST be the only action in that turn. Never proceed without a real user response.
|
|
29
|
+
|
|
30
|
+
## Subagents & tooling (Claude-native)
|
|
31
|
+
|
|
32
|
+
- Spawn subagents via the Task tool (`subagent_type: <name>`); the roster + routing is
|
|
33
|
+
`.claude/agents/REGISTRY.md`.
|
|
34
|
+
{{#output_style}}
|
|
35
|
+
- Output style: `{{ output_style }}` is active (select via `/config` → Output Style).
|
|
36
|
+
{{/output_style}}
|
|
37
|
+
- Hooks (PreToolUse / edit gates) are Claude-only and configured in `.claude/settings.json`.
|
|
38
|
+
|
|
39
|
+
<!-- OVERLAY: project-specific Claude tweaks live in `.baldart/overlays/CLAUDE.md` and are
|
|
40
|
+
merged below this line (same OVERRIDE/APPEND/PREPEND model as AGENTS.md). Keep this file
|
|
41
|
+
thin — cross-tool rules belong in the AGENTS.md overlay, not here. -->
|
package/package.json
CHANGED
package/src/commands/add.js
CHANGED
|
@@ -296,6 +296,21 @@ async function add(repo, options) {
|
|
|
296
296
|
UI.info('Skipped. Run `npx baldart configure` before invoking skills.');
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
+
// Re-write the root primitives (AGENTS.md + CLAUDE.md) now that configure
|
|
300
|
+
// has populated baldart.config.yml — so the slots resolve to the final
|
|
301
|
+
// values (name, stack, commands, paths, trunk branch). createAllSymlinks
|
|
302
|
+
// already wrote them once with whatever config existed; this is idempotent
|
|
303
|
+
// (byte-stable when nothing changed) and just refreshes the concrete facts.
|
|
304
|
+
try {
|
|
305
|
+
const rootPrimitives = require('../utils/root-primitives');
|
|
306
|
+
const res = rootPrimitives.writeRootPrimitives({ cwd: process.cwd(), tools: enabledTools, autonomous: nonInteractive });
|
|
307
|
+
if (res.adopted && res.adopted.length) {
|
|
308
|
+
for (const a of res.adopted) UI.info(`Adopted existing ${a.name}.md → ${a.overlay} (verbatim). Review & re-file when convenient.`);
|
|
309
|
+
}
|
|
310
|
+
} catch (err) {
|
|
311
|
+
UI.warning(`Root primitives step failed: ${err.message}`);
|
|
312
|
+
}
|
|
313
|
+
|
|
299
314
|
// Post-flight assert — never declare success with missing hooks.
|
|
300
315
|
// Re-reads .claude/settings.json and confirms every active hook in
|
|
301
316
|
// HOOK_REGISTRY is present. Guards against the silent partial-install
|
package/src/commands/doctor.js
CHANGED
|
@@ -343,6 +343,28 @@ async function detectState(cwd, opts = {}) {
|
|
|
343
343
|
}
|
|
344
344
|
} catch (_) { /* never block doctor on probe */ }
|
|
345
345
|
|
|
346
|
+
// Root primitives (AGENTS.md + CLAUDE.md) integrity (since v4.83.0).
|
|
347
|
+
// Stale = missing, still a legacy symlink, or drifted vs the current
|
|
348
|
+
// skeleton / overlay / config. A handwritten file is surfaced separately
|
|
349
|
+
// (needs interactive adoption) and is NOT auto-regenerated.
|
|
350
|
+
state.rootPrimitivesStale = [];
|
|
351
|
+
state.rootPrimitivesAdoptable = [];
|
|
352
|
+
try {
|
|
353
|
+
if (state.frameworkPresent) {
|
|
354
|
+
const rp = require('../utils/root-primitives');
|
|
355
|
+
for (const name of rp.PRIMITIVES) {
|
|
356
|
+
if (name === 'CLAUDE') {
|
|
357
|
+
const toolsEnabled = (config && !config.__malformed && config.tools && Array.isArray(config.tools.enabled))
|
|
358
|
+
? config.tools.enabled : ['claude'];
|
|
359
|
+
if (!toolsEnabled.includes('claude')) continue;
|
|
360
|
+
}
|
|
361
|
+
const st = rp.rootPrimitiveStatus({ cwd, name, cfg: config && !config.__malformed ? config : undefined });
|
|
362
|
+
if (st.state === 'handwritten') state.rootPrimitivesAdoptable.push({ name, reason: st.reason });
|
|
363
|
+
else if (st.stale) state.rootPrimitivesStale.push({ name, reason: st.reason });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
} catch (_) { /* never block doctor on probe */ }
|
|
367
|
+
|
|
346
368
|
// `.framework/` is a git subtree — gitignore matching it breaks every
|
|
347
369
|
// future `git add` under that path (the failure mode that hit v3.13.0
|
|
348
370
|
// consumers during push). Detect any gitignore rule that catches the
|
|
@@ -861,6 +883,49 @@ function planActions(state) {
|
|
|
861
883
|
});
|
|
862
884
|
}
|
|
863
885
|
|
|
886
|
+
// Root primitives integrity (since v4.83.0). Regenerates AGENTS.md / CLAUDE.md
|
|
887
|
+
// from the versioned skeleton + config + overlay when missing, still a legacy
|
|
888
|
+
// symlink, or drifted. Offline + idempotent. A handwritten file is NOT touched
|
|
889
|
+
// here — it needs interactive adoption (reported below, not auto-run).
|
|
890
|
+
if (state.rootPrimitivesStale && state.rootPrimitivesStale.length > 0) {
|
|
891
|
+
const names = state.rootPrimitivesStale.map((r) => `${r.name}.md`).join(', ');
|
|
892
|
+
actions.push({
|
|
893
|
+
key: 'regenerate-root-primitives',
|
|
894
|
+
label: `Regenerate root primitive(s): ${names}`,
|
|
895
|
+
why: `These root files are out of date vs the shipped skeleton / your overlay / baldart.config.yml (${state.rootPrimitivesStale.map((r) => `${r.name}: ${r.reason}`).join('; ')}). They are BALDART-generated — edit .baldart/overlays/<name>.md, not the file itself.`,
|
|
896
|
+
autoOk: true,
|
|
897
|
+
run: async () => {
|
|
898
|
+
const cfg = loadConfig(process.cwd());
|
|
899
|
+
const enabledTools = (cfg && !cfg.__malformed
|
|
900
|
+
&& cfg.tools && Array.isArray(cfg.tools.enabled))
|
|
901
|
+
? cfg.tools.enabled : ['claude'];
|
|
902
|
+
const res = require('../utils/root-primitives').writeRootPrimitives({ cwd: process.cwd(), tools: enabledTools });
|
|
903
|
+
UI.success(`Regenerated root primitives: ${(res.written || []).map((w) => `${w.name}.md`).join(', ') || '(none changed)'}`);
|
|
904
|
+
},
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// Handwritten AGENTS.md/CLAUDE.md that predate BALDART's ownership — offer a
|
|
909
|
+
// zero-loss adoption (preserve verbatim into an overlay, then generate). Never
|
|
910
|
+
// auto-run: mutating a hand-authored file must be an explicit choice.
|
|
911
|
+
if (state.rootPrimitivesAdoptable && state.rootPrimitivesAdoptable.length > 0) {
|
|
912
|
+
const names = state.rootPrimitivesAdoptable.map((r) => `${r.name}.md`).join(', ');
|
|
913
|
+
actions.push({
|
|
914
|
+
key: 'adopt-root-primitives',
|
|
915
|
+
label: `Adopt handwritten ${names} into overlay(s)`,
|
|
916
|
+
why: `${names} are hand-authored files with no BALDART marker. Adoption preserves them verbatim into .baldart/overlays/<name>.md (zero loss) and regenerates the root as skeleton + your overlay. Review afterwards — cross-tool rules belong in the AGENTS.md overlay.`,
|
|
917
|
+
autoOk: false,
|
|
918
|
+
run: async () => {
|
|
919
|
+
const cfg = loadConfig(process.cwd());
|
|
920
|
+
const enabledTools = (cfg && !cfg.__malformed
|
|
921
|
+
&& cfg.tools && Array.isArray(cfg.tools.enabled))
|
|
922
|
+
? cfg.tools.enabled : ['claude'];
|
|
923
|
+
const res = require('../utils/root-primitives').writeRootPrimitives({ cwd: process.cwd(), tools: enabledTools, autonomous: false });
|
|
924
|
+
for (const a of (res.adopted || [])) UI.success(`Adopted ${a.name}.md → ${a.overlay} (verbatim). Review & re-file when convenient.`);
|
|
925
|
+
},
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
|
|
864
929
|
if (state.lspEnabled && state.lspBroken && state.lspBroken.length > 0) {
|
|
865
930
|
actions.push({
|
|
866
931
|
key: 'lsp-fix',
|
package/src/commands/migrate.js
CHANGED
|
@@ -75,6 +75,17 @@ async function migrate() {
|
|
|
75
75
|
UI.warning(`Found .claude/skills.backup with ${entries.length} entries (from a prior failed update).`);
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
+
// Root primitives (since v4.83.0): AGENTS.md moved from a bulk symlink to a
|
|
79
|
+
// generated real file, and CLAUDE.md is now shipped. Report a legacy symlink.
|
|
80
|
+
const agentsMdPath = path.join(cwd, 'AGENTS.md');
|
|
81
|
+
let agentsMdLegacySymlink = false;
|
|
82
|
+
try {
|
|
83
|
+
if (fs.lstatSync(agentsMdPath).isSymbolicLink()) {
|
|
84
|
+
agentsMdLegacySymlink = true;
|
|
85
|
+
UI.warning('AGENTS.md is a legacy bulk symlink → will become a generated real file.');
|
|
86
|
+
}
|
|
87
|
+
} catch (_) { /* absent or real file — nothing to report here */ }
|
|
88
|
+
|
|
78
89
|
// --- Plan ---------------------------------------------------------------
|
|
79
90
|
|
|
80
91
|
const actions = [];
|
|
@@ -88,6 +99,11 @@ async function migrate() {
|
|
|
88
99
|
if (backupPresent) {
|
|
89
100
|
actions.push('Restore user skills from .claude/skills.backup into .claude/skills/ (no overwrites).');
|
|
90
101
|
}
|
|
102
|
+
if (agentsMdLegacySymlink) {
|
|
103
|
+
actions.push('Convert legacy AGENTS.md symlink into a generated real file (skeleton + config + overlay).');
|
|
104
|
+
} else {
|
|
105
|
+
actions.push('Generate/refresh root primitives AGENTS.md + CLAUDE.md (handwritten files are adopted into overlays, zero-loss).');
|
|
106
|
+
}
|
|
91
107
|
actions.push('Record any name collisions in .baldart/skill-conflicts.json.');
|
|
92
108
|
|
|
93
109
|
UI.section('Planned actions');
|
|
@@ -133,6 +149,20 @@ async function migrate() {
|
|
|
133
149
|
symlinks.mergeCommands({ tools: enabledTools });
|
|
134
150
|
UI.info(`Linked ${mergeResult.linked.length} framework skills, kept ${mergeResult.skipped.length} as-is, ${mergeResult.conflicts.length} conflict(s).`);
|
|
135
151
|
|
|
152
|
+
// --- Step 2b: root primitives (v4.83.0) --------------------------------
|
|
153
|
+
// Converts a legacy AGENTS.md symlink → generated real file, ships CLAUDE.md,
|
|
154
|
+
// and adopts any handwritten root file into an overlay (interactive, zero-loss).
|
|
155
|
+
UI.section('Step 2b: root primitives (AGENTS.md + CLAUDE.md)');
|
|
156
|
+
try {
|
|
157
|
+
const res = require('../utils/root-primitives').writeRootPrimitives({ cwd, tools: enabledTools, autonomous: false });
|
|
158
|
+
for (const a of (res.adopted || [])) UI.success(`Adopted ${a.name}.md → ${a.overlay} (verbatim, zero-loss). Review & re-file later.`);
|
|
159
|
+
const wrote = (res.written || []).map((w) => `${w.name}.md (${w.from})`);
|
|
160
|
+
UI.info(wrote.length ? `Root primitives written: ${wrote.join(', ')}.` : 'Root primitives already up to date.');
|
|
161
|
+
for (const b of (res.blockers || [])) UI.warning(`Root primitive ${b.name}: ${b.reason}`);
|
|
162
|
+
} catch (err) {
|
|
163
|
+
UI.warning(`Root primitives step skipped: ${err.message}`);
|
|
164
|
+
}
|
|
165
|
+
|
|
136
166
|
// --- Step 3: restore user skills from .backup --------------------------
|
|
137
167
|
|
|
138
168
|
if (backupPresent) {
|
package/src/commands/update.js
CHANGED
|
@@ -123,6 +123,7 @@ function scaffoldOverlaysForCommits(commits) {
|
|
|
123
123
|
const BALDART_MANAGED_PATTERNS = [
|
|
124
124
|
/^\.framework(\/|$)/,
|
|
125
125
|
/^AGENTS\.md$/,
|
|
126
|
+
/^CLAUDE\.md$/,
|
|
126
127
|
/^agents(\.backup)?(\/|$)/,
|
|
127
128
|
/^\.claude\/(agents|commands|skills|workflows|hooks|routines|settings\.json)(\/|$)/,
|
|
128
129
|
/^\.baldart(\/|$)/,
|
|
@@ -137,6 +138,28 @@ function isBaldartManagedPath(p) {
|
|
|
137
138
|
return BALDART_MANAGED_PATTERNS.some((rx) => rx.test(p));
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
// Re-generate the root primitives (AGENTS.md + CLAUDE.md) from the versioned
|
|
142
|
+
// skeletons + baldart.config.yml + overlays. Idempotent + byte-stable, so it is
|
|
143
|
+
// safe to call on both the aligned-install and post-pull paths. Never throws —
|
|
144
|
+
// a failure here must not abort an otherwise-successful update. Adoption of a
|
|
145
|
+
// handwritten file is interactive-gated inside the writer (a CI/autonomous run
|
|
146
|
+
// surfaces a blocker and leaves the file untouched rather than mutating it).
|
|
147
|
+
function reconcileRootPrimitives(cwd, tools, autonomous) {
|
|
148
|
+
try {
|
|
149
|
+
const res = require('../utils/root-primitives').writeRootPrimitives({ cwd, tools, autonomous });
|
|
150
|
+
for (const a of (res.adopted || [])) {
|
|
151
|
+
UI.info(`Adopted existing ${a.name}.md → ${a.overlay} (verbatim, zero-loss). Review & re-file when convenient.`);
|
|
152
|
+
}
|
|
153
|
+
for (const b of (res.blockers || [])) {
|
|
154
|
+
UI.warning(`Root primitive ${b.name}: ${b.reason}`);
|
|
155
|
+
}
|
|
156
|
+
const wrote = (res.written || []).map((w) => `${w.name}.md`);
|
|
157
|
+
if (wrote.length) UI.info(`Root primitives updated: ${wrote.join(', ')}.`);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
UI.warning(`Root primitives step skipped: ${err.message}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
140
163
|
// Real preview (v4.8.0+) — the old `git log HEAD..FETCH_HEAD -- .framework/`
|
|
141
164
|
// is always empty under subtree-squash, so the user never saw what changed.
|
|
142
165
|
// Instead, read the UPSTREAM CHANGELOG and return every entry above the
|
|
@@ -912,6 +935,10 @@ async function update(options = {}, unknownArgs = []) {
|
|
|
912
935
|
if (reconciled > 0) {
|
|
913
936
|
UI.info(`Reconciled ${reconciled} newly-shipped item(s) into per-tool dir(s) — payload drift from a prior CLI-skew update, now healed.`);
|
|
914
937
|
}
|
|
938
|
+
// Re-generate the root primitives too (AGENTS.md + CLAUDE.md): a primitive
|
|
939
|
+
// version bump or a slot/overlay change must land even on an aligned install.
|
|
940
|
+
// --json is programmatic → force autonomous so a handwritten file is never mutated.
|
|
941
|
+
reconcileRootPrimitives(process.cwd(), alignedTools, options && options.json === true ? true : undefined);
|
|
915
942
|
UI.success(`Already up to date! (v${status.installedVersion})`);
|
|
916
943
|
emitUpdateJson({ ok: true, action: 'already-current',
|
|
917
944
|
installed_before: status.installedVersion,
|
|
@@ -1242,6 +1269,10 @@ async function update(options = {}, unknownArgs = []) {
|
|
|
1242
1269
|
symlinks.mergeOutputStyles({ tools: enabledTools });
|
|
1243
1270
|
}
|
|
1244
1271
|
|
|
1272
|
+
// Re-generate the root primitives (AGENTS.md + CLAUDE.md) after the pull so
|
|
1273
|
+
// a primitive/slot/overlay change lands. Idempotent + byte-stable.
|
|
1274
|
+
reconcileRootPrimitives(process.cwd(), enabledTools, options && options.json === true ? true : undefined);
|
|
1275
|
+
|
|
1245
1276
|
// Routines wizard (since v2.1.0) — surfaces routines added in the new framework version
|
|
1246
1277
|
try {
|
|
1247
1278
|
const routinesCmd = require('./routines');
|