@adia-ai/adia-ui-factory 0.8.57 → 0.8.59
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/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/CHANGELOG.md +58 -0
- package/README.md +69 -1
- package/hermes-mcp.yaml +1 -1
- package/package.json +2 -2
- package/plugin.yaml +1 -1
- package/references/shell-admin.md +12 -12
- package/scripts/adia-manifest-cache.mjs +77 -0
- package/scripts/adia-manifest-lite.mjs +137 -0
- package/scripts/adia-posture.d.mts +11 -0
- package/scripts/adia-posture.mjs +81 -0
- package/scripts/adia-scaffold +93 -12
- package/scripts/lab-posture-guard.mjs +174 -0
- package/scripts/lint-rules.generated.mjs +29342 -131
- package/scripts/tests/lab-posture-guard.selftest.mjs +248 -0
- package/scripts/ui-architect-pi-selftest.mjs +18 -0
- package/skills/app-audit/references/gap-classes.md +1 -1
- package/skills/pattern-catalog/SKILL.md +20 -0
- package/skills/pattern-catalog/references/annotations.yaml +2 -1
- package/skills/pattern-catalog/references/pattern-index.md +42 -42
- package/skills/screen-composition/assets/figma-make/guidelines/components.md +10 -12
- package/skills/shell-selection/SKILL.md +1 -1
- package/skills/surface-qa/references/verification.md +1 -1
- package/skills/token-selection/references/a-alias-layer.md +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adia-ui-kit-factory",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.59",
|
|
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.
|
|
3
|
+
"version": "0.8.59",
|
|
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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# Changelog — adia-ui-kit-factory
|
|
2
2
|
|
|
3
|
+
## [0.8.59] — 2026-09-04
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- **`scripts/adia-posture.d.mts`: ambient TypeScript types for `scripts/adia-posture.mjs` (gh#2875).** Lets `@adia-ai/mcp`'s factory server's new `scaffold_prototype` tool import `readPosture()` directly (the SAME reader `lab-posture-guard.mjs` uses) once vendored, without pulling the plain-JS module into that package's TypeScript build as a compiled input. No behavior change to `adia-posture.mjs` itself; see `@adia-ai/mcp`'s own CHANGELOG for the tool this enables.
|
|
7
|
+
|
|
8
|
+
### Fixed
|
|
9
|
+
- **`skills/pattern-catalog/references/pattern-index.md` regenerated after gh#2916's `dashboard-page-header` corpus-chunk rebuild reordered `dashboard-admin-page`'s components list.** Derived-only (`npm run build:factory-mcp-assets`), no source change in this package.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- **`scripts/lint-rules.generated.mjs` (vendored lab-posture bank) regenerated after gh#2939's EXAMPLE-PROP-CONTRACT `data-*` exemption and GENUI-DOC-CONFLICT self-scope fix landed in the shared rule sources.** Derived-only, no source change in this package; the vendored bank picks up both predicate narrowings — full-corpus warn count 1413 → 1410 (see repo-root CHANGELOG.md for the gate's own detail).
|
|
13
|
+
- **`scripts/lint-rules.generated.mjs` (vendored lab-posture bank) regenerated after gh#2937's `tagTree()` memoization landed in `scripts/lint/engine/primitives.mjs` (root).** Derived-only, no source change in this package; the vendored bank picks up the shared engine's single-slot memo, so the 48 generated `COMPOSITION-*` rules now share one tag-tree parse per file instead of each re-parsing it — full golden-corpus `check:lint-efficacy`: 110.9s → 9.45s. Byte-identical rule semantics (38/38 seeded-fixture catch rate, 0 error-severity across 438 files, unchanged).
|
|
14
|
+
- **`scripts/lint-rules.generated.mjs` (vendored lab-posture bank) regenerated to carry `enum_meta` tier data for gh#2831 batch 2's 9 components** (feed-item-ui, inline-message-ui, link-ui, loading-overlay-ui, mark-ui, menu-item-ui, nav-group-ui, nav-item-ui, nav-ui). Derived-only, no source change in this package; the vendored bank follows the yaml `enum_meta` authored in `@adia-ai/web-components`.
|
|
15
|
+
- **`scripts/lint-rules.generated.mjs` (vendored lab-posture bank) regenerated to carry `enum_meta` tier data for gh#2831 batch 3's 13 components (FINAL batch)** (pagination-ui, preview-ui, rating-ui, select-ui, shader-texture-ui, spinner-ui, table-toolbar-ui, tabs-ui, tag-ui, text-ui, timeline-item-ui, toast-ui, toggle-scheme-ui). Derived-only, no source change in this package; the vendored bank follows the yaml `enum_meta` authored in `@adia-ai/web-components`. Closes the 31-component sweep (gh#2831).
|
|
16
|
+
|
|
17
|
+
### Maintenance
|
|
18
|
+
- **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
|
|
19
|
+
- **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
|
|
20
|
+
|
|
21
|
+
## [0.8.58] — 2026-09-02
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- **Lab posture: `.adia` marker, manifest-derived lint rules, and a blocking `PreToolUse` write-time hook (ADR-0108 D1/D5, LLD-0019 C3-C5, gh#2856).** New `Posture` README section documents the `.adia` marker schema (`{ "posture": "lab" | "bare-repo", "kit"?: { "web-components": "<version>" } }`) and `scripts/adia-posture.mjs`'s `readPosture(repoRoot)` reader, posture is explicit, never inferred from `package.json`/`node_modules`. New `lab-posture-guard` hook (`PreToolUse`, `Write|Edit`): in lab posture, denies a write containing an unknown tag/attribute/enum value or an `internal`-tier value (the same four rules, `UNKNOWN-TAG`/`UNKNOWN-ATTR`/`ENUM-VALUE`/`INTERNAL-TIER`, imported straight from this plugin's own vendored `lint-rules.generated.mjs`, never a second matcher implementation), with the violating tag/attr/value and the manifest's allowed set in the deny reason; a no-op in bare-repo/absent posture, `adia-lint`'s existing advisory `PostToolUse` contract is completely unchanged. New `SessionStart` hook warms the manifest cache once per session. Reads a lab consumer's LIVE installed manifest via a new dependency-free `adia-manifest-lite.mjs` (JSON-only: none of the four rules needs `a2ui.rules`, so no vendored yaml parser required for this path), cached per-session by `adia-manifest-cache.mjs` under the OS temp dir (never a tracked repo artifact). Hook integration selftest (`npm run check:lab-posture-guard-selftest`) builds two tmp-dir fixture consumer repos (lab / bare-repo, mirroring `get-manifest.test.js`'s own fixture-building pattern rather than committing a real `.a2ui.json` fixture, which would trip `adia-ui-forge`'s `sidecar-prewrite-guard`) and asserts an unknown-tag write is denied in lab and allowed bare-repo, plus clean/enum/internal-tier/unknown-attr/non-markup cases. Non-Claude harnesses (Codex/Hermes/Pi) get no hook runtime, see the README's "Non-Claude harnesses" section for the `npm run lint`-at-`warn` degradation. Hooks roster: 1 → 2 (site's `factory-plugin.html` roster page updated, `check:plugin-count-claims` guards it).
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- **`adia-scaffold spa`'s `_SPA_JS`/`_SPA_HTML` templates register only the tags the placeholder markup uses (page-ui, text-ui) via per-component JS + CSS imports, instead of the full `@adia-ai/web-components` / `@adia-ai/web-components/css` barrel (ADR-0107, gh#2765).** The generated app's `dist/` shrinks from ~39MB / 9,092 files (barrel JS + the full Phosphor icon glob) to ~272KB / 3 files, JS gzip 430KB→7.7KB, CSS gzip drops similarly, measured against this checkout's own `packages/web-components`. The barrel stays fully supported: the template's own header comment documents the two-line kitchen-sink opt-in (`import '@adia-ai/web-components'` + `/css`) for consumers who want zero-config registration of every primitive. Scaffolded SPAs now also get a `README.md` with a "Bundle cost" section stating this trade-off, the icon-subsetting `installIconLoaders()` path, and the kitchen-sink opt-in.
|
|
28
|
+
- **`selftest`'s `_resolve_export` wildcard-match tie-break fixed to match Node's own exports-resolution specificity rule** (prefix+suffix length, not prefix length alone), the old tie-break could resolve a `.css`-suffixed subpath against the wrong same-prefix wildcard key (e.g. `./components/*` instead of the more specific `./components/*.css`), which the previous templates never exercised but the new per-component CSS imports do.
|
|
29
|
+
|
|
30
|
+
### Fixed
|
|
31
|
+
- **Pi `ui-architect` self-test now re-execs with Node's explicit TypeScript-stripping flag on early Node 22 patch releases (gh#2803).** Direct and npm-script invocations can import `ui-architect.extension.ts` across the supported Node 22 major instead of depending on the later patch that enabled type stripping by default.
|
|
32
|
+
- **ATTR-TYPO/LLM-KEY-IN-CLIENT/NATIVE-PRIMITIVE matcher hardening (gh#2708)**, propagated to this plugin's vendored `scripts/lint-rules.generated.mjs` via `npm run build:lint-rules`, byte-identical to the shared lint-rule bank, no change to `adia-lint.mjs`'s own CLI contract.
|
|
33
|
+
|
|
34
|
+
### Docs
|
|
35
|
+
- **`scripts/lint-rules.generated.mjs` (vendored lab-posture bank) regenerated to carry `enum_meta` tier data for gh#2831 batch 3's 13 components** (pagination-ui, preview-ui, rating-ui, select-ui, shader-texture-ui, spinner-ui, table-toolbar-ui, tabs-ui, tag-ui, text-ui, timeline-item-ui, toast-ui, toggle-scheme-ui). Derived-only, no source change in this package; the vendored bank follows the yaml `enum_meta` authored in `@adia-ai/web-components`. Closes the 31-component sweep (gh#2831).
|
|
36
|
+
- **generated lint-rule bank refreshed (gh#2821, gh#2824)**: a full `npm run build:lint-rules` regen pass, run while rebasing the heatmap/noodles a11y fixes onto main, picked up `enum_meta` `tier`/`when` data already landed upstream (gh#2864) that this vendored copy hadn't caught up to yet; vendored copy byte-identical to the shared bank, no behavior change.
|
|
37
|
+
- **generated lint-rule bank refreshed (gh#2845)**: `gen-composition-rules.mjs`'s scan widened from `<dirname>.yaml` only to every `*.yaml` in a component directory, discovering 14 previously-missed child/sub-item yamls; composition-rule count 34 → 48 (mechanics: repo-root CHANGELOG.md); vendored copy byte-identical to the shared bank, no behavior change.
|
|
38
|
+
- **generated lint-rule bank refreshed (gh#2801)**: `gen-composition-rules.mjs`'s eligibility widened from 1 to 34 generated composition rules, decoupled from component.md pilot status (mechanics: repo-root CHANGELOG.md); vendored copy byte-identical to the shared bank, no behavior change.
|
|
39
|
+
- **generated lint-rule bank refreshed** by wave-5A component.md eligibility regen (lld-0018).
|
|
40
|
+
- **generated lint-rule bank refreshed** by wave-5B component.md eligibility regen (lld-0018).
|
|
41
|
+
- **generated lint-rule bank refreshed**: wave-1A component.md eligibility added 3 composition rules (accordion/action-list/choice-card, lld-0018).
|
|
42
|
+
- **pattern-catalog `pattern-index.md` regenerated** for the header-ui→header sweep (gh#2793).
|
|
43
|
+
- **pattern-catalog `pattern-index.md` regenerated** for the admin-page-header pattern entry's admin-scroll→page-scroll wrapper swap (gh#2734).
|
|
44
|
+
- **`pattern-catalog/SKILL.md`'s ADR-0084 forward note gains a build-status update (gh#2736).** The generated `templates/` npm mirror generator has landed (`@adia-ai/web-components`'s CHANGELOG carries the mechanics); this note records that the `ships:`/resolution-table flip is a separate, not-yet-built wave.
|
|
45
|
+
- **ADR-0098 drain: remaining admin-page prose in `references/shell-admin.md`, `skills/app-audit/references/gap-classes.md`, and `skills/screen-composition/assets/figma-make/guidelines/components.md` rewritten to `<page-ui band>`.** These three still described the retired `<admin-page>`/`<admin-page-header>`/`<admin-page-body>` triad as the current admin-shell chrome pattern, the canonical skeleton example, the chrome-tier allocation table, a gotcha row on the "without inner column-owner" failure mode, and the Figma-Make shells vocabulary/skeleton all now show `<page-ui band>` with `<header-ui>`/`<section-ui>` children. `demo-audit/references/admin-shell-anatomy.md`, `demo-audit/references/app-shell-pitfalls.md`, and `primitive-authoring/references/shell-patterns.md` were checked and already carry correct dated ADR-0098 deprecation notes from an earlier harvest, no change needed there.
|
|
46
|
+
- **`pattern-catalog/references/pattern-index.md` + `annotations.yaml` updated for `admin-page-header.html`'s ADR-0098 rewrite (gh#2748).** The docs-site pattern page itself moved off the deprecated `<admin-page-header>`/`<admin-page>`/`<admin-page-body>` triad onto `<page-ui band>`'s direct `<header-ui>` child (root `CHANGELOG.md` carries the page-rewrite detail); `annotations.yaml`'s `admin-page-header.html` entry intent/keywords updated to name `page-ui band` instead of `admin-page`, and `pattern-index.md` regenerated (`npm run build:patterns-index`) picks up the entry's `components:` list dropping `admin-page`/`admin-page-body`/`admin-page-header`/`alert-ui` for `page-ui`.
|
|
47
|
+
- **`pattern-catalog/references/pattern-index.md` regenerated (`npm run build:patterns-index`, gh#2730).** `patterns-form-drawer`'s `components:` list re-sorts after gh#2730's label-variant sweep collapsed `form-drawer.examples.html`'s `field-ui[inline]`-wrapping-`switch-ui` anti-pattern to the switch's own `[label]` attribute, shifting the extracted tag order.
|
|
48
|
+
- **ADR-0098 knowledge-harvest applied to `shell-selection/SKILL.md`.** The "Canonical nesting" verify-target row gains a dated deprecation note (2026-09-01) pointing the `admin-page`/`admin-scroll` nesting example at its successor, `page-ui[band]` nested inside `page-scroll`.
|
|
49
|
+
- **`pattern-catalog/references/pattern-index.md` regenerated (`npm run build:patterns-index`, gh#2706).** The "Admin Shell" and "Admin Content Aside" pattern entries' `components:` lists still named `admin-page`/`admin-page-header`/`admin-page-body`/`admin-scroll`, their source demo (`packages/web-components/patterns/admin-shell/admin-shell.examples.html`) had been missed by the earlier ADR-0098 sweep; now byte-resynced to `page-scroll`/`page-ui[band]` after the source migration.
|
|
50
|
+
- **ADR-0084 knowledge-harvest applied to `pattern-catalog/SKILL.md`.** The "Reach the source" section's "none of them a template screen" `ships:` claim gains a dated forward-note (2026-09-01) recording ADR-0084's ruled `templates/` npm-mirror plan for all 121 template screens (Class R, build-at-publish); the 6 `/site/pages/patterns/` docs-hub pages stay `monorepo-only` permanently either way.
|
|
51
|
+
- **ADR-0102 harvest: `surface-qa/references/verification.md:62`'s superseded heading-role workaround corrected.** The Accessibility checklist row recommending `role="heading"` + `aria-level` or an `<h*>` wrapper for a `text-ui variant="heading"` node now points at `text-ui[level]` (1-6, promotes to a real native `<h1>`-`<h6>`) as the current fix, citing ADR-0102 (2026-09-01); `variant` stays presentational-only, `level` is the tag-promoting prop.
|
|
52
|
+
|
|
53
|
+
### Chore
|
|
54
|
+
- **Author email in `package.json` updated to `kim.granlund@adia.ai`** (was a stale personal address).
|
|
55
|
+
|
|
56
|
+
### Maintenance
|
|
57
|
+
- **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
|
|
58
|
+
- **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
|
|
59
|
+
- **`adapters/` touched in this release window** (1 file(s), e.g. `claude/hooks.json`) — carried by the entries above.
|
|
60
|
+
|
|
3
61
|
## [0.8.57] — 2026-09-01
|
|
4
62
|
|
|
5
63
|
### Fixed
|
package/README.md
CHANGED
|
@@ -96,6 +96,58 @@ surface-qa-agent) · `component-model.md` · the `references/contracts/` twins.
|
|
|
96
96
|
|
|
97
97
|
`adia-lint` — PostToolUse advisory consumer-smell repair loop (selftest ships).
|
|
98
98
|
|
|
99
|
+
`lab-posture-guard` — PreToolUse (`Write|Edit`) blocking guard, active only
|
|
100
|
+
in lab posture (see Posture, below); a no-op in bare-repo/absent posture,
|
|
101
|
+
leaving `adia-lint`'s existing advisory contract completely unchanged. A
|
|
102
|
+
`SessionStart` hook warms its manifest cache once per session; selftest
|
|
103
|
+
ships (`npm run check:lab-posture-guard-selftest`).
|
|
104
|
+
|
|
105
|
+
## Posture
|
|
106
|
+
|
|
107
|
+
Two postures a consumer repo can declare (ADR-0108, LLD-0019 §C3): **bare-repo**
|
|
108
|
+
(ADR-0040, the default — this plugin working standalone with no monorepo
|
|
109
|
+
path assumptions) and **lab** (ADR-0108 — a repo that installs
|
|
110
|
+
`@adia-ai/web-components` at root, pinned, and scaffolds prototypes that
|
|
111
|
+
consume those root packages directly as app source). Posture is an
|
|
112
|
+
EXPLICIT marker, never inferred from `package.json` or `node_modules` —
|
|
113
|
+
that inventory-only question ("what's installed") is `adia-info`'s
|
|
114
|
+
separate probe, not a declared-intent signal.
|
|
115
|
+
|
|
116
|
+
A repo-root `.adia` JSON file declares posture:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"posture": "lab",
|
|
121
|
+
"kit": { "web-components": "9.9.9" }
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- `posture` — `"lab"` or `"bare-repo"`, required when the file exists. An
|
|
126
|
+
explicit `"bare-repo"` value is accepted alongside the file's plain
|
|
127
|
+
absence — both read as bare-repo behavior — so a repo can affirmatively
|
|
128
|
+
opt OUT of lab posture in its own marker rather than relying on absence
|
|
129
|
+
alone.
|
|
130
|
+
- `kit` — optional. When present, `kit["web-components"]` records the
|
|
131
|
+
installed `@adia-ai/web-components` version the marker was written
|
|
132
|
+
against — informational only; nothing in this plugin reads it yet.
|
|
133
|
+
- Missing file, unparseable JSON, or a missing/unrecognized `posture`
|
|
134
|
+
value all resolve to bare-repo behavior — the permissive branch. A
|
|
135
|
+
corrupt marker must never accidentally turn ON the stricter lab-posture
|
|
136
|
+
enforcement below.
|
|
137
|
+
|
|
138
|
+
`scripts/adia-posture.mjs` (`readPosture(repoRoot)` → `'lab' | 'bare-repo' |
|
|
139
|
+
'absent'`) is the one reader both `lab-posture-guard` and a future
|
|
140
|
+
`scaffold_prototype` (LLD-0019 §C6, not shipped yet) key on.
|
|
141
|
+
|
|
142
|
+
**In lab posture**, `lab-posture-guard` validates every `Write`/`Edit`
|
|
143
|
+
against the installed manifest's tags, attributes, and declared enum
|
|
144
|
+
values (`UNKNOWN-TAG`/`UNKNOWN-ATTR`/`ENUM-VALUE`/`INTERNAL-TIER`) and
|
|
145
|
+
DENIES a violating write before it lands, naming the offending
|
|
146
|
+
tag/attr/value and the manifest's allowed set in the deny reason so the
|
|
147
|
+
agent can self-correct. **In bare-repo or absent posture**, this hook is a
|
|
148
|
+
complete no-op — `adia-lint`'s existing `PostToolUse` advisory contract is
|
|
149
|
+
the only signal, unchanged.
|
|
150
|
+
|
|
99
151
|
## Commands
|
|
100
152
|
|
|
101
153
|
`/project-scaffolding` · `/app-planning` · `/surface-qa` · `/app-migration` · `/find-unused` · `/gen-ui-wiring` · `/app-audit` · `/theme-audit`
|
|
@@ -111,7 +163,7 @@ attributes vs. the shipped component contracts), and `adia-theme-audit.mjs`
|
|
|
111
163
|
|
|
112
164
|
## MCP
|
|
113
165
|
|
|
114
|
-
`adia-gen-ui` server pinned: `@adia-ai/mcp@0.8.
|
|
166
|
+
`adia-gen-ui` server pinned: `@adia-ai/mcp@0.8.59` (tool SoT:
|
|
115
167
|
`packages/gen-ui/mcp/TOOLS.md` — the `gen-ui` section; stability rule in
|
|
116
168
|
`references/contracts/`; pin lives in `.mcp.json` — `check:plugin-count-claims`
|
|
117
169
|
guards this README copy against it). The server is `adia-mcp gen-ui`,
|
|
@@ -136,6 +188,22 @@ plain Node script with no Claude-specific runtime dependency; the
|
|
|
136
188
|
Hermes, and becomes a genuine `prompts/theme-audit.md` Pi command via
|
|
137
189
|
`build:harness-manifests`.
|
|
138
190
|
|
|
191
|
+
**Lab posture's write-time enforcement (ADR-0108 D5/D6) is a Claude Code-only
|
|
192
|
+
guarantee** — `AGENTS.md`'s own "Non-Claude harnesses" section states plainly
|
|
193
|
+
that none of Codex, Hermes, or Pi has a manifest key for `hooks/`, and none
|
|
194
|
+
has a hook runtime a `PreToolUse` deny could wire into, so `lab-posture-guard`
|
|
195
|
+
simply does not run on any of the three. They degrade to `npm run lint` in
|
|
196
|
+
the monorepo (or the equivalent vendored `adia-lint`/lint-bank invocation in
|
|
197
|
+
a consumer repo) picking up the same four manifest-derived rules
|
|
198
|
+
(`UNKNOWN-TAG`/`UNKNOWN-ATTR`/`ENUM-VALUE`/`INTERNAL-TIER`, ADR-0108 D5) at
|
|
199
|
+
`warn` severity — after the fact, at lint/check time, rather than before the
|
|
200
|
+
write lands. `get_manifest` reachability without the hook is not uniform
|
|
201
|
+
either: Codex reaches it through its plain `.mcp.json` passthrough; Hermes
|
|
202
|
+
needs its `hermes-mcp.yaml` fragment hand-merged into `~/.hermes/config.yaml`;
|
|
203
|
+
Pi has no native MCP support at all, so a Pi session without the third-party
|
|
204
|
+
`pi-mcp-adapter` gets neither the manifest nor the lint-time signal until a
|
|
205
|
+
`npm run check`/`npm run lint` pass runs.
|
|
206
|
+
|
|
139
207
|
- **Codex** (gh#1888): `.codex-plugin/plugin.json` + per-skill
|
|
140
208
|
`agents/openai.yaml`, derived; `mcpServers` points at the same
|
|
141
209
|
`.mcp.json` Claude Code uses. No manifest key for hooks/commands/agents —
|
package/hermes-mcp.yaml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adia-ai/adia-ui-factory",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.59",
|
|
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",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "Kim",
|
|
17
|
-
"email": "kim@
|
|
17
|
+
"email": "kim.granlund@adia.ai"
|
|
18
18
|
},
|
|
19
19
|
"bugs": {
|
|
20
20
|
"email": "kim.granlund@adia.ai"
|
package/plugin.yaml
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
name: "adia-ui-kit-factory"
|
|
2
|
-
version: "0.8.
|
|
2
|
+
version: "0.8.59"
|
|
3
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
4
|
manifest_version: 1
|
|
@@ -11,7 +11,7 @@ Full SaaS/admin chrome from `@adia-ai/web-modules`. Register the **cluster barre
|
|
|
11
11
|
|
|
12
12
|
## Cluster roster
|
|
13
13
|
|
|
14
|
-
`<admin-shell>` (host coordinator) · `<admin-sidebar>` (resizable/collapsible, `slot="leading"|"trailing"`) · `<admin-command>` (Cmd+K palette) · `<admin-content>` (center column) · `<admin-topbar>` / `<admin-statusbar>` (chrome bars, shared slots) · `<admin-scroll>` (vertical scroll container) · `<
|
|
14
|
+
`<admin-shell>` (host coordinator) · `<admin-sidebar>` (resizable/collapsible, `slot="leading"|"trailing"`) · `<admin-command>` (Cmd+K palette) · `<admin-content>` (center column) · `<admin-topbar>` / `<admin-statusbar>` (chrome bars, shared slots) · `<admin-scroll>` (vertical scroll container) · `<page-ui band>` + `<header-ui>` / `<section-ui>` / `<footer-ui>` (the page; header/footer always-sticky with border+background, body scrolls) · `<admin-entity-item>` (icon+label+badge identity row). Three are JS-bearing (shell, sidebar, command); the rest are CSS-only structure.
|
|
15
15
|
|
|
16
16
|
## Canonical skeleton
|
|
17
17
|
|
|
@@ -29,10 +29,10 @@ Full SaaS/admin chrome from `@adia-ai/web-modules`. Register the **cluster barre
|
|
|
29
29
|
<breadcrumb-ui slot="heading">…</breadcrumb-ui>
|
|
30
30
|
</admin-topbar>
|
|
31
31
|
<admin-scroll>
|
|
32
|
-
<
|
|
33
|
-
<
|
|
34
|
-
<
|
|
35
|
-
</
|
|
32
|
+
<page-ui band max-width="xwide" padding="10">
|
|
33
|
+
<header-ui><span slot="heading">Page</span></header-ui>
|
|
34
|
+
<section-ui>…</section-ui>
|
|
35
|
+
</page-ui>
|
|
36
36
|
</admin-scroll>
|
|
37
37
|
<admin-statusbar slot="footer"><span>Status</span></admin-statusbar>
|
|
38
38
|
</admin-content>
|
|
@@ -69,14 +69,14 @@ the command palette, the statusbar). Page-tier content is *per-route* — it cha
|
|
|
69
69
|
|---|---|---|---|
|
|
70
70
|
| Shell sidebar | `<admin-sidebar slot="leading">` | primary nav; brand in `slot="header"`; **user-account controls only** in `slot="footer"` | app-wide preference toggles (scheme/theme) |
|
|
71
71
|
| Shell topbar | `<admin-topbar>` in `<admin-content>` | sidebar-toggle + **breadcrumb** (location); trailing cluster = **persistent global controls** (theme, notifications, command trigger) | page title, page CTAs, dataset search |
|
|
72
|
-
| Page header | `<
|
|
73
|
-
| Page body | `<
|
|
72
|
+
| Page header | `<header-ui>` inside `<page-ui band>` | **page title `<h1>`** + page-scoped CTAs (`+ New`, `Export`) via `slot="action"` | location chrome |
|
|
73
|
+
| Page body | `<section-ui>` inside `<page-ui band>` | the page's primary content | — |
|
|
74
74
|
| Table toolbar | `<table-toolbar-ui>` adjacent to `<table-ui>` | in-page search/filter/sort scoped to the table | — |
|
|
75
75
|
| Statusbar | `<admin-statusbar slot="footer">` | app-wide status (sync, row count, version) | — |
|
|
76
76
|
|
|
77
77
|
**The four allocation anti-patterns** (each silently breaks the model, no warning):
|
|
78
78
|
|
|
79
|
-
- ❌ **Page CTA (`+ New Claim`) in the shell topbar** → it sticks across every route and never updates. Page CTAs go in `<
|
|
79
|
+
- ❌ **Page CTA (`+ New Claim`) in the shell topbar** → it sticks across every route and never updates. Page CTAs go in the `<header-ui>` inside `<page-ui band>`, via `slot="action"`.
|
|
80
80
|
- ❌ **Page title (`<h1>`) in the topbar** instead of a breadcrumb → the topbar carries *location*; the breadcrumb's terminal segment IS the page title.
|
|
81
81
|
- ❌ **Dataset-search input in the topbar** → a search scoped to one dataset belongs in `<table-toolbar-ui>`, not the app-wide topbar.
|
|
82
82
|
- ❌ **Scheme/theme toggle in the sidebar footer** → it reads as nav and vanishes when the sidebar collapses. App-wide preferences belong in the topbar trailing cluster.
|
|
@@ -88,16 +88,16 @@ SPA mounts the full markup. SSR keeps the shell + chrome fixed and swaps only th
|
|
|
88
88
|
## Gotchas (mechanized where noted)
|
|
89
89
|
|
|
90
90
|
- **Piecemeal import** → `AdminSidebar`/`AdminCommand` unregistered; `.toggle()/.show()` undefined. Import the barrel.
|
|
91
|
-
- **Wrapping shell children in `<col-ui>`/`<row-ui>`** → breaks the grid (it reads tag selectors). Generics go _inside_ `admin-content`/`
|
|
92
|
-
- **Raw `<header>` vs `<header-ui>` inside `<
|
|
91
|
+
- **Wrapping shell children in `<col-ui>`/`<row-ui>`** → breaks the grid (it reads tag selectors). Generics go _inside_ `admin-content`/`page-ui`'s `<section-ui>`.
|
|
92
|
+
- **Raw `<header>` vs `<header-ui>` inside `<page-ui band>`**: prefer `<header-ui>`. `page-ui`'s `@scope` rules target both `<header>` and `<header-ui>` identically for geometry, but only `<header-ui>` has the shadow DOM that routes `slot="heading"` / `slot="description"` / `slot="icon"` / `slot="action"` children — a raw `<header>` matches the layout rhythm but silently drops every named slot.
|
|
93
93
|
- **`[resizable]` without a child `<div data-sidebar-resize>`** → no drag handle.
|
|
94
94
|
- **Hardcoded `[collapsed]`** → won't auto-clear on resize; use `.collapse()/.expand()`.
|
|
95
|
-
- **Multiple `<
|
|
95
|
+
- **Multiple `<page-ui>` in one `<admin-scroll>`** → single-axis scroll breaks; one page per scroll.
|
|
96
96
|
- **`@container (…)` instead of `@container page-content (…)`** → won't react to sidebar collapse.
|
|
97
97
|
- **Legacy shapes** (`<aside data-sidebar>`, `<dialog data-command>`) — retired v0.4.0 (`adia-lint` `LEGACY-SHELL`).
|
|
98
98
|
- **Sidebar nav is `<nav-ui>` + `<nav-item-ui>`**, never `<menu-ui>`/`<menu-item-ui>` — menu-ui is for Popover-API dropdowns, not persistent navigation.
|
|
99
99
|
- **Full-height mount:** an intermediate wrapper (`<main id="app">`) between `body { height: 100dvh; display: flex }` and the shell needs `flex: 1; display: flex; min-height: 0` — without it the flex chain breaks and the shell collapses to content height.
|
|
100
|
-
- **`header-ui` has no CSS of its own** — its icon/heading/description/action grid comes from the parent's `@scope` (`
|
|
100
|
+
- **`header-ui` has no CSS of its own** — its icon/heading/description/action grid comes from the parent's `@scope` (`page-ui` provides it); bespoke chrome reusing the header-ui vocabulary must supply the grid + text ellipsis locally.
|
|
101
101
|
- **A persistent `<drawer-ui data-mobile-nav-drawer>` is always in the DOM** (relocated mobile-nav mechanism, gh#1984/ADR-0090), not conditionally created — only its visibility responds to a container query at the shell's mobile-nav breakpoint. A selector assuming exactly one `drawer-ui`/`dialog` on the page (e.g. an E2E test) must scope past it: `drawer-ui:not([data-mobile-nav-drawer])`.
|
|
102
102
|
|
|
103
103
|
Real usage: `apps/saas/app/admin-dashboard/`.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adia-manifest-cache.mjs — ADR-0108 D6 / LLD-0019 C4: the session-scoped
|
|
3
|
+
* manifest cache the lab-posture write-time hook reads instead of calling
|
|
4
|
+
* `get_manifest` over MCP at write time. A hook invocation is a fresh Node
|
|
5
|
+
* process every time (no in-memory persistence across PreToolUse calls),
|
|
6
|
+
* so "cache built once per session" is realized as a small file under the
|
|
7
|
+
* OS temp dir, keyed by the hook event's `session_id` when the harness
|
|
8
|
+
* supplies one (every refresh call from a new session gets a fresh file —
|
|
9
|
+
* LLD-0019 §C4's own "refreshes once per session" contract) and falling
|
|
10
|
+
* back to a `repoRoot`-only key when it doesn't (a run outside a hook
|
|
11
|
+
* event, e.g. this module's own tests) — LLD-0019 R-2/OQ-2 already name
|
|
12
|
+
* mid-session staleness as an accepted, unsolved risk; this cache's own
|
|
13
|
+
* "session-keyed when possible" behavior does not attempt to solve it,
|
|
14
|
+
* only to match the design's stated refresh point as closely as a
|
|
15
|
+
* stateless-process hook can.
|
|
16
|
+
*
|
|
17
|
+
* Never a tracked repo artifact (LLD-0019 §Data: "generated, session-
|
|
18
|
+
* scoped, not committed... never classified R or C").
|
|
19
|
+
*/
|
|
20
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
21
|
+
import { createHash } from 'node:crypto';
|
|
22
|
+
import { tmpdir } from 'node:os';
|
|
23
|
+
import { join } from 'node:path';
|
|
24
|
+
import { readManifestLite } from './adia-manifest-lite.mjs';
|
|
25
|
+
|
|
26
|
+
const CACHE_DIR_NAME = 'adia-lab-posture-manifest-cache';
|
|
27
|
+
|
|
28
|
+
function cacheKey(repoRoot, sessionId) {
|
|
29
|
+
const basis = sessionId ? `session:${sessionId}` : `repo:${repoRoot}`;
|
|
30
|
+
return createHash('sha256').update(basis).digest('hex').slice(0, 32);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function cachePath(repoRoot, sessionId) {
|
|
34
|
+
return join(tmpdir(), CACHE_DIR_NAME, `${cacheKey(repoRoot, sessionId)}.json`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Reads the cached manifest for this `(repoRoot, sessionId)` pair, if
|
|
38
|
+
* any. Returns `null` on a cache miss or an unreadable/corrupt cache file
|
|
39
|
+
* — a miss is the caller's cue to refresh (§refreshManifestCache), never a
|
|
40
|
+
* crash. */
|
|
41
|
+
export function readManifestCache(repoRoot, sessionId) {
|
|
42
|
+
const path = cachePath(repoRoot, sessionId);
|
|
43
|
+
if (!existsSync(path)) return null;
|
|
44
|
+
try {
|
|
45
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
46
|
+
return parsed?.manifest ?? null;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Builds a fresh manifest from `repoRoot`'s installed `node_modules`
|
|
53
|
+
* (`adia-manifest-lite.mjs`) and writes it to the session cache. Returns
|
|
54
|
+
* the manifest (or `null` when `@adia-ai/web-components` is not
|
|
55
|
+
* installed — a lab-posture repo missing its own root install, distinct
|
|
56
|
+
* from "no cache yet"). */
|
|
57
|
+
export function refreshManifestCache(repoRoot, sessionId) {
|
|
58
|
+
const manifest = readManifestLite(repoRoot);
|
|
59
|
+
const path = cachePath(repoRoot, sessionId);
|
|
60
|
+
mkdirSync(join(tmpdir(), CACHE_DIR_NAME), { recursive: true });
|
|
61
|
+
writeFileSync(path, JSON.stringify({ repoRoot, builtAt: new Date().toISOString(), manifest }));
|
|
62
|
+
return manifest;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Cache-or-refresh: the one entry point both the hook and a SessionStart
|
|
66
|
+
* warm call use. Never returns stale-by-construction data — a hit returns
|
|
67
|
+
* exactly what the last refresh in this session wrote; a miss refreshes
|
|
68
|
+
* inline before returning (LLD-0019 D6: "a write attempted before [the
|
|
69
|
+
* refresh] has completed denies against an empty cache the same way an
|
|
70
|
+
* unknown tag would" — this function IS that refresh, so by the time it
|
|
71
|
+
* returns, the cache is never empty except when the consumer repo truly
|
|
72
|
+
* has no installed manifest). */
|
|
73
|
+
export function getOrBuildManifest(repoRoot, sessionId) {
|
|
74
|
+
const cached = readManifestCache(repoRoot, sessionId);
|
|
75
|
+
if (cached) return cached;
|
|
76
|
+
return refreshManifestCache(repoRoot, sessionId);
|
|
77
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adia-manifest-lite.mjs — ADR-0108 D6 / LLD-0019 C2, C4: a small,
|
|
3
|
+
* dependency-free reader of a CONSUMER repo's installed
|
|
4
|
+
* `node_modules/@adia-ai/web-components` (+ `@adia-ai/web-modules`)
|
|
5
|
+
* manifest, scoped to exactly what the lab-posture write-time hook
|
|
6
|
+
* (lab-posture-guard.mjs, LLD-0019 C4) needs: `{ tag, attrs: [{ name,
|
|
7
|
+
* values?: [{ value, tier }] }] }` per component — the same shape
|
|
8
|
+
* `unknownTagFindings`/`unknownAttrFindings`/`enumValueFindings`/
|
|
9
|
+
* `internalTierFindings` (this plugin's vendored lint-rules.generated.mjs,
|
|
10
|
+
* ADR-0108 D5) already expect.
|
|
11
|
+
*
|
|
12
|
+
* WHY A SECOND READER, NOT packages/gen-ui/mcp/factory/manifest.js's
|
|
13
|
+
* `getManifest` DIRECTLY: that module lives in the `@adia-ai/mcp` package
|
|
14
|
+
* tree, reachable inside THIS monorepo checkout via a relative import, but
|
|
15
|
+
* NOT present at all once this plugin is installed standalone into a lab
|
|
16
|
+
* consumer repo (ADR-0108's actual deployment target) — `@adia-ai/mcp` is
|
|
17
|
+
* not a dependency of `@adia-ai/adia-ui-factory` (this plugin ships
|
|
18
|
+
* self-contained, vendored scripts, no npm dependency on sibling
|
|
19
|
+
* packages). The narrower scope also means this reader needs NO yaml
|
|
20
|
+
* parsing at all: `getManifest`'s one yaml-only field is `a2ui.rules`
|
|
21
|
+
* (packages/gen-ui/mcp/factory/manifest.ts's own header explains why:
|
|
22
|
+
* `components.mjs` never forwards it onto the `.a2ui.json` sidecar), and
|
|
23
|
+
* none of the four lab-posture rules reads `rules` — every field they DO
|
|
24
|
+
* need (`tag`, attr names, enum `value`/`tier` pairs) already lives in the
|
|
25
|
+
* sidecar's plain JSON `properties`/`x-adiaui` blocks. So this reader is a
|
|
26
|
+
* pure `JSON.parse` walk, no vendored yaml-lite parser needed for the hook
|
|
27
|
+
* path (gh#2856 Open Question 5's other half, resolved: the GENERATOR
|
|
28
|
+
* reads in-tree yaml via manifest.ts's `buildManifestFromRoots`; the HOOK
|
|
29
|
+
* reads a consumer's installed JSON sidecars via this file — two readers,
|
|
30
|
+
* same shape, deliberately different dependency footprints for their two
|
|
31
|
+
* different execution contexts).
|
|
32
|
+
*/
|
|
33
|
+
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
|
|
36
|
+
function resolvePackageRoot(repoRoot, pkgName) {
|
|
37
|
+
const root = join(repoRoot, 'node_modules', ...pkgName.split('/'));
|
|
38
|
+
return existsSync(join(root, 'package.json')) ? root : null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function listSubdirs(dirPath) {
|
|
42
|
+
if (!existsSync(dirPath)) return [];
|
|
43
|
+
return readdirSync(dirPath).filter((name) => {
|
|
44
|
+
try {
|
|
45
|
+
return statSync(join(dirPath, name)).isDirectory();
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function listSidecarsInDir(dirPath) {
|
|
53
|
+
let files;
|
|
54
|
+
try {
|
|
55
|
+
files = readdirSync(dirPath);
|
|
56
|
+
} catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
return files.filter((f) => f.endsWith('.a2ui.json')).map((f) => join(dirPath, f));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function buildAttrs(sidecar, includeInternal) {
|
|
63
|
+
const props = sidecar.properties ?? {};
|
|
64
|
+
const attrs = [];
|
|
65
|
+
for (const [name, meta] of Object.entries(props)) {
|
|
66
|
+
if (meta.const !== undefined) continue; // the `component` discriminator — not an author-facing attribute
|
|
67
|
+
const attr = { name };
|
|
68
|
+
if (Array.isArray(meta.enum) && meta.enum.length) {
|
|
69
|
+
const enumMeta = meta.enum_meta ?? {};
|
|
70
|
+
attr.values = meta.enum
|
|
71
|
+
.map((v) => {
|
|
72
|
+
const key = String(v);
|
|
73
|
+
const tier = enumMeta[key]?.tier;
|
|
74
|
+
return tier ? { value: key, tier } : { value: key };
|
|
75
|
+
})
|
|
76
|
+
.filter((v) => includeInternal || v.tier !== 'internal');
|
|
77
|
+
}
|
|
78
|
+
attrs.push(attr);
|
|
79
|
+
}
|
|
80
|
+
return attrs;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function readOneComponent(sidecarPath, includeInternal) {
|
|
84
|
+
let sidecar;
|
|
85
|
+
try {
|
|
86
|
+
sidecar = JSON.parse(readFileSync(sidecarPath, 'utf8'));
|
|
87
|
+
} catch {
|
|
88
|
+
return null; // an unreadable sidecar must never crash the hook — treated as "not in the manifest"
|
|
89
|
+
}
|
|
90
|
+
const tag = sidecar['x-adiaui']?.tag;
|
|
91
|
+
if (!tag) return null;
|
|
92
|
+
return { tag, attrs: buildAttrs(sidecar, includeInternal) };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function readComponentsDir(componentsDir, includeInternal) {
|
|
96
|
+
const out = [];
|
|
97
|
+
for (const dirName of listSubdirs(componentsDir)) {
|
|
98
|
+
for (const sidecarPath of listSidecarsInDir(join(componentsDir, dirName))) {
|
|
99
|
+
const c = readOneComponent(sidecarPath, includeInternal);
|
|
100
|
+
if (c) out.push(c);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const WEB_MODULES_NON_CLUSTER_DIRS = new Set(['dist', 'node_modules']);
|
|
107
|
+
|
|
108
|
+
function readWebModulesComponents(wmRoot, includeInternal) {
|
|
109
|
+
const out = [];
|
|
110
|
+
for (const clusterName of listSubdirs(wmRoot)) {
|
|
111
|
+
if (WEB_MODULES_NON_CLUSTER_DIRS.has(clusterName)) continue;
|
|
112
|
+
const clusterDir = join(wmRoot, clusterName);
|
|
113
|
+
for (const compositeName of listSubdirs(clusterDir)) {
|
|
114
|
+
for (const sidecarPath of listSidecarsInDir(join(clusterDir, compositeName))) {
|
|
115
|
+
const c = readOneComponent(sidecarPath, includeInternal);
|
|
116
|
+
if (c) out.push(c);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Reads `{ components: [{ tag, attrs }] }` from `repoRoot`'s installed
|
|
125
|
+
* `@adia-ai/web-components` (+ `@adia-ai/web-modules` when present).
|
|
126
|
+
* Returns `null` when `@adia-ai/web-components` is not installed —
|
|
127
|
+
* callers treat that the same as "no manifest cached yet" (LLD-0019 C4's
|
|
128
|
+
* own "deny against an empty cache" contract), never a crash.
|
|
129
|
+
*/
|
|
130
|
+
export function readManifestLite(repoRoot, { includeInternal = true } = {}) {
|
|
131
|
+
const wcRoot = resolvePackageRoot(repoRoot, '@adia-ai/web-components');
|
|
132
|
+
if (!wcRoot) return null;
|
|
133
|
+
const components = readComponentsDir(join(wcRoot, 'components'), includeInternal);
|
|
134
|
+
const wmRoot = resolvePackageRoot(repoRoot, '@adia-ai/web-modules');
|
|
135
|
+
if (wmRoot) components.push(...readWebModulesComponents(wmRoot, includeInternal));
|
|
136
|
+
return { components };
|
|
137
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Ambient types for adia-posture.mjs (vendored alongside it into the
|
|
2
|
+
* factory MCP package, gh#2875) — lets a TypeScript consumer
|
|
3
|
+
* (packages/gen-ui/mcp/factory/tools/factory.ts) import the plain-JS module
|
|
4
|
+
* for types without pulling it into the program as a compiled INPUT, same
|
|
5
|
+
* outDir/rootDir emit-collision avoidance as yaml-lite.d.mts.
|
|
6
|
+
*/
|
|
7
|
+
export function readPostureMarker(repoRoot: string): { posture: 'lab' | 'bare-repo'; kit?: { 'web-components'?: string } } | null;
|
|
8
|
+
|
|
9
|
+
export function readPosture(repoRoot: string): 'lab' | 'bare-repo' | 'absent';
|
|
10
|
+
|
|
11
|
+
export function isLabPosture(repoRoot: string): boolean;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adia-posture.mjs — ADR-0108 D1 / LLD-0019 C3: reads a repo's `.adia`
|
|
3
|
+
* posture marker. The ONE thing both the lab-posture write-time hook
|
|
4
|
+
* (lab-posture-guard.mjs, LLD-0019 C4) and a future `scaffold_prototype`
|
|
5
|
+
* (LLD-0019 C6, ticket 6, out of scope here) check — posture is an
|
|
6
|
+
* EXPLICIT declared marker, never inferred from `package.json` or
|
|
7
|
+
* `node_modules` (that's `adia-info`'s separate inventory probe, a
|
|
8
|
+
* different question: "what's installed", not "what posture is
|
|
9
|
+
* declared" — ADR-0108 D1's own rejected-alternative note).
|
|
10
|
+
*
|
|
11
|
+
* MARKER SHAPE (gh#2856 Open Question 1, resolved — broader than
|
|
12
|
+
* ADR-0108 D1's own minimal `{ "posture": "lab" }`):
|
|
13
|
+
* { "posture": "lab" | "bare-repo", "kit"?: { "web-components": "<installed version>" } }
|
|
14
|
+
* `posture` is required; an explicit `"bare-repo"` value is accepted
|
|
15
|
+
* alongside plain absence (both read as bare-repo behavior, ADR-0040
|
|
16
|
+
* unchanged) so a repo can affirmatively opt OUT of lab posture in its own
|
|
17
|
+
* marker file rather than relying on the file's absence alone — useful for
|
|
18
|
+
* a repo that once ran lab posture and deliberately reverted. `kit` is
|
|
19
|
+
* optional: when present, `kit["web-components"]` records the installed
|
|
20
|
+
* `@adia-ai/web-components` version the marker was written against (a
|
|
21
|
+
* version-drift signal for a human or a future `scaffold_prototype`
|
|
22
|
+
* refresh check — LLD-0019 C3 left this open, this ticket resolves it as
|
|
23
|
+
* informational only; nothing in this ticket's build reads it yet).
|
|
24
|
+
*
|
|
25
|
+
* Malformed marker handling: missing file, unparseable JSON, missing/
|
|
26
|
+
* unrecognized `posture` value all resolve to `'bare-repo'` — the
|
|
27
|
+
* PERMISSIVE branch — never `'lab'`. A corrupt marker must never
|
|
28
|
+
* accidentally turn ON the stricter PreToolUse deny hook (ADR-0108 D5); a
|
|
29
|
+
* corrupt marker silently falling back to advisory-only bare-repo
|
|
30
|
+
* behavior is the safe failure direction. `'absent'` is reported
|
|
31
|
+
* separately from `'bare-repo'` only for callers that want to distinguish
|
|
32
|
+
* "no marker at all" from "marker present, explicitly bare-repo" — both
|
|
33
|
+
* behave identically everywhere in this plugin today.
|
|
34
|
+
*/
|
|
35
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
36
|
+
import { join } from 'node:path';
|
|
37
|
+
|
|
38
|
+
const MARKER_FILENAME = '.adia';
|
|
39
|
+
const RECOGNIZED_POSTURES = new Set(['lab', 'bare-repo']);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Reads and validates the raw `.adia` marker at `repoRoot`. Returns the
|
|
43
|
+
* parsed object when the file exists, is valid JSON, and carries a
|
|
44
|
+
* recognized `posture` value; returns `null` in every other case
|
|
45
|
+
* (absent, unreadable, malformed JSON, or an unrecognized `posture`).
|
|
46
|
+
* Never throws.
|
|
47
|
+
*/
|
|
48
|
+
export function readPostureMarker(repoRoot) {
|
|
49
|
+
const path = join(repoRoot, MARKER_FILENAME);
|
|
50
|
+
if (!existsSync(path)) return null;
|
|
51
|
+
let raw;
|
|
52
|
+
try {
|
|
53
|
+
raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
54
|
+
} catch {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return null;
|
|
58
|
+
if (!RECOGNIZED_POSTURES.has(raw.posture)) return null;
|
|
59
|
+
return raw;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Returns `'lab'`, `'bare-repo'`, or `'absent'` for `repoRoot` — the one
|
|
64
|
+
* signal LLD-0019 C4 (the write-time hook) and any future C6
|
|
65
|
+
* (`scaffold_prototype`) key on. A malformed or unreadable marker resolves
|
|
66
|
+
* to `'bare-repo'`, not `'absent'` — see the module header's "malformed
|
|
67
|
+
* marker handling" note for why the distinction stops there.
|
|
68
|
+
*/
|
|
69
|
+
export function readPosture(repoRoot) {
|
|
70
|
+
const path = join(repoRoot, MARKER_FILENAME);
|
|
71
|
+
if (!existsSync(path)) return 'absent';
|
|
72
|
+
const marker = readPostureMarker(repoRoot);
|
|
73
|
+
if (!marker) return 'bare-repo';
|
|
74
|
+
return marker.posture;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** `true` only when `readPosture(repoRoot)` is exactly `'lab'` — the
|
|
78
|
+
* single boolean the write-time hook (LLD-0019 C4) gates on. */
|
|
79
|
+
export function isLabPosture(repoRoot) {
|
|
80
|
+
return readPosture(repoRoot) === 'lab';
|
|
81
|
+
}
|