baldart 3.27.0 → 3.28.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 CHANGED
@@ -5,6 +5,43 @@ 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.28.0] - 2026-05-28
9
+
10
+ Tre additions indipendenti accumulate nel working tree e pronte per il rilascio: convenzione testuale per le dipendenze MCP, supporto `.zip` per il mockup intake del PRD, e nuovo hook `capture-detect` come parte del proactive mode dello skill `/capture` (registrazione opt-in, non auto-attivata). Tre temi distinti ma raggruppati in un'unica release di accumulo perché tutti additive e zero-schema-change.
11
+
12
+ ### Added — Convenzione MCP integration
13
+
14
+ - **[framework/docs/MCP-INTEGRATION.md](framework/docs/MCP-INTEGRATION.md)**: nuovo documento centralizzato. Spiega quando un agent/skill deve appoggiarsi a un MCP server, fornisce un catalogo di MCP raccomandati per i casi d'uso comuni (doc-rag, playwright, firestore/db, LSP, …), e definisce la convenzione testuale "MCP dependencies" per dichiarare le dipendenze nel body dell'agent/skill. Zero nuove chiavi config in `baldart.config.yml` — la convenzione resta testuale finché una skill concreta non dovrà gate-arne il comportamento (regola di schema-change propagation: niente layer drift).
15
+ - **[framework/.claude/agents/codebase-architect.md](framework/.claude/agents/codebase-architect.md)**: aggiunta sezione `## MCP dependencies` con classificazione Required (`doc-rag` come primo step dell'Investigation Protocol) vs Optional (LSP find-references, DB MCP per inspect runtime). Documentato il degrade path quando l'MCP non è disponibile.
16
+ - **[framework/.claude/skills/bug/SKILL.md](framework/.claude/skills/bug/SKILL.md)**: aggiunta sezione `## MCP dependencies` con Required (`playwright` per Phase 1 reproduction di UI/Client bug — senza è impossibile catturare stato deterministico) e Optional (`doc-rag` per known patterns, DB MCP per Data bugs, `codebase-architect` agent).
17
+
18
+ ### Added — PRD mockup intake da archivio `.zip`
19
+
20
+ - **[framework/.claude/skills/prd/SKILL.md](framework/.claude/skills/prd/SKILL.md)** Step 1.6: nuovo formato `local-archive` accettato accanto a `chat-images` / `local-paths` / `mixed`. Caso d'uso: export "Download as .zip" da Claude Design (o qualsiasi altro tool che produce bundle di mockup) già scaricato sul disco dell'utente.
21
+ - **[framework/.claude/skills/prd/references/discovery-phase.md](framework/.claude/skills/prd/references/discovery-phase.md)** § Mockup Intake: Step 1.6.4b descrive l'intero flow per `.zip` — `unzip` in scratch dir `mockups/_archive/`, enumerazione candidati visivi, scoring di rilevanza contro lo scope del Kickoff, selezione auto-proposta, STOP per conferma, materializzazione del sottoinsieme confermato in `${paths.prd_dir}/<slug>/mockups/`. Evita di copiare PNG irrilevanti nell'asset directory definitiva.
22
+ - **[framework/.claude/skills/prd/assets/state-template.md](framework/.claude/skills/prd/assets/state-template.md)**: aggiornato `mockups.format` enum per includere `local-archive`.
23
+
24
+ ### Added — Hook `capture-detect` (proactive mode, opt-in)
25
+
26
+ - **[framework/.claude/hooks/capture-detect.sh](framework/.claude/hooks/capture-detect.sh)**: nuovo Stop hook che drop una candidate JSON in `.claude/capture-queue/` quando rileva ragionamento cross-document nella sessione appena chiusa. La distillazione vera in synthesis page wiki resta sempre una human-approved manual step via lo skill `/capture`. Failure-isolated (exit 0 sempre, non blocca lo Stop event).
27
+ - **Registrazione opt-in (NOT auto-attivata)**: la entry corrispondente in [src/utils/hooks.js](src/utils/hooks.js) resta commentata (linee 90-97). Lo skill `/capture` funziona già in modalità manual senza il hook; il proactive mode è una feature opzionale. Per attivarlo nel proprio consumer: scommentare la entry e re-installare via `npx baldart update`. Una futura release potrà rendere il proactive il default.
28
+
29
+ ### Added — Smoke test del hooks registry
30
+
31
+ - **[scripts/test-hooks.sh](scripts/test-hooks.sh)**: nuovo smoke test per `src/utils/hooks.js` (multi-hook registry introdotto v3.18.0). Verifica register/unregister/isRegistered/merge idempotente con fixture in tmpdir. Dev tooling, non user-visible.
32
+
33
+ ## [3.27.1] - 2026-05-28
34
+
35
+ Patch follow-up alla v3.27.0 dopo l'incidente reale in `mayo`: l'utente aveva CLI globale v3.24.0 e ha eseguito `/baldart-update` per aggiornarsi a v3.27.0. La skill ha rilanciato `npx baldart update`, che ha aggiornato il framework payload da v3.24 a v3.27, ma `update.js` in esecuzione era il binario v3.24.0 — quello che precede sia il meccanismo di auto-relaunch (introdotto v3.26.0) sia la logica di symlink-indirection (v3.27.0). Risultato: i 6 agent overlay-merged sono stati rigenerati con la **vecchia logica** (file regolari direttamente in `.claude/agents/`), e quindi il fix v3.27.0 NON si è attivato — `npx baldart doctor` ha riportato "tutto healthy" ma in realtà gli agent erano ancora invisibili a Claude Code (bug #20931). Chicken-and-egg: il CLI vecchio non sa rilanciare il nuovo, il nuovo non è ancora in esecuzione.
36
+
37
+ ### Fixed — /baldart-update skill: CLI version drift gate
38
+
39
+ - **[framework/.claude/skills/baldart-update/SKILL.md](framework/.claude/skills/baldart-update/SKILL.md)** Step 0: la skill ora rileva esplicitamente il CLI version drift e HALTA pre-update quando il CLI globale è < v3.26.0 (no auto-relaunch) e il payload sta per attraversare il boundary v3.27.0 (migration breaking). Decision logic articolata: se CLI è già >= v3.26.0 lascia che l'auto-relaunch faccia il suo lavoro; altrimenti istruisce `npm i -g baldart@latest` PRIMA di ri-eseguire la skill. Evita il silent-fail dove il CLI vecchio applica una pseudo-migration con codice obsoleto.
40
+
41
+ ### Note al curator
42
+
43
+ Questo è un edge case strutturale che si manifesta SOLO quando un consumer attraversa il boundary v3.26.0 / v3.27.0 partendo da un CLI globale precedente alla v3.26.0. I consumer già su CLI v3.26.0+ hanno l'auto-relaunch (`npx baldart@latest`) e non sono affected. Per i consumer pre-v3.26.0 il fix in skill aiuta solo le sessioni future (skill caricata da framework payload >= v3.27.1); chi aveva già la skill vecchia in sessione al momento dell'incidente — come `mayo` ieri — può solo riparare manualmente: `npm i -g baldart@latest` + restart sessione CC + `/baldart-update`.
44
+
8
45
  ## [3.27.0] - 2026-05-28
9
46
 
10
47
  Fix critico al meccanismo di overlay-merge degli agent. Sintomo riproducibile osservato in `mayo` durante `/new FEAT-0009 -full` (team mode, claude-opus-4-7): gli agent overlay-merged (`coder`, `ui-expert`, `code-reviewer`, `doc-reviewer`, `qa-sentinel`, `security-reviewer` quando hanno un overlay) **non comparivano nella lista "Available agent types"** del Claude Code session reminder. Sequenza concreta: `codebase-architect` (symlink) parte e completa; i 4 background agent (`coder×2` + `ui-expert×2`) lanciati subito dopo falliscono con `InputValidationError: subagent_type not recognized`; l'orchestratore halta con un AskUserQuestion fuori-protocollo. Repro: `ls -la .claude/agents/` mostra 21 symlink + 6 file regolari (i 6 overlay-merged); CC discoverizza i 21 symlink e omette i 6 file regolari. Confermato su sessioni indipendenti del 2026-05-27 e 2026-05-28 → bug strutturale, non transiente. Workflow `/new` in team mode, `/check`, `/qa`, `/codexreview` e skill `prd` erano **bloccati end-to-end** su qualsiasi consumer con overlay agent.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 3.27.0
1
+ 3.28.0
@@ -37,6 +37,17 @@ This codebase follows strict protocols defined in AGENTS.md. Key architectural r
37
37
  - `/docs/references/project-status.md` - Current system state and active work
38
38
  - `agents/coding-standards.md` - Terminology and coding conventions
39
39
 
40
+ ## MCP dependencies
41
+
42
+ See [framework/docs/MCP-INTEGRATION.md](../../docs/MCP-INTEGRATION.md) for the BALDART MCP integration contract and the rationale behind this section.
43
+
44
+ **Required** (this agent's primary investigation protocol assumes them; degraded operation only):
45
+ - `mcp__doc-rag__search_docs` — semantic + knowledge-graph search across project docs. This is the **first** call in the Investigation Protocol; without it the agent falls back to broad `rg` sweeps that blow the token budget and miss canonical sources. If the MCP is unavailable the agent must announce the degrade explicitly and switch to targeted `rg` over `docs/`, `backlog/`, and `.claude/agents/` (see § Investigation Protocol step 1).
46
+
47
+ **Optional** (the agent benefits if present, has a documented fallback):
48
+ - LSP find-references / go-to-definition (whichever LSP MCP / plugin is wired by [features.has_lsp_layer](../../templates/baldart.config.template.yml)) — preferred over `rg` for symbol queries (cap: 3 LSP calls per task, then escalate to direct file reads). Fallback: silent degrade to Grep, per [code-search-protocol.md](../../agents/code-search-protocol.md).
49
+ - `mcp__plugin_firebase_firebase__firestore_*` (or equivalent DB MCP) — only when the architectural analysis needs to inspect runtime DB state (rare for an architect agent). Fallback: read schema docs in `${paths.references_dir}` and rely on declared structure.
50
+
40
51
  ## Linking Protocol Consumption (MUST)
41
52
 
42
53
  You consume Linking Protocol v1 in read-only mode to find the right facts faster before
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # capture-detect.sh — BALDART proactive capture hook (event: Stop)
4
+ #
5
+ # Runs at the end of every Claude Code session. When the just-finished
6
+ # session contains cross-document reasoning (the user has been weaving
7
+ # together facts from multiple docs/references files), this hook drops a
8
+ # *candidate* file into `.claude/capture-queue/` so the next `/capture`
9
+ # invocation can surface it and offer to distill the synthesis into a
10
+ # durable wiki page.
11
+ #
12
+ # This hook NEVER spawns Claude itself — it only writes a small JSON
13
+ # candidate. The actual synthesis is always a human-approved manual step
14
+ # via the `capture` skill.
15
+ #
16
+ # Stdin: Claude Code passes a JSON envelope; we read `transcript_path`,
17
+ # `session_id`, `cwd` (best-effort — falls back if jq is unavailable).
18
+ #
19
+ # Exit: always 0. Failure isolation — a non-zero exit on a Stop hook can
20
+ # be ignored by the runtime, but as a matter of hygiene we never
21
+ # propagate errors out of an instrumentation hook.
22
+
23
+ set -u
24
+ exec 2>/dev/null # silence stderr; we are not user-visible
25
+
26
+ # --- read envelope --------------------------------------------------------
27
+
28
+ INPUT=""
29
+ if [ ! -t 0 ]; then
30
+ INPUT="$(cat)"
31
+ fi
32
+
33
+ # Try jq first, fall back to grep/sed extraction.
34
+ get_field() {
35
+ local field="$1"
36
+ if command -v jq >/dev/null 2>&1; then
37
+ printf '%s' "$INPUT" | jq -r ".$field // empty" 2>/dev/null
38
+ else
39
+ printf '%s' "$INPUT" | sed -n "s/.*\"$field\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p" | head -1
40
+ fi
41
+ }
42
+
43
+ TRANSCRIPT_PATH="$(get_field transcript_path)"
44
+ SESSION_ID="$(get_field session_id)"
45
+ CWD_FROM_ENV="$(get_field cwd)"
46
+ CWD="${CWD_FROM_ENV:-$PWD}"
47
+
48
+ [ -n "$TRANSCRIPT_PATH" ] || exit 0
49
+ [ -f "$TRANSCRIPT_PATH" ] || exit 0
50
+
51
+ cd "$CWD" 2>/dev/null || exit 0
52
+
53
+ # --- gate on features.has_wiki_overlay -----------------------------------
54
+
55
+ CONFIG="baldart.config.yml"
56
+ [ -f "$CONFIG" ] || exit 0
57
+ # Coarse check: look for `has_wiki_overlay: true` (whitespace-tolerant).
58
+ if ! grep -E '^\s*has_wiki_overlay\s*:\s*true\b' "$CONFIG" >/dev/null 2>&1; then
59
+ exit 0
60
+ fi
61
+
62
+ # --- gates: kill switch + anti-spam --------------------------------------
63
+
64
+ QUEUE_DIR=".claude/capture-queue"
65
+ mkdir -p "$QUEUE_DIR" || exit 0
66
+
67
+ [ -f "$QUEUE_DIR/.disabled" ] && exit 0
68
+
69
+ # Anti-spam: refuse when ≥10 unprocessed candidates already queued.
70
+ EXISTING="$(find "$QUEUE_DIR" -maxdepth 1 -type f -name '*.json' 2>/dev/null | wc -l | tr -d ' ')"
71
+ [ "${EXISTING:-0}" -ge 10 ] && exit 0
72
+
73
+ # --- heuristic: is this session worth flagging? --------------------------
74
+ #
75
+ # We treat "≥3 distinct docs/references-style paths mentioned across the
76
+ # session" as a weak signal of cross-document reasoning. False positives
77
+ # are acceptable here — the user reviews every candidate in /capture and
78
+ # can reject. False negatives are also fine — the user can always invoke
79
+ # /capture manually.
80
+
81
+ REFS="$(grep -ohE '(\./|/|\b)((docs|wiki|references|backlog|prd)/[A-Za-z0-9._/-]+\.(md|yml|yaml|json))' "$TRANSCRIPT_PATH" 2>/dev/null \
82
+ | sort -u | head -10)"
83
+
84
+ REF_COUNT="$(printf '%s\n' "$REFS" | grep -c . || true)"
85
+ [ "${REF_COUNT:-0}" -lt 3 ] && exit 0
86
+
87
+ # --- write candidate ------------------------------------------------------
88
+
89
+ TS_FILE="$(date -u +%Y%m%dT%H%M%SZ)"
90
+ TS_HUMAN="$(date -u +'%Y-%m-%d %H:%M UTC')"
91
+ OUT="$QUEUE_DIR/${TS_FILE}-${SESSION_ID:-unknown}.json"
92
+
93
+ # JSON-escape with awk (no jq required for emit either).
94
+ escape_json() {
95
+ printf '%s' "$1" | awk 'BEGIN{ORS=""} {gsub(/\\/,"\\\\"); gsub(/"/,"\\\""); gsub(/\r/,""); gsub(/\n/,"\\n"); print}'
96
+ }
97
+
98
+ REFS_JSON="["
99
+ first=1
100
+ while IFS= read -r r; do
101
+ [ -z "$r" ] && continue
102
+ if [ $first -eq 1 ]; then first=0; else REFS_JSON="$REFS_JSON,"; fi
103
+ REFS_JSON="$REFS_JSON\"$(escape_json "$r")\""
104
+ done <<EOF
105
+ $REFS
106
+ EOF
107
+ REFS_JSON="$REFS_JSON]"
108
+
109
+ cat > "$OUT" <<JSON
110
+ {
111
+ "trigger": "cross_doc_refs",
112
+ "detected_at": "$TS_HUMAN",
113
+ "session_id": "$(escape_json "${SESSION_ID:-unknown}")",
114
+ "transcript_path": "$(escape_json "$TRANSCRIPT_PATH")",
115
+ "refs_mentioned": $REFS_JSON,
116
+ "excerpt": "Session referenced ${REF_COUNT} distinct doc paths — possible cross-document synthesis worth capturing.",
117
+ "hint": "Run /capture to review this candidate."
118
+ }
119
+ JSON
120
+
121
+ exit 0
@@ -178,9 +178,28 @@ Match the `Remote:` line in this order (first match wins):
178
178
 
179
179
  Always also capture:
180
180
  - `Installed:\s+v(\S+)` → `installedVersion` (saved for post-flight assert).
181
- - `CLI:\s+v\S+\s+→\s+v(\S+)\s+available`if present, tell the user once:
182
- "Il CLI globale è vecchio (v<old> v<new>). L'update lo gestirà
183
- auto-relanciando via `npx baldart@latest` — niente da fare."
181
+ - `CLI:\s+v(\S+?)(?:\s+→\s+v(\S+)\s+available)?``cliVersion` (current CLI binary)
182
+ + optional `cliLatest` (newest npm version).
183
+
184
+ **CLI version drift gate (BLOCKING)** — added in v3.27.1 after a real
185
+ incident in `mayo`: a consumer on CLI v3.24.0 ran update against payload
186
+ v3.27.0 and the v3.27.0 symlink-indirection migration silently did NOT
187
+ trigger (the running `update.js` was the v3.24.0 binary, predating both
188
+ auto-relaunch and the migration logic). The CLI v3.26.0+ self-relaunches
189
+ via `npx baldart@latest`, but pre-v3.26.0 binaries do not — chicken-and-egg.
190
+
191
+ Decision logic (apply after parsing):
192
+
193
+ | If… | Then… |
194
+ |---|---|
195
+ | `cliVersion >= 3.26.0` AND `cliLatest` present | Tell once: "Il CLI globale è v<cliVersion>, ne esiste v<cliLatest>. L'update si autorelancerà via `npx baldart@latest` — niente da fare." Continue. |
196
+ | `cliVersion < 3.26.0` AND `installedVersion >= 3.27.0` (already on the new payload but old binary) | **HALT**. Tell the user: "Il CLI globale (v<cliVersion>) è troppo vecchio per applicare la migration symlink-indirection di v3.27.0 (introdotta v3.26.0+ self-relaunch e v3.27.0 symlink-indirection). Lancia `npm i -g baldart@latest` PRIMA di ri-eseguire /baldart-update, altrimenti `update.js` non vedrà la nuova logica e i 6 agent overlay-merged resteranno file regolari invisibili a Claude Code (bug #20931)." Do not launch `npx baldart update`. |
197
+ | `cliVersion < 3.26.0` AND `installedVersion < 3.27.0` AND update is going to bring the payload to `>= 3.27.0` (parse the remoteVersion from Step 0) | **HALT** with the same message — the payload will cross the v3.27.0 boundary during this update, and the running `update.js` will lack the migration logic. |
198
+ | Otherwise | Continue normally. |
199
+
200
+ Semver compare: split on `.`, compare numerically. Treat any non-numeric
201
+ segment (e.g. `3.27.0-rc1`) as "less than" the same major.minor.patch
202
+ without suffix only when strictly required; if unsure, ASK the user.
184
203
 
185
204
  **Decision after parsing**:
186
205
 
@@ -21,6 +21,18 @@ Argument: optional bug description (e.g., `/bug feature X is not saving`).
21
21
  **Overlay:** loads `.baldart/overlays/bug.md` if present — project-specific debug entry points (e.g. SWR debug switches, env summary helpers, error-code modules). The base skill stays generic; project-specific code paths live in the overlay.
22
22
  **On missing/empty keys:** ask the user; do not assume defaults. See `framework/agents/project-context.md` § 3.
23
23
 
24
+ ## MCP dependencies
25
+
26
+ See [framework/docs/MCP-INTEGRATION.md](../../../docs/MCP-INTEGRATION.md) for the BALDART MCP integration contract and the rationale behind this section.
27
+
28
+ **Required** (skill cannot complete its task without these MCPs for the relevant bug class):
29
+ - `mcp__playwright__browser_*` (`browser_navigate`, `browser_take_screenshot`, `browser_console_messages`, `browser_network_requests`, `browser_evaluate`) — Phase 1 reproduction for **UI/Client** bugs. Without it the skill cannot capture deterministic state and Phase 1 degrades to "ask the user to reproduce manually and paste console logs". Phase 2's client-side `window.fetch` interception is impossible without `browser_evaluate`.
30
+
31
+ **Optional** (the skill benefits if present, has a documented fallback):
32
+ - `mcp__doc-rag__search_docs` — Phase 0 lookup for known patterns and error codes. Fallback: targeted `rg` over the project's error-code module (path listed in `.baldart/overlays/bug.md`) and `docs/wiki/`.
33
+ - `mcp__plugin_firebase_firebase__firestore_*` (or the project's DB MCP, when `stack.database.primary` is set) — Phase 1 inspection of runtime data state for **Data** bugs. Fallback: ask the user for a document export, or read the relevant code path and infer.
34
+ - `codebase-architect` agent (always available, listed for completeness) — Phase 0 code-path mapping. Not an MCP per se but is treated as a required dependency by this skill.
35
+
24
36
  ## ANTI-PATTERNS (NEVER DO)
25
37
 
26
38
  - NEVER change code before reproducing the bug
@@ -92,8 +92,17 @@ message. You ask questions, wait for answers, and iterate.
92
92
  structure and `assets/card-template.yml` for child structure.
93
93
  16. **Mockup Intake (MANDATORY).** Subito dopo Kickoff (Step 1) e prima di entrare in
94
94
  Discovery (Step 2), esegui Step 1.6 — chiedi all'utente se ha mockup a disposizione.
95
- Se **sì**: chiedi formato (immagini in chat / path locali), **STOP**, attendi. Sull'arrivo:
96
- copia i path locali in `${paths.prd_dir}/<slug>/mockups/`, analizza in dettaglio (vedi
95
+ Se **sì**: chiedi formato (immagini in chat / path locali singoli file **o
96
+ archivi `.zip`** / misti), **STOP**, attendi. Sull'arrivo:
97
+ - **chat-images** / file singoli in **local-paths**: copia in `${paths.prd_dir}/<slug>/mockups/`,
98
+ passa subito all'analisi.
99
+ - **`.zip` in local-paths** (es. export "Download as .zip" da Claude Design già
100
+ sul disco): esegui Step 1.6.4b — `unzip` in scratch dir sotto `mockups/_archive/`,
101
+ enumera i candidati visivi, calcola uno score di rilevanza contro lo scope
102
+ del Kickoff, presenta la selezione auto-proposta, **STOP**, attendi conferma,
103
+ poi materializza solo il sottoinsieme confermato in `${paths.prd_dir}/<slug>/mockups/`.
104
+
105
+ Quindi analizza in dettaglio (vedi
97
106
  [discovery-phase.md](references/discovery-phase.md) § "Mockup analysis schema"), popola
98
107
  `## UI Design` nello state, poi entra in Discovery. Se **no**: marca
99
108
  `mockups.status: none` e procedi col flow standard. Il decision tree di Step 3 (UI Design)
@@ -74,11 +74,18 @@ Scenarios: pending
74
74
 
75
75
  ## UI Design
76
76
 
77
- mockups.status: pending # none | requested | provided | partial
78
- mockups.format: pending # chat-images | local-paths | mixed
79
- mockups.original_paths: [] # user-provided paths or chat://image-N
77
+ mockups.status: pending # none | requested | provided | partial | pending_external
78
+ mockups.format: pending # chat-images | local-paths | local-archive | mixed
79
+ mockups.original_paths: [] # user-provided paths, chat://image-N, or archive://<rel-path>
80
80
  mockups.canonical_paths: [] # mockups/<file> after copy
81
81
 
82
+ mockups.archive: # populated only when format includes local-archive
83
+ local_zip_path: pending # abs path of the .zip the user gave us
84
+ kind: pending # zip
85
+ extracted_dir: pending # abs path of _archive/extracted/
86
+ truncated: false # true when candidate list capped at 200
87
+ candidates: [] # see discovery-phase.md § 1.6.4b step b-c
88
+
82
89
  mockup_analysis.screens: [] # see discovery-phase.md § Mockup analysis schema
83
90
  mockup_analysis.user_flow: []
84
91
  mockup_analysis.design_system_alignment:
@@ -172,9 +172,12 @@ Append this follow-up to the SAME message:
172
172
  Perfetto. In che forma me li passi?
173
173
 
174
174
  - **Immagini in chat** — incollale direttamente nel prossimo messaggio.
175
- - **Path locali** — incolla i percorsi (assoluti o relativi al repo) e li copio
176
- in `${paths.prd_dir}/<slug>/mockups/`.
177
- - **Misti** entrambe le cose.
175
+ - **Path locali** — incolla i percorsi (assoluti o relativi al repo). Funzionano:
176
+ - file singoli (PNG, HTML, SVG, …) → li copio in `${paths.prd_dir}/<slug>/mockups/`.
177
+ - **archivi `.zip`** (es. export "Download as .zip" da Claude Design già
178
+ scaricato sul tuo disco) → lo unzippo io, analizzo TUTTI i mockup dentro
179
+ e seleziono automaticamente quelli rilevanti per questa feature.
180
+ - **Misti** — combinazioni delle modalità sopra.
178
181
  ```
179
182
 
180
183
  - Update state file `## UI Design`: `mockups.status: requested`, `mockups.format: pending`.
@@ -184,19 +187,170 @@ Perfetto. In che forma me li passi?
184
187
 
185
188
  When the user replies with the mockups:
186
189
 
187
- 1. **Detect format** and update `mockups.format` ∈ {`chat-images`, `local-paths`, `mixed`}.
190
+ 1. **Detect format** and update `mockups.format` ∈ {`chat-images`, `local-paths`, `local-archive`, `mixed`}.
191
+ - `local-archive` applies when at least one of the provided local paths is a
192
+ `.zip` (case-insensitive) — also if mixed with other regular files.
188
193
  2. **For each local path provided:**
189
194
  - Resolve to absolute path. If file does not exist: ask the user to confirm/correct
190
195
  (do not silently skip).
191
- - Ensure `${paths.prd_dir}/<slug>/mockups/` exists (create if missing).
192
- - Copy the file into that directory, preserving filename. On collision (file with
193
- same name already there): append numeric suffix (`<name>-2.png`).
196
+ - **If the path is a `.zip`** (extension match OR `file <path>` reports a Zip
197
+ archive): route it to Step 1.6.4b and DO NOT copy the zip itself into
198
+ `${paths.prd_dir}/<slug>/mockups/`. Only the user-confirmed subset extracted
199
+ from it ends up there.
200
+ - **Otherwise** (regular mockup file): ensure `${paths.prd_dir}/<slug>/mockups/`
201
+ exists (create if missing), then copy the file into that directory,
202
+ preserving filename. On collision (file with same name already there):
203
+ append numeric suffix (`<name>-2.png`).
194
204
  - Record both paths in state: `mockups.original_paths[]` and `mockups.canonical_paths[]`.
195
205
  3. **For chat images:** record in state `mockups.original_paths[]` as `chat://image-N`
196
206
  (the images remain in the conversation context window).
197
- 4. Update `mockups.status: provided` (or `partial` only if the user explicitly says
207
+ 4. **For zip archives:** execute Step 1.6.4b BEFORE marking `status: provided`. The
208
+ archive flow has its own scan + selection + confirmation gate; only the
209
+ user-confirmed subset is copied into `${paths.prd_dir}/<slug>/mockups/`.
210
+ 5. Update `mockups.status: provided` (or `partial` only if the user explicitly says
198
211
  "ho solo questi 2 ma servono anche altri").
199
212
 
213
+ ### 1.6.4b — Archive ingestion & relevance selection (when `mockups.format` includes `local-archive`)
214
+
215
+ Triggered when the user hands a local `.zip` path (typically a Claude Design
216
+ "Download as .zip" export already on disk). The flow scans every mockup inside,
217
+ scores each against the feature scope from Kickoff, and asks the user to confirm
218
+ the auto-selected subset before analysis.
219
+
220
+ #### Step a — Detect & unpack
221
+
222
+ For each zip path provided:
223
+
224
+ ```bash
225
+ file "<user-zip-path>"
226
+ ```
227
+
228
+ - If `file` does NOT report a Zip archive, treat the path as a regular file and
229
+ go back to step 1.6.4 point 2's "Otherwise" branch — no special handling.
230
+ - If it IS a zip, record in state:
231
+ - `mockups.archive.local_zip_path: <user-zip-path>` (absolute)
232
+ - `mockups.archive.kind: zip`
233
+ - Unpack into a scratch directory under the PRD's mockup folder:
234
+
235
+ ```bash
236
+ mkdir -p "$WORKTREE_PATH/${paths.prd_dir}/<slug>/mockups/_archive"
237
+ unzip -q "<user-zip-path>" \
238
+ -d "$WORKTREE_PATH/${paths.prd_dir}/<slug>/mockups/_archive/extracted"
239
+ ```
240
+
241
+ - Record `mockups.archive.extracted_dir`.
242
+ - If `unzip` fails (corrupted/encrypted): STOP, surface the exit code, ask the
243
+ user to verify the zip or provide an alternative.
244
+
245
+ #### Step b — Enumerate candidates
246
+
247
+ Scan the extracted tree for mockup-shaped files. The candidate filter is:
248
+
249
+ - Extensions: `.html`, `.htm`, `.png`, `.jpg`, `.jpeg`, `.svg`, `.webp`, `.gif`, `.pdf`.
250
+ - Exclude: `node_modules/`, `.git/`, anything under a directory named `assets/`
251
+ whose parent is itself excluded, `*.min.*`, `thumbs.db`, `.DS_Store`.
252
+ - Size sanity: skip files > 50 MB (likely video/binary blobs, never a mockup).
253
+
254
+ ```bash
255
+ find "$WORKTREE_PATH/${paths.prd_dir}/<slug>/mockups/_archive/extracted" \
256
+ -type f \
257
+ \( -iname '*.html' -o -iname '*.htm' -o -iname '*.png' -o -iname '*.jpg' \
258
+ -o -iname '*.jpeg' -o -iname '*.svg' -o -iname '*.webp' -o -iname '*.gif' \
259
+ -o -iname '*.pdf' \) \
260
+ -size -50M \
261
+ -not -path '*/node_modules/*' -not -path '*/.git/*' \
262
+ -not -name '*.min.*' -not -name 'thumbs.db' -not -name '.DS_Store'
263
+ ```
264
+
265
+ For each candidate, build a lightweight index entry:
266
+ - `path` (relative to `extracted/`)
267
+ - `filename`
268
+ - `ext`
269
+ - `size_kb`
270
+ - For HTML: `<title>` content + first `<h1>` (use `grep -oE` or a single Read of
271
+ the first ~200 lines; do NOT load the full file — they can be massive).
272
+ - For images: nothing extra at this stage (filename is the only cheap signal).
273
+
274
+ Cap the indexed list at 200 entries. If the archive contains more, sort by
275
+ `size_kb DESC` (larger files are typically the rendered screens) and keep the
276
+ top 200, recording `mockups.archive.truncated: true`.
277
+
278
+ Record the full index in state under `mockups.archive.candidates[]`.
279
+
280
+ #### Step c — Score against feature scope
281
+
282
+ Build the scope vector from the Kickoff state (Step 1):
283
+ - `feature_title` (from `## Feature Description`).
284
+ - `slug`.
285
+ - ISA touchpoints (if Discovery dimension 9 already ran).
286
+ - Verbatim screen names the user mentioned in the initial description.
287
+ - Keywords from `feature_objective_one_sentence` (strip stopwords).
288
+
289
+ For each candidate, compute a relevance score 0-100:
290
+ - `+40` if any scope keyword appears in the filename (case-insensitive,
291
+ whole-word; for kebab/snake/camelCase, split before matching).
292
+ - `+30` if any scope keyword appears in the HTML `<title>` or `<h1>`.
293
+ - `+15` if the file path contains a directory name matching a scope keyword.
294
+ - `+10` if the filename matches typical screen patterns (`screen`, `page`,
295
+ `view`, `flow`, the user's domain entity names).
296
+ - `-20` if the filename matches typical non-screen patterns (`icon`, `logo`,
297
+ `favicon`, `thumbnail`, `og-image`, `sitemap`, `manifest`).
298
+
299
+ Selection threshold: keep candidates with `score ≥ 30` as the auto-selected set.
300
+ If fewer than 3 candidates clear the threshold, lower to `≥ 20` and surface the
301
+ fact. If zero candidates clear `≥ 20`, surface "selezione non automatizzabile —
302
+ ti chiedo di indicarmi a mano quali sono rilevanti" and present the full
303
+ candidate list.
304
+
305
+ Record per-candidate: `score`, `matched_keywords[]`, `selected: bool`.
306
+
307
+ #### Step d — Confirmation gate (STOP)
308
+
309
+ Present a compact table to the user — selected candidates first, then up to 10
310
+ discarded with the highest scores, then a footer count for "altri N scartati":
311
+
312
+ ```
313
+ Bundle scaricato e analizzato: <N> file candidati, <K> selezionati come rilevanti.
314
+
315
+ **Selezionati** (score ≥ 30 vs. feature scope):
316
+ | # | File | Score | Match |
317
+ |---|-------------------------------|-------|------------------------|
318
+ | 1 | screens/lista-fornitori.html | 75 | "fornitori", "lista" |
319
+ | 2 | screens/dettaglio-ordine.html | 55 | "ordine" |
320
+
321
+
322
+ **Scartati ad alto score** (per trasparenza):
323
+ | # | File | Score | Motivo |
324
+ |---|-------------------------------|-------|------------------------|
325
+ | 1 | screens/landing-marketing.html| 22 | nessun keyword match |
326
+
327
+
328
+ Altri <M> file scartati (score < 20 o pattern non-schermata).
329
+
330
+ Procedo all'analisi visiva dei <K> selezionati? Oppure indicami modifiche
331
+ (es. "aggiungi il #3 degli scartati", "togli il #2 dei selezionati").
332
+ ```
333
+
334
+ **STOP.** Wait for explicit confirmation or an adjustment list. On adjustment,
335
+ recompute `mockups.archive.candidates[].selected`, then re-present and STOP again
336
+ until the user confirms.
337
+
338
+ #### Step e — Materialize the confirmed subset
339
+
340
+ Copy ONLY the confirmed selection into `${paths.prd_dir}/<slug>/mockups/`,
341
+ preserving relative directory structure under the extracted root so that
342
+ relative asset references (CSS, images) keep working. On filename collision,
343
+ use the `<name>-2.<ext>` rule from 1.6.4 point 2.
344
+
345
+ Record `mockups.original_paths[]` as `archive://<relative-path-in-zip>` and
346
+ `mockups.canonical_paths[]` as the new path under `mockups/`. The scratch
347
+ `_archive/extracted/` directory is kept for the duration of the PRD session
348
+ (may be needed to resolve `<link>` / `<img src>` references during analysis)
349
+ and cleaned up at Step 7 (Commit & Merge) — add a `.gitignore` entry for
350
+ `mockups/_archive/` if not already present.
351
+
352
+ Then continue to Step 1.6.5 with the selected subset.
353
+
200
354
  ### 1.6.5 — Mockup analysis (MANDATORY before Discovery)
201
355
 
202
356
  Analyze ALL provided mockups (visual analysis for chat images, Read for copied files
@@ -561,11 +715,26 @@ structure. Be specific — vague analysis defeats the purpose of skipping Step 3
561
715
  ```yaml
562
716
  mockups:
563
717
  status: provided | partial | none | requested
564
- format: chat-images | local-paths | mixed
718
+ format: chat-images | local-paths | local-archive | mixed
565
719
  original_paths:
566
- - <user-provided path or chat://image-N>
720
+ - <user-provided path, chat://image-N, or archive://<rel-path-in-zip>>
567
721
  canonical_paths:
568
- - mockups/<file>.png # only for local-paths
722
+ - mockups/<file>.png # only for local-paths and local-archive
723
+ archive: # only when format includes local-archive
724
+ local_zip_path: <abs path to the user-provided .zip>
725
+ kind: zip
726
+ extracted_dir: <abs path to _archive/extracted/>
727
+ truncated: false # true when candidate list capped at 200
728
+ candidates: # full enumerated list (post-filter)
729
+ - path: <rel path under extracted/>
730
+ filename: <basename>
731
+ ext: html | png | jpg | svg | …
732
+ size_kb: <int>
733
+ html_title: <optional, HTML only>
734
+ html_h1: <optional, HTML only>
735
+ score: <0-100>
736
+ matched_keywords: [<keyword>, …]
737
+ selected: <bool> # true = part of the confirmed subset
569
738
  mockup_analysis:
570
739
  screens:
571
740
  - name: <inferred name, e.g. "Lista ordini">
@@ -0,0 +1,119 @@
1
+ # MCP Integration — Guida BALDART per i consumer
2
+
3
+ Questo documento spiega **quando** un agent/skill BALDART deve appoggiarsi a un Model Context Protocol (MCP) server, **quali** MCP sono raccomandati per i casi d'uso più comuni, e **come** dichiarare la dipendenza MCP nel body dell'agent in modo che il consumer sappia subito cosa configurare.
4
+
5
+ > **Scope di questa release**: convenzione testuale, **zero nuove chiavi config** in `baldart.config.yml`. Le chiavi `mcp.*` (es. `mcp.required: []` / `mcp.optional: []`) entreranno solo se e quando una skill concreta dovrà gatearne il comportamento, in una release dedicata con propagazione end-to-end completa (regola di [schema-change propagation](../docs/PROJECT-CONFIGURATION.md)).
6
+
7
+ ---
8
+
9
+ ## 1. Quando usare un MCP
10
+
11
+ Tre opzioni per integrare un sistema esterno in un agent BALDART. Scegli il più semplice che risolve il problema:
12
+
13
+ | Opzione | Quando ha senso | Costi/limiti |
14
+ |---|---|---|
15
+ | **MCP server** | Il sistema è **bidirezionale**, ha molte operazioni discrete (search, get, mutate), serve a **molti agent diversi**, o richiede **stato di sessione** (browser, DB). | Setup nel consumer (configurazione MCP), dipendenza esplicita, esposizione dei dati al modello. |
16
+ | **Script CLI / Bash** | È un'azione **one-shot** o **idempotente** (build, test, git, lint, deploy), output testuale ben definito, niente stato di sessione. | Niente — è la baseline. Preferibile quando funziona. |
17
+ | **API call diretta (fetch/curl)** | Serve **un'unica chiamata** verso un endpoint REST/GraphQL pubblico o autenticato via env var, **non serve a più agent**. | Risposta opaca al framework (non recuperabile per altri agent), parsing manuale, niente cache strutturata. |
18
+
19
+ **Regola pratica**: se la stessa integrazione tornerà utile a **2+ agent o skill**, è un MCP. Se è un'esecuzione una-tantum da una singola skill, è uno script CLI o un fetch.
20
+
21
+ ---
22
+
23
+ ## 2. Catalogo MCP raccomandati per i casi d'uso comuni
24
+
25
+ Non è un'imposizione: BALDART non installa MCP, non li valida, non li richiede. È una lista curata di MCP che hanno senso nei deployment dove BALDART è già in uso.
26
+
27
+ ### Browser automation
28
+ - **Playwright MCP** (`@modelcontextprotocol/server-playwright`) — usato da [bug](../.claude/skills/bug/SKILL.md) e [webapp-testing](../.claude/skills/webapp-testing/SKILL.md) per cattura visiva, network, console.
29
+
30
+ ### Documentation retrieval (RAG)
31
+ - **doc-rag MCP** (interno o `@modelcontextprotocol/server-everything-search` come stand-in) — usato da [codebase-architect](../.claude/agents/codebase-architect.md) e [code-search-protocol.md](../agents/code-search-protocol.md) per ricerca semantica nei doc + knowledge graph.
32
+
33
+ ### Database
34
+ - **Firebase MCP** (`@modelcontextprotocol/server-firebase` — già integrato come plugin BALDART quando `stack.database.primary` è Firestore) — ispezione documenti, query collection, transazioni.
35
+ - **Postgres MCP** (`@modelcontextprotocol/server-postgres`) — per stack SQL.
36
+
37
+ ### Issue tracker / project management
38
+ - **GitHub MCP** (`@modelcontextprotocol/server-github`) o **`gh` CLI** — issue triage, PR review, gist creation. Per BALDART consigliamo `gh` (è una CLI già universale), MCP solo se servono molte mutazioni in pipeline.
39
+ - **Linear MCP**, **Jira MCP** — disponibili nella registry ufficiale Anthropic per chi usa quei ticketing system.
40
+
41
+ ### Design tool
42
+ - **Figma MCP** — usato da agent di design / UI per estrarre token, screenshot, asset.
43
+
44
+ ### Observability
45
+ - **Sentry MCP** — già disponibile in BALDART come [sentry plugin](../../CLAUDE.md). Per analisi error → root cause.
46
+
47
+ ### Communication
48
+ - **Slack MCP**, **Gmail MCP** — disponibili nel marketplace MCP. Usali solo se l'agent deve davvero comunicare verso umani (es. post-mortem auto-pubblicato).
49
+
50
+ **Anti-pattern**: NON installare MCP "perché esistono". Ogni MCP aggiunge tool al system prompt del modello, consuma context, e crea dipendenze infrastrutturali nel consumer. Il pattern BALDART è "tanti agent specializzati, pochi MCP universalmente utili".
51
+
52
+ ---
53
+
54
+ ## 3. La convenzione `## MCP dependencies` (per author di agent/skill BALDART)
55
+
56
+ Ogni agent o skill BALDART che usa MCP deve dichiararlo esplicitamente nel body, in una sezione dedicata. Questo permette:
57
+ - al **consumer** di sapere cosa configurare (e cosa succede se non lo configura);
58
+ - al `baldart doctor` futuro di leggere queste sezioni e verificare gli MCP `required`;
59
+ - ad altri agent di sapere se invocare questo agent è "free" o richiede setup.
60
+
61
+ ### Struttura
62
+
63
+ Subito dopo la sezione `## Project Context` (o, in mancanza, subito dopo il frontmatter e l'intro):
64
+
65
+ ```markdown
66
+ ## MCP dependencies
67
+
68
+ **Required** (skill/agent rifiuta o degrada visibilmente senza questi MCP):
69
+ - `mcp__<server>__<tool>` — cosa serve, perché.
70
+
71
+ **Optional** (l'agent ne approfitta se disponibile, ma ha un fallback):
72
+ - `mcp__<server>__<tool>` — cosa offre in più, qual è il fallback.
73
+
74
+ **None** (per skill senza MCP — utile dichiararlo esplicitamente):
75
+ - This skill uses no MCP servers. <breve nota se rilevante>
76
+ ```
77
+
78
+ ### Regole
79
+
80
+ 1. **Sii specifico**: cita il prefisso del tool (`mcp__playwright__browser_navigate`), non il "concetto" (`Playwright MCP`). Aiuta il consumer a fare grep.
81
+ 2. **Required vs Optional**: required = la skill non può completare il proprio task senza il MCP (o degrada gravemente). Optional = la skill ha un fallback funzionante.
82
+ 3. **Fallback espliciti**: per ogni `Optional`, descrivi il fallback in una frase ("se MCP unavailable, usa `rg` su `docs/` per trovare pattern noti").
83
+ 4. **Niente nuovo schema in `baldart.config.yml`**: non aggiungere `mcp.required` o `mcp.optional` nel config — è solo convenzione testuale finché non c'è una skill che ha bisogno di gatearne il comportamento a runtime.
84
+ 5. **Aggiorna la dichiarazione quando la dipendenza cambia**: se rimuovi un MCP dall'agent, rimuovi la riga; se aggiungi un fallback Grep e l'MCP diventa optional, sposta la riga.
85
+
86
+ ### Esempi nel framework corrente
87
+
88
+ Vedi le seguenti dichiarazioni per pattern di riferimento:
89
+ - [framework/.claude/agents/codebase-architect.md](../.claude/agents/codebase-architect.md) — `## MCP dependencies` con doc-rag required + LSP-related optional.
90
+ - [framework/.claude/skills/bug/SKILL.md](../.claude/skills/bug/SKILL.md) — Playwright required per UI bug, doc-rag + Firestore optional.
91
+ - [framework/.claude/skills/playwright-skill/SKILL.md](../.claude/skills/playwright-skill/SKILL.md) — esempio di sezione "None" (la skill usa Playwright Test CLI, non MCP).
92
+
93
+ ---
94
+
95
+ ## 4. FAQ per il consumer
96
+
97
+ **D: BALDART installa gli MCP per me?**
98
+ R: No. BALDART distribuisce agent e skill che eventualmente li **usano**. L'installazione e configurazione MCP è responsabilità del consumer (di solito via `.mcp.json` o `.claude/settings.json`). Vedi la [documentazione Claude Code](https://docs.claude.com) per il setup MCP.
99
+
100
+ **D: Cosa succede se invoco un agent che richiede un MCP non configurato?**
101
+ R: Dipende da come l'agent è stato scritto. Gli agent BALDART che seguono il pattern raccomandato fanno **graceful degrade** (riportano il missing MCP e provano un fallback documentato). Quelli che dichiarano un MCP come **Required** dovrebbero rifiutare visibilmente di proseguire — ma oggi BALDART non enforza questa policy in modo automatico. Il `baldart doctor` futuro potrà controllarla.
102
+
103
+ **D: Posso aggiungere un MCP custom non nella lista del § 2?**
104
+ R: Sì, e se diventa utile a più agent valuta una PR upstream a BALDART per aggiungerlo al catalogo. Vedi [MAINTAINING.md](../../MAINTAINING.md) per il workflow di contribuzione.
105
+
106
+ **D: La mia organizzazione ha un sistema interno (ticketing, docs, monitoring) — devo per forza esporlo via MCP?**
107
+ R: Non per forza. Se l'integrazione serve a **uno** script una tantum, una semplice fetch è meglio. Se serve a 2+ agent BALDART o se vuoi che gli agent possano "scoprire" dati senza che tu glieli passi manualmente, allora vale la pena un MCP custom.
108
+
109
+ ---
110
+
111
+ ## 5. Cosa cambia per BALDART nelle prossime release
112
+
113
+ Roadmap (vedi piano [gap-analysis vs. large codebases](../../.claude/plans/voglio-che-studi-questo-fluttering-nova.md)):
114
+
115
+ - **Adesso** (questa release): convenzione testuale `## MCP dependencies`, doc + 3 agent/skill aggiornati come reference.
116
+ - **Prossima release MINOR** (se serve): introduzione di una skill che richiede gating runtime su MCP → in quella release sarà propagata la chiave `mcp.required/optional` end-to-end (template + configure + doctor + CHANGELOG), seguendo la regola di schema-change propagation.
117
+ - **Routine `mcp-check`** (opzionale, T-level): controllo settimanale che ogni MCP `Required` dichiarato nel framework abbia un endpoint sano nel consumer.
118
+
119
+ Finché la chiave config non esiste, la convenzione testuale **è** il contratto.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "baldart",
3
- "version": "3.27.0",
3
+ "version": "3.28.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"