@adia-ai/adia-ui-factory 0.8.48 → 0.8.51

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adia-ui-kit-factory",
3
- "version": "0.8.48",
3
+ "version": "0.8.51",
4
4
  "description": "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework \u2014 orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation.",
5
5
  "author": {
6
6
  "name": "Kim",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adia-ui-kit-factory",
3
- "version": "0.8.48",
3
+ "version": "0.8.51",
4
4
  "description": "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework — orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation.",
5
5
  "author": {
6
6
  "name": "Kim",
package/.mcp.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "mcpServers": {
3
3
  "adia-gen-ui": {
4
4
  "command": "npx",
5
- "args": ["-y", "@adia-ai/mcp@0.8.48", "gen-ui"]
5
+ "args": ["-y", "@adia-ai/mcp@0.8.51", "gen-ui"]
6
6
  }
7
7
  }
8
8
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog — adia-ui-kit-factory
2
2
 
3
+ ## [0.8.51] — 2026-08-24
4
+
5
+ ### Added
6
+ - Hermes + Pi external-distribution manifests (gh#1954) — `plugin.yaml` +
7
+ `__init__.py` + `hermes-mcp.yaml` (Hermes) and `prompts/*.md` + a `"pi"`
8
+ field in `package.json` (Pi), derived from `.claude-plugin/plugin.json` +
9
+ `skills/` + `commands/` + `.mcp.json` via `npm run
10
+ build:harness-manifests` (`check:harness-manifests-fresh` gates
11
+ freshness). Additive: the Claude Code plugin surface is unchanged.
12
+ **MCP caveat, not parity:** Hermes MCP is user-level config — merge the
13
+ generated `hermes-mcp.yaml` into `~/.hermes/config.yaml` by hand. Pi core
14
+ has no native MCP — the `adia-gen-ui` server only reaches Pi through the
15
+ third-party `pi-mcp-adapter` reading this plugin's `.mcp.json` unmodified.
16
+ See the README's "Non-Claude harnesses" section before promising Pi/Hermes
17
+ parity for this plugin's headline MCP-wiring feature.
18
+
19
+ ### Maintenance
20
+ - **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
21
+ - **`scripts/` touched in this release window** (2 file(s), e.g. `scripts/adia-contract-check.mjs`) — carried by the entries above.
22
+
23
+ ## [0.8.50] — 2026-08-23
24
+
25
+ ### Maintenance
26
+ - **Lockstep bump riding the v0.8.50 cut** (`scripts/release/check-lockstep.mjs`); the headline v0.8.50 work ships in @adia-ai/web-components (prose/verse sheet removal gh#1885 / PR #1918, input/search fill-default posture gh#1856 — see `packages/web-components/CHANGELOG.md#0850--2026-08-23`). Package-local changes are listed below.
27
+ - **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
28
+ - **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
29
+
30
+ ### Docs
31
+ - **`references/` + `skills/` docs refreshed for the release window** — `component-model.md` and `spa-architecture.md` plus the `find-unused` (`recon-and-audits.md`), `host-wiring` (`SKILL.md`, `mode-wiring-detail.md`), and `table-composition` (`base-table.md`) skill references updated to current framework contracts (incl. the gh#1918 `[prose]`/`[verse]` removal and gh#1856 fill-default posture).
32
+
3
33
  ## [0.8.48] — 2026-08-23
4
34
 
5
35
  ### Added
package/README.md CHANGED
@@ -98,7 +98,7 @@ surface-qa-agent) · `component-model.md` · the `references/contracts/` twins.
98
98
 
99
99
  ## MCP
100
100
 
101
- `adia-gen-ui` server pinned: `@adia-ai/mcp@0.8.48` (tool SoT:
101
+ `adia-gen-ui` server pinned: `@adia-ai/mcp@0.8.51` (tool SoT:
102
102
  `packages/gen-ui/mcp/TOOLS.md` — the `gen-ui` section; stability rule in
103
103
  `references/contracts/`; pin lives in `.mcp.json` — `check:plugin-count-claims`
104
104
  guards this README copy against it). The server is `adia-mcp gen-ui`,
@@ -108,6 +108,35 @@ before either ever published; the pin name and the `mcpServers` key flipped
108
108
  in the P7 release commit per the 2026-08-14 config-key ruling — new entries
109
109
  for the other subcommands use `adia-protocol` / `adia-factory`).
110
110
 
111
+ ## Non-Claude harnesses
112
+
113
+ External install beyond Claude Code (AGENTS.md's own "Non-Claude harnesses"
114
+ section carries the shared degradation table this summarizes). **The MCP
115
+ story is the one place neither non-Claude harness matches Claude Code's
116
+ plug-and-play install** — read this section before promising Pi/Hermes
117
+ support for this plugin specifically, since its whole value proposition is
118
+ wiring the `adia-gen-ui` MCP server.
119
+
120
+ - **Codex** (gh#1888): `.codex-plugin/plugin.json` + per-skill
121
+ `agents/openai.yaml`, derived; `mcpServers` points at the same
122
+ `.mcp.json` Claude Code uses. No manifest key for hooks/commands/agents —
123
+ a command degrades to invoking its skill directly.
124
+ - **Hermes** (gh#1954): `plugin.yaml` + `__init__.py` at this package's
125
+ root — point Hermes's plugin loading at the installed
126
+ `node_modules/@adia-ai/adia-ui-factory` directory. **MCP is user-level
127
+ config in Hermes, not a plugin field** — merge the generated
128
+ `hermes-mcp.yaml` into your own `~/.hermes/config.yaml` by hand; there is
129
+ no automatic wiring. No hooks/agents surface.
130
+ - **Pi** (gh#1954): discovered via the `pi-package` keyword and the `"pi"`
131
+ field in `package.json`; `prompts/*.md` gives every `/command` a genuine
132
+ Pi prompt template. **Pi core has no native MCP support at all** — the
133
+ `adia-gen-ui` server only reaches Pi through the third-party
134
+ [`pi-mcp-adapter`](https://pi.dev) package, which reads this plugin's
135
+ `.mcp.json` unmodified (same shape, no substitution needed). Install that
136
+ adapter first; without it, this plugin's headline feature — the a2ui MCP
137
+ wiring — silently does nothing on a stock Pi install. No hooks/agents
138
+ surface.
139
+
111
140
  ## Requirements
112
141
 
113
142
  Python 3 (hook) + Node 22. Installs standalone in consumer repos (no monorepo
package/__init__.py ADDED
@@ -0,0 +1,97 @@
1
+ """adia-ui-kit-factory — generated by scripts/build/harness-manifests.mjs. Do not edit."""
2
+ import os
3
+
4
+ _HERE = os.path.dirname(os.path.abspath(__file__))
5
+
6
+
7
+ def register(ctx):
8
+ ctx.register_skill(
9
+ name="app-audit",
10
+ path=os.path.join(_HERE, "skills", "app-audit", "SKILL.md"),
11
+ description="Diagnose an EXISTING AdiaUI consumer codebase before touching it — recon, classify manifest/version/spec/capability drift, root-cause a DRIFT-caused wrong-output symptom via four-layer triage (skill/codebase/substrate/spec) instead of patching the surface. Use for \"audit this UI\", \"is this up to date\", \"what should we migrate\", \"doesn't match the reference\", \"looks unstyled/off-brand\", or a suspected-drifted @adia-ai/* consumer's version health check. Produces a ranked, evidence-cited report; hands CONFIRMED sweeps to a builder. NOT greenfield planning (app-planning) or confirmed migration (app-migration); NOT a live blank-screen/console-error check (surface-qa) or chart-specific render behavior (chart-selection); NOT a duplication review (project-scaffolding).",
12
+ )
13
+ ctx.register_skill(
14
+ name="app-migration",
15
+ path=os.path.join(_HERE, "skills", "app-migration", "SKILL.md"),
16
+ description="Migrates a consumer adia-ui app. Use when asked to \"migrate to v0.X\", \"upgrade @adia-ai/web-components\", \"fix breaking changes\", \"sweep deprecated attributes\", port @agent-ui-kit/foreign code, or change SPA↔SSR mode — grep audit, mechanical sweep, verify gates. NOT for authoring the MIGRATION GUIDE or cutting releases (package-release, forge plugin).",
17
+ )
18
+ ctx.register_skill(
19
+ name="app-planning",
20
+ path=os.path.join(_HERE, "skills", "app-planning", "SKILL.md"),
21
+ description="Cold-start router for adia-ui consumer-app work. Use when the user asks to \"build/start an adia-ui app\", \"add a surface\", \"orient in this repo\" — run FIRST, also on any task whose mode/shape/shell is undecided — classifies four axes on cited signals, emits an Orientation Record, routes onward. NOT for framework-monorepo internals (adia-ui-forge).",
22
+ )
23
+ ctx.register_skill(
24
+ name="chart-selection",
25
+ path=os.path.join(_HERE, "skills", "chart-selection", "SKILL.md"),
26
+ description="Which adia-ui component renders a chart, graph, sparkline, gauge, or heatmap, and how to wire its data/legend/theming. Use for \"add a chart\", \"show a graph\", \"visualize this data\", \"add a sparkline/gauge/heatmap\", or \"wire a chart legend\". NOT for the surrounding screen (screen-composition), color tokens (token-selection), or data fetching (data-wiring).",
27
+ )
28
+ ctx.register_skill(
29
+ name="data-wiring",
30
+ path=os.path.join(_HERE, "skills", "data-wiring", "SKILL.md"),
31
+ description="Data, state & hydration in an adia-ui app. Use for \"wire up the data\" / \"manage state\" / \"hydrate the island\": pattern choice (signals · shared app store · Service/Command · DataClient · property-API · data-*), hydration per mode (SPA/SSR/hybrid). NOT for host bootstrap/registration (host-wiring) or screen markup (screen-composition).",
32
+ )
33
+ ctx.register_skill(
34
+ name="domain-planning",
35
+ path=os.path.join(_HERE, "skills", "domain-planning", "SKILL.md"),
36
+ description="Reasons through a PRD, spec, mockup, schema, or role/user-story BEFORE any UI is chosen — resolves intent, domain entities, roles, tasks, and decisions, then scores a semantic wireframe against the gate dimensions. Use when the input is spec-shaped (not a signed-off wireframe) and no Domain Plan exists yet for it. Preloaded by app-planning-agent; produces the Orientation Record's Domain Plan block. NOT for composing the UI itself (screen-composition) or classifying rendering mode/shape/shell (app-planning).",
37
+ )
38
+ ctx.register_skill(
39
+ name="find-unused",
40
+ path=os.path.join(_HERE, "skills", "find-unused", "SKILL.md"),
41
+ description="Finds what a non-breaking @adia-ai upgrade shipped that this app never picked up — the opt-in layers nothing imports, the local workarounds a fixed bug made redundant, retired enum values still sitting in stored state, and the fixes that never reached MIGRATION.md. Use after \"we bumped and nothing broke\", \"what are we missing from 0.8.x\", \"are we actually using what we ship\", \"why is [scale]/[theme] doing nothing\", or when a PATCH span needs adoption rather than repair. NOT for breaking-change repair (app-migration); NOT for a first-time install (adia-scaffold); NOT for diagnosing a broken surface (app-audit).",
42
+ )
43
+ ctx.register_skill(
44
+ name="gen-ui-wiring",
45
+ path=os.path.join(_HERE, "skills", "gen-ui-wiring", "SKILL.md"),
46
+ description="Authors a runtime generative-UI experience on the a2ui runtime — mounts a2ui-root/gen-root, wires generate_ui + data resolvers, grounds generation in a corpus (harvest chunks from our own pages, point retrieval at it). Use when END USERS generate UI at runtime. NOT for one-shot 'generate UI for X' screen asks (screen-composition), chat features (llm-wiring), or pipeline/MCP tool ops (adia-forge's a2ui-maintenance).",
47
+ )
48
+ ctx.register_skill(
49
+ name="host-wiring",
50
+ path=os.path.join(_HERE, "skills", "host-wiring", "SKILL.md"),
51
+ description="Wires an adia-ui app's host — picks SPA/SSR/hybrid, then that mode's registration, CSS cascade, routing ownership. Use for \"wire the host\", \"set up index.html\", unstyled or non-upgrading elements, \"HTMLElement is not defined\" under Next/Nuxt/SvelteKit/Astro. NOT for screen UI (screen-composition), data/state (data-wiring), app layout (project-scaffolding).",
52
+ )
53
+ ctx.register_skill(
54
+ name="llm-wiring",
55
+ path=os.path.join(_HERE, "skills", "llm-wiring", "SKILL.md"),
56
+ description="The @adia-ai/llm client contract — chat/streamChat streaming, StreamChunk, smart-proxy vs passthrough key security, chat-shell wiring. Use when adding a chat box or any LLM feature to an adia-ui app. NOT for runtime gen-UI (gen-ui-wiring), one-shot intent→screen composition (screen-composition), or @adia-ai/llm internals (adia-forge's llm-client-maintenance).",
57
+ )
58
+ ctx.register_skill(
59
+ name="pattern-catalog",
60
+ path=os.path.join(_HERE, "skills", "pattern-catalog", "SKILL.md"),
61
+ description="Index of adia-ui's pre-assembled surfaces — hundreds of patterns and template screens (auth, registration, onboarding, settings, dashboards) — see references/pattern-index.md for the current count. Use BEFORE composing any screen from primitives, or when asked \"is there an existing pattern/template for X\", \"start from a pattern\", \"what patterns exist\". Answers and points at source — NOT for composing new UI (screen-composition), shell chrome (shell-selection), or authoring new patterns (primitive-authoring).",
62
+ )
63
+ ctx.register_skill(
64
+ name="project-scaffolding",
65
+ path=os.path.join(_HERE, "skills", "project-scaffolding", "SKILL.md"),
66
+ description="Lays out an adia-ui app's on-disk structure — picks the project shape (single-surface / rollup / shared-foundation) and page form, scaffolds via scripts/adia-scaffold, gates on the structure rubric. Use for \"scaffold an app\", \"add a page/surface/component\" WHEN mode/shape are already decided, \"audit its structure\". If mode/shape/shell are still undecided, app-planning classifies first. NOT for the UI inside a screen (screen-composition) or host wiring (host-wiring).",
67
+ )
68
+ ctx.register_skill(
69
+ name="screen-composition",
70
+ path=os.path.join(_HERE, "skills", "screen-composition", "SKILL.md"),
71
+ description="Composes adia-ui screens from light-DOM catalog primitives — discovers tags/props via the a2ui MCP, themes via --a-* tokens. Use when asked to build or 'generate UI for' a screen, page, form, dashboard, or nav pattern, or when a PRD/spec/mockup needs UI. NOT for shell chrome (shell-selection), host wiring (host-wiring), runtime gen-UI (gen-ui-wiring).",
72
+ )
73
+ ctx.register_skill(
74
+ name="shell-selection",
75
+ path=os.path.join(_HERE, "skills", "shell-selection", "SKILL.md"),
76
+ description="Picks and composes an adia-ui page-chrome shell (@adia-ai/web-modules): admin (app frame), chat (LLM), editor (canvas+panes), simple (marketing/error), or embed. Use when asked to \"use a shell\", \"sidebar + topbar layout\", \"embed this surface\", or on shell markup debugging. NOT for screens inside it (screen-composition) or host/SSR wiring (host-wiring).",
77
+ )
78
+ ctx.register_skill(
79
+ name="surface-qa",
80
+ path=os.path.join(_HERE, "skills", "surface-qa", "SKILL.md"),
81
+ description="Browser-QA gate for a CONSUMER app's adia-ui surfaces — renders headless with zero console/page errors, non-zero bounding boxes, and a screenshot actually read, plus AdiaUI a11y checks. Use when shipping a surface, on \"verify/QA this page\" or a page that renders blank/empty, or when \"tests pass\" is the only evidence. NOT for composing/fixing UI (screen-composition), structural lint (adia-lint hook), or the framework repo's own dogfood/ demo-page sweep (forge's demo-audit).",
82
+ )
83
+ ctx.register_skill(
84
+ name="table-composition",
85
+ path=os.path.join(_HERE, "skills", "table-composition", "SKILL.md"),
86
+ description="Answers how to display/organize tabular data with table-ui: contained vs uncontained chrome, [raw] (consumer-owned body, a separate axis), opt-in striped rows, and resize/sort defaults that flip between JS .columns and declarative col-def forms, plus inline-edit-grid and tree/hierarchical-row patterns. Use for \"how do I show a table\", \"add sorting/filtering\", \"make this table striped\", \"columns aren't resizable\", \"card vs bare table\", \"editable/spreadsheet grid\", \"tree table / nested rows\", \"huge list of rows\". NOT for composing the surrounding screen (screen-composition), data wiring (data-wiring), OTHER non-table patterns (pattern-catalog), or 1000+-row virtualized lists (list-window-ui).",
87
+ )
88
+ ctx.register_skill(
89
+ name="token-selection",
90
+ path=os.path.join(_HERE, "skills", "token-selection", "SKILL.md"),
91
+ description="Answers which color token/role an adia-ui surface should use — role grammar, pairing laws, state families, the --a-* alias layer. Use when asked \"which token for this background/text/hover/border\" or \"--a-* or --md-sys-color-*\". ANSWERS only. NOT for composing the screen (screen-composition) or designing/verifying palettes (framework-side).",
92
+ )
93
+ ctx.register_skill(
94
+ name="wave-coordination",
95
+ path=os.path.join(_HERE, "skills", "wave-coordination", "SKILL.md"),
96
+ description="Team-pattern mechanics for a multi-screen build under ui-architect's GEAR 2 — who merges parallel screen-composition-agent branches and when, PR/branch mechanics, shared-file conflict rules for shared-foundation shapes, and how to size a wave. Use when planning or running a GEAR-2 dispatch: \"how do two parallel screen-composition-agent branches come back together\", \"who merges\", \"how big should a wave be\", \"what happens when two screens touch the same shared file\". NOT for picking a project's shape (project-scaffolding, which this skill's conflict rules build on) or composing one screen (screen-composition).",
97
+ )
@@ -0,0 +1,10 @@
1
+ # Generated from .mcp.json — Hermes MCP is user-level config,
2
+ # not a plugin manifest field. Merge this fragment into your own
3
+ # ~/.hermes/config.yaml (see this plugin's README, "Non-Claude harnesses").
4
+ mcp_servers:
5
+ adia-gen-ui:
6
+ command: "npx"
7
+ args:
8
+ - "-y"
9
+ - "@adia-ai/mcp@0.8.51"
10
+ - "gen-ui"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/adia-ui-factory",
3
- "version": "0.8.48",
3
+ "version": "0.8.51",
4
4
  "description": "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework — orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation.",
5
5
  "keywords": [
6
6
  "adia-ui",
@@ -8,7 +8,8 @@
8
8
  "gen-ui",
9
9
  "web-components",
10
10
  "app-authoring",
11
- "consumer"
11
+ "consumer",
12
+ "pi-package"
12
13
  ],
13
14
  "license": "MIT",
14
15
  "author": {
@@ -18,15 +19,23 @@
18
19
  "bugs": {
19
20
  "email": "kim.granlund@adia.ai"
20
21
  },
22
+ "pi": {
23
+ "skills": ["./skills"],
24
+ "prompts": ["./prompts"]
25
+ },
21
26
  "files": [
22
27
  ".claude-plugin",
23
28
  ".codex-plugin",
24
29
  "skills",
25
30
  "agents",
26
31
  "commands",
32
+ "prompts",
27
33
  "references",
28
34
  "scripts",
29
35
  "hooks",
36
+ "plugin.yaml",
37
+ "__init__.py",
38
+ "hermes-mcp.yaml",
30
39
  ".mcp.json",
31
40
  "README.md",
32
41
  "CHANGELOG.md",
package/plugin.yaml ADDED
@@ -0,0 +1,4 @@
1
+ name: "adia-ui-kit-factory"
2
+ version: "0.8.51"
3
+ description: "Author and verify apps built ON the adia-ui (@adia-ai) light-DOM web-component framework — orient, scaffold, compose, wire, verify, and migrate across SPA and SSR rendering modes. Wires the a2ui MCP for catalog retrieval, UI generation, and validation."
4
+ manifest_version: 1
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: "Probe this repo's adia-ui project context — declared vs installed versions, rendering-mode/framework signals, shells in use, theming knobs, MCP pin, monorepo misroute flag."
3
+ argument-hint: "[dir]"
4
+ ---
5
+
6
+ Probe the project context. **$ARGUMENTS**
7
+
8
+ Run `python3 "${CLAUDE_PLUGIN_ROOT}/scripts/adia-info" $ARGUMENTS` and present
9
+ the JSON as a short read: each field with the signal it derived from and
10
+ the decision it drives (screen-composition §Current project context carries the
11
+ field→decision table). An `installedVersion` that is null or mismatched vs
12
+ `adiaPackages` means `npm install` before trusting anything else;
13
+ `isFrameworkMonorepo: true` means this is framework territory — route to
14
+ the adia-ui-forge plugin, not this one.
@@ -0,0 +1,15 @@
1
+ ---
2
+ description: "Migrate consumer code across @adia-ai versions (or port a foreign codebase to adia-ui) — guide-driven audit, per-cluster consent, verify gates, Migration Report."
3
+ argument-hint: "[target version]"
4
+ ---
5
+
6
+ Migrate this codebase. **$ARGUMENTS**
7
+
8
+ Invoke **`app-migration`**: read the shipped guide for the span
9
+ (`node_modules/@adia-ai/web-components/MIGRATION.md` — install/upgrade the
10
+ target version first so the guide covers it; monorepo canon:
11
+ `.claude/docs/MIGRATION GUIDE.md`), audit every breaking item with counts
12
+ before any change, present the per-cluster consent options (sweep ·
13
+ show-diff · skip · manual — a blanket approval never covers judgment
14
+ items), run the verify gates, and return the Migration Report (the skill's
15
+ §Deliverable) with every cluster's consent decision recorded.
@@ -0,0 +1,12 @@
1
+ ---
2
+ description: "Orient in an existing adia-ui app — classify rendering mode, project shape, shell, and state; produce the Orientation Record."
3
+ argument-hint: "[path or question]"
4
+ ---
5
+
6
+ Orient in this repo. **$ARGUMENTS**
7
+
8
+ Invoke **`app-planning`** and produce the Orientation Record in the skill's
9
+ own contract shape (Rendering mode · Project shape · Shell · Task ·
10
+ Screen plan (start mode) · → Route · Verify target · Open questions), each
11
+ axis citing its signal. Self-check the record with
12
+ `python3 "${CLAUDE_PLUGIN_ROOT}/scripts/record-lint" -` before returning it.
@@ -0,0 +1,23 @@
1
+ ---
2
+ description: "Find what a clean @adia-ai upgrade left unused — opt-in layers nothing imports, workarounds a fixed bug made redundant, retired values in stored state, shipped advice that is wrong for this app."
3
+ argument-hint: "[target version]"
4
+ ---
5
+
6
+ Find what this app is not using. **$ARGUMENTS**
7
+
8
+ Invoke **`find-unused`**: enumerate every `@adia-ai/*` package each manifest
9
+ declares (never assume `web-components`), recon the three versions that differ
10
+ per package — declared range, installed, and the target: **the version in
11
+ `$ARGUMENTS` when one was given, registry latest only as the default**. Read
12
+ every `node_modules/@adia-ai/*/CHANGELOG.md` section across the
13
+ installed→target span, then run all four audits — inert opt-in layers, redundant local
14
+ workarounds, retired enum values sitting in persisted state, and shipped
15
+ advice that does not fit this app. Report counts and file:line before
16
+ proposing any edit, split mechanical items from judgment items and take
17
+ consent per item, verify each adoption with a check that would fail on
18
+ revert, and write the Adoption Record.
19
+
20
+ Reach for **`app-migration`** instead when the span carries actual breaking
21
+ changes — that skill is guide-driven and sweeps them with per-cluster
22
+ consent. This one runs when nothing broke and the question is what went
23
+ unused.
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: "Author a generative-UI experience — wire the a2ui runtime, generate_ui/refine_ui via the MCP, choose core vs custom corpus."
3
+ argument-hint: "[experience or surface]"
4
+ ---
5
+
6
+ Build a gen-UI experience. **$ARGUMENTS**
7
+
8
+ Invoke **`gen-ui-wiring`** and run its generate → validate → render → iterate
9
+ loop; generated A2UI is validated as data before it is serialized, and the
10
+ run closes with the Generation Record (the skill's §Deliverable) — the
11
+ validate-before-serialize slot filled, never skipped silently.
@@ -0,0 +1,13 @@
1
+ ---
2
+ description: "Scaffold a new adia-ui app, page, or component skeleton (scripts/adia-scaffold emits the mechanical skeleton; the skill owns the shape decisions)."
3
+ argument-hint: "[spa|ssr|page|component|inventory|selftest] [name or app-root]"
4
+ ---
5
+
6
+ Scaffold an adia-ui surface. **$ARGUMENTS**
7
+
8
+ Invoke **`project-scaffolding`** to classify the project shape and target, then use
9
+ `${CLAUDE_PLUGIN_ROOT}/scripts/adia-scaffold` for the mechanical skeleton. Shapes
10
+ the bin doesn't one-shot (rollup, shared-foundation) are composed per the
11
+ project-shapes reference. `inventory <app-root>` scores an existing app
12
+ against the structure rubric (4 mechanized gates with cited paths + the 2
13
+ judgment rows the model completes).
@@ -0,0 +1,20 @@
1
+ ---
2
+ description: "Run the consumer-side exit gate on a composed surface — dispatches the read-only surface-qa-agent seat; returns the completed VerifyProof (ship | hold)."
3
+ argument-hint: "[url or surface] [key selectors]"
4
+ ---
5
+
6
+ Verify a composed surface. **$ARGUMENTS**
7
+
8
+ Dispatch the **surface-qa-agent** agent on the named surface/URL — the exit
9
+ gate runs ISOLATED from whatever context built the surface (generator ≠
10
+ critic). It runs the shipped probe (`scripts/adia-probe.mjs`: console/page
11
+ errors, bounding boxes, the deviceScaleFactor:2 capture), completes the
12
+ judgment half (`imageRead` carries what the pixels actually show; the a11y
13
+ row checked item by item), and returns the VerifyProof (surface-qa
14
+ §Deliverable) with verdict **ship | hold**.
15
+
16
+ Findings route back to the owning builder skill (screen-composition · data-wiring ·
17
+ shell-selection · host-wiring) — never fix inline from this command. No running
18
+ app or missing Playwright → the gate reports UNMEASURED with the reason,
19
+ never a silent pass. (Mid-build self-checks still invoke the `surface-qa`
20
+ skill inline; this command is the independent verdict at definition-of-done.)
@@ -119,7 +119,7 @@ Three levels, all `--a-*`:
119
119
 
120
120
  ## "Registers" — typographic treatments, opt-in
121
121
 
122
- A _register_ (e.g. `verse`, `prose`) is a typographic treatment applied two ways together: **link the register stylesheet** (`styles/verse.css`) **and** put the **attribute** on the surface (`<my-surface verse>`). One without the other is a no-op — a common smell. Body/UI text defaults to `--a-font-family-ui`; registers opt a subtree into a different family/rhythm.
122
+ A _register_ (e.g. `scale="ui-sm"`, `scale="content-md"`) is a typographic treatment applied two ways together: **link the register stylesheet** (`styles/scale.css`, or wrap the surface in `<theme-provider scale="…">`, which adopts it on demand) **and** put the **attribute** on the surface. One without the other is a no-op — a common smell. Body/UI text defaults to `--a-font-family-ui`; registers opt a subtree into a different family/rhythm.
123
123
 
124
124
  ## Layout realities
125
125
 
@@ -53,7 +53,7 @@ alternatives:
53
53
  - **One registration script** — the side-effecting barrel `import '@adia-ai/web-components'`. Don't piecemeal-import primitives: composites render internal `*-ui` tags (e.g. `chat-input-ui` internally renders `textarea-ui` + `select-ui`) that stay unregistered and collapse to 0px unless the barrel ran.
54
54
  - Bespoke shell children need the **cluster barrel** (`@adia-ai/web-modules/shell`, `/chat`, `/editor`, `/simple`) — importing `admin-shell.js` alone registers only the host tag, not `admin-sidebar` / `admin-page` / the other children.
55
55
  - Never hand-roll `:where(html,body){}` — the foundation owns the page frame; re-rolling it drifts from the system (the classic serif-leak bug).
56
- - A `[scale]` register has two halves: link `scale.css` **and** put `scale="…"` (one of the six tiers — `ui-sm | ui-md | ui-lg | content-sm | content-md | content-lg`) on the surface. One without the other is a no-op. `[verse]`/`[prose]` still work as deprecated aliases (`ui-sm`/`content-md`) but are removed at v1.0 — new code should reach for `[scale]` directly.
56
+ - A `[scale]` register has two halves: link `scale.css` **and** put `scale="…"` (one of the six tiers — `ui-sm | ui-md | ui-lg | content-sm | content-md | content-lg`) on the surface. One without the other is a no-op.
57
57
  - `themes.css` (named palettes) is **not** in the styles barrel — link it separately. `data-scheme` switches light/dark; `data-theme` picks the named palette.
58
58
  - Guards: `defineIfFree(tag, ctor)` (`core/register.js`) for defines; a `#booted` flag in `connected()` — the callback re-fires on DOM moves. And `customElements.whenDefined(name)` never rejects: a `Promise.all([...whenDefined])` boot gate hangs forever on one unimported tag — chrome renders (tag-keyed CSS), the page stays "empty", zero console errors.
59
59
 
@@ -12,10 +12,10 @@
12
12
  // tag — see resolveA2uiDoc()).
13
13
  //
14
14
  // v1 scope (stated honestly, per the SPEC's Non-goals): attribute names +
15
- // enum values on `*-ui` tags; class/id/slot/style/role/data-*/aria-*/
16
- // framework-global attributes allowlisted; one content-model rule — raw <thead>/
17
- // <tbody> children of <table-ui> (3 of 3 occurrences broken, gh#924). Slot
18
- // and composition semantics are v2.
15
+ // enum values on `*-ui` tags; class/id/slot/style/role/tabindex/autocomplete/
16
+ // data-*/aria-*/framework-global attributes allowlisted; one content-model
17
+ // rule — raw <thead>/<tbody> children of <table-ui> (3 of 3 occurrences
18
+ // broken, gh#924). Slot and composition semantics are v2.
19
19
  //
20
20
  // The shipped contract mixes kebab-case and camelCase attribute names across
21
21
  // components (an existing inconsistency in the generated manifest — e.g.
@@ -56,7 +56,20 @@ const SKIP_DIRS = new Set(['node_modules', 'dist', 'build', '.git', '.next', 'co
56
56
  // `role` is a host-language global (like `aria-*`) — text.yaml itself documents
57
57
  // role="heading" + aria-level as the sanctioned semantic-heading path (gh#1256);
58
58
  // globals are never component contract members and never CLASS-5-UNKNOWN.
59
- const ALLOWLIST_EXACT = new Set(['class', 'classname', 'id', 'slot', 'style', 'key', 'ref', 'part', 'role']);
59
+ // `tabindex` and `autocomplete` are the same class of host-language global
60
+ // (gh#1925): `tabindex` sets DOM focus order on any element (traits like
61
+ // long-press/typeahead and app code use it directly on `*-ui` tags — see
62
+ // e.g. packages/web-components/traits/typeahead/typeahead.examples.html —
63
+ // and it is the sanctioned pattern for post-screen-swap heading focus);
64
+ // `autocomplete` is the standard form-control autofill hint, and at least
65
+ // one component's own yaml SoT instructs authors to set it directly
66
+ // (otp-input.yaml:73, `autocomplete="one-time-code"`) even though the
67
+ // component does not re-declare it as a reflected prop. Neither is ever a
68
+ // component contract member, so neither is ever CLASS-5-UNKNOWN.
69
+ const ALLOWLIST_EXACT = new Set([
70
+ 'class', 'classname', 'id', 'slot', 'style', 'key', 'ref', 'part', 'role',
71
+ 'tabindex', 'autocomplete',
72
+ ]);
60
73
  const ALLOWLIST_PREFIX = ['data-', 'aria-'];
61
74
 
62
75
  function normalize(name) {
@@ -346,6 +359,17 @@ function selftest() {
346
359
  const roleGlobal = checkMarkup('<text-ui variant="title" role="heading" aria-level="1">Dashboard</text-ui>', 'f.html', index);
347
360
  if (roleGlobal.length !== 0) fails.push(`role="heading" aria-level flagged (gh#1256): ${JSON.stringify(roleGlobal)}`);
348
361
 
362
+ // 9a. gh#1925: `tabindex` on any *-ui tag (e.g. text-ui for post-screen-swap
363
+ // heading focus) is a host-language global, never CLASS-5-UNKNOWN.
364
+ const tabindexGlobal = checkMarkup('<text-ui variant="title" tabindex="-1">Dashboard</text-ui>', 'f.html', index);
365
+ if (tabindexGlobal.length !== 0) fails.push(`tabindex="-1" flagged (gh#1925): ${JSON.stringify(tabindexGlobal)}`);
366
+
367
+ // 9b. gh#1925: `autocomplete` is a host-language global too, and otp-input-ui's
368
+ // own yaml SoT instructs authors to set it — must never be CLASS-5-UNKNOWN
369
+ // even though the component doesn't re-declare it as a reflected prop.
370
+ const autocompleteGlobal = checkMarkup('<icon-ui name="house" autocomplete="one-time-code"></icon-ui>', 'f.html', index);
371
+ if (autocompleteGlobal.length !== 0) fails.push(`autocomplete flagged (gh#1925): ${JSON.stringify(autocompleteGlobal)}`);
372
+
349
373
  // 10. Setup-error path (AC-4's 0/1/2 matrix): no @adia-ai/web-components in
350
374
  // node_modules anywhere up the tree must resolve to null (drives exit 2 in
351
375
  // main()), never a thrown exception or a silent empty-clean pass.
@@ -375,7 +399,7 @@ function selftest() {
375
399
  console.error('selftest FAIL: ' + fails.join(' | '));
376
400
  return 1;
377
401
  }
378
- console.log('selftest OK — 11 fixtures (unknown attr, clean, enum, camelCase-normalize, table content-model, unknown tag, seed+un-seed, role/aria globals, setup-error, -h/--help)');
402
+ console.log('selftest OK — 13 fixtures (unknown attr, clean, enum, camelCase-normalize, table content-model, unknown tag, seed+un-seed, role/aria globals, tabindex/autocomplete globals, setup-error, -h/--help)');
379
403
  return 0;
380
404
  }
381
405
 
@@ -260,7 +260,13 @@ async function main() {
260
260
  const { pathToFileURL } = await import('node:url');
261
261
  const path = await import('node:path');
262
262
  const req = createRequire(path.join(process.cwd(), 'package.json'));
263
- ({ chromium } = await import(pathToFileURL(req.resolve('playwright')).href));
263
+ // The plugin-cache copy resolves playwright's CJS entry; importing a
264
+ // CJS module via file URL exposes named exports only through
265
+ // `.default` (no static analysis to hoist `chromium` onto the
266
+ // namespace itself) — `mod.chromium` is undefined there, so fall back
267
+ // to `mod.default.chromium` (gh#1926).
268
+ const mod = await import(pathToFileURL(req.resolve('playwright')).href);
269
+ chromium = mod.chromium ?? mod.default.chromium;
264
270
  } catch {
265
271
  console.error('adia-probe: playwright not installed in this app — npm i -D playwright');
266
272
  process.exit(2);
@@ -329,33 +335,53 @@ async function main() {
329
335
  const samples = [];
330
336
  let skipped = 0;
331
337
  const seen = new Set();
332
- const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT);
333
- let node;
334
- while ((node = walker.nextNode()) && samples.length < 120) {
335
- const text = node.textContent.trim();
336
- if (!text) continue;
337
- const el = node.parentElement;
338
- if (!el || ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEMPLATE'].includes(el.tagName)) continue;
339
- const cs = getComputedStyle(el);
340
- if (cs.display === 'none' || cs.visibility === 'hidden' || Number(cs.opacity) === 0) continue;
341
- const rect = el.getBoundingClientRect();
342
- if (rect.width <= 0 || rect.height <= 0) continue;
343
- const fgc = parse(cs.color);
344
- if (!fgc) continue;
345
- const bg = bgFor(el);
346
- if (!bg) { skipped += 1; continue; }
347
- const fg = fgc.a >= 1 ? fgc.rgb.map(Math.round) : over(fgc.rgb, fgc.a, bg);
348
- const fontSizePx = parseFloat(cs.fontSize) || 16;
349
- const fontWeight = cs.fontWeight === 'bold' ? 700 : Number(cs.fontWeight) || 400;
350
- const key = `${fg.join()}|${bg.join()}|${fontSizePx}|${fontWeight}`;
351
- if (seen.has(key)) continue;
352
- seen.add(key);
353
- samples.push({
354
- text: text.slice(0, 40),
355
- element: el.tagName.toLowerCase() + (el.id ? `#${el.id}` : ''),
356
- fg, bg, fontSizePx, fontWeight,
357
- });
358
- }
338
+ // AdiaUI is light-DOM by convention (AGENTS.md), but a plain
339
+ // TreeWalker rooted at document.body never descends into a shadow
340
+ // tree regardless so any *-ui element that DOES attach a shadow
341
+ // root (its own internal structure, a third-party element, or a
342
+ // future exception to the light-DOM stance) went unwalked entirely,
343
+ // which is why the sampler read checked:0 on custom-element-heavy
344
+ // pages (gh#1926). Walk every root — document, then recurse into
345
+ // each element's shadowRoot as it's discovered — so light-DOM slotted
346
+ // content and shadow-DOM internals both get sampled.
347
+ const visitRoot = (root) => {
348
+ const walker = document.createTreeWalker(
349
+ root,
350
+ NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
351
+ );
352
+ let node;
353
+ while ((node = walker.nextNode())) {
354
+ if (node.nodeType === 1) {
355
+ if (node.shadowRoot) visitRoot(node.shadowRoot);
356
+ continue;
357
+ }
358
+ if (samples.length >= 120) return;
359
+ const text = node.textContent.trim();
360
+ if (!text) continue;
361
+ const el = node.parentElement;
362
+ if (!el || ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEMPLATE'].includes(el.tagName)) continue;
363
+ const cs = getComputedStyle(el);
364
+ if (cs.display === 'none' || cs.visibility === 'hidden' || Number(cs.opacity) === 0) continue;
365
+ const rect = el.getBoundingClientRect();
366
+ if (rect.width <= 0 || rect.height <= 0) continue;
367
+ const fgc = parse(cs.color);
368
+ if (!fgc) continue;
369
+ const bg = bgFor(el);
370
+ if (!bg) { skipped += 1; continue; }
371
+ const fg = fgc.a >= 1 ? fgc.rgb.map(Math.round) : over(fgc.rgb, fgc.a, bg);
372
+ const fontSizePx = parseFloat(cs.fontSize) || 16;
373
+ const fontWeight = cs.fontWeight === 'bold' ? 700 : Number(cs.fontWeight) || 400;
374
+ const key = `${fg.join()}|${bg.join()}|${fontSizePx}|${fontWeight}`;
375
+ if (seen.has(key)) continue;
376
+ seen.add(key);
377
+ samples.push({
378
+ text: text.slice(0, 40),
379
+ element: el.tagName.toLowerCase() + (el.id ? `#${el.id}` : ''),
380
+ fg, bg, fontSizePx, fontWeight,
381
+ });
382
+ }
383
+ };
384
+ visitRoot(document.body);
359
385
  return { samples, skipped };
360
386
  }).catch(() => null);
361
387
  await page.screenshot({ path: screenshotPath, scale: 'device', fullPage: true });
@@ -85,7 +85,7 @@ proposing any edit.
85
85
  opt-in, confirm something imports it:
86
86
 
87
87
  ```bash
88
- git grep -n "styles/scale.css\|styles/themes.css\|styles/verse.css\|styles/prose.css"
88
+ git grep -n "styles/scale.css\|styles/themes.css"
89
89
 
90
90
  # Attributes reach the DOM from markup AND from script — a setAttribute('scale', …)
91
91
  # in a .js module counts. Searching templates only produces a false "unused".
@@ -90,7 +90,7 @@ Mode: SPA | SSR | hybrid — signal: <file / dep
90
90
  Framework: <name, or none> — [D] documented | [G] inferred, per ssr-integration.md
91
91
  Registration: <file>:<call site> — one side-effecting barrel import, client-only
92
92
  Route owner: <router-ui> | <framework router> — exactly one per scope
93
- CSS links: host.css + styles/index.css (+ scale.css, or its deprecated verse.css/prose.css alias) — both live, or primitives render unstyled
93
+ CSS links: host.css + styles/index.css (+ scale.css) — both live, or primitives render unstyled
94
94
  State placement: cookies/localStorage/session | client signal — per mode's lifetime rule
95
95
  Gates: registration / route-owner / CSS-links / state — pass | fail, each
96
96
  ```
@@ -43,8 +43,7 @@ Build order — snippets and the full invariant list in `spa-architecture.md`;
43
43
 
44
44
  1. **Host document** — npm-consumer paths; cascade order later-wins:
45
45
  `host.css` → `styles/index.css` → opt-in `scale.css` (the ancestor
46
- `[scale]` register `verse.css`/`prose.css` are its deprecated,
47
- still-working aliases) → page → component CSS. Link BOTH `host.css`
46
+ `[scale]` register) page → component CSS. Link BOTH `host.css`
48
47
  and `styles/index.css` — the styles barrel is split and `host.css` is
49
48
  foundation-only; alone it renders primitives unstyled.
50
49
  2. **Surface container** — self-booting custom element rendering in