baldart 3.16.0 → 3.17.1

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 CHANGED
@@ -5,6 +5,45 @@ All notable changes to BALDART will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [3.17.1] - 2026-05-23
9
+
10
+ CLI quality-of-life patch shaken loose by a hands-on consumer update simulation (mayo, 3.13.0 → 3.16.0). Four real bugs surfaced when actually driving `baldart update` end-to-end from a scripted context. Two were show-stoppers for any non-interactive use (CI, AI-driven, headless update agent), one was cosmetic but ugly, one was a stale stub that lied to the user. All four are fixed; nothing in this release changes framework payload behaviour for end-users running BALDART interactively in a TTY.
11
+
12
+ ### Fixed
13
+
14
+ - **`baldart update` gains `--yes`/`-y` and `--auto-stash` flags** in [bin/baldart.js](bin/baldart.js) and [src/commands/update.js](src/commands/update.js). Until now, every `UI.confirm` and the `UI.select` arrow-key stash prompt blocked indefinitely on a non-TTY stdin (inquirer's pattern detection mis-handles re-rendered prompts when fed via pipe or `expect`). Both flags route through a single internal `confirm()` wrapper that short-circuits to the prompt's default when `--yes` is set; `--auto-stash` is the narrower variant for users who want manual control over confirms but auto-stash on dirty trees. `--yes` implies `--auto-stash` and additionally passes `--non-interactive` down to the `baldart configure` sub-invocation triggered by the schema-drift detector, plus switches symlink reconcile from `mode: 'prompt'` to `mode: 'force'` (the conflict-resolution prompts inside `SymlinkUtils` would otherwise reintroduce blocking I/O even with `--yes`).
15
+ - **`baldart status` actually checks the framework against the upstream tip** in [src/commands/status.js](src/commands/status.js). Previously the "Update Status" section was a hard-coded `UI.success('Up to date')` stub regardless of the installed version — a 4-versions-stale consumer was told everything was fine. The fix calls `git.fetch('antbald/BALDART')` + `git.getRemoteVersion(...)` (already exported by `src/utils/git.js`), compares semver with a tiny inline `cmpSemver` helper, and renders one of four outcomes: update available (`X → Y`), local ahead of remote (unreleased commit, common during BALDART self-development), up to date, or "Cannot check (offline?)" on fetch failure. The SUMMARY box now carries the update line too, so it's visible without scrolling past the per-section output.
16
+ - **`baldart --version` and `baldart --help` exit cleanly** in [bin/baldart.js](bin/baldart.js). Commander 9 throws a `CommanderError` with `exitCode: 0` from these flags as its internal flow-control mechanism; the previous `bin/baldart.js` had no try/catch around `program.parse(process.argv)`, so the throw escaped to Node and printed a 12-line `CommanderError: 3.17.0` stack trace right after the version itself. Now wrapped in try/catch that recognises the three known sentinel codes (`commander.version`, `commander.help`, `commander.helpDisplayed`) and exits 0 silently; real errors keep the previous behaviour (log message + non-zero exit).
17
+ - **CLI version is read from the `VERSION` file, not `package.json`** in [bin/baldart.js](bin/baldart.js). `package.json.version` is only synced at publish time via `npm run sync-version` (see `scripts/sync-version`), so in dev / local-source / pre-publish contexts it lagged the actual `VERSION` file — `node bin/baldart.js --version` was printing `3.14.1` while VERSION was `3.17.0`. A new `resolveVersion()` reads `VERSION` at module load with `package.json.version` as fallback. Published `baldart@<x.y.z>` on npm is unaffected (the workflow still runs `sync-version` first, so both files agree in published artefacts).
18
+
19
+ ### Notes
20
+
21
+ - **No framework payload changes** — all four fixes are inside `bin/` and `src/`. Skills, agents, templates, hooks, routines, docs are untouched. The next `baldart update` for consumers picks up the new flags and the working status check without any change to the framework files they install.
22
+ - **Not a v3.18.0 because no new capability shipped** — this is purely "what should already have worked". The new flags are additive (zero-config end-users in a TTY see no change) and the status fix is replacing a stub with the real check it was always supposed to be.
23
+ - **Bug #4 from the mayo simulation (schema-drift detector skipped with `--no-commit`) was a false positive** — the drift detector at lines 462–503 runs before `postUpdateAutoCommit` (line 506), so `--no-commit` doesn't reach it. Verified by reading the call sequence, no code change needed.
24
+ - **Verification** (manual, no test suite): (1) `node bin/baldart.js --version` prints `3.17.1` and exits 0 silently; (2) `node bin/baldart.js update --help` shows the three options (`--no-commit`, `--yes/-y`, `--auto-stash`); (3) in a stale consumer repo, `baldart status` reports "Update available" with the version diff; (4) `baldart update --yes` on a dirty consumer auto-stashes, pulls, reconciles symlinks in force mode, runs `configure --non-interactive`, and exits 0 with no blocking prompts.
25
+
26
+ ## [3.17.0] - 2026-05-23
27
+
28
+ The `/prd` skill gains a third option in the design phase. Until v3.16.0, when a user reached Step 3 without having provided mockups (`mockups.status: none`), the only path forward was internal generation via the `ui-design` skill (3 HTML options → iteration → save). With Claude Design now offering a design-system-sync'd visual generator, users who prefer iterating in that surface had to abandon the PRD flow halfway, work externally, and then re-enter as if they'd had mockups from the start. This release adds **Step 3.0 — Mockup Source Decision**: when `mockups.status: none` AND UI impact ≠ N/A, the skill explicitly asks "internal generation or handoff to Claude Design?". The handoff branch generates a context-rich prompt (discovery + brand voice + design-system tokens + screens in scope + stack target) ready to paste, then STOPs. On user re-entry with local paths, it re-uses the existing Step 1.6 Mockup Intake verbatim — copy → analyze → populate state — and Step 3 falls naturally into Hybrid mode. Zero code paths downstream of the bivio are new; the entire feature is a decision point + a prompt generator on top of pre-existing infrastructure.
29
+
30
+ ### Added — PRD Step 3.0 Mockup Source Decision
31
+
32
+ - **§ Step 3.0 in [framework/.claude/skills/prd/references/ui-design-phase.md](framework/.claude/skills/prd/references/ui-design-phase.md)** before the precondition decision tree. Activates only when `mockups.status: none` AND UI impact ≠ N/A. Poses the binary question (Internal `ui-design` vs Handoff Claude Design) with explicit STOP gate — silence or ambiguity re-asks rather than defaulting silently. Branch A sets `mockups.source: internal` and falls through unchanged. Branch B collects context from state file + `baldart.config.yml` + `.baldart/overlays/prd.md` + `${paths.design_system}/INDEX.md` + `tokens-reference.md` (when `features.has_design_system: true`), renders the handoff prompt template, presents it inside a fenced code-block with copy-paste instructions, persists `mockups.status: pending_external` + `mockups.source: claude-design` + `mockups.handoff_prompt_path` in the state file (prompt saved to `${paths.prd_dir}/<slug>/handoff/claude-design-prompt.md` for session-restart survival), and STOPs.
33
+ - **Re-entry handler in same file**. When the user returns with local paths after handoff, the skill invokes the existing Step 1.6 Mockup Intake procedure verbatim (no code duplication) — copy into `mockups/` with collision-safe rename, run Mockup analysis schema, populate `## UI Design`. Then flips `mockups.status` from `pending_external` to `provided` or `partial` based on coverage of `screens_in_scope[]`, keeps `mockups.source: claude-design` as audit trail, and falls through to the precondition decision tree which routes to Hybrid mode unchanged.
34
+ - **Precondition decision tree extended** with two new rows: `mockups.status: pending_external` ⇒ STOP (handoff in flight); `mockups.status: none` AND user picked Branch A ⇒ Full (preserves backwards-compatible default). The existing `provided` / `partial` ⇒ Hybrid and UI = N/A ⇒ Skip rows are unchanged.
35
+ - **New template asset [framework/.claude/skills/prd/assets/claude-design-handoff-prompt.template.md](framework/.claude/skills/prd/assets/claude-design-handoff-prompt.template.md)**. Seven-section structure populated at runtime: (1) feature objective, (2) user flow + personas, (3) screens in scope with required states + primary actions + data shown, (4) brand & voice (conditional on overlay), (5) design system constraints with tokens excerpt + registered primitives + numeric tables from `framework/agents/design-system-protocol.md` (conditional on `features.has_design_system: true`), (6) stack target + output format preference derived from `stack.framework`, (7) deliverable spec (PNG exports per state + code/Figma + inline notes on new components / token deviations / layout decisions). Generic and project-agnostic — all project-specific content is injected as placeholders.
36
+ - **Hard Rule 16 note in [framework/.claude/skills/prd/SKILL.md](framework/.claude/skills/prd/SKILL.md)** added to the existing Mockup Intake rule. Codifies that a NO answer at intake routes through Step 3.0 (not directly to Full mode), with cross-reference to the ui-design-phase section. The rule itself remains unchanged for backwards compatibility — Step 3.0 is purely additive.
37
+
38
+ ### Notes
39
+
40
+ - **No new `baldart.config.yml` keys** — the schema-change propagation rule does not apply to this release. The bivio is runtime-only per the user's preference (zero persistent flag); no template/configure/update/doctor updates needed. All context for the handoff prompt is read from existing config keys (`paths.*`, `stack.*`, `features.has_design_system`).
41
+ - **Backwards compatibility.** PRDs in flight on v3.16.x finish on v3.16.x semantics. New PRD sessions started on v3.17.0+ see Step 3.0 only when they actually reach Step 3 with `mockups.status: none` — the default (Branch A) preserves the v3.16.x behavior exactly. Existing PRDs that already provided mockups never enter Step 3.0.
42
+ - **`ui-design` skill is unchanged** — Step 3.0 sits in front of it, doesn't modify it. The Full pipeline (3a → 3d) is reached identically when the user picks Branch A or when no mockup-related state exists at all. The `ui-expert` agent and `huashu-design`/`frontend-design` skills are equally untouched.
43
+ - **`mockup_analysis` schema is unchanged** — the new `pending_external` value of `mockups.status` is purely transitional and is overwritten by `provided` / `partial` on user re-entry, so existing PRD templates and state files keep validating without migration.
44
+ - **Files modified outside the Step 3.0 scope** (`card-template.yml`, `backlog-phase.md`, `validation-phase.md`, `prd-card-writer.md`, `framework/.claude/skills/new/SKILL.md`) are the v3.16.0 in-flight release (agent routing + UI specialist briefing — see entry below) and are NOT part of v3.17.0. v3.16.0 should be tagged and pushed before v3.17.0 in the release sequence.
45
+ - **Verification is manual** (no test suite). Dogfood scenarios: (1) `/prd` with mockups provided at intake → never enters Step 3.0, Hybrid mode as today (regression check); (2) `/prd` without mockups, answer "A" at Step 3.0 → Full mode runs identically to v3.16.x; (3) `/prd` without mockups, answer "B" → prompt rendered with discovery + tokens + screens + stack, saved to `handoff/`, state shows `pending_external`, skill STOPs; (4) simulate re-entry with a PNG path → file copied to `mockups/`, status flips to `provided`, Step 3 enters Hybrid mode invoking `ui-design` only for uncovered screens; (5) `features.has_design_system: false` → § 5 of the rendered prompt is omitted entirely; (6) no overlay present → § 4 brand voice omitted.
46
+
8
47
  ## [3.16.0] - 2026-05-23
9
48
 
10
49
  The `/new` orchestrator becomes specialist-aware: it now reads `owner_agent` from each card YAML and dispatches the right implementation agent — `coder` for backend/logic, `ui-expert` for UI cards, with `plan`/`visual-designer`/`motion-expert` reserved as stubs that fall back to `coder` with a WARN. UI cards (`owner_agent: ui-expert`) receive a dedicated mission briefing that enforces the registry-first BLOCKING cascade (read `${paths.design_system}/INDEX.md` + `tokens-reference.md` + per-component specs BEFORE coding), the implementation-only constraint (no mockup redesign — STOP and ask on registry conflicts), and the Post-Intervention Coherence Check (reconcile INDEX + components + tokens in the same commit). The PRD side enforces the contract end-to-end: `owner_agent` is now a mandatory enum on every child card, UI cards have UI-only scope (logic moves to a sibling `coder` card), and the validation phase blocks commit on enum violations + warns on mixed-scope UI cards.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.16.0
1
+ 3.17.1
package/bin/baldart.js CHANGED
@@ -2,9 +2,27 @@
2
2
 
3
3
  const { Command } = require('commander');
4
4
  const chalk = require('chalk');
5
+ const fs = require('fs');
6
+ const path = require('path');
5
7
  const packageJson = require('../package.json');
6
8
  const updateNotifier = require('../src/utils/update-notifier');
7
9
 
10
+ // Source of truth for the running version is the VERSION file at the repo
11
+ // root. package.json.version is only synced at publish time (see scripts/
12
+ // sync-version), so in dev / local-source / pre-publish contexts it lags.
13
+ // Reading VERSION here keeps `baldart --version` honest in every scenario.
14
+ function resolveVersion() {
15
+ try {
16
+ const versionFile = path.join(__dirname, '..', 'VERSION');
17
+ const fromFile = fs.readFileSync(versionFile, 'utf8').trim();
18
+ if (fromFile) return fromFile;
19
+ } catch (_) { /* fall through */ }
20
+ // Fall back to package.json (already required above). Avoids the TDZ trap
21
+ // of self-referencing `CLI_VERSION` before its initialiser runs.
22
+ return packageJson.version;
23
+ }
24
+ const CLI_VERSION = resolveVersion();
25
+
8
26
  // CLI self-update notifier (v3.13.0+). Best-effort; non-blocking; suppressed
9
27
  // in CI / non-TTY / via BALDART_NO_UPDATE_CHECK=1 / when --offline is passed.
10
28
  // Prints a one-shot hint at the top of the run if a newer npm version was
@@ -14,7 +32,7 @@ const updateNotifier = require('../src/utils/update-notifier');
14
32
  const rawArgsForNotifier = process.argv.slice(2);
15
33
  const offlineForNotifier = rawArgsForNotifier.includes('--offline');
16
34
  updateNotifier.announceAndRefresh({
17
- currentVersion: packageJson.version,
35
+ currentVersion: CLI_VERSION,
18
36
  offline: offlineForNotifier,
19
37
  });
20
38
 
@@ -23,7 +41,7 @@ const program = new Command();
23
41
  program
24
42
  .name('baldart')
25
43
  .description('Claude Agent Framework - AI agent coordination for software projects')
26
- .version(packageJson.version);
44
+ .version(CLI_VERSION);
27
45
 
28
46
  program
29
47
  .command('add [repo]')
@@ -38,6 +56,8 @@ program
38
56
  .command('update')
39
57
  .description('Update the framework to the latest version')
40
58
  .option('--no-commit', 'Skip the post-update auto-commit prompt')
59
+ .option('-y, --yes', 'Auto-confirm all prompts (CI / scripting). Implies --auto-stash.')
60
+ .option('--auto-stash', 'When the working tree is dirty, stash automatically instead of prompting.')
41
61
  .action(async (options) => {
42
62
  const updateCommand = require('../src/commands/update');
43
63
  await updateCommand(options);
@@ -153,5 +173,16 @@ if (isDoctorShortcut) {
153
173
  process.exit(1);
154
174
  });
155
175
  } else {
156
- program.parse(process.argv);
176
+ try {
177
+ program.parse(process.argv);
178
+ } catch (err) {
179
+ // Commander throws CommanderError(exitCode: 0) for --version / --help.
180
+ // That's a normal exit, not a crash — swallow the throw and exit cleanly.
181
+ if (err && err.code === 'commander.version') process.exit(0);
182
+ if (err && err.code === 'commander.help') process.exit(0);
183
+ if (err && err.code === 'commander.helpDisplayed') process.exit(0);
184
+ // Real error: log and exit non-zero like before.
185
+ console.error(err && err.message ? err.message : err);
186
+ process.exit(err && Number.isInteger(err.exitCode) ? err.exitCode : 1);
187
+ }
157
188
  }
@@ -101,6 +101,13 @@ message. You ask questions, wait for answers, and iterate.
101
101
  (`mockup_analysis.screens[].gaps[]`) DEVONO diventare domande mirate in Discovery o
102
102
  essere esplicitamente waivate.
103
103
 
104
+ **Nota — handoff esterno post-intake.** Se l'utente risponde NO al mockup intake, lo
105
+ Step 3.0 in UI Design gli chiederà se vuole generare i mockup internamente (workflow
106
+ `ui-design`) o fare handoff a **Claude Design**. Nel ramo handoff la skill genera un
107
+ prompt context-rich pronto da incollare, fa STOP, e al rientro con i path locali
108
+ riusa esattamente questa stessa procedura di Step 1.6 → Step 3 entra in Hybrid mode.
109
+ Vedi [ui-design-phase.md](references/ui-design-phase.md) § "Step 3.0 — Mockup Source Decision".
110
+
104
111
  ---
105
112
 
106
113
  ## TEMPLATES
@@ -0,0 +1,116 @@
1
+ # Claude Design Handoff Prompt — Template
2
+
3
+ > Template populated at runtime by **Step 3.0 (Mockup Source Decision)** in
4
+ > [../references/ui-design-phase.md](../references/ui-design-phase.md) when the
5
+ > user picks the **handoff** branch. All `{{placeholders}}` are resolved from the
6
+ > PRD state file (`${paths.prd_dir}/sessions/YYYY-MM-DD-<slug>-state.md`),
7
+ > `baldart.config.yml`, and optional overlays.
8
+ >
9
+ > Render rules:
10
+ > - Conditional sections (marked `{{#if …}} … {{/if}}`) are emitted only when the
11
+ > condition is true. Skip the entire section (including its heading) when false.
12
+ > - Lists (marked `{{#each …}}`) render one bullet per item; emit "—" if empty.
13
+ > - The rendered output is presented to the user inside a fenced markdown
14
+ > code-block so they can copy-paste verbatim into Claude Design.
15
+
16
+ ---
17
+
18
+ # Feature: {{feature_title}}
19
+
20
+ ## 1. Obiettivo
21
+
22
+ {{feature_objective_one_sentence}}
23
+
24
+ ## 2. User flow
25
+
26
+ {{user_flow_summary}}
27
+
28
+ {{#if primary_personas}}
29
+ **Personas target**:
30
+ {{#each primary_personas}}
31
+ - {{this.name}} — {{this.context}}
32
+ {{/each}}
33
+ {{/if}}
34
+
35
+ ## 3. Schermate in scope
36
+
37
+ {{#each screens_in_scope}}
38
+ ### {{this.name}}
39
+
40
+ - **Scopo**: {{this.purpose}}
41
+ - **Stati richiesti**: {{this.states_required}} <!-- empty, loading, populated, error, success, ecc. -->
42
+ - **Azioni primarie**: {{this.primary_actions}}
43
+ - **Dati visualizzati**: {{this.data_shown}}
44
+ {{#if this.notes}}
45
+ - **Note**: {{this.notes}}
46
+ {{/if}}
47
+
48
+ {{/each}}
49
+
50
+ {{#if brand_voice}}
51
+ ## 4. Brand & voice
52
+
53
+ - **Tono**: {{brand_voice.tone}}
54
+ - **Personalità**: {{brand_voice.personality}}
55
+ - **Do**: {{brand_voice.do}}
56
+ - **Don't**: {{brand_voice.dont}}
57
+ {{#if brand_voice.references}}
58
+ - **Riferimenti visivi**: {{brand_voice.references}}
59
+ {{/if}}
60
+ {{/if}}
61
+
62
+ {{#if has_design_system}}
63
+ ## 5. Design system constraints
64
+
65
+ > Riusa SOLO i token e le primitive elencate. Se devi proporre qualcosa di nuovo,
66
+ > segnalalo esplicitamente nella consegna così posso aggiornare il registro.
67
+
68
+ ### Token chiave
69
+
70
+ ```
71
+ {{design_tokens_excerpt}}
72
+ ```
73
+
74
+ ### Primitive da riusare
75
+
76
+ {{#each registered_primitives}}
77
+ - **{{this.name}}** — {{this.purpose}} ({{this.path}})
78
+ {{/each}}
79
+
80
+ ### Vincoli numerici (da `design-system-protocol.md`)
81
+
82
+ - Type scale: {{tokens.type_scale}}
83
+ - Spacing scale: {{tokens.spacing_scale}}
84
+ - Radius: {{tokens.radius_scale}}
85
+ - Contrast minimo: {{tokens.contrast_target}} (WCAG AA + APCA Lc ≥ {{tokens.apca_target}})
86
+ - Motion durations: {{tokens.motion_durations}}
87
+ {{/if}}
88
+
89
+ ## 6. Stack target
90
+
91
+ - **Framework**: {{stack.framework}}
92
+ - **Output preferito**: {{output_format_preference}} <!-- "HTML/CSS standalone" | "React component (Tailwind)" | "Figma frame" -->
93
+ {{#if stack.charting}}
94
+ - **Charting library**: {{stack.charting}}
95
+ {{/if}}
96
+ {{#if stack.animation}}
97
+ - **Animation library**: {{stack.animation}}
98
+ {{/if}}
99
+
100
+ ## 7. Cosa restituirmi
101
+
102
+ Per ogni schermata elencata in § 3:
103
+
104
+ 1. **PNG export** ad alta risoluzione (≥ 2x) di OGNI stato richiesto (es. `lista-ordini__empty.png`, `lista-ordini__populated.png`).
105
+ 2. {{output_format_preference_block}} <!-- "Codice HTML/CSS in un file unico" | "Codice React (.tsx) con import Tailwind" | "Link Figma alla frame" -->
106
+ 3. **Note inline** su:
107
+ - Componenti nuovi (non presenti nel registry sopra) che hai introdotto e perché.
108
+ - Token deviation: qualunque colore/spacing/radius fuori dalla palette sopra, con motivazione.
109
+ - Decisioni di layout non ovvie (es. priority sort, hierarchy visiva, density).
110
+
111
+ Quando hai pronto, dimmi i **path locali** dei file (PNG/HTML/TSX) — li importerò in questa sessione PRD per analisi e validazione contro il design system.
112
+
113
+ ---
114
+
115
+ <!-- baldart-handoff: do-not-edit-manually -->
116
+ <!-- Generated from claude-design-handoff-prompt.template.md by /prd Step 3.0 -->
@@ -1,15 +1,97 @@
1
1
  # UI Design Phase (Step 3)
2
2
 
3
+ ## Step 3.0 — Mockup Source Decision (only when `mockups.status: none`)
4
+
5
+ When the state file shows `mockups.status: none` AND UI impact ≠ N/A, the user
6
+ has not provided any mockup at intake. Before falling into the **Full** pipeline,
7
+ offer them an explicit choice — generate internally or hand off to **Claude
8
+ Design** (Anthropic's design product, design-system-sync'd) — so they don't have
9
+ to abandon the PRD flow halfway.
10
+
11
+ Skip this step entirely when `mockups.status` ∈ {`provided`, `partial`,
12
+ `pending_external`} (already decided) or when UI impact = N/A (no design work).
13
+
14
+ ### The question (single STOP)
15
+
16
+ Ask the user, verbatim:
17
+
18
+ > Non hai fornito mockup all'intake. Come preferisci produrli per questa feature?
19
+ > **(A) Internamente** — genero io 3 opzioni HTML (workflow `ui-design`), iteriamo qui.
20
+ > **(B) Handoff a Claude Design** — ti preparo un prompt context-rich da incollare in Claude Design; quando hai i mockup tornami con i path locali.
21
+
22
+ **STOP.** Wait for an explicit `A` / `B` answer. Silence or ambiguity ⇒ ask
23
+ again (do NOT default silently).
24
+
25
+ ### Branch A — Internal generation
26
+
27
+ Set `mockups.source: internal` in the state file, then fall through to the
28
+ **Precondition decision tree** below — the `none` row routes to Full mode
29
+ unchanged. No other side-effect of Step 3.0.
30
+
31
+ ### Branch B — Claude Design handoff
32
+
33
+ 1. **Collect context from the state file + config + overlays:**
34
+ - `feature_title`, `feature_objective_one_sentence` from `## Feature Description`.
35
+ - `user_flow_summary`, `primary_personas` from `## Discovery Log` (dimensions 1 + 2).
36
+ - `screens_in_scope[]` from `## UI Design` (derive from User Stories + ISA touchpoints if not yet populated — same logic as Hybrid mode § "Build the per-screen routing table").
37
+ - `brand_voice` from `.baldart/overlays/prd.md` if present (skip section otherwise).
38
+ - When `features.has_design_system: true`: read `${paths.design_system}/INDEX.md` (primitive list) + `${paths.design_system}/tokens-reference.md` (tokens excerpt). When `false`: omit § 5 of the prompt entirely.
39
+ - `stack.framework`, `stack.charting`, `stack.animation` from `baldart.config.yml`.
40
+ - `output_format_preference` derived from `stack.framework` (Next/Remix → React+Tailwind; vanilla → HTML/CSS; otherwise ask the user).
41
+ - Numeric token tables (type scale, spacing, radius, contrast, motion durations) from `framework/agents/design-system-protocol.md` — cite verbatim.
42
+
43
+ 2. **Render the prompt** by populating
44
+ [../assets/claude-design-handoff-prompt.template.md](../assets/claude-design-handoff-prompt.template.md)
45
+ with the collected context. Strip empty conditional sections.
46
+
47
+ 3. **Present the rendered prompt** to the user inside a fenced markdown
48
+ code-block, prefixed with this instruction (verbatim):
49
+
50
+ > Incolla il prompt qui sotto in Claude Design. Quando hai i mockup pronti,
51
+ > tornami con i **path locali** dei file (PNG, HTML, TSX, o link Figma) e
52
+ > riprendo da qui — copio i file nella cartella del PRD, li analizzo come
53
+ > al solito (Step 1.6 Mockup Intake), e Step 3 entra in Hybrid mode.
54
+
55
+ 4. **Update state file** before stopping:
56
+ - `mockups.status: pending_external`
57
+ - `mockups.source: claude-design`
58
+ - `mockups.handoff_prompt_path: ${paths.prd_dir}/<slug>/handoff/claude-design-prompt.md` (save the rendered prompt to disk so it survives session restarts).
59
+ - `step_3_mode: pending_external_handoff` (transitional — overwritten when user returns).
60
+
61
+ 5. **STOP.** Wait for the user to return with local paths.
62
+
63
+ ### Re-entry from handoff (user returns with paths)
64
+
65
+ When the user comes back with one or more local paths after the handoff:
66
+
67
+ 1. Invoke **Step 1.6 — Mockup Intake** verbatim
68
+ ([SKILL.md](../SKILL.md) § Hard Rule 16,
69
+ [discovery-phase.md](discovery-phase.md) § "Mockup analysis schema"):
70
+ - Copy local files into `${paths.prd_dir}/<slug>/mockups/` with collision-safe
71
+ rename.
72
+ - Run the full Mockup analysis schema (screens, user_flow, components,
73
+ states_visible, copy_excerpts, gaps, design_system_alignment).
74
+ - Populate `## UI Design` in the state file.
75
+ 2. Flip `mockups.status` from `pending_external` to `provided` or `partial`
76
+ based on coverage of `screens_in_scope[]`. Keep `mockups.source: claude-design`
77
+ as audit trail.
78
+ 3. Fall through to the **Precondition decision tree** below — the `provided` /
79
+ `partial` row routes to **Hybrid mode** unchanged.
80
+
81
+ ---
82
+
3
83
  ## Precondition decision tree
4
84
 
5
85
  Read `mockups.status` and `screens_in_scope[]` from the state file `## UI Design`
6
- section (populated by Step 1.6) plus the UI impact dimension from the discovery
7
- checklist. Pick exactly one branch:
86
+ section (populated by Step 1.6 and/or Step 3.0 re-entry) plus the UI impact
87
+ dimension from the discovery checklist. Pick exactly one branch:
8
88
 
9
89
  | Condition | Branch | Action |
10
90
  |-----------|--------|--------|
11
91
  | UI impact = N/A | **Skip totale** | Mark task 2 `completed` ("Skipped — no UI"). `step_3_mode: skipped`. Proceed to PRD Writing Phase. |
12
- | `mockups.status: none` AND UI impact ≠ N/A | **Full** | Run the standard pipeline 3a 3b 3c 3d as before. `step_3_mode: full`. |
92
+ | `mockups.status: none` AND UI impact ≠ N/A | **3.0 Decision** | Run Step 3.0 above first. Branch A fall through to **Full**; Branch B STOP, then re-enter. |
93
+ | `mockups.status: pending_external` | **STOP** | User is in handoff with Claude Design. Do not advance until paths arrive (see § "Re-entry from handoff"). |
94
+ | `mockups.status: none` AND user picked Branch A in Step 3.0 | **Full** | Run the standard pipeline 3a → 3b → 3c → 3d as before. `step_3_mode: full`. |
13
95
  | `mockups.status` ∈ {`provided`, `partial`} AND UI impact ≠ N/A | **Hybrid** | Per-screen routing (see § Hybrid mode below). `step_3_mode: hybrid` (or `skipped` if every screen is covered AND no `mockup_analysis.design_system_alignment.violations`). |
14
96
 
15
97
  Mark task 2 as `in_progress` for the Full and Hybrid branches.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "3.16.0",
3
+ "version": "3.17.1",
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"
@@ -4,6 +4,19 @@ const UI = require('../utils/ui');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
 
7
+ // Lightweight semver compare. Returns positive if a > b, negative if a < b,
8
+ // zero if equal. Treats malformed input as 0 — degrade open rather than throw.
9
+ function cmpSemver(a, b) {
10
+ if (!a || !b) return 0;
11
+ const pa = String(a).split('.').map((n) => parseInt(n, 10) || 0);
12
+ const pb = String(b).split('.').map((n) => parseInt(n, 10) || 0);
13
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
14
+ const d = (pa[i] || 0) - (pb[i] || 0);
15
+ if (d !== 0) return d;
16
+ }
17
+ return 0;
18
+ }
19
+
7
20
  async function status() {
8
21
  const git = new GitUtils();
9
22
  const symlinks = new SymlinkUtils();
@@ -94,24 +107,43 @@ async function status() {
94
107
  }
95
108
  }
96
109
 
97
- // Check for pending updates
110
+ // Check for pending updates (since v3.17.1 — was a no-op stub before).
98
111
  UI.newline();
99
112
  UI.section('Update Status');
100
113
 
114
+ let updateAvailable = false;
115
+ let remoteVersion = null;
101
116
  try {
102
117
  await git.fetch('antbald/BALDART');
103
- UI.info('Checking for updates...');
104
- // Simplified - would need more sophisticated version comparison
105
- UI.success('Up to date (check with: baldart update)');
118
+ remoteVersion = await git.getRemoteVersion('antbald/BALDART', 'main');
119
+ if (!remoteVersion || remoteVersion === 'unknown') {
120
+ UI.warning('Could not read remote VERSION — check `baldart update` manually.');
121
+ } else if (cmpSemver(remoteVersion, version) > 0) {
122
+ updateAvailable = true;
123
+ UI.warning(`Update available: ${version} → ${remoteVersion}. Run: baldart update`);
124
+ } else if (cmpSemver(remoteVersion, version) < 0) {
125
+ UI.info(`Local v${version} is ahead of remote v${remoteVersion} (likely an unreleased commit).`);
126
+ } else {
127
+ UI.success(`Up to date (v${version})`);
128
+ }
106
129
  } catch (error) {
107
130
  UI.warning('Cannot check for updates (offline?)');
108
131
  }
109
132
 
110
133
  // Summary
111
134
  UI.newline();
135
+ let updateLine;
136
+ if (updateAvailable && remoteVersion) {
137
+ updateLine = `Update: ${version} → ${remoteVersion} available`;
138
+ } else if (remoteVersion && remoteVersion !== 'unknown') {
139
+ updateLine = `Update: up to date`;
140
+ } else {
141
+ updateLine = `Update: unknown (could not reach remote)`;
142
+ }
112
143
  UI.box('SUMMARY', [
113
144
  `Version: ${version}`,
114
145
  `Installation: ${symlinkValid ? 'Valid' : 'Issues detected'}`,
146
+ updateLine,
115
147
  '',
116
148
  'Commands available:',
117
149
  ' • baldart update - Update framework',
@@ -81,7 +81,10 @@ async function postUpdateAutoCommit(git, newVersion, options) {
81
81
  if (userOwned.length > 0) {
82
82
  UI.warning(`${userOwned.length} other dirty path(s) will be left untouched (not BALDART-managed).`);
83
83
  }
84
- const ok = await UI.confirm(`Commit them as \`chore(baldart): post-update reconcile to v${newVersion}\`?`, true);
84
+ const autoYes = options && options.yes === true;
85
+ const ok = autoYes
86
+ ? true
87
+ : await UI.confirm(`Commit them as \`chore(baldart): post-update reconcile to v${newVersion}\`?`, true);
85
88
  if (!ok) {
86
89
  UI.info('Auto-commit declined. Your worktree keeps the changes for manual review.');
87
90
  return;
@@ -110,6 +113,13 @@ async function update(options = {}) {
110
113
  const symlinks = new SymlinkUtils();
111
114
  const repo = 'antbald/BALDART'; // Default repo
112
115
 
116
+ // Non-interactive flags (since v3.17.1). `--yes` implies `--auto-stash`.
117
+ // When `yes` is set, every UI.confirm/UI.select downstream short-circuits
118
+ // to its default — the same value an unattended user would have produced.
119
+ const autoYes = options.yes === true;
120
+ const autoStash = autoYes || options.autoStash === true;
121
+ const confirm = async (msg, def = true) => (autoYes ? def : UI.confirm(msg, def));
122
+
113
123
  try {
114
124
  // Step 1: Verify installation
115
125
  UI.header('STEP 1/5: Verify Installation');
@@ -194,14 +204,14 @@ async function update(options = {}) {
194
204
  const diff = await git.diffWithRemote();
195
205
  if (diff) {
196
206
  UI.info('Changes detected. Review recommended.');
197
- const showDiff = await UI.confirm('Show detailed diff?', false);
207
+ const showDiff = await confirm('Show detailed diff?', false);
198
208
 
199
209
  if (showDiff) {
200
210
  console.log(diff);
201
211
  }
202
212
  }
203
213
 
204
- const proceedUpdate = await UI.confirm('Proceed with update?', true);
214
+ const proceedUpdate = await confirm('Proceed with update?', true);
205
215
  if (!proceedUpdate) {
206
216
  UI.info('Update cancelled');
207
217
  process.exit(0);
@@ -225,10 +235,16 @@ async function update(options = {}) {
225
235
  UI.list(unique.slice(0, 8), 'yellow');
226
236
  if (unique.length > 8) UI.info(`…and ${unique.length - 8} more.`);
227
237
 
228
- const action = await UI.select('How would you like to proceed?', [
229
- { name: 'Auto-stash now and re-apply after the update (recommended)', value: 'stash' },
230
- { name: 'Abort I want to commit or stash manually first', value: 'abort' }
231
- ]);
238
+ let action;
239
+ if (autoStash) {
240
+ UI.info('Auto-stashing dirty paths (--yes / --auto-stash).');
241
+ action = 'stash';
242
+ } else {
243
+ action = await UI.select('How would you like to proceed?', [
244
+ { name: 'Auto-stash now and re-apply after the update (recommended)', value: 'stash' },
245
+ { name: 'Abort — I want to commit or stash manually first', value: 'abort' }
246
+ ]);
247
+ }
232
248
  if (action === 'abort') {
233
249
  UI.info('Aborted. Commit or `git stash -u`, then re-run `npx baldart update`.');
234
250
  process.exit(0);
@@ -361,9 +377,12 @@ async function update(options = {}) {
361
377
  'Convert legacy .claude/skills/ bulk symlink (v2.0.x) into the new per-item layout.',
362
378
  `Merge framework skills into per-tool skill dirs (${enabledTools.join(', ')}) without touching your personal skills.`
363
379
  ]);
364
- const recreate = await UI.confirm('Reconcile symlinks now?', true);
380
+ const recreate = await confirm('Reconcile symlinks now?', true);
365
381
  if (recreate) {
366
- await symlinks.createAllSymlinks({ mode: 'prompt', tools: enabledTools });
382
+ // In auto-yes mode pass 'force' instead of 'prompt' so per-conflict
383
+ // prompts inside SymlinkUtils don't reintroduce blocking I/O.
384
+ const mode = autoYes ? 'force' : 'prompt';
385
+ await symlinks.createAllSymlinks({ mode, tools: enabledTools });
367
386
  }
368
387
  } else {
369
388
  // Re-run the per-item merges for every enabled tool so newly-shipped
@@ -403,10 +422,10 @@ async function update(options = {}) {
403
422
  if (!fs.existsSync(configPath)) {
404
423
  UI.newline();
405
424
  UI.warning('No baldart.config.yml in this project — skills installed by this framework version require it.');
406
- const runConfigure = await UI.confirm('Run `baldart configure` now?', true);
425
+ const runConfigure = await confirm('Run `baldart configure` now?', true);
407
426
  if (runConfigure) {
408
427
  const configureCmd = require('./configure');
409
- await configureCmd();
428
+ await configureCmd(autoYes ? { nonInteractive: true } : undefined);
410
429
  }
411
430
  } else {
412
431
  // Schema migration: backfill `tools.enabled` for configs created
@@ -425,7 +444,7 @@ async function update(options = {}) {
425
444
  UI.warning('Pre-v3.7.0 config detected — `tools.enabled` is missing.');
426
445
  UI.info(`From v3.7.0, BALDART can install framework skills for multiple AI tools (Claude Code, OpenAI Codex CLI, …) from the same source.`);
427
446
  UI.info(`Autodetected on this machine: ${suggested.join(', ')}`);
428
- const ok = await UI.confirm(`Backfill \`tools.enabled: [${suggested.join(', ')}]\` into baldart.config.yml?`, true);
447
+ const ok = await confirm(`Backfill \`tools.enabled: [${suggested.join(', ')}]\` into baldart.config.yml?`, true);
429
448
  if (ok) {
430
449
  cur.tools = cur.tools || {};
431
450
  cur.tools.enabled = suggested;
@@ -470,10 +489,10 @@ async function update(options = {}) {
470
489
  UI.warning(`New config keys in this version: ${allMissing.join(', ')}.`);
471
490
  // Auto-offer configure so the user actually answers, instead of
472
491
  // silently falling back to template defaults on first use.
473
- const runConfigure = await UI.confirm('Run `baldart configure` now to populate them? (existing values are preserved)', true);
492
+ const runConfigure = await confirm('Run `baldart configure` now to populate them? (existing values are preserved)', true);
474
493
  if (runConfigure) {
475
494
  const configureCmd = require('./configure');
476
- await configureCmd();
495
+ await configureCmd(autoYes ? { nonInteractive: true } : undefined);
477
496
  } else {
478
497
  UI.info('Skipped. Skills will prompt for the missing keys on first use.');
479
498
  }