@adia-ai/adia-ui-forge 0.8.56 → 0.8.58

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.
Files changed (41) hide show
  1. package/.claude-plugin/plugin.json +3 -2
  2. package/.codex-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +47 -0
  4. package/README.md +3 -3
  5. package/__init__.py +1 -1
  6. package/commands/demo-audit.md +1 -1
  7. package/commands/gen-ui-review.md +1 -1
  8. package/commands/package-release.md +1 -1
  9. package/commands/site-deployment.md +1 -1
  10. package/package.json +2 -2
  11. package/plugin.yaml +1 -1
  12. package/prompts/demo-audit.md +1 -1
  13. package/prompts/gen-ui-review.md +1 -1
  14. package/prompts/package-release.md +1 -1
  15. package/prompts/site-deployment.md +1 -1
  16. package/scripts/forge-lint.mjs +168 -0
  17. package/scripts/lint-rules.generated.mjs +22210 -0
  18. package/scripts/site-postwrite-derivation-gate +23 -127
  19. package/skills/a2ui-maintenance/references/pipeline-overview.md +13 -11
  20. package/skills/demo-audit/references/admin-shell-anatomy.md +9 -1
  21. package/skills/demo-audit/references/app-shell-pitfalls.md +1 -1
  22. package/skills/demo-audit/references/visual-probe-triage.md +4 -1
  23. package/skills/gen-ui-review/SKILL.md +4 -1
  24. package/skills/gen-ui-review/references/loop-protocol.md +6 -5
  25. package/skills/package-release/references/changelog-discipline.md +6 -3
  26. package/skills/package-release/references/cut-procedure.md +40 -13
  27. package/skills/package-release/references/gates-catalog.md +5 -2
  28. package/skills/package-release/references/recovery-paths.md +6 -3
  29. package/skills/package-release/scripts/gate-roster.mjs +18 -7
  30. package/skills/primitive-authoring/SKILL.md +5 -2
  31. package/skills/primitive-authoring/agents/openai.yaml +1 -1
  32. package/skills/primitive-authoring/references/anti-patterns.md +40 -0
  33. package/skills/primitive-authoring/references/common-gotchas.md +6 -6
  34. package/skills/primitive-authoring/references/css-patterns.md +12 -0
  35. package/skills/primitive-authoring/references/form-control-sizing.md +10 -0
  36. package/skills/primitive-authoring/references/shell-patterns.md +8 -1
  37. package/skills/primitive-authoring/references/token-contract.md +19 -1
  38. package/skills/primitive-authoring/references/yaml-contract.md +43 -14
  39. package/skills/site-docs-authoring/SKILL.md +7 -9
  40. package/hooks/hooks.json +0 -44
  41. package/scripts/forge-lint +0 -315
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adia-ui-kit-forge",
3
- "version": "0.8.56",
3
+ "version": "0.8.58",
4
4
  "description": "Maintain the adia-ui (@adia-ai) framework itself \u2014 author primitives and shells, run the A2UI generation pipeline and its corpus, review gen-UI quality, sweep QA, cut releases, deploy. The maintainer counterpart to adia-ui-kit-factory (the consumer/app-author plugin).",
5
5
  "author": {
6
6
  "name": "Kim",
@@ -16,5 +16,6 @@
16
16
  "framework-authoring",
17
17
  "release-engineering",
18
18
  "maintainer"
19
- ]
19
+ ],
20
+ "hooks": "./adapters/claude/hooks.json"
20
21
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adia-ui-kit-forge",
3
- "version": "0.8.56",
3
+ "version": "0.8.58",
4
4
  "description": "Maintain the adia-ui (@adia-ai) framework itself — author primitives and shells, run the A2UI generation pipeline and its corpus, review gen-UI quality, sweep QA, cut releases, deploy. The maintainer counterpart to adia-ui-kit-factory (the consumer/app-author plugin).",
5
5
  "author": {
6
6
  "name": "Kim",
package/CHANGELOG.md CHANGED
@@ -1,5 +1,52 @@
1
1
  # Changelog — adia-ui-kit-forge
2
2
 
3
+ ## [0.8.58] — 2026-09-02
4
+ ### Fixed
5
+ - **`forge-lint.mjs`'s PostToolUse hook gains a `timeout: 90` key (gh#2709), matching its Python siblings (`sidecar-prewrite-guard`, `release-pretag-docs-gate`) that already carry one.** Both `hooks.json` and the derived `adapters/claude/hooks.json` mirror updated.
6
+ - **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 `forge-lint`'s own hook contract.
7
+ - **`README.md`'s `site-postwrite-derivation-gate` hook description still described the retired site-a2ui fragment-staleness half ("docs-site pages must derive from their canonical source") after ADR-0072 Decision 2's active drain (gh#2410, #2665) removed that half of the hook, found by build-2692 during gh#2692/#2698, out of scope there (gh#2699).** Repointed at the hook's real remaining behavior: a `site/sitemap.json` edit must regenerate `site/llms.txt` and the patterns/templates index, the same two derived artifacts the hook's own `SITEMAP_REASON` message names (`scripts/site-postwrite-derivation-gate`, unchanged) and that #2698 itself regenerated.
8
+
9
+ ### Docs
10
+ - **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 for this plugin's own `forge-lint.mjs`.
11
+ - **generated lint-rule bank refreshed (ADR-0108 D5, gh#2856)**: four new manifest-derived rules (`UNKNOWN-TAG`, `UNKNOWN-ATTR`, `ENUM-VALUE`, `INTERNAL-TIER`, `scripts/build/gen-lab-posture-rules.mjs`) generated into `scripts/lint/rules/generated/lab-posture/`; the bank's export list widened so a new lab-posture write-time hook (adia-ui-factory plugin) can import the same matcher functions (mechanics: repo-root CHANGELOG.md); vendored copy byte-identical to the shared bank, no behavior change for this plugin's own `forge-lint.mjs`.
12
+ - **`primitive-authoring/SKILL.md` gains the LLD-0018 R-6 checklist line (gh#2802).** A state or aria-behavior change on a yaml with a `component.md` sibling now points back at `component-md-authoring`'s authored `screenReader`/`behavioral` fields, the recommendation `component-md-authoring/SKILL.md` had carried since PR #2618 but that never landed on this skill's own side.
13
+ - **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.
14
+ - **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.
15
+ - **generated lint-rule bank refreshed** by wave-5A component.md eligibility regen (lld-0018).
16
+ - **generated lint-rule bank refreshed** by wave-5B component.md eligibility regen (lld-0018).
17
+ - **generated lint-rule bank refreshed**: wave-1A component.md eligibility added 3 composition rules (accordion/action-list/choice-card, lld-0018).
18
+ - **`primitive-authoring` skill's description clarified to say "components" instead of "primitives" (ADR-0106, gh#2766).** The installed skill name and its `primitive-audit` cross-reference are deliberately NOT renamed (ADR-0106 Decision §4: rename cost across every cross-reference and derived multi-harness manifest exceeds the benefit; the charter already reads unambiguously in context). Codex (`agents/openai.yaml`) and the Python entry point (`__init__.py`) manifests regenerated via `npm run build:codex-manifests` + `npm run build:harness-manifests` to match.
19
+ - **Region-styling rule ratified (proposed) as ADR-0105 (gh#2761).** Region elements, native `<header>`/`<section>`/`<footer>` and `<header-ui>`/`<section-ui>`/`<footer-ui>` (a CSS-only slot-routing anatomy stub, no JS/events/`.css` of its own), never carry their own layout CSS; the closest host pattern's `@scope` (`page-ui`/`card-ui`/`drawer-ui`/`modal-ui`/`anchor-bar`/the shells) always owns it, and a new host accepting region children must ship its own `@scope` treatment before it ships (gh#2760 is the live counter-example: `page-ui`'s header band missing the tabs-in-action-cluster grid `admin-page-header` carried). Harvested into `primitive-authoring/references/css-patterns.md`'s new §Region elements never self-style, cross-referenced from `shell-patterns.md`. A lint gate for the "region element ships its own CSS" direction is named as feasible future work; the "host's `@scope` treatment is incomplete for a shape" direction is rejected as a lint-gate candidate (demo-audit visual coverage is the correct mitigation).
20
+ - **`package-release` cut-procedure gains the milestone close-out step (ADR-0103, gh#2729).** `cut-procedure.md` now instructs closing the release's GitHub Milestone and opening the next one at cut time, matching the planning-surfaces contract (`scripts/verify/check-planning-surfaces.mjs`, advisory).
21
+ - **ADR knowledge-harvest batch (0095, 0098) applied to reference docs.** `primitive-authoring/references/form-control-sizing.md` gains a dated amendment note (2026-08-31, gh#2534, ADR-0095) scoping the square-minimum rule to a primitive's own outer inline-size floor, never an internal grid-column token like `--field-label-inline-min`. `demo-audit/references/{admin-shell-anatomy,app-shell-pitfalls}.md` and `primitive-authoring/references/shell-patterns.md` gain dated deprecation notes (ADR-0098) pointing the `admin-page` family and `admin-scroll` at their successors, `page-ui[band]` and `page-scroll`. adr-0097/adr-0056/adr-0059 candidates verified already satisfied by #2580.
22
+ - **ADR hygiene + harvest batch 2 (2026-09-01): adr-0100 id collision resolved, adr-0101 ratified, adr-0100 badge-weight harvested.** decision-watcher's 2026-09-01T13:51:25Z firing found two `docs/ops/adr/` files both declaring `id: adr-0100` (`adr-0100-badge-weight-exemption.md`, accepted, and `adr-0100-text-ui-level-prop-native-heading-promotion.md`, proposed), `adr_checkpoint.py`'s dict-keyed scan silently dropped the accepted badge-weight content. The proposed text-ui-level file is renumbered `adr-0102` (frontmatter, filename, and every inbound reference: `lld-0017`, three `CHANGELOG.md` files, `renderer.js` comments, `text-level-tag.test.js`) and flipped `accepted`/`ratified: 2026-09-01` (shipped via gh#2683/PR #2685). `adr-0101-inspector-ui-keeps-preview-ui-tab-chrome.md` flips `proposed` → `accepted`, normalizing its nonstandard `ratified-by` field to this corpus's `ratified: 2026-09-01` convention (Kim's ruling + PR #2682). `adr-0084`'s locked body still read "Recommended for ratification, not yet ratified" despite an `accepted` frontmatter, a dated status-update note is appended (T4-compliant, the stale sentence itself untouched) citing PR #2694. Harvested: `primitive-authoring/references/token-contract.md`'s §11 exemption list gains `badge-ui[weight]` → `icon-ui[weight]`, ADR-0100 (the badge-weight file, which keeps its id).
23
+ - **ADR harvest batch 3 (adr-0102, adr-0101).** `primitive-authoring/references/yaml-contract.md` gains a dated subsection (2026-09-01) documenting `text-ui[level]` as the worked example of a component-scoped (non-`CatalogComponentCommon`) prop, including the `0 = off` sentinel-default convention it shares with `lines: 0`. `primitive-authoring/references/token-contract.md` gains a dated note on why a `@scope` donor selector must stay tag-independent (`@scope (text-ui, [data-text])`) once a component can promote its own rendered tag, citing ADR-0102's `text-ui`/`toc-ui` fix. `primitive-authoring/references/anti-patterns.md` gains AP-R2, the mirror-don't-fold precedent from ADR-0101 (`inspector-ui`/`preview-ui`): two widgets converging visually but differing in content model get a CSS-mirrored chrome treatment, never a premature shared-module extraction.
24
+
25
+ ### Chore
26
+ - **`package-release`'s pre-cut roster gains gates 34/35, `check:yaml-events` and `check:yaml-impl-coverage` (gh#2829).** gh#2808/PR #2815 (ADR-0108 ticket 3) promoted both scripts into `npm run check` but deliberately left the release pre-flight roster untouched, so a cut could still ship a yaml `events:` block that phantoms or misses a runtime dispatch, or a schema field with no implementation evidence, without either failing pre-flight. `gate-roster.mjs` (the single source) and `cut-procedure.md` §3.1 (its transcription) both updated; `cut-procedure.md` §3.1 also picked up gate 33 (`verify:patterns-index`), which had drifted out of the transcription independently. Root `AGENTS.md`'s release pre-flight gate count bumped 32 → 34 to match `GATE_ROSTER.length`.
27
+ - **Author email in `package.json` updated to `kim.granlund@adia.ai`** (was a stale personal address).
28
+
29
+ ### Maintenance
30
+ - **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
31
+ - **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
32
+ - **`skills/` touched in this release window** (13 file(s), e.g. `references/pipeline-overview.md`) — carried by the entries above.
33
+
34
+ ## [0.8.57] — 2026-09-01
35
+
36
+ ### Removed
37
+ - **`package-release`'s release-roster gate 19 (`verify:site-a2ui`) retired with the site-a2ui mechanism itself (ADR-0072 Decision 2, gh#2410 active drain).** `gate-roster.mjs` drops the gate 19 entry (33 gates → 32; gate number left unassigned rather than renumbering 20+, per the roster's own no-renumber discipline for gate identities cited in shipped release notes); `cut-procedure.md` §3.1's transcription and its `.examples.html`-generator trigger prose updated to match. The `site-postwrite-derivation-gate` hook's fragment-staleness half (the site-a2ui regeneration nudge) is removed; its sitemap-staleness half (`site/sitemap.json` → `build:llms`/`build:patterns-index`), unrelated to site-a2ui, is unchanged. `a2ui-maintenance`'s `pipeline-overview.md` and `site-docs-authoring`'s `SKILL.md`/`primitive-authoring`'s `yaml-contract.md` no longer instruct regenerating a compiled A2UI artifact that no longer exists.
38
+
39
+ ### Fixed
40
+ - **Plugin passes `author-cross-harness-plugins`'s cross-harness validator, modulo one documented, permanent, irreconcilable exception (gh#2668).** `${CLAUDE_PLUGIN_ROOT}`/`$ARGUMENTS`/the `` !`command` `` auto-injection syntax removed from shared `gen-ui-review/SKILL.md`, the four `commands/*.md` bodies, and their linked reference docs (`demo-audit/references/visual-probe-triage.md`, `package-release/references/{cut-procedure,changelog-discipline,gates-catalog,recovery-paths}.md`, `gen-ui-review/references/loop-protocol.md`) — portable `<plugin-root>` prose / "named in the current request" phrasing throughout; command frontmatter, including `package-release`/`site-deployment`'s deliberate `disable-model-invocation: true` (AGENTS.md-ruled), is untouched since the validator never scans command frontmatter. `hooks/hooks.json` moved to `adapters/claude/hooks.json` (unchanged, `${CLAUDE_PLUGIN_ROOT}`-based); new root `hooks.json` mirrors the same event/matcher/action signature with monorepo-root-relative command paths for Codex's convention-discovered hook surface — an unverified-but-honest fallback documented in `HARNESS-NOTES.md`. **The one exception:** every skill's `disable-model-invocation:`/`user-invocable:` frontmatter — required, explicitly, by this repo's own blocking `check:skills` gate (`plugin-estate-v2.md`'s species-pinning charter) — collides head-on with the validator's opposite requirement on the exact same file, and Codex is hard-pinned to read root `skills/` directly (no adapter escape). The repo's own charter is not weakened; `npm run check:cross-harness-plugins` (`scripts/verify/check-cross-harness-plugins.mjs`, wrapping the vendored `scripts/verify/vendor/validate_cross_harness.py`) treats only this one exact finding shape as accepted (logged as INFO) and still fails on every other portability class. Full reasoning in `HARNESS-NOTES.md`.
41
+
42
+ ### Changed
43
+ - **`forge-lint` rewritten as a thin Node CLI (`forge-lint.mjs`) consuming a shared generated lint-rule bank, retiring the Python script (gh#2638, LLD-0016, ADR-0099).** The framework-authoring structural checks (SHADOW-DOM, SLOTTED, RAW-COLOR, RAW-PX, SCOPE-EXTENT, DEAD-FONT-TOKEN, BOOL-DEFAULT-TRUE, ATTR-TYPO, NATIVE-PRIMITIVE, LEGACY-SHELL, MISSING-SCOPE) now live as individually-fixtured rule modules under `scripts/lint/rules/` in the repo root, built into `scripts/lint/generated/rule-bank.mjs` and vendored byte-identically into this plugin, `adia-ui-factory`, and the gen-ui MCP's factory vendor tree (`npm run build:lint-rules`, freshness gated by `npm run check:lint-rules-fresh`). `hooks/hooks.json`'s PostToolUse command changes from `python3 ".../forge-lint" --hook` to `node ".../forge-lint.mjs" --hook`; the CLI contract, exit codes, and PostToolUse never-block invariant are unchanged. `npm run check:lint-rules-selftest` proves finding-parity against the retired script's own `selftest()` fixtures. New `npm run lint` (`scripts/lint/lint-repo.mjs`) runs the full bank against every git-tracked lintable file — standalone for now, not yet wired into `npm run check` (a first repo-wide run surfaced 330 error-severity pre-existing findings that need triage first — gh#2646). The suppression comment (`/* adia-lint-disable <rule-id> — <reason> */`) is now load-bearing, not cosmetic: it drops the matching finding, and a bare disable with no reason is itself a finding (`SUPPRESS-NO-REASON`).
44
+
45
+ ### Maintenance
46
+ - **`.claude-plugin/plugin.json` version bump** — moves in lockstep with package.json (the `/plugin update` cache key).
47
+ - **`.codex-plugin/` touched in this release window** (1 file(s), e.g. `.codex-plugin/plugin.json`) — carried by the entries above.
48
+ - **`prompts/` touched in this release window** (4 file(s), e.g. `prompts/demo-audit.md`) — carried by the entries above.
49
+
3
50
  ## [0.8.56] — 2026-08-31
4
51
 
5
52
  ### Added
package/README.md CHANGED
@@ -36,15 +36,15 @@ same decomposition scoped to A2UI catalog/tier cohesion, never touches corpus or
36
36
  `framework-checker` (fable, **no Write/Edit** — read-only corpus-wide cohesion
37
37
  audit: color tokens, typography, state naming, attrs-as-api, implementation patterns).
38
38
 
39
- ## Hooks (5 — `hooks/hooks.json` is the roster; `check:plugin-count-claims` guards this header count)
39
+ ## Hooks (5 — `adapters/claude/hooks.json` is the roster; `check:plugin-count-claims` guards this header count)
40
40
 
41
41
  `sidecar-prewrite-guard` (PreToolUse deny on hand-edits to generated
42
42
  artifacts) · `release-pretag-docs-gate` (PreToolUse: release-tagging Bash
43
43
  calls need their docs gate) · `forge-lint` (PostToolUse advisory
44
44
  authoring-smell repair loop) · `demo-postwrite-pattern-gate` (PostToolUse:
45
45
  composite demos must cite a `Pattern source:`) · `site-postwrite-derivation-gate`
46
- (PostToolUse: docs-site pages must derive from their canonical source). All
47
- ship `selftest` modes.
46
+ (PostToolUse: `site/sitemap.json` edits must regenerate `site/llms.txt` +
47
+ the patterns/templates index). All ship `selftest` modes.
48
48
 
49
49
  ## Commands
50
50
 
package/__init__.py CHANGED
@@ -49,7 +49,7 @@ def register(ctx):
49
49
  ctx.register_skill(
50
50
  name="primitive-authoring",
51
51
  path=Path(os.path.join(_HERE, "skills", "primitive-authoring", "SKILL.md")),
52
- description="Author or modify AdiaUI framework source inside the monorepo — primitives (packages/web-components), shells/composites (packages/web-modules), yaml SoTs, demos. Use to add a new primitive, fix a prop/slot/attribute/CSS variant, update a yaml, build or fix a shell (chat-shell, admin-shell, editor-shell — sidebar/pane/bespoke-tier composition), promote repeated inline content into a shared module, audit a component's four-axis contract/token usage/lifecycle for drift, or author a demo or examples.html. NOT for app screens (screen-composition), A2UI internals (a2ui-maintenance), @adia-ai/llm internals (llm-client-maintenance), or site/pages docs (site-docs-authoring).",
52
+ description="Author or modify AdiaUI framework source inside the monorepo — components (packages/web-components), shells/composites (packages/web-modules), yaml SoTs, demos. Use to add a new component, fix a prop/slot/attribute/CSS variant, update a yaml, build or fix a shell (chat-shell, admin-shell, editor-shell — sidebar/pane/bespoke-tier composition), promote repeated inline content into a shared module, audit a component's four-axis contract/token usage/lifecycle for drift, or author a demo or examples.html. NOT for app screens (screen-composition), A2UI internals (a2ui-maintenance), @adia-ai/llm internals (llm-client-maintenance), or site/pages docs (site-docs-authoring).",
53
53
  )
54
54
  ctx.register_skill(
55
55
  name="site-deployment",
@@ -6,7 +6,7 @@ disable-model-invocation: false
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Run a dogfood sweep. **$ARGUMENTS**
9
+ Run a dogfood sweep against the mode/surface named in the current request (all, if unscoped).
10
10
 
11
11
  Invoke **`demo-audit`**; run the requested modes (all, if unscoped), then
12
12
  triage findings per its severity contract. Detection is script-driven; the
@@ -6,7 +6,7 @@ disable-model-invocation: false
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Run a gen-UI review cycle. **$ARGUMENTS**
9
+ Run a gen-UI review cycle, scoped to the prompt subset named in the current request (if any).
10
10
 
11
11
  Invoke **`gen-ui-review`** and run its loop: derive ideal → decompose actual
12
12
  → score → root-cause → plan. Corpus fixes route to `a2ui-maintenance`.
@@ -6,7 +6,7 @@ disable-model-invocation: true
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Run a release. **$ARGUMENTS**
9
+ Run a release for the version/package named in the current request.
10
10
 
11
11
  Invoke **`package-release`** and run its cut procedure end-to-end: pre-flight
12
12
  gates → cut → tag → publish → deploy → notes. This command's invocation is
@@ -6,7 +6,7 @@ disable-model-invocation: true
6
6
  user-invocable: true
7
7
  ---
8
8
 
9
- Deploy or diagnose an exe.dev surface. **$ARGUMENTS**
9
+ Deploy or diagnose the exe.dev host/service named in the current request.
10
10
 
11
11
  Invoke **`site-deployment`** and follow its playbook for the named host; verify
12
12
  against the live endpoint before reporting done.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adia-ai/adia-ui-forge",
3
- "version": "0.8.56",
3
+ "version": "0.8.58",
4
4
  "description": "Maintain the adia-ui (@adia-ai) framework itself — author primitives and shells, run the A2UI generation pipeline and its corpus, review gen-UI quality, sweep QA, cut releases, deploy. The maintainer counterpart to adia-factory (the consumer/app-author plugin).",
5
5
  "keywords": [
6
6
  "adia-ui",
@@ -15,7 +15,7 @@
15
15
  "license": "MIT",
16
16
  "author": {
17
17
  "name": "Kim",
18
- "email": "kim@sublimeheroics.com"
18
+ "email": "kim.granlund@adia.ai"
19
19
  },
20
20
  "bugs": {
21
21
  "email": "kim.granlund@adia.ai"
package/plugin.yaml CHANGED
@@ -1,4 +1,4 @@
1
1
  name: "adia-ui-kit-forge"
2
- version: "0.8.56"
2
+ version: "0.8.58"
3
3
  description: "Maintain the adia-ui (@adia-ai) framework itself — author primitives and shells, run the A2UI generation pipeline and its corpus, review gen-UI quality, sweep QA, cut releases, deploy. The maintainer counterpart to adia-ui-kit-factory (the consumer/app-author plugin)."
4
4
  manifest_version: 1
@@ -3,7 +3,7 @@ description: "Run the multi-mode QA sweep (visual probe, static QA, typo, native
3
3
  argument-hint: "[mode or surface]"
4
4
  ---
5
5
 
6
- Run a dogfood sweep. **$ARGUMENTS**
6
+ Run a dogfood sweep against the mode/surface named in the current request (all, if unscoped).
7
7
 
8
8
  Invoke **`demo-audit`**; run the requested modes (all, if unscoped), then
9
9
  triage findings per its severity contract. Detection is script-driven; the
@@ -3,7 +3,7 @@ description: "Run the closed-loop gen-UI quality review — score gallery output
3
3
  argument-hint: "[prompt subset]"
4
4
  ---
5
5
 
6
- Run a gen-UI review cycle. **$ARGUMENTS**
6
+ Run a gen-UI review cycle, scoped to the prompt subset named in the current request (if any).
7
7
 
8
8
  Invoke **`gen-ui-review`** and run its loop: derive ideal → decompose actual
9
9
  → score → root-cause → plan. Corpus fixes route to `a2ui-maintenance`.
@@ -3,7 +3,7 @@ description: "Cut, tag, publish, and deploy an @adia-ai release (lockstep roster
3
3
  argument-hint: "[version or package]"
4
4
  ---
5
5
 
6
- Run a release. **$ARGUMENTS**
6
+ Run a release for the version/package named in the current request.
7
7
 
8
8
  Invoke **`package-release`** and run its cut procedure end-to-end: pre-flight
9
9
  gates → cut → tag → publish → deploy → notes. This command's invocation is
@@ -3,7 +3,7 @@ description: "Deploy a service or dist to the exe.dev VMs (ui-kit.exe.xyz and si
3
3
  argument-hint: "[host or service]"
4
4
  ---
5
5
 
6
- Deploy or diagnose an exe.dev surface. **$ARGUMENTS**
6
+ Deploy or diagnose the exe.dev host/service named in the current request.
7
7
 
8
8
  Invoke **`site-deployment`** and follow its playbook for the named host; verify
9
9
  against the live endpoint before reporting done.
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * forge-lint.mjs — thin Node CLI consuming the vendored generated rule bank
4
+ * (lint-rules.generated.mjs, LLD-0016 §C5) for the adia-ui FRAMEWORK source
5
+ * (maintainer slice): the same structural-smell checks forge-lint (Python)
6
+ * ran, now run: 'shared' + 'forge-only' scope rules only — no consumer/app
7
+ * traps (SSR, hardcoded overlay open, LLM key, genui). See ADR-0099 for why
8
+ * this replaced the Python script as the hooks.json entry point.
9
+ *
10
+ * Usage:
11
+ * forge-lint.mjs <file>... lint files; exit 1 if any error-severity finding, else 0
12
+ * forge-lint.mjs - lint stdin as a generic source file
13
+ * forge-lint.mjs --hook PostToolUse hook mode: read event JSON on stdin, lint the
14
+ * written source file, print advisory findings, ALWAYS exit 0
15
+ * forge-lint.mjs selftest run the inherited parity fixtures + the hook exit-0 invariant
16
+ */
17
+ import { readFileSync, existsSync } from 'node:fs';
18
+ import { dirname, join } from 'node:path';
19
+ import { fileURLToPath } from 'node:url';
20
+
21
+ const __dirname = dirname(fileURLToPath(import.meta.url));
22
+ const { makeLinter, ext, LINT_EXT, RULES } = await import(join(__dirname, 'lint-rules.generated.mjs'));
23
+
24
+ /**
25
+ * Repo-root-relative parity fixtures are only reachable when this CLI is
26
+ * running from inside the monorepo checkout (the plugin's own source tree,
27
+ * not a packed/vendored install elsewhere) — walk up from this file looking
28
+ * for scripts/lint/rule-bank.selftest.mjs rather than a fixed `../..` depth,
29
+ * since this same CLI shape is vendored to more than one directory depth
30
+ * (ADR-0099). A vendored copy outside the monorepo has no fixtures to find
31
+ * and `selftest` reports that plainly instead of crashing.
32
+ */
33
+ function findParityFixtures() {
34
+ let dir = __dirname;
35
+ for (let i = 0; i < 8; i += 1) {
36
+ const candidate = join(dir, 'scripts/lint/rule-bank.selftest.mjs');
37
+ if (existsSync(candidate)) return candidate;
38
+ dir = join(dir, '..');
39
+ }
40
+ return null;
41
+ }
42
+
43
+ const SCOPE_ANCHOR = ['packages/web-components/', 'packages/web-modules/'];
44
+ const { lintText } = makeLinter(RULES, { scopes: ['shared', 'forge-only'] });
45
+
46
+ function render(path, findings) {
47
+ const out = [`forge-lint: ${findings.length} structural smell(s) in ${path || '<stdin>'}`];
48
+ for (const f of findings) {
49
+ out.push(` [${f.name}] line ${f.line}: ${f.snippet}`);
50
+ out.push(` → ${f.why}`);
51
+ }
52
+ return out.join('\n');
53
+ }
54
+
55
+ function lintPath(path) {
56
+ try {
57
+ return lintText(readFileSync(path, 'utf8'), path);
58
+ } catch (err) {
59
+ return [{ name: 'READ-ERROR', line: 1, snippet: path.slice(0, 90), why: `failed to read file: ${err.message}` }];
60
+ }
61
+ }
62
+
63
+ async function readStdin() {
64
+ const chunks = [];
65
+ for await (const c of process.stdin) chunks.push(c);
66
+ return Buffer.concat(chunks).toString('utf8');
67
+ }
68
+
69
+ async function hook() {
70
+ let event;
71
+ try {
72
+ event = JSON.parse(await readStdin());
73
+ } catch {
74
+ return 0;
75
+ }
76
+ const ti = event.tool_input || {};
77
+ const path = ti.file_path || '';
78
+ const norm = path.replace(/\\/g, '/');
79
+ if (!SCOPE_ANCHOR.some((a) => norm.includes(a))) return 0;
80
+ if (!LINT_EXT.includes(ext(path))) return 0;
81
+ let text = ti.content;
82
+ if (text === undefined || text === null) {
83
+ if (!existsSync(path)) return 0;
84
+ try {
85
+ text = readFileSync(path, 'utf8');
86
+ } catch {
87
+ return 0;
88
+ }
89
+ }
90
+ const findings = lintText(text, path);
91
+ if (findings.length) {
92
+ const context = render(path, findings)
93
+ + '\n (forge-lint advisory — adia-ui framework authoring smells; the skills own the judgment)';
94
+ process.stdout.write(JSON.stringify({
95
+ hookSpecificOutput: { hookEventName: 'PostToolUse', additionalContext: context },
96
+ }) + '\n');
97
+ }
98
+ return 0; // NEVER block
99
+ }
100
+
101
+ async function selftest() {
102
+ const fixturesPath = findParityFixtures();
103
+ if (!fixturesPath) {
104
+ console.log('selftest: SKIP — no monorepo checkout found (parity fixtures live in scripts/lint/, not vendored)');
105
+ return 0;
106
+ }
107
+ const { PARITY_CASES } = await import(fixturesPath);
108
+ let ok = true;
109
+ for (const { name, text, expected, scopes } of PARITY_CASES) {
110
+ if (scopes && !(scopes.includes('forge-only'))) continue; // this CLI's own cases only
111
+ const got = new Set(lintText(text, name).map((f) => f.name));
112
+ const missing = [...expected].filter((e) => !got.has(e));
113
+ if (missing.length) {
114
+ ok = false;
115
+ console.error(`selftest: ${name} MISSING ${JSON.stringify(missing)} (got ${JSON.stringify([...got])})`);
116
+ }
117
+ if (expected.size === 0 && got.size) {
118
+ ok = false;
119
+ console.error(`selftest: ${name} expected clean, got ${JSON.stringify([...got])}`);
120
+ }
121
+ }
122
+ // never-block invariant
123
+ const savedWrite = process.stdout.write.bind(process.stdout);
124
+ let hookOut = '';
125
+ process.stdout.write = (chunk, ...rest) => { hookOut += chunk; return true; };
126
+ const stdinMock = Buffer.from(JSON.stringify({ tool_input: { file_path: 'packages/web-components/c.css', content: '.a { color:#f00; }' } }));
127
+ const origStdin = process.stdin;
128
+ Object.defineProperty(process, 'stdin', { value: (async function* () { yield stdinMock; })(), configurable: true });
129
+ let rc;
130
+ try {
131
+ rc = await hook();
132
+ } finally {
133
+ process.stdout.write = savedWrite;
134
+ Object.defineProperty(process, 'stdin', { value: origStdin, configurable: true });
135
+ }
136
+ if (rc !== 0) {
137
+ ok = false;
138
+ console.error('selftest: --hook did not exit 0 on smelly input');
139
+ }
140
+ if (rc === 0 && hookOut && !hookOut.includes('"hookSpecificOutput"')) {
141
+ ok = false;
142
+ console.error('selftest: --hook stdout is not structured hookSpecificOutput JSON');
143
+ }
144
+ console.log(ok ? 'selftest: PASS' : 'selftest: FAIL');
145
+ return ok ? 0 : 1;
146
+ }
147
+
148
+ async function main(argv) {
149
+ if (argv[0] === 'selftest') return selftest();
150
+ if (argv.includes('--hook')) return hook();
151
+ const args = argv.filter((a) => a === '-' || !a.startsWith('-'));
152
+ if (!args.length) {
153
+ console.error('usage: forge-lint.mjs <file>... | forge-lint.mjs - | forge-lint.mjs --hook | forge-lint.mjs selftest');
154
+ return 2;
155
+ }
156
+ let total = 0;
157
+ for (let path of args) {
158
+ const findings = path === '-' ? lintText(await readStdin(), '<stdin>') : lintPath(path);
159
+ if (path === '-') path = '<stdin>';
160
+ if (findings.length) {
161
+ total += findings.length;
162
+ console.log(render(path, findings));
163
+ }
164
+ }
165
+ return total ? 1 : 0;
166
+ }
167
+
168
+ process.exit(await main(process.argv.slice(2)));