baldart 5.6.0 → 5.7.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 +55 -0
- package/VERSION +1 -1
- package/framework/.claude/skills/capture/CHANGELOG.md +4 -0
- package/framework/.claude/skills/capture/SKILL.md +2 -1
- package/framework/.claude/skills/design-sync/CHANGELOG.md +4 -0
- package/framework/.claude/skills/design-sync/SKILL.md +2 -1
- package/framework/.claude/skills/ds-edit/CHANGELOG.md +5 -0
- package/framework/.claude/skills/ds-edit/SKILL.md +9 -10
- package/framework/.claude/skills/ds-handoff/CHANGELOG.md +5 -0
- package/framework/.claude/skills/ds-handoff/SKILL.md +3 -2
- package/framework/.claude/skills/ds-new/CHANGELOG.md +4 -0
- package/framework/.claude/skills/ds-new/SKILL.md +2 -1
- package/framework/.claude/skills/ds-render/CHANGELOG.md +4 -0
- package/framework/.claude/skills/ds-render/SKILL.md +2 -1
- package/framework/.claude/skills/e2e-review/CHANGELOG.md +4 -0
- package/framework/.claude/skills/e2e-review/SKILL.md +2 -1
- package/framework/.claude/skills/gamification-design/CHANGELOG.md +4 -0
- package/framework/.claude/skills/gamification-design/SKILL.md +2 -1
- package/framework/.claude/skills/graph-align/CHANGELOG.md +4 -0
- package/framework/.claude/skills/graph-align/SKILL.md +2 -1
- package/framework/.claude/skills/i18n/CHANGELOG.md +4 -0
- package/framework/.claude/skills/i18n/SKILL.md +2 -1
- package/framework/.claude/skills/motion-design/CHANGELOG.md +4 -0
- package/framework/.claude/skills/motion-design/SKILL.md +2 -1
- package/framework/.claude/skills/new/CHANGELOG.md +4 -0
- package/framework/.claude/skills/new/SKILL.md +2 -1
- package/framework/.claude/skills/new2/CHANGELOG.md +4 -0
- package/framework/.claude/skills/new2/SKILL.md +2 -1
- package/framework/.claude/skills/prd/CHANGELOG.md +4 -0
- package/framework/.claude/skills/prd/SKILL.md +2 -1
- package/framework/.claude/skills/prd-add/CHANGELOG.md +4 -0
- package/framework/.claude/skills/prd-add/SKILL.md +2 -1
- package/framework/.claude/skills/skill-creator/references/skill-structure.md +31 -2
- package/framework/.claude/skills/skill-creator/scripts/quick_validate.py +25 -3
- package/framework/.claude/skills/ui-design/CHANGELOG.md +4 -0
- package/framework/.claude/skills/ui-design/SKILL.md +2 -1
- package/framework/.claude/skills/ui-implement/CHANGELOG.md +4 -0
- package/framework/.claude/skills/ui-implement/SKILL.md +2 -1
- package/framework/.claude/workflows/new2.js +5 -1
- package/package.json +1 -1
- package/src/commands/configure.js +9 -2
- package/src/commands/doctor.js +65 -0
- package/src/utils/skill-gate.js +139 -0
- package/src/utils/symlinks.js +72 -7
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,61 @@ 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
|
+
## [5.7.0] - 2026-07-06
|
|
9
|
+
|
|
10
|
+
**Feature-gated skill linking — stop wasting Codex's 2% skill-description budget.** Codex
|
|
11
|
+
reserves ~2% of its context window for skill descriptions; BALDART's ~40 skills alone
|
|
12
|
+
already exceed it (~6.5k tokens), so Codex warns *"Skill descriptions were shortened to fit
|
|
13
|
+
the 2% skills context budget"* and truncates them (degrading skill routing). Until now the
|
|
14
|
+
installer linked EVERY framework skill into a consumer — including ones whose gating
|
|
15
|
+
`features.*` flag is OFF (e.g. the `ds-*` cluster, `i18n`, `graph-align`, `e2e-review`),
|
|
16
|
+
which then merely refuse at runtime: dead weight on the budget. Now a skill is linked only
|
|
17
|
+
when its declared feature is enabled, trimming ~2.5k tokens (~40%) on a typical project
|
|
18
|
+
without design-system/i18n/code-graph/e2e.
|
|
19
|
+
|
|
20
|
+
- **`requires_feature` frontmatter (SSOT) + `src/utils/skill-gate.js`**: an OPTIONAL
|
|
21
|
+
`requires_feature: has_x` (string or list = AND) on a skill's `SKILL.md`. Makes
|
|
22
|
+
machine-readable a dependency the skills already declared in prose (their "Project
|
|
23
|
+
Context" header) — no second SSOT, same pattern as `version:`/`effort:`. `resolveSkillGate`
|
|
24
|
+
reads `features.*` **directly** from `baldart.config.yml` (NOT `agent-slots.resolveAgentFlags`,
|
|
25
|
+
which omits `has_prd_workflow`/`has_e2e_review`) and **fails open** globally (config
|
|
26
|
+
absent/unreadable → link everything) and per-skill (malformed frontmatter → link that skill).
|
|
27
|
+
- **Applied to BOTH runtimes from one code path** (`mergeSkills` in `src/utils/symlinks.js`):
|
|
28
|
+
the gate is shared SEMANTICS (which skills are relevant), not runtime mechanics, so Claude
|
|
29
|
+
(`.claude/skills/`) and Codex (`.agents/skills/`) stay identical by construction. Adds the
|
|
30
|
+
**prune** direction — a feature flipped off (or a fail-open first install) now removes stale
|
|
31
|
+
framework symlinks, never touching a user skill dir or a user-override symlink pointing
|
|
32
|
+
elsewhere.
|
|
33
|
+
- **17 skills gated**: `has_design_system` → ui-design, ui-implement, ds-new, ds-edit,
|
|
34
|
+
ds-render, design-sync, ds-handoff, motion-design, gamification-design · `has_i18n` → i18n ·
|
|
35
|
+
`has_code_graph` → graph-align · `has_e2e_review` → e2e-review · `has_prd_workflow` → prd,
|
|
36
|
+
prd-add · `has_backlog` → new, new2 · `has_wiki_overlay` → capture. The five bootstrap
|
|
37
|
+
skills (`design-system-init`, `i18n-adopt`, `graphify-bootstrap`, `lsp-bootstrap`,
|
|
38
|
+
`toolchain-bootstrap`) stay ALWAYS-linked so a feature can always be turned on — then
|
|
39
|
+
`configure`/`update` re-merge and the cluster appears.
|
|
40
|
+
- **Lifecycle wiring**: `configure` now re-runs `mergeSkills` after writing config (first
|
|
41
|
+
install linked fail-open before the config existed — this is what actually prunes);
|
|
42
|
+
`update` already re-merges (prune committed via `BALDART_MANAGED_PATTERNS`); `doctor` gains
|
|
43
|
+
a non-blocking `reconcile-skills` advisory when a gated-off skill is still linked, and its
|
|
44
|
+
skill sample-probe no longer samples gatable skills (`prd`/`capture`). `skill-creator`:
|
|
45
|
+
`quick_validate.py` allow-lists `requires_feature` (it previously HARD-FAILED on unknown
|
|
46
|
+
keys) + soft-warns unknown flags; `references/skill-structure.md` documents the field.
|
|
47
|
+
`new2.js` binding-gate now guards on `has_design_system`.
|
|
48
|
+
- **Behaviour change for Claude consumers**: a skill whose feature is off now *disappears*
|
|
49
|
+
from `.claude/skills/` instead of appearing and refusing at runtime. Mitigated: bootstraps
|
|
50
|
+
stay visible, and re-enabling the feature (then any merge) brings the cluster back.
|
|
51
|
+
- **Rider — two skill descriptions made `quick_validate`-clean** (pre-existing failures, both
|
|
52
|
+
among the heaviest so this also shrinks the Codex budget): `ds-edit` description trimmed
|
|
53
|
+
1080→1014 chars (was over the 1024 max), `ds-handoff` angle-bracket placeholder
|
|
54
|
+
(`<screens-or-feature>`) rephrased bracket-free — no trigger phrases lost.
|
|
55
|
+
- Verified: unit-level gate partitioning (design_system-off/backlog-on → 15 gated off, `new`
|
|
56
|
+
linked, bootstrap present), YAML validity of all 17 frontmatters, `quick_validate` PASS.
|
|
57
|
+
- **No new `baldart.config.yml` key** (consumes existing `features.*`) → the schema-change
|
|
58
|
+
propagation rule does NOT apply; it is skill-frontmatter metadata like `version`/`effort`.
|
|
59
|
+
- **Codex parity: portable** — single semantic filter, both runtimes, install-time only;
|
|
60
|
+
`requires_feature` is ignored by both skill loaders (they read only name+description, as
|
|
61
|
+
they already do for `version`/`effort`).
|
|
62
|
+
|
|
8
63
|
## [5.6.0] - 2026-07-04
|
|
9
64
|
|
|
10
65
|
**Outcome-integrity wave (FEAT-0068 post-mortem — mayo, 72.78M token, DONE falso).** Il
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.7.0
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.0.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_wiki_overlay` frontmatter — this skill is now linked into a consumer only when `features.has_wiki_overlay` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.0.0 — 2026-07-01
|
|
6
10
|
|
|
7
11
|
- Baseline: versioning per-skill introdotto al framework v4.82.0.
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
name: capture
|
|
3
3
|
effort: medium
|
|
4
4
|
description: Distilla una sintesi cross-document della conversazione recente in una synthesis page riusabile del wiki overlay (path da `paths.wiki_dir`/syntheses/ in `baldart.config.yml`). Usare quando l'utente invoca `/capture`, dice "capture synthesis", "salva sintesi", "distilla conversazione", "save this as wiki", "questa conversazione in wiki", o quando chiede di trasformare un ragionamento cross-document appena fatto in una pagina stabile. Due modalità — manual (sempre funzionante) e proactive (conditional sui candidati in `.claude/capture-queue/`). Implementa il loop query→wiki file-back di Karpathy.
|
|
5
|
-
version: 1.0.
|
|
5
|
+
version: 1.0.1
|
|
6
|
+
requires_feature: has_wiki_overlay
|
|
6
7
|
---
|
|
7
8
|
|
|
8
9
|
# /capture — Synthesis Capture Skill
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.0.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_design_system` frontmatter — this skill is now linked into a consumer only when `features.has_design_system` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.0.0 — 2026-07-01
|
|
6
10
|
|
|
7
11
|
- Baseline: versioning per-skill introdotto al framework v4.82.0.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: design-sync
|
|
3
3
|
effort: high
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
|
+
requires_feature: has_design_system
|
|
5
6
|
description: >
|
|
6
7
|
Keep the in-repo design system (the SSOT) and its Claude Design satellite
|
|
7
8
|
"mirror" project in sync — the code→design half of the closed loop. Three modes:
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.1.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_design_system` frontmatter — this skill is now linked into a consumer only when `features.has_design_system` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
- **Description trimmed to pass `quick_validate`**: the `description` was 1080 chars (over the 1024 max); compressed to 1014 without losing any trigger phrase. Also shrinks the Codex skill-description budget.
|
|
9
|
+
|
|
5
10
|
## 1.1.0 — 2026-07-02
|
|
6
11
|
|
|
7
12
|
- **P1 locate hardening.** Reso esplicito che lo step P1 è un lookup deterministico
|
|
@@ -1,23 +1,22 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ds-edit
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.1
|
|
5
|
+
requires_feature: has_design_system
|
|
5
6
|
description: >
|
|
6
7
|
Update ONE existing canonical design-system element correctly — the deliberate
|
|
7
8
|
edit twin of /ds-new. Classifies the change (resync the spec to changed source /
|
|
8
|
-
extend with a
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
framework/templates/component-spec.template.md filled if a spec is missing),
|
|
9
|
+
extend with a variant or prop / breaking change + migration / re-govern the
|
|
10
|
+
closed-set role) and runs it end to end: apply the source change (via ui-expert),
|
|
11
|
+
regenerate the spec via the serializer (HEAD from source, agentic + prose
|
|
12
|
+
PRESERVED, the canonical component-spec template filled if a spec is missing),
|
|
13
13
|
re-govern if needed, verify. A THIN orchestrator — delegates every rule to the
|
|
14
14
|
existing SSOTs, never hand-writes a spec HEAD. Use when the user says /ds-edit,
|
|
15
15
|
"aggiorna un componente", "modifica una primitive", "aggiungi una variante",
|
|
16
16
|
"estendi il componente", "resync dello spec", "breaking change su un
|
|
17
|
-
componente", or when `/prd` confirms a REUSE+VARIANT extension. The
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
features.has_design_system.
|
|
17
|
+
componente", or when `/prd` confirms a REUSE+VARIANT extension. The mechanical
|
|
18
|
+
doc-resync after a code change is ALREADY automatic (`DS_COMPONENT_STALE` gate)
|
|
19
|
+
— this skill is for the DELIBERATE change. Gated on features.has_design_system.
|
|
21
20
|
---
|
|
22
21
|
|
|
23
22
|
# DS Edit — update one existing canonical element
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.2.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_design_system` frontmatter — this skill is now linked into a consumer only when `features.has_design_system` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
- **Description made `quick_validate`-clean**: removed the angle-bracket placeholder (`<screens-or-feature>`) the validator rejects, rephrased bracket-free without losing the standalone-invocation trigger.
|
|
9
|
+
|
|
5
10
|
## 1.2.0 — 2026-07-02
|
|
6
11
|
|
|
7
12
|
- **Analysis-profile contract (framework v4.94.0)**: the standalone screen-resolution probe passes `PROFILE=ui` to `codebase-architect` per `framework/agents/analysis-profiles.md`. The Step 3 field-extraction retrieval order is unchanged.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ds-handoff
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.1
|
|
5
|
+
requires_feature: has_design_system
|
|
5
6
|
description: >
|
|
6
7
|
Produce a FIELD-LEVEL, 1:1, interaction-complete handoff brief for Claude Design
|
|
7
8
|
— every screen's real fields (label, control type, required, validation, default,
|
|
@@ -9,7 +10,7 @@ description: >
|
|
|
9
10
|
data schemas (Zod / TS types / form schemas) via a coverage gate that BLOCKS prompt
|
|
10
11
|
emission when a backed screen's fields are unverified. The single SSOT for the
|
|
11
12
|
Claude Design handoff prompt: `/prd` Step 3.0 Branch B delegates here; usable
|
|
12
|
-
standalone via `/ds-handoff
|
|
13
|
+
standalone via `/ds-handoff` with a screen list or feature. A THIN orchestrator — cites
|
|
13
14
|
`design-system-protocol.md` (Functional Traceability Gate, Reference Tables) and
|
|
14
15
|
reads `mockup_analysis` as input, never duplicating their rules; reuses existing
|
|
15
16
|
retrieval (code-graph / LSP / codebase-architect / Grep) rather than shipping a
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.1.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_design_system` frontmatter — this skill is now linked into a consumer only when `features.has_design_system` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.1.0 — 2026-07-02
|
|
6
10
|
|
|
7
11
|
- **Reuse-first hardening.** Reso esplicito che lo step 1 reuse-first resta il
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ds-new
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.1
|
|
5
|
+
requires_feature: has_design_system
|
|
5
6
|
description: >
|
|
6
7
|
Create ONE canonical design-system element — a component OR a token — end to
|
|
7
8
|
end with the full discipline: reuse-first research, (optional) scaffold, then
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.0.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_design_system` frontmatter — this skill is now linked into a consumer only when `features.has_design_system` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.0.0 — 2026-07-01
|
|
6
10
|
|
|
7
11
|
- Baseline: versioning per-skill introdotto al framework v4.82.0.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ds-render
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
|
+
requires_feature: has_design_system
|
|
5
6
|
description: >
|
|
6
7
|
Render the design-system primitives in ISOLATION (one component × variant at a
|
|
7
8
|
time) and capture a PNG per entry — the render harness BALDART never had (today
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.3.2 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_e2e_review` frontmatter — this skill is now linked into a consumer only when `features.has_e2e_review` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.3.1 — 2026-07-03
|
|
6
10
|
|
|
7
11
|
- Boundary pointer: "Not a design generator" ora punta solo a `ui-design`
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: e2e-review
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 1.3.
|
|
4
|
+
version: 1.3.2
|
|
5
|
+
requires_feature: has_e2e_review
|
|
5
6
|
description: >
|
|
6
7
|
Deterministic, BLOCKING end-to-end review run after a feature is implemented.
|
|
7
8
|
Combines functional E2E (Playwright spec written by `coder`, executed via
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.0.2 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_design_system` frontmatter — this skill is now linked into a consumer only when `features.has_design_system` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.0.1 — 2026-07-03
|
|
6
10
|
|
|
7
11
|
- Related Skills: `ui-ux-pro-max` (superseded) sostituito con `ui-design` v2
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: gamification-design
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.2
|
|
5
|
+
requires_feature: has_design_system
|
|
5
6
|
description: Use when designing or analyzing points systems, reward loops, progression mechanics, engagement features, or loyalty programs. Triggers include "points system," "rewards design," "progression," "engagement loop," "battle pass," "loyalty mechanics," "gamification," or "retention hooks." For B2C customer-facing features in this codebase, this skill invokes the hyper-gamification-designer agent.
|
|
6
7
|
---
|
|
7
8
|
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.0.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_code_graph` frontmatter — this skill is now linked into a consumer only when `features.has_code_graph` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.0.0 — 2026-07-01
|
|
6
10
|
|
|
7
11
|
- Baseline: versioning per-skill introdotto al framework v4.82.0.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: graph-align
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
|
+
requires_feature: has_code_graph
|
|
5
6
|
description: >
|
|
6
7
|
On-demand documentation alignment via the Graphify code knowledge graph.
|
|
7
8
|
Rebuilds the graph offline (graphify update .), then cross-checks graph
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.0.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_i18n` frontmatter — this skill is now linked into a consumer only when `features.has_i18n` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.0.0 — 2026-07-01
|
|
6
10
|
|
|
7
11
|
- Baseline: versioning per-skill introdotto al framework v4.82.0.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: i18n
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
|
+
requires_feature: has_i18n
|
|
5
6
|
description: >
|
|
6
7
|
On-demand internationalization audit + context-aware translation. Audits the
|
|
7
8
|
i18n context registry against the t() calls and native locale files (drift
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.0.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_design_system` frontmatter — this skill is now linked into a consumer only when `features.has_design_system` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.0.0 — 2026-07-01
|
|
6
10
|
|
|
7
11
|
- Baseline: versioning per-skill introdotto al framework v4.82.0.
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 2.3.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_backlog` frontmatter — this skill is now linked into a consumer only when `features.has_backlog` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 2.3.0 — 2026-07-04 (FEAT-0068 post-mortem: integrità dell'esito)
|
|
6
10
|
|
|
7
11
|
- **Chiusura epic: solo in merge phase, predicato esteso** — `commit.md` c-bis RITIRATO
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: new
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 2.3.
|
|
4
|
+
version: 2.3.1
|
|
5
|
+
requires_feature: has_backlog
|
|
5
6
|
description: >
|
|
6
7
|
Orchestrate a team of specialized agents to implement one or more backlog cards
|
|
7
8
|
end-to-end inside a dedicated worktree, with code review, doc review, QA, and
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.8.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_backlog` frontmatter — this skill is now linked into a consumer only when `features.has_backlog` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.8.0 — 2026-07-04 (FEAT-0068 post-mortem: integrità dell'esito + igiene git)
|
|
6
10
|
|
|
7
11
|
- **`new2.js`**: step 4b (chiusura epic) RIMOSSO dal commit agent — la chiusura vive
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.6.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_prd_workflow` frontmatter — this skill is now linked into a consumer only when `features.has_prd_workflow` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.6.0 — 2026-07-04 (FEAT-0068 post-mortem: il root cause era qui)
|
|
6
10
|
|
|
7
11
|
- **ISA "UI ROLE FAMILY (registry-first)"** (`discovery-phase.md` Dimension 9, gated su
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: prd
|
|
3
3
|
effort: high
|
|
4
|
-
version: 1.6.
|
|
4
|
+
version: 1.6.1
|
|
5
|
+
requires_feature: has_prd_workflow
|
|
5
6
|
description: "Structured PRD creation skill. Use when the user says /prd, 'crea un prd', 'nuova funzionalita', 'pianifica feature', or wants to plan a new feature end-to-end. Also handles /prd-add, 'aggiungi requisito', 'serve anche', 'manca un endpoint' for change requests on active PRD sessions — runs ICIAS impact analysis to determine which phases need SKIP/PATCH/REDO. Produces PRD + UI design + backlog cards, all validated and committed. Multi-turn conversation: asks questions, waits for answers, iterates through discovery, design, spec writing, card creation, and validation phases."
|
|
6
7
|
---
|
|
7
8
|
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.1.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_prd_workflow` frontmatter — this skill is now linked into a consumer only when `features.has_prd_workflow` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.1.0 — 2026-07-02
|
|
6
10
|
|
|
7
11
|
- **Analysis-profile contract (framework v4.94.0)**: PATCH-mode re-discovery passes `PROFILE=impact` to `codebase-architect` (blast-radius scoping per `framework/agents/analysis-profiles.md`).
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: prd-add
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.1
|
|
5
|
+
requires_feature: has_prd_workflow
|
|
5
6
|
description: "Change Request skill for active PRD sessions. Use when the user says /prd-add, 'aggiungi requisito', 'serve anche', 'manca un endpoint', or describes a new requirement that impacts an existing PRD. Also auto-triggered by /prd during discovery when the user's answer reveals a new sub-feature not covered by the original scope. Runs ICIAS impact analysis (semantic scan + structural propagation + scoring) to determine which PRD phases need SKIP/PATCH/REDO, then executes only affected phases."
|
|
6
7
|
---
|
|
7
8
|
|
|
@@ -27,13 +27,42 @@ when the skill needs them** — no empty placeholder dirs (that was upstream
|
|
|
27
27
|
name: <kebab-case> # matches the directory name
|
|
28
28
|
effort: medium # low | medium | high | xhigh | max (effort-protocol.md)
|
|
29
29
|
version: 1.0.0 # the skill's OWN SemVer
|
|
30
|
+
requires_feature: has_x # OPTIONAL — install-time feature-gate (see below)
|
|
30
31
|
description: > # triggers + what/when; NO angle brackets
|
|
31
32
|
...
|
|
32
33
|
---
|
|
33
34
|
```
|
|
34
35
|
|
|
35
|
-
Order: `name` → `effort` → `version` → `
|
|
36
|
-
tolerated only on imported third-party skills; do not
|
|
36
|
+
Order: `name` → `effort` → `version` → (`requires_feature`) → `description`.
|
|
37
|
+
`license` / `metadata` are tolerated only on imported third-party skills; do not
|
|
38
|
+
add other keys.
|
|
39
|
+
|
|
40
|
+
### `requires_feature` — install-time feature-gate (OPTIONAL)
|
|
41
|
+
|
|
42
|
+
A skill that is only meaningful when a `baldart.config.yml` `features.*` flag is
|
|
43
|
+
on (and already REFUSES at runtime otherwise) declares that dependency here, so
|
|
44
|
+
the installer links it into a consumer ONLY when the feature is enabled. This
|
|
45
|
+
trims Codex's ~2% skill-description budget on projects that don't use the feature.
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
requires_feature: has_design_system # string → link iff true
|
|
49
|
+
requires_feature: [has_design_system, has_e2e_review] # list = AND (ALL true)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
- **Omit it** for any skill that should always be linked (the default). The five
|
|
53
|
+
bootstrap skills that ENABLE a feature (`design-system-init`, `i18n-adopt`,
|
|
54
|
+
`graphify-bootstrap`, `lsp-bootstrap`, `toolchain-bootstrap`) MUST stay ungated
|
|
55
|
+
— otherwise the feature could never be turned on.
|
|
56
|
+
- The gate is **install-time only** and **portable**: both Claude (`.claude/skills/`)
|
|
57
|
+
and Codex (`.agents/skills/`) simply see fewer directories. Neither runtime reads
|
|
58
|
+
the field — it is ignored exactly like `version`/`effort`. Resolution lives in
|
|
59
|
+
`src/utils/skill-gate.js`; it reads `features.*` directly and **fails open** (a
|
|
60
|
+
missing config, or a malformed frontmatter, links the skill rather than hiding it).
|
|
61
|
+
- **NOT a `baldart.config.yml` key** — it consumes existing `features.*`, so the
|
|
62
|
+
schema-change propagation rule does NOT apply. It is skill-frontmatter metadata.
|
|
63
|
+
- **Invariant** (latent, for future Codex `agents/openai.yaml` metadata): a gated
|
|
64
|
+
skill MUST NOT be declared as a `dependencies:` target of an always-linked skill's
|
|
65
|
+
`openai.yaml` — pruning the gated skill would leave a dangling dependency.
|
|
37
66
|
|
|
38
67
|
## Body headers (in order, when applicable)
|
|
39
68
|
|
|
@@ -39,10 +39,22 @@ def validate_skill(skill_path):
|
|
|
39
39
|
return False, f"Invalid YAML in frontmatter: {e}"
|
|
40
40
|
|
|
41
41
|
# Define allowed properties
|
|
42
|
-
# BALDART additions: `effort` (per-skill reasoning baseline — effort-protocol.md)
|
|
43
|
-
#
|
|
42
|
+
# BALDART additions: `effort` (per-skill reasoning baseline — effort-protocol.md),
|
|
43
|
+
# `version` (per-skill SemVer — canonical structure, see references/skill-structure.md)
|
|
44
|
+
# and `requires_feature` (install-time feature-gate — a skill is linked into a
|
|
45
|
+
# consumer only when this `features.*` flag is on; see references/skill-structure.md
|
|
46
|
+
# and src/utils/skill-gate.js). All three are OPTIONAL frontmatter metadata,
|
|
47
|
+
# ignored by the Claude/Codex skill loaders (they only read name+description).
|
|
44
48
|
ALLOWED_PROPERTIES = {'name', 'description', 'license', 'allowed-tools', 'metadata',
|
|
45
|
-
'effort', 'version'}
|
|
49
|
+
'effort', 'version', 'requires_feature'}
|
|
50
|
+
|
|
51
|
+
# Known feature flags a `requires_feature` value may reference (mirrors the
|
|
52
|
+
# features.* block of baldart.config.template.yml). Used for a SOFT warning
|
|
53
|
+
# only — an unknown value is not fatal (fail-open: the gate links the skill).
|
|
54
|
+
KNOWN_FEATURES = {'has_design_system', 'multi_tenant_theming', 'has_api_docs',
|
|
55
|
+
'has_backlog', 'has_adrs', 'has_prd_workflow', 'has_wiki_overlay',
|
|
56
|
+
'has_lsp_layer', 'has_code_graph', 'has_e2e_review',
|
|
57
|
+
'has_toolchain', 'has_i18n'}
|
|
46
58
|
|
|
47
59
|
# Check for unexpected properties (excluding nested keys under metadata)
|
|
48
60
|
unexpected_keys = set(frontmatter.keys()) - ALLOWED_PROPERTIES
|
|
@@ -90,6 +102,16 @@ def validate_skill(skill_path):
|
|
|
90
102
|
# `version` + a per-skill CHANGELOG.md are REQUIRED for new skills but WARNED (not
|
|
91
103
|
# hard-failed) here for backwards-compat with skills authored before the convention.
|
|
92
104
|
warnings = []
|
|
105
|
+
# Soft-validate requires_feature values against the known features.* set.
|
|
106
|
+
if 'requires_feature' in frontmatter:
|
|
107
|
+
req = frontmatter['requires_feature']
|
|
108
|
+
req_list = req if isinstance(req, list) else [req]
|
|
109
|
+
unknown = [str(f) for f in req_list if str(f) not in KNOWN_FEATURES]
|
|
110
|
+
if unknown:
|
|
111
|
+
warnings.append(
|
|
112
|
+
f"`requires_feature` references unknown flag(s): {', '.join(sorted(unknown))}. "
|
|
113
|
+
f"The gate fails open (skill still linked). Known: {', '.join(sorted(KNOWN_FEATURES))}"
|
|
114
|
+
)
|
|
93
115
|
if 'version' not in frontmatter:
|
|
94
116
|
warnings.append("missing `version:` in frontmatter (add `version: 1.0.0`)")
|
|
95
117
|
if 'effort' not in frontmatter:
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 2.0.1 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_design_system` frontmatter — this skill is now linked into a consumer only when `features.has_design_system` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 2.0.0 — 2026-07-03
|
|
6
10
|
|
|
7
11
|
**The local design studio.** Root-level refactor (framework v5.2.0): the
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ui-design
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 2.0.
|
|
4
|
+
version: 2.0.1
|
|
5
|
+
requires_feature: has_design_system
|
|
5
6
|
description: "The local design studio — THE framework skill for designing, prototyping and restyling UI directly in the repo (the internal twin of the Claude Design handoff). Produces context-aware, token-compliant HTML mockups in 3 distinct directions, with a locked Design Read, a deterministic anti-slop craft gate and separated generator/evaluator verification. Use when: (1) designing new pages, screens or components, (2) the /prd skill reaches its UI design phase (Step 3), (3) redesigning/restyling existing pages ('rifai la UI', 'migliora il design', 'restyle'), (4) exploring design options ('3 opzioni', 'opzioni di design', 'direzioni estetiche'), (5) prototyping a page/landing/dashboard locally ('prototipo', 'mockup', 'progetta interfaccia', 'design UI'). SUPERSEDES frontend-design (now a router) and ui-ux-pro-max for ALL design/prototyping work. Not for: implementing an already-approved mockup (→ /ui-implement), in-code UI fixes with no design decision (→ ui-expert agent), motion specs (→ /motion-design)."
|
|
6
7
|
---
|
|
7
8
|
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
Formato: [Keep a Changelog](https://keepachangelog.com/) · [SemVer](https://semver.org/).
|
|
4
4
|
|
|
5
|
+
## 1.1.2 — 2026-07-06
|
|
6
|
+
|
|
7
|
+
- **Feature-gate**: added `requires_feature: has_design_system` frontmatter — this skill is now linked into a consumer only when `features.has_design_system` is enabled (it already refused at runtime otherwise). Trims Codex's ~2% skill-description budget on projects without the feature. Install-time gate, portable across Claude + Codex.
|
|
8
|
+
|
|
5
9
|
## 1.1.1 — 2026-07-02
|
|
6
10
|
|
|
7
11
|
- Return Contract (SKILL.md): documentati i campi `bindings` + `lane_coverage`/`lanes`
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ui-implement
|
|
3
3
|
effort: medium
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.2
|
|
5
|
+
requires_feature: has_design_system
|
|
5
6
|
description: >
|
|
6
7
|
Implement an ALREADY-APPROVED mockup into the codebase end-to-end and
|
|
7
8
|
self-verify its fidelity. Takes a mockup in any channel — a Claude Design
|
|
@@ -634,7 +634,11 @@ async function runCard(cardId, cardPath) {
|
|
|
634
634
|
: `Phase 1 fallback: the architect specialist was unavailable — do the Phase 1 claim+architect exploration yourself per ${REF}/implement.md and persist the baseline to ${baselinePath} before coding.`
|
|
635
635
|
// T1.A (v4.90.0) — mockup cards: binding-compliance is verified deterministically by the
|
|
636
636
|
// check-bindings.mjs script (ui-implement Step 5b is the SSOT; this is the new2 parallel).
|
|
637
|
-
|
|
637
|
+
// Guard on has_design_system: component_bindings only exist when a /prd design
|
|
638
|
+
// phase (which requires the design system) produced them, so absent-DS + bindings
|
|
639
|
+
// is a malformed hand-authored card — don't emit a gate whose script path could
|
|
640
|
+
// resolve empty. Mirrors the has_design_system ds-gate guard above.
|
|
641
|
+
const bindingBit = features.has_design_system && node.hasMockup && node.hasBindings
|
|
638
642
|
? `\nBINDING GATE (deterministic — ui-implement Step 5b parallel): after building, run \`node "$(ls .claude/skills/ui-implement/scripts/check-bindings.mjs .framework/framework/.claude/skills/ui-implement/scripts/check-bindings.mjs 2>/dev/null | head -1)" --bindings-json '<the card component_bindings as JSON>' --files <your touched implementation files>\`. Exit 3 = a reuse/reuse-variant binding is NOT used in the code: the map is AUTHORITATIVE — fix it before returning (one pass). Include the script's one-line JSON output as bindingCheck in your return.\n`
|
|
639
643
|
: ''
|
|
640
644
|
let impl
|
package/package.json
CHANGED
|
@@ -1508,10 +1508,17 @@ async function configure(opts = {}) {
|
|
|
1508
1508
|
const enabledTools = (merged.tools && Array.isArray(merged.tools.enabled) && merged.tools.enabled.length)
|
|
1509
1509
|
? merged.tools.enabled : ['claude'];
|
|
1510
1510
|
if (fs.existsSync(path.join(cwd, '.framework'))) {
|
|
1511
|
-
new SymlinkUtils(cwd)
|
|
1511
|
+
const symlinks = new SymlinkUtils(cwd);
|
|
1512
|
+
symlinks.mergeAgents({ tools: enabledTools });
|
|
1513
|
+
// Also re-merge SKILLS: the feature-gate (src/utils/skill-gate.js) depends
|
|
1514
|
+
// on the `features.*` just written. On first install `mergeSkills` ran
|
|
1515
|
+
// fail-open BEFORE this config existed (linking everything), so this pass
|
|
1516
|
+
// is what actually prunes the gated-off skills. Without it, gating never
|
|
1517
|
+
// takes effect until the next `baldart update`.
|
|
1518
|
+
symlinks.mergeSkills({ tools: enabledTools });
|
|
1512
1519
|
}
|
|
1513
1520
|
} catch (err) {
|
|
1514
|
-
UI.warning(`Agent re-merge after configure failed (${err.message}) — run \`npx baldart doctor\` to heal.`);
|
|
1521
|
+
UI.warning(`Agent/skill re-merge after configure failed (${err.message}) — run \`npx baldart doctor\` to heal.`);
|
|
1515
1522
|
}
|
|
1516
1523
|
|
|
1517
1524
|
// v5.1.0 — research library: create + seed ${paths.research_dir} right after
|
package/src/commands/doctor.js
CHANGED
|
@@ -409,6 +409,47 @@ async function detectState(cwd, opts = {}) {
|
|
|
409
409
|
}
|
|
410
410
|
} catch (_) { /* never block doctor on probe */ }
|
|
411
411
|
|
|
412
|
+
// ---- Feature-gated skill drift (skill-gate) ------------------------
|
|
413
|
+
// A skill whose `requires_feature` frontmatter is no longer satisfied
|
|
414
|
+
// (feature flipped off in baldart.config.yml) may still be linked from a
|
|
415
|
+
// prior install. `configure` and `update` both re-merge (which prunes), but
|
|
416
|
+
// a user who edits the config and runs ONLY `doctor` should be told. The
|
|
417
|
+
// reverse (a feature just turned ON, gated skill not yet linked) is covered
|
|
418
|
+
// by the missing-skills path. Advisory only (fix = `reconcile-skills`, a
|
|
419
|
+
// plain re-merge); never blocks. Fully fail-safe.
|
|
420
|
+
state.gatedSkillsLinked = [];
|
|
421
|
+
try {
|
|
422
|
+
if (state.frameworkPresent) {
|
|
423
|
+
const { resolveSkillGate } = require('../utils/skill-gate');
|
|
424
|
+
const { getAdapter } = require('../utils/tool-adapters');
|
|
425
|
+
const frameworkSkillsDir = path.join(cwd, '.framework', 'framework', '.claude', 'skills');
|
|
426
|
+
const gate = resolveSkillGate(frameworkSkillsDir, cwd);
|
|
427
|
+
if (!gate.failOpen && gate.gatedOff.length) {
|
|
428
|
+
const toolsEnabled = (config && !config.__malformed && config.tools && Array.isArray(config.tools.enabled))
|
|
429
|
+
? config.tools.enabled : ['claude'];
|
|
430
|
+
const seen = new Set();
|
|
431
|
+
for (const toolName of toolsEnabled) {
|
|
432
|
+
let adapter;
|
|
433
|
+
try { adapter = getAdapter(toolName, cwd); } catch (_) { continue; }
|
|
434
|
+
const skillsRel = adapter.skillsDir();
|
|
435
|
+
for (const name of gate.gatedOff) {
|
|
436
|
+
if (seen.has(name)) continue;
|
|
437
|
+
const linkPath = path.join(cwd, skillsRel, name);
|
|
438
|
+
try {
|
|
439
|
+
const lst = fs.lstatSync(linkPath);
|
|
440
|
+
if (!lst.isSymbolicLink()) continue;
|
|
441
|
+
const resolved = path.resolve(path.dirname(linkPath), fs.readlinkSync(linkPath));
|
|
442
|
+
if (resolved === path.join(frameworkSkillsDir, name)) {
|
|
443
|
+
seen.add(name);
|
|
444
|
+
state.gatedSkillsLinked.push(name);
|
|
445
|
+
}
|
|
446
|
+
} catch (_) { /* absent */ }
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
} catch (_) { /* never block doctor on probe */ }
|
|
452
|
+
|
|
412
453
|
// ---- Workflow symlink integrity (since v4.14.0) --------------------
|
|
413
454
|
// Dynamic workflows (.js) are Claude-only and per-item symlinked into
|
|
414
455
|
// `.claude/workflows/`. Unlike agents there is NO hard discovery gate —
|
|
@@ -1086,6 +1127,30 @@ function planActions(state) {
|
|
|
1086
1127
|
});
|
|
1087
1128
|
}
|
|
1088
1129
|
|
|
1130
|
+
// Feature-gated skill drift (skill-gate). Advisory — these skills' gating
|
|
1131
|
+
// feature is now off, but they're still linked from a prior install; they'd
|
|
1132
|
+
// only refuse at runtime and waste the Codex skill-description budget. A plain
|
|
1133
|
+
// re-merge prunes them (same rationale as the agent/workflow repairs).
|
|
1134
|
+
if (state.gatedSkillsLinked && state.gatedSkillsLinked.length > 0) {
|
|
1135
|
+
actions.push({
|
|
1136
|
+
key: 'reconcile-skills',
|
|
1137
|
+
label: `Unlink ${state.gatedSkillsLinked.length} feature-gated skill(s)`,
|
|
1138
|
+
why: `These skills are still linked but their gating feature is now off in baldart.config.yml: ${state.gatedSkillsLinked.join(', ')}. They refuse at runtime and consume Codex's ~2% skill-description budget. Re-merging prunes them (re-enable the feature to bring them back).`,
|
|
1139
|
+
autoOk: true,
|
|
1140
|
+
run: async () => {
|
|
1141
|
+
const SymlinkUtils = require('../utils/symlinks');
|
|
1142
|
+
const cfg = loadConfig(process.cwd());
|
|
1143
|
+
const enabledTools = (cfg && !cfg.__malformed
|
|
1144
|
+
&& cfg.tools && Array.isArray(cfg.tools.enabled))
|
|
1145
|
+
? cfg.tools.enabled
|
|
1146
|
+
: ['claude'];
|
|
1147
|
+
const symlinks = new SymlinkUtils();
|
|
1148
|
+
symlinks.mergeSkills({ tools: enabledTools });
|
|
1149
|
+
UI.success(`Reconciled feature-gated skills for tools: ${enabledTools.join(', ')}`);
|
|
1150
|
+
},
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1089
1154
|
// Root primitives integrity (since v4.83.0). Regenerates AGENTS.md / CLAUDE.md
|
|
1090
1155
|
// from the versioned skeleton + config + overlay when missing, still a legacy
|
|
1091
1156
|
// symlink, or drifted. Offline + idempotent. A handwritten file is NOT touched
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skill-gate.js — install-time feature-gating for framework skills.
|
|
3
|
+
*
|
|
4
|
+
* BALDART ships ~40 skills; Codex reserves only ~2% of its context window for
|
|
5
|
+
* skill descriptions, so linking every skill (including ones whose gating
|
|
6
|
+
* `features.*` flag is OFF, which then refuse at runtime anyway) wastes that
|
|
7
|
+
* budget. This module decides, per install/update, WHICH framework skills to
|
|
8
|
+
* link into a consumer, based on an OPTIONAL `requires_feature` frontmatter
|
|
9
|
+
* field in each skill's `SKILL.md`.
|
|
10
|
+
*
|
|
11
|
+
* The gate is PURELY install-time (resolved here by the Node installer) — both
|
|
12
|
+
* Claude (`.claude/skills/`) and Codex (`.agents/skills/`) simply see fewer
|
|
13
|
+
* directories. Neither runtime interprets `requires_feature`; it is a BALDART
|
|
14
|
+
* authoring construct, ignored by the skill loaders exactly like `version:` /
|
|
15
|
+
* `effort:`. So the filter is natively portable across both tools and applied
|
|
16
|
+
* from a single code path (the gate is shared SEMANTICS, not runtime mechanics).
|
|
17
|
+
*
|
|
18
|
+
* Frontmatter contract (all OPTIONAL — a skill with no `requires_feature` is
|
|
19
|
+
* ALWAYS linked, backwards-compatible default):
|
|
20
|
+
* requires_feature: has_design_system # string → link iff true
|
|
21
|
+
* requires_feature: [has_design_system, has_e2e_review] # list = AND (all true)
|
|
22
|
+
*
|
|
23
|
+
* Fail-open everywhere (never hide a skill by accident — the safety net must
|
|
24
|
+
* not become a tribunal):
|
|
25
|
+
* - config absent / unreadable / malformed → link EVERYTHING.
|
|
26
|
+
* - a single skill's frontmatter malformed → link THAT skill (per-skill),
|
|
27
|
+
* never crash the whole merge.
|
|
28
|
+
*
|
|
29
|
+
* NOTE: we read `features.*` DIRECTLY from baldart.config.yml rather than reuse
|
|
30
|
+
* `agent-slots.resolveAgentFlags()` — that flag set intentionally covers only
|
|
31
|
+
* the keys the agent SLOT sources reference and omits `has_prd_workflow` /
|
|
32
|
+
* `has_e2e_review`, which ARE skill gates. Reading the raw block keeps the gate
|
|
33
|
+
* correct for every existing (and future) `features.*` key with no coupling.
|
|
34
|
+
*
|
|
35
|
+
* This introduces NO new `baldart.config.yml` key (it consumes existing
|
|
36
|
+
* `features.*`) — the schema-change propagation rule does NOT apply. It is
|
|
37
|
+
* skill-frontmatter metadata, like `version`/`effort`.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
const fs = require('fs');
|
|
41
|
+
const path = require('path');
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Load the `features` block from baldart.config.yml.
|
|
45
|
+
* @returns {{ features: Object|null }} `features: null` signals "config
|
|
46
|
+
* unavailable" → callers must fail-open (link everything).
|
|
47
|
+
*/
|
|
48
|
+
function loadFeatures(cwd) {
|
|
49
|
+
try {
|
|
50
|
+
const yaml = require('js-yaml');
|
|
51
|
+
const cfgPath = path.join(cwd, 'baldart.config.yml');
|
|
52
|
+
if (!fs.existsSync(cfgPath)) return { features: null };
|
|
53
|
+
const cfg = yaml.load(fs.readFileSync(cfgPath, 'utf8'));
|
|
54
|
+
if (!cfg || typeof cfg !== 'object') return { features: null };
|
|
55
|
+
const features = (cfg.features && typeof cfg.features === 'object') ? cfg.features : {};
|
|
56
|
+
return { features };
|
|
57
|
+
} catch (_) {
|
|
58
|
+
return { features: null };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Extract the OPTIONAL `requires_feature` from a skill's SKILL.md frontmatter.
|
|
64
|
+
* @returns {string[]|null} normalized list of required feature keys, or null
|
|
65
|
+
* when the skill is ungated / has no SKILL.md / frontmatter is unparseable
|
|
66
|
+
* (fail-open: null ⇒ always link).
|
|
67
|
+
*/
|
|
68
|
+
function readRequiredFeatures(skillDir) {
|
|
69
|
+
try {
|
|
70
|
+
const skillMd = path.join(skillDir, 'SKILL.md');
|
|
71
|
+
if (!fs.existsSync(skillMd)) return null;
|
|
72
|
+
const raw = fs.readFileSync(skillMd, 'utf8');
|
|
73
|
+
if (!raw.startsWith('---')) return null;
|
|
74
|
+
// Frontmatter is between the first two `---` fences.
|
|
75
|
+
const end = raw.indexOf('\n---', 3);
|
|
76
|
+
if (end === -1) return null;
|
|
77
|
+
const fmText = raw.slice(3, end);
|
|
78
|
+
const yaml = require('js-yaml');
|
|
79
|
+
const fm = yaml.load(fmText);
|
|
80
|
+
if (!fm || typeof fm !== 'object') return null;
|
|
81
|
+
const req = fm.requires_feature;
|
|
82
|
+
if (req == null) return null;
|
|
83
|
+
const list = (Array.isArray(req) ? req : [req])
|
|
84
|
+
.map(x => String(x).trim())
|
|
85
|
+
.filter(Boolean);
|
|
86
|
+
return list.length ? list : null;
|
|
87
|
+
} catch (_) {
|
|
88
|
+
// Malformed frontmatter for THIS skill → fail-open (link it).
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Decide which framework skills to link vs skip for this consumer.
|
|
95
|
+
*
|
|
96
|
+
* @param {string} frameworkSkillsDir absolute path to
|
|
97
|
+
* `.framework/framework/.claude/skills/`
|
|
98
|
+
* @param {string} cwd consumer repo root (where baldart.config.yml lives)
|
|
99
|
+
* @returns {{ toLink: string[], gatedOff: string[], failOpen: boolean }}
|
|
100
|
+
* `toLink` = skill dir names to link; `gatedOff` = skill dir names to skip
|
|
101
|
+
* (and prune if already linked); `failOpen` = true when config was
|
|
102
|
+
* unavailable so nothing was gated.
|
|
103
|
+
*/
|
|
104
|
+
function resolveSkillGate(frameworkSkillsDir, cwd) {
|
|
105
|
+
const result = { toLink: [], gatedOff: [], failOpen: false };
|
|
106
|
+
|
|
107
|
+
let names = [];
|
|
108
|
+
try {
|
|
109
|
+
names = fs.readdirSync(frameworkSkillsDir).filter(name => {
|
|
110
|
+
if (name.startsWith('.')) return false;
|
|
111
|
+
try { return fs.lstatSync(path.join(frameworkSkillsDir, name)).isDirectory(); }
|
|
112
|
+
catch (_) { return false; }
|
|
113
|
+
});
|
|
114
|
+
} catch (_) {
|
|
115
|
+
return result; // no framework skills dir → empty sets, caller handles
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const { features } = loadFeatures(cwd);
|
|
119
|
+
|
|
120
|
+
// Config unavailable → fail-open: link everything, gate nothing.
|
|
121
|
+
if (features === null) {
|
|
122
|
+
result.failOpen = true;
|
|
123
|
+
result.toLink = names.slice();
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (const name of names) {
|
|
128
|
+
const required = readRequiredFeatures(path.join(frameworkSkillsDir, name));
|
|
129
|
+
if (!required) { result.toLink.push(name); continue; }
|
|
130
|
+
// AND semantics: link iff EVERY required feature is explicitly true.
|
|
131
|
+
const allOn = required.every(f => features[f] === true);
|
|
132
|
+
if (allOn) result.toLink.push(name);
|
|
133
|
+
else result.gatedOff.push(name);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
module.exports = { resolveSkillGate, readRequiredFeatures, loadFeatures };
|
package/src/utils/symlinks.js
CHANGED
|
@@ -210,7 +210,7 @@ class SymlinkUtils {
|
|
|
210
210
|
*/
|
|
211
211
|
mergeSkills(opts = {}) {
|
|
212
212
|
const tools = (opts.tools && opts.tools.length) ? opts.tools : ['claude'];
|
|
213
|
-
const aggregate = { linked: [], skipped: [], conflicts: [] };
|
|
213
|
+
const aggregate = { linked: [], skipped: [], conflicts: [], pruned: [], gatedOff: [] };
|
|
214
214
|
|
|
215
215
|
const frameworkSkillsDir = path.join(this.cwd, FRAMEWORK_PAYLOAD, '.claude', 'skills');
|
|
216
216
|
if (!fs.existsSync(frameworkSkillsDir)) {
|
|
@@ -218,17 +218,31 @@ class SymlinkUtils {
|
|
|
218
218
|
return aggregate;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
221
|
+
// Feature-gate: link only the skills whose `requires_feature` frontmatter is
|
|
222
|
+
// satisfied by this consumer's `features.*` (fail-open when config is
|
|
223
|
+
// unavailable → link everything). Resolved ONCE and applied to EVERY tool
|
|
224
|
+
// from this single code path — the gate is shared semantics, so Claude and
|
|
225
|
+
// Codex stay identical by construction. See src/utils/skill-gate.js.
|
|
226
|
+
const gate = require('./skill-gate').resolveSkillGate(frameworkSkillsDir, this.cwd);
|
|
227
|
+
const frameworkSkills = gate.toLink;
|
|
228
|
+
aggregate.gatedOff = gate.gatedOff.slice();
|
|
229
|
+
if (gate.gatedOff.length) {
|
|
230
|
+
UI.info(`Feature-gate: ${gate.gatedOff.length} skill(s) skipped (gating feature off): ${gate.gatedOff.join(', ')}`);
|
|
231
|
+
}
|
|
226
232
|
|
|
227
233
|
for (const tool of tools) {
|
|
228
234
|
const result = this._mergeSkillsForTool(tool, frameworkSkills, frameworkSkillsDir);
|
|
229
235
|
aggregate.linked.push(...result.linked);
|
|
230
236
|
aggregate.skipped.push(...result.skipped);
|
|
231
237
|
aggregate.conflicts.push(...result.conflicts);
|
|
238
|
+
|
|
239
|
+
// Prune direction: remove any pre-existing framework symlink for a skill
|
|
240
|
+
// that is now gated-off (e.g. a feature flipped false after a prior
|
|
241
|
+
// install, or a fail-open install that linked everything before config
|
|
242
|
+
// existed). Only OUR framework symlinks are removed — never a real user
|
|
243
|
+
// skill dir, never a user-override symlink pointing elsewhere.
|
|
244
|
+
const pruneResult = this._pruneGatedSkillsForTool(tool, gate.gatedOff, frameworkSkillsDir);
|
|
245
|
+
aggregate.pruned.push(...pruneResult.pruned);
|
|
232
246
|
}
|
|
233
247
|
|
|
234
248
|
if (aggregate.conflicts.length > 0) {
|
|
@@ -329,6 +343,54 @@ class SymlinkUtils {
|
|
|
329
343
|
return result;
|
|
330
344
|
}
|
|
331
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Prune framework skill symlinks that are now gated-off — internal.
|
|
348
|
+
*
|
|
349
|
+
* This is the reconcile (removal) direction of the feature-gate: turning a
|
|
350
|
+
* `features.*` flag off (or a fail-open first install that linked everything
|
|
351
|
+
* before config existed) must actually REMOVE the stale symlinks, or the
|
|
352
|
+
* skill-description budget never shrinks. Each `name` in `gatedOff` is a
|
|
353
|
+
* framework skill name by construction (it came from the framework catalog),
|
|
354
|
+
* so basename∈catalog is guaranteed; we still verify the link RESOLVES to our
|
|
355
|
+
* framework source for that exact skill before unlinking. Safety invariants:
|
|
356
|
+
* - never touch a real directory (a user-authored skill of the same name);
|
|
357
|
+
* - never touch a symlink pointing ANYWHERE ELSE (a user override, cf. the
|
|
358
|
+
* `user-symlink-override` branch in _mergeSkillsForTool).
|
|
359
|
+
*/
|
|
360
|
+
_pruneGatedSkillsForTool(toolName, gatedOff, frameworkSkillsDir) {
|
|
361
|
+
const { getAdapter } = require('./tool-adapters');
|
|
362
|
+
const adapter = getAdapter(toolName, this.cwd);
|
|
363
|
+
const result = { pruned: [] };
|
|
364
|
+
if (!gatedOff || !gatedOff.length) return result;
|
|
365
|
+
|
|
366
|
+
const skillsRel = adapter.skillsDir();
|
|
367
|
+
const userSkillsDir = path.join(this.cwd, skillsRel);
|
|
368
|
+
if (!fs.existsSync(userSkillsDir)) return result;
|
|
369
|
+
|
|
370
|
+
for (const name of gatedOff) {
|
|
371
|
+
const linkPath = path.join(userSkillsDir, name);
|
|
372
|
+
let lstat = null;
|
|
373
|
+
try { lstat = fs.lstatSync(linkPath); } catch (_) { continue; /* absent */ }
|
|
374
|
+
|
|
375
|
+
// Only ever remove a symlink — a real dir here is a user-authored skill.
|
|
376
|
+
if (!lstat.isSymbolicLink()) continue;
|
|
377
|
+
|
|
378
|
+
const current = fs.readlinkSync(linkPath);
|
|
379
|
+
const resolved = path.resolve(path.dirname(linkPath), current);
|
|
380
|
+
const frameworkTarget = path.join(frameworkSkillsDir, name);
|
|
381
|
+
|
|
382
|
+
// Unlink ONLY when it resolves to OUR framework source for this skill.
|
|
383
|
+
// A symlink pointing elsewhere is a deliberate user override → leave it.
|
|
384
|
+
if (resolved === frameworkTarget) {
|
|
385
|
+
fs.unlinkSync(linkPath);
|
|
386
|
+
UI.info(`[${adapter.label}] Skill unlinked (feature gated off): ${path.join(skillsRel, name)}`);
|
|
387
|
+
result.pruned.push({ tool: toolName, name });
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return result;
|
|
392
|
+
}
|
|
393
|
+
|
|
332
394
|
// -----------------------------------------------------------------------
|
|
333
395
|
// Public composite operations
|
|
334
396
|
// -----------------------------------------------------------------------
|
|
@@ -827,7 +889,10 @@ class SymlinkUtils {
|
|
|
827
889
|
UI.warning(`[${adapter.label}] Legacy bulk skills symlink at ${skillsRel}. Run \`npx baldart update\` to convert to per-item.`);
|
|
828
890
|
allValid = false;
|
|
829
891
|
} else {
|
|
830
|
-
|
|
892
|
+
// Sample ONLY always-linked skills — never feature-gated ones (e.g.
|
|
893
|
+
// prd/capture), which are legitimately absent when their feature is
|
|
894
|
+
// off and would make this probe brittle / falsely alarming.
|
|
895
|
+
const sample = ['skill-creator', 'bug', 'simplify', 'overlay', 'find-skills'];
|
|
831
896
|
let frameworkLinks = 0;
|
|
832
897
|
sample.forEach(name => {
|
|
833
898
|
const p = path.join(skillsDir, name);
|