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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "4.82.0",
3
+ "version": "4.84.0",
4
4
  "description": "Claude Agent Framework - Reusable framework for coordinating AI agents and humans in software projects",
5
5
  "bin": {
6
6
  "baldart": "./bin/baldart.js"
@@ -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
@@ -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',
@@ -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) {
@@ -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');