@adia-ai/adia-ui-factory 0.2.4 → 0.8.1

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 (145) hide show
  1. package/.claude-plugin/plugin.json +6 -8
  2. package/.mcp.json +1 -1
  3. package/CHANGELOG.md +40 -67
  4. package/README.md +32 -53
  5. package/agents/app-architect.md +28 -0
  6. package/agents/routing-corpus.json +202 -0
  7. package/agents/screen-composer.md +27 -0
  8. package/bin/adia-info +235 -0
  9. package/bin/adia-lint +17 -2
  10. package/commands/adia-genui.md +5 -8
  11. package/commands/adia-migrate.md +5 -6
  12. package/commands/adia-orient.md +5 -10
  13. package/commands/adia-scaffold.md +7 -13
  14. package/commands/adia-verify.md +5 -6
  15. package/package.json +5 -6
  16. package/references/a2ui-mcp-tools.md +11 -8
  17. package/references/authoring-components.md +25 -1
  18. package/references/component-model.md +52 -4
  19. package/references/contracts/a2ui-mcp-surface.md +35 -0
  20. package/references/contracts/migration-guide-format.md +34 -0
  21. package/references/data-and-hydration.md +8 -5
  22. package/references/genui-a2ui.md +12 -6
  23. package/references/llm.md +22 -39
  24. package/references/migration.md +110 -36
  25. package/references/project-shapes.md +11 -13
  26. package/references/shell-admin.md +5 -2
  27. package/references/shell-chat.md +5 -3
  28. package/references/shell-editor.md +1 -1
  29. package/references/shell-embed.md +40 -26
  30. package/references/shell-simple.md +5 -2
  31. package/references/spa-architecture.md +28 -73
  32. package/references/ssr-integration.md +1 -1
  33. package/references/verification.md +8 -12
  34. package/skills/adia-compose/SKILL.md +121 -0
  35. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/Guidelines.md +3 -3
  36. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/components.md +1 -1
  37. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/styles.md +1 -1
  38. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/tokens.md +3 -3
  39. package/skills/{adia-ui-kit → adia-compose}/assets/templates/FEEDBACK.template.md +7 -7
  40. package/skills/{adia-ui-kit → adia-compose}/assets/templates/RESPONSE.template.md +6 -7
  41. package/skills/adia-compose/references/composition-traps.md +58 -0
  42. package/skills/adia-compose/references/feedback-discipline.md +20 -0
  43. package/skills/adia-compose/references/spec-to-ui-reasoning.md +158 -0
  44. package/skills/adia-data/SKILL.md +78 -0
  45. package/skills/adia-genui/SKILL.md +83 -0
  46. package/skills/adia-host/SKILL.md +119 -0
  47. package/skills/adia-llm/SKILL.md +36 -0
  48. package/skills/adia-migrate/SKILL.md +95 -0
  49. package/skills/adia-orient/SKILL.md +135 -0
  50. package/skills/adia-project/SKILL.md +83 -0
  51. package/skills/adia-shells/SKILL.md +74 -0
  52. package/skills/adia-verify/SKILL.md +43 -0
  53. package/skills/adia-verify/references/verification.md +42 -0
  54. package/commands/adia-compose.md +0 -10
  55. package/commands/adia-wire.md +0 -13
  56. package/skills/adia-ui-compose/SKILL.md +0 -57
  57. package/skills/adia-ui-data/SKILL.md +0 -62
  58. package/skills/adia-ui-factory/SKILL.md +0 -113
  59. package/skills/adia-ui-genui/SKILL.md +0 -74
  60. package/skills/adia-ui-kit/CHANGELOG.md +0 -41
  61. package/skills/adia-ui-kit/SKILL.md +0 -442
  62. package/skills/adia-ui-kit/assets/team-scaffold/archive-README.md +0 -30
  63. package/skills/adia-ui-kit/assets/team-scaffold/drafts-README.md +0 -57
  64. package/skills/adia-ui-kit/assets/team-scaffold/inbox-README.md +0 -76
  65. package/skills/adia-ui-kit/assets/team-scaffold/outbox-README.md +0 -46
  66. package/skills/adia-ui-kit/assets/team-scaffold/team-root.README.md +0 -51
  67. package/skills/adia-ui-kit/assets/team-scaffold/tickets-README.md +0 -208
  68. package/skills/adia-ui-kit/assets/team-scaffold/tickets-REGISTRY.md +0 -81
  69. package/skills/adia-ui-kit/assets/templates/AGENTS.template.md +0 -128
  70. package/skills/adia-ui-kit/assets/templates/CLAUDE.template.md +0 -1
  71. package/skills/adia-ui-kit/assets/templates/figma-make-guidelines.md +0 -90
  72. package/skills/adia-ui-kit/evals/adversarial-content-trust.json +0 -83
  73. package/skills/adia-ui-kit/evals/evals.json +0 -55
  74. package/skills/adia-ui-kit/evals/routing-corpus.json +0 -121
  75. package/skills/adia-ui-kit/evals/teach-routing-cases.json +0 -92
  76. package/skills/adia-ui-kit/references/capabilities.md +0 -245
  77. package/skills/adia-ui-kit/references/catalog-lookup.md +0 -186
  78. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v1.md +0 -8
  79. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v2.md +0 -3585
  80. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v3.md +0 -1899
  81. package/skills/adia-ui-kit/references/common-gotchas-consumer.md +0 -522
  82. package/skills/adia-ui-kit/references/component-literacy.md +0 -291
  83. package/skills/adia-ui-kit/references/composition.md +0 -245
  84. package/skills/adia-ui-kit/references/correction-loop.md +0 -462
  85. package/skills/adia-ui-kit/references/css-policy.md +0 -343
  86. package/skills/adia-ui-kit/references/decision-model.md +0 -158
  87. package/skills/adia-ui-kit/references/diagnostic-output.md +0 -57
  88. package/skills/adia-ui-kit/references/docs-catalog.md +0 -239
  89. package/skills/adia-ui-kit/references/exclusions.md +0 -225
  90. package/skills/adia-ui-kit/references/feedback-authoring.md +0 -466
  91. package/skills/adia-ui-kit/references/figma-make.md +0 -244
  92. package/skills/adia-ui-kit/references/flows-auth.md +0 -307
  93. package/skills/adia-ui-kit/references/flows-content.md +0 -206
  94. package/skills/adia-ui-kit/references/flows-data.md +0 -348
  95. package/skills/adia-ui-kit/references/flows-interaction.md +0 -181
  96. package/skills/adia-ui-kit/references/flows-shells.md +0 -336
  97. package/skills/adia-ui-kit/references/gap-detection.md +0 -266
  98. package/skills/adia-ui-kit/references/harness.md +0 -284
  99. package/skills/adia-ui-kit/references/loading-protocol.md +0 -406
  100. package/skills/adia-ui-kit/references/local-link.md +0 -198
  101. package/skills/adia-ui-kit/references/mcp.md +0 -401
  102. package/skills/adia-ui-kit/references/migration.md +0 -116
  103. package/skills/adia-ui-kit/references/mission.md +0 -194
  104. package/skills/adia-ui-kit/references/mode-routing.md +0 -298
  105. package/skills/adia-ui-kit/references/patterns-agent.md +0 -223
  106. package/skills/adia-ui-kit/references/patterns-data.md +0 -1065
  107. package/skills/adia-ui-kit/references/patterns-forms.md +0 -223
  108. package/skills/adia-ui-kit/references/patterns-layout.md +0 -326
  109. package/skills/adia-ui-kit/references/patterns-recipes.md +0 -1416
  110. package/skills/adia-ui-kit/references/patterns-shells.md +0 -1097
  111. package/skills/adia-ui-kit/references/patterns-traits.md +0 -259
  112. package/skills/adia-ui-kit/references/patterns-wiring.md +0 -339
  113. package/skills/adia-ui-kit/references/pev-rationale.md +0 -137
  114. package/skills/adia-ui-kit/references/reasoning-gate.md +0 -146
  115. package/skills/adia-ui-kit/references/reasoning-methodologies.md +0 -112
  116. package/skills/adia-ui-kit/references/recon.md +0 -192
  117. package/skills/adia-ui-kit/references/rendering-model.md +0 -431
  118. package/skills/adia-ui-kit/references/response-authoring.md +0 -398
  119. package/skills/adia-ui-kit/references/session-log-schema.md +0 -203
  120. package/skills/adia-ui-kit/references/session-log-template.md +0 -118
  121. package/skills/adia-ui-kit/references/setup.md +0 -652
  122. package/skills/adia-ui-kit/references/skill-hygiene.md +0 -135
  123. package/skills/adia-ui-kit/references/spec-to-ui-reasoning.md +0 -394
  124. package/skills/adia-ui-kit/references/state.md +0 -229
  125. package/skills/adia-ui-kit/references/teach-protocol.md +0 -421
  126. package/skills/adia-ui-kit/references/theme.md +0 -142
  127. package/skills/adia-ui-kit/references/version-check.md +0 -69
  128. package/skills/adia-ui-kit/scripts/audit-decision-trace.mjs +0 -198
  129. package/skills/adia-ui-kit/scripts/audit-kit-roster.mjs +0 -366
  130. package/skills/adia-ui-kit/scripts/changelog-archive.mjs +0 -126
  131. package/skills/adia-ui-kit/scripts/check-adia-versions.py +0 -404
  132. package/skills/adia-ui-kit/scripts/run-skill-evals.mjs +0 -358
  133. package/skills/adia-ui-kit/scripts/validate-session-log.mjs +0 -242
  134. package/skills/adia-ui-kit/skill.json +0 -102
  135. package/skills/adia-ui-llm/SKILL.md +0 -51
  136. package/skills/adia-ui-migrate/SKILL.md +0 -64
  137. package/skills/adia-ui-migration/CHANGELOG.md +0 -47
  138. package/skills/adia-ui-migration/SKILL.md +0 -381
  139. package/skills/adia-ui-migration/skill.json +0 -29
  140. package/skills/adia-ui-project/SKILL.md +0 -77
  141. package/skills/adia-ui-shells/SKILL.md +0 -62
  142. package/skills/adia-ui-spa/SKILL.md +0 -52
  143. package/skills/adia-ui-ssr/SKILL.md +0 -52
  144. package/skills/adia-ui-verify/SKILL.md +0 -44
  145. /package/skills/{adia-ui-kit → adia-compose}/assets/templates/FEEDBACK-OMISSIONS-DEFENSE.template.md +0 -0
@@ -1,366 +0,0 @@
1
- #!/usr/bin/env node
2
- // audit-kit-roster.mjs — §SelfAudit enforcement for adia-ui-kit (factory/consumer edition).
3
- // Standalone version — audit-axes inlined (no .agents/ import; safe in bare consumer repos).
4
- //
5
- // Axes 1–8 (universal) + Axis 9 = pattern/flow bundle-roster currency.
6
- //
7
- // This is the kit's structural self-audit — the mechanical check the
8
- // §Plan-Execute-Verify loop and §Teach landing name as their verify target
9
- // ("audit-kit-roster.mjs --strict"). It answers: does the SKILL.md itself still
10
- // satisfy its structural invariants? (manifest ⊨ disk, reference graph resolves,
11
- // version literals agree, every pattern/flow bundle the seed advertises exists).
12
- //
13
- // Usage (from the skill dir, or from anywhere with --dir):
14
- // node scripts/audit-kit-roster.mjs
15
- // node scripts/audit-kit-roster.mjs --json
16
- // node scripts/audit-kit-roster.mjs --strict # exit 1 on any drift
17
- // node scripts/audit-kit-roster.mjs --dir <path-to-skill-root>
18
-
19
- import fs from 'node:fs';
20
- import path from 'node:path';
21
- import process from 'node:process';
22
- import { fileURLToPath } from 'node:url';
23
-
24
- const __filename = fileURLToPath(import.meta.url);
25
- const SCRIPT_DIR = path.dirname(__filename);
26
-
27
- // Resolve skill dir: --dir flag or parent of scripts/
28
- function resolveSkillDir(argv) {
29
- const dirFlag = argv.find(a => a.startsWith('--dir='));
30
- if (dirFlag) return path.resolve(dirFlag.slice('--dir='.length));
31
- const idx = argv.indexOf('--dir');
32
- if (idx >= 0 && argv[idx + 1]) return path.resolve(argv[idx + 1]);
33
- return path.dirname(SCRIPT_DIR);
34
- }
35
-
36
- const SKILL_DIR = resolveSkillDir(process.argv.slice(2));
37
- const SKILL_MD = path.join(SKILL_DIR, 'SKILL.md');
38
- const SKILL_JSON = path.join(SKILL_DIR, 'skill.json');
39
-
40
- const read = (p) => { try { return fs.readFileSync(p, 'utf8'); } catch { return ''; } };
41
-
42
- function parseArgs(argv) {
43
- if (argv.includes('--help') || argv.includes('-h')) {
44
- console.log(`audit-kit-roster.mjs — adia-ui-kit §SelfAudit (9 axes, factory/consumer edition).
45
-
46
- USAGE
47
- node scripts/audit-kit-roster.mjs [--json] [--strict] [--dir <skill-root>]
48
-
49
- AXES (1-8 universal, inlined; 9 kit-specific)
50
- 1 Manifest enforcement — skill.json files[] matches disk (.claude/docs/ excluded)
51
- 2 Reference graph — every markdown link in SKILL.md resolves
52
- 3 Capability-menu drift — §ColdStartTriage entry refs exist (skipped if absent)
53
- 4 Version-literal parity — SKILL.md frontmatter version == skill.json
54
- 5 Phase-label absence — no stale "(Phase N)" annotations
55
- 6 Fence-leak detection — no headings orphaned inside code fences
56
- 7 Content currency — every files[] entry exists on disk
57
- 8 CLI helper currency — npm/node helpers cited in SKILL.md resolve
58
- 9 Bundle-roster currency — every references/patterns-*.md and
59
- references/flows-*.md named in §Patterns/§Flows bundle maps exists on
60
- disk AND is declared in skill.json files[].
61
-
62
- EXIT CODES
63
- 0 All axes clean 1 At least one axis drifting (with --strict)
64
- `);
65
- process.exit(0);
66
- }
67
- return { json: argv.includes('--json'), strict: argv.includes('--strict') };
68
- }
69
-
70
- // ─── inlined audit-axes (universal axes 1-8) ─────────────────────────────────
71
-
72
- function walkDir(dir, { include = null, exclude = ['node_modules', '.git', 'docs'] } = {}) {
73
- const results = [];
74
- if (!fs.existsSync(dir)) return results;
75
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
76
- if (exclude.some(x => entry.name === x)) continue;
77
- const fullPath = path.join(dir, entry.name);
78
- if (entry.isDirectory()) {
79
- results.push(...walkDir(fullPath, { include, exclude }));
80
- } else {
81
- if (!include || include.some(ext => entry.name.endsWith(ext))) {
82
- results.push(fullPath);
83
- }
84
- }
85
- }
86
- return results;
87
- }
88
-
89
- function readSafe(p) {
90
- try { return fs.readFileSync(p, 'utf8'); } catch { return ''; }
91
- }
92
-
93
- function parseFrontmatter(content) {
94
- const match = content.match(/^---\n([\s\S]*?)\n---/);
95
- if (!match) return {};
96
- const result = {};
97
- for (const line of match[1].split('\n')) {
98
- const m = line.match(/^(\w+):\s*['"]?(.*?)['"]?\s*$/);
99
- if (m) result[m[1]] = m[2];
100
- }
101
- return result;
102
- }
103
-
104
- function extractMarkdownLinks(content, filePath) {
105
- const links = [];
106
- const lines = content.split('\n');
107
- let inFence = false;
108
- for (let i = 0; i < lines.length; i++) {
109
- const line = lines[i];
110
- if (/^```/.test(line)) { inFence = !inFence; continue; }
111
- if (inFence) continue;
112
- const lineStripped = line.replace(/`[^`]*`/g, '``');
113
- const re = /\[([^\]]*)\]\(([^)]+)\)/g;
114
- let m;
115
- while ((m = re.exec(lineStripped)) !== null) {
116
- const url = m[2];
117
- if (url.startsWith('http') || url.startsWith('#') || url.startsWith('mailto:')) continue;
118
- if (url.startsWith('~/')) continue;
119
- if (/<[^>]+>/.test(url)) continue;
120
- if (/^[a-z]+$/.test(url) && !url.includes('/')) continue;
121
- const cleanUrl = url.split('#')[0];
122
- if (!cleanUrl) continue;
123
- links.push({ text: m[1], url: cleanUrl, line: i + 1, file: filePath });
124
- }
125
- }
126
- return links;
127
- }
128
-
129
- function finding(type, message, extra = {}) {
130
- return { type, message, ...extra };
131
- }
132
-
133
- function axis1_manifestEnforcement(ctx) {
134
- const { skillDir, skillJson } = ctx;
135
- const pkg = JSON.parse(readSafe(skillJson) || '{}');
136
- const declared = new Set((pkg.files || []).map(f => path.resolve(skillDir, f)));
137
- const onDisk = new Set();
138
-
139
- for (const entry of fs.readdirSync(skillDir, { withFileTypes: true })) {
140
- if (!entry.isFile()) continue;
141
- if (!/\.(md|json|mjs|js)$/.test(entry.name)) continue;
142
- onDisk.add(path.join(skillDir, entry.name));
143
- }
144
-
145
- for (const subDir of ['references', 'scripts', 'evals', 'assets']) {
146
- const full = path.join(skillDir, subDir);
147
- for (const f of walkDir(full, { include: ['.md', '.json', '.mjs', '.js', '.ts', '.py'] })) {
148
- onDisk.add(f);
149
- }
150
- }
151
-
152
- const undeclared = [...onDisk].filter(f => !declared.has(f)).map(f => path.relative(skillDir, f));
153
- const missing = [...declared].filter(f => !onDisk.has(f)).map(f => path.relative(skillDir, f));
154
- const status = (undeclared.length + missing.length) > 0 ? 'drift' : 'ok';
155
- const findings = [
156
- ...undeclared.map(f => finding('undeclared', `on disk but not in files[]: ${f}`, { path: f })),
157
- ...missing.map(f => finding('missing', `in files[] but not on disk: ${f}`, { path: f })),
158
- ];
159
- return { axis: 'manifestEnforcement', axis_num: 1, status, findings, summary: `${undeclared.length} undeclared, ${missing.length} missing` };
160
- }
161
-
162
- function axis2_referenceGraph(ctx) {
163
- const { skillDir, skillMd } = ctx;
164
- const findings = [];
165
- const content = readSafe(skillMd);
166
- const links = extractMarkdownLinks(content, skillMd);
167
- for (const { url, line, file } of links) {
168
- const resolved = path.resolve(path.dirname(file), url);
169
- if (!fs.existsSync(resolved)) {
170
- const relFile = path.relative(skillDir, file);
171
- const relResolved = path.relative(skillDir, resolved);
172
- findings.push(finding('broken-link', `${relFile}:${line} → ${url} (resolves to ${relResolved}, not found)`, { file: relFile, line, url }));
173
- }
174
- }
175
- return { axis: 'referenceGraph', axis_num: 2, status: findings.length > 0 ? 'drift' : 'ok', findings, summary: `${findings.length} broken link(s)` };
176
- }
177
-
178
- function axis3_capabilityMenuDrift(ctx) {
179
- const { skillDir, skillMd } = ctx;
180
- const content = readSafe(skillMd);
181
- const sectionMatch = content.match(/##\s+§ColdStartTriage[\s\S]*?(?=\n##\s+|$)/);
182
- if (!sectionMatch) {
183
- return { axis: 'capabilityMenuDrift', axis_num: 3, status: 'ok', findings: [], summary: 'No §ColdStartTriage section found (skip)' };
184
- }
185
- const section = sectionMatch[0];
186
- const links = extractMarkdownLinks(section, skillMd);
187
- const barePaths = [...section.matchAll(/`(references\/[^`]+\.md)`/g)].map(m => m[1]);
188
- const findings = [];
189
- for (const { url, text } of links) {
190
- if (!url.startsWith('references/') && !url.startsWith('../')) continue;
191
- const resolved = path.resolve(skillDir, url);
192
- if (!fs.existsSync(resolved)) findings.push(finding('missing-entry-ref', `Mode entry ref missing: [${text}](${url})`, { url }));
193
- }
194
- for (const p of barePaths) {
195
- const resolved = path.join(skillDir, p);
196
- if (!fs.existsSync(resolved)) findings.push(finding('missing-entry-ref', `Bare path in §ColdStartTriage not found: ${p}`, { url: p }));
197
- }
198
- return { axis: 'capabilityMenuDrift', axis_num: 3, status: findings.length > 0 ? 'drift' : 'ok', findings, summary: `${findings.length} missing entry-reference path(s)` };
199
- }
200
-
201
- function axis4_versionLiteralParity(ctx) {
202
- const { skillMd, skillJson } = ctx;
203
- const content = readSafe(skillMd);
204
- const pkg = JSON.parse(readSafe(skillJson) || '{}');
205
- const jsonVersion = pkg.version || null;
206
- const findings = [];
207
- if (!jsonVersion) {
208
- findings.push(finding('no-version', 'skill.json has no version field'));
209
- return { axis: 'versionLiteralParity', axis_num: 4, status: 'drift', findings, summary: 'no version in skill.json' };
210
- }
211
- const fm = parseFrontmatter(content);
212
- if (fm.version && fm.version !== jsonVersion) {
213
- findings.push(finding('frontmatter-drift', `SKILL.md frontmatter version: ${fm.version} != skill.json: ${jsonVersion}`));
214
- }
215
- const statusMatch = content.match(/##\s+§Status([\s\S]*?)(?=\n##\s+|$)/);
216
- if (statusMatch) {
217
- const versionRe = /\bv?(\d+\.\d+\.\d+)\b/g;
218
- let m;
219
- while ((m = versionRe.exec(statusMatch[1])) !== null) {
220
- if (m[1] !== jsonVersion) findings.push(finding('status-section-stale', `§Status body contains version ${m[1]} != skill.json ${jsonVersion}`));
221
- }
222
- }
223
- return { axis: 'versionLiteralParity', axis_num: 4, status: findings.length > 0 ? 'drift' : 'ok', findings, summary: findings.length > 0 ? `${findings.length} version-literal mismatch(es)` : `all literals match ${jsonVersion}` };
224
- }
225
-
226
- function axis5_phaseLabelAbsence(ctx) {
227
- const { skillMd } = ctx;
228
- const content = readSafe(skillMd);
229
- const lines = content.split('\n');
230
- const findings = [];
231
- const phaseRe = /\(Phase\s+\d+[\w\s—.]*\)|Phase\s+\d+\s*\(v\d+\.\d+\.\d+|Phase\s+\d+\s+references\s*\(planned\)/i;
232
- for (let i = 0; i < lines.length; i++) {
233
- if (phaseRe.test(lines[i])) findings.push(finding('phase-label', `Line ${i + 1}: stale phase annotation — ${lines[i].trim()}`, { line: i + 1 }));
234
- }
235
- return { axis: 'phaseLabelAbsence', axis_num: 5, status: findings.length > 0 ? 'drift' : 'ok', findings, summary: `${findings.length} stale phase label(s)` };
236
- }
237
-
238
- function axis6_fenceLeakDetection(ctx) {
239
- const { skillMd } = ctx;
240
- const content = readSafe(skillMd);
241
- const lines = content.split('\n');
242
- const findings = [];
243
- let inFence = false;
244
- let fenceLang = '';
245
- for (let i = 0; i < lines.length; i++) {
246
- const line = lines[i];
247
- const fenceOpen = line.match(/^```(\w*)/);
248
- if (fenceOpen && !inFence) { inFence = true; fenceLang = fenceOpen[1].toLowerCase(); continue; }
249
- if (inFence && line.startsWith('```') && !line.startsWith('````')) { inFence = false; fenceLang = ''; continue; }
250
- if (inFence) {
251
- if (['markdown', 'md', 'mdx'].includes(fenceLang)) continue;
252
- if (/^#{2,3}\s/.test(line)) findings.push(finding('fence-leak', `Line ${i + 1}: heading inside \`\`\`${fenceLang}\`\`\` fence — ${line.trim()}`, { line: i + 1 }));
253
- }
254
- }
255
- return { axis: 'fenceLeakDetection', axis_num: 6, status: findings.length > 0 ? 'drift' : 'ok', findings, summary: `${findings.length} fence-leak heading(s)` };
256
- }
257
-
258
- function axis7_contentCurrency(ctx) {
259
- const { skillDir, skillJson } = ctx;
260
- const pkg = JSON.parse(readSafe(skillJson) || '{}');
261
- const declared = pkg.files || [];
262
- const findings = [];
263
- for (const relPath of declared) {
264
- const full = path.resolve(skillDir, relPath);
265
- if (!fs.existsSync(full)) findings.push(finding('missing-declared', `Declared in files[] but not on disk: ${relPath}`, { path: relPath }));
266
- }
267
- return { axis: 'contentCurrency', axis_num: 7, status: findings.length > 0 ? 'drift' : 'ok', findings, summary: `${findings.length} declared-but-missing file(s)` };
268
- }
269
-
270
- function axis8_cliHelperCurrency(ctx) {
271
- const { skillDir, skillMd, repoRoot } = ctx;
272
- const content = readSafe(skillMd);
273
- const findings = [];
274
- let pkgScripts = null;
275
- if (repoRoot) {
276
- try { pkgScripts = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8')).scripts || {}; } catch { /* no package.json */ }
277
- }
278
- const lines = content.split('\n');
279
- for (let i = 0; i < lines.length; i++) {
280
- const line = lines[i];
281
- if (/(substrate.only|chat.ui.only)/i.test(line)) continue;
282
- if (/^\s*(#|\/\/)/.test(line)) continue;
283
- const npmRe = /`npm run ([\w:.-]+)`/g;
284
- let m;
285
- while ((m = npmRe.exec(line)) !== null) {
286
- if (pkgScripts && !pkgScripts[m[1]]) findings.push(finding('missing-npm-script', `Line ${i + 1}: npm run ${m[1]} not found in package.json scripts`, { line: i + 1, script: m[1] }));
287
- }
288
- const nodeRe = /`node\s+(\.agents\/skills\/[^\s`]+|scripts\/[^\s`]+)`/g;
289
- while ((m = nodeRe.exec(line)) !== null) {
290
- const scriptPath = m[1];
291
- const base = repoRoot || skillDir;
292
- const resolved = path.resolve(base, scriptPath);
293
- if (!fs.existsSync(resolved)) findings.push(finding('missing-script', `Line ${i + 1}: node ${scriptPath} — file not found`, { line: i + 1, scriptPath }));
294
- }
295
- }
296
- return { axis: 'cliHelperCurrency', axis_num: 8, status: findings.length > 0 ? 'drift' : 'ok', findings, summary: `${findings.length} missing CLI helper(s)` };
297
- }
298
-
299
- // ─── Axis 9 — kit-specific bundle-roster currency ─────────────────────────────
300
-
301
- function axis9_bundleRosterCurrency() {
302
- const content = read(SKILL_MD);
303
- const declared = new Set((JSON.parse(read(SKILL_JSON) || '{}').files) || []);
304
- const bundles = new Set();
305
- const re = /`(references\/(?:patterns|flows)-[a-z0-9-]+\.md)`/g;
306
- let m;
307
- while ((m = re.exec(content)) !== null) bundles.add(m[1]);
308
-
309
- const findings = [];
310
- for (const b of [...bundles].sort()) {
311
- const onDisk = fs.existsSync(path.join(SKILL_DIR, b));
312
- if (!onDisk) {
313
- findings.push({ type: 'bundle-missing-disk', message: `${b} advertised in a bundle map but not on disk` });
314
- } else if (!declared.has(b)) {
315
- findings.push({ type: 'bundle-undeclared', message: `${b} on disk + advertised but not in skill.json files[]` });
316
- }
317
- }
318
- return {
319
- axis: 'kitBundleRosterCurrency',
320
- axis_num: 9,
321
- status: findings.length > 0 ? 'drift' : 'ok',
322
- findings,
323
- summary: `${bundles.size} pattern/flow bundle(s) checked; ${findings.length} issue(s)`,
324
- };
325
- }
326
-
327
- function formatResults(results) {
328
- const lines = [];
329
- for (const r of results) {
330
- const icon = r.status === 'ok' ? '✓' : '✗';
331
- lines.push(`${icon} ${r.axis}: ${r.summary}`);
332
- if (r.status === 'drift' && r.findings.length > 0) {
333
- for (const f of r.findings) lines.push(` ${f.type}: ${f.message}`);
334
- }
335
- }
336
- return lines.join('\n');
337
- }
338
-
339
- function main() {
340
- const args = parseArgs(process.argv.slice(2));
341
- const ctx = { skillDir: SKILL_DIR, skillMd: SKILL_MD, skillJson: SKILL_JSON };
342
- const universal = [
343
- axis1_manifestEnforcement(ctx),
344
- axis2_referenceGraph(ctx),
345
- axis3_capabilityMenuDrift(ctx),
346
- axis4_versionLiteralParity(ctx),
347
- axis5_phaseLabelAbsence(ctx),
348
- axis6_fenceLeakDetection(ctx),
349
- axis7_contentCurrency(ctx),
350
- axis8_cliHelperCurrency(ctx),
351
- ];
352
- const allResults = [...universal, axis9_bundleRosterCurrency()];
353
- const driftCount = allResults.filter((r) => r.status === 'drift').length;
354
-
355
- if (args.json) {
356
- console.log(JSON.stringify({ results: allResults, driftCount }, null, 2));
357
- } else {
358
- console.log(`[audit-kit-roster] adia-ui-kit §SelfAudit — ${driftCount} drifting axis/axes`);
359
- console.log(formatResults(allResults));
360
- if (driftCount === 0) console.log('\n✓ All clean.');
361
- }
362
-
363
- if (args.strict && driftCount > 0) process.exit(1);
364
- }
365
-
366
- main();
@@ -1,126 +0,0 @@
1
- #!/usr/bin/env node
2
- // changelog-archive.mjs — keep CHANGELOG.md lean; preserve full history in a per-major archive set.
3
- //
4
- // THE SYSTEM: the live CHANGELOG.md holds only the most recent N entries (default 3);
5
- // every older entry lives in references/changelog-archive/CHANGELOG-v<major>.md (newest-first).
6
- // Idempotent + re-runnable: run it after each cut and it rolls the now-oldest live entry
7
- // into the archive. Re-running with nothing to roll is a no-op (only the archive index is
8
- // regenerated). Merge-by-version + semver-sort means an entry is never duplicated or lost.
9
- //
10
- // Usage (from the skill dir, or anywhere with --dir):
11
- // node scripts/changelog-archive.mjs # keep newest 3, archive the rest
12
- // node scripts/changelog-archive.mjs --keep 5 # keep newest 5
13
- // node scripts/changelog-archive.mjs --check # report what WOULD change; exit 1 if live > keep
14
- //
15
- // Entry heading shape: `## <major>.<minor>.<patch> (date)`. Non-version `##` lines (e.g. a
16
- // "[Previously-unreleased …]" note) stay attached to the entry they sit under.
17
-
18
- import fs from 'node:fs';
19
- import path from 'node:path';
20
- import process from 'node:process';
21
- import { fileURLToPath } from 'node:url';
22
-
23
- const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
24
- const SKILL_DIR = path.dirname(SCRIPT_DIR);
25
-
26
- function parseArgs(argv) {
27
- const a = { keep: 3, check: false, dir: SKILL_DIR };
28
- for (let i = 0; i < argv.length; i++) {
29
- if (argv[i] === '--keep') a.keep = parseInt(argv[++i], 10);
30
- else if (argv[i] === '--check') a.check = true;
31
- else if (argv[i] === '--dir') a.dir = path.resolve(argv[++i]);
32
- }
33
- if (!Number.isInteger(a.keep) || a.keep < 1) { console.error('--keep must be a positive integer'); process.exit(2); }
34
- return a;
35
- }
36
-
37
- const HEADING = /^## v?(\d+)\.(\d+)\.(\d+)\b/;
38
- const cmpDesc = (a, b) => b.M - a.M || b.m - a.m || b.p - a.p;
39
-
40
- // Split a changelog body into { preamble, entries[] }. Strips any trailing "## Archive"
41
- // index section first (it is always regenerated, never parsed as an entry).
42
- function parse(body) {
43
- const archiveAt = body.search(/^## Archive\b/m);
44
- if (archiveAt >= 0) body = body.slice(0, archiveAt).replace(/\s+$/, '') + '\n';
45
- const lines = body.split('\n');
46
- const idx = [];
47
- for (let i = 0; i < lines.length; i++) if (HEADING.test(lines[i])) idx.push(i);
48
- if (idx.length === 0) return { preamble: body.replace(/\s+$/, ''), entries: [] };
49
- const preamble = lines.slice(0, idx[0]).join('\n').replace(/\s+$/, '');
50
- const entries = idx.map((start, j) => {
51
- const end = j + 1 < idx.length ? idx[j + 1] : lines.length;
52
- const h = lines[start].match(HEADING);
53
- return {
54
- version: `${h[1]}.${h[2]}.${h[3]}`, major: h[1],
55
- M: +h[1], m: +h[2], p: +h[3],
56
- text: lines.slice(start, end).join('\n').replace(/\s+$/, ''),
57
- };
58
- });
59
- return { preamble, entries };
60
- }
61
-
62
- function readEntries(file) {
63
- if (!fs.existsSync(file)) return [];
64
- return parse(fs.readFileSync(file, 'utf8')).entries;
65
- }
66
-
67
- function dedupe(entries) {
68
- const seen = new Set(); const out = [];
69
- for (const e of entries.sort(cmpDesc)) { if (seen.has(e.version)) continue; seen.add(e.version); out.push(e); }
70
- return out;
71
- }
72
-
73
- function main() {
74
- const args = parseArgs(process.argv.slice(2));
75
- const CHANGELOG = path.join(args.dir, 'CHANGELOG.md');
76
- const ARCHIVE_DIR = path.join(args.dir, 'references/changelog-archive');
77
- if (!fs.existsSync(CHANGELOG)) { console.error(`not found: ${CHANGELOG}`); process.exit(2); }
78
-
79
- const { preamble, entries } = parse(fs.readFileSync(CHANGELOG, 'utf8'));
80
- const keep = entries.slice(0, args.keep);
81
- const toArchive = entries.slice(args.keep);
82
-
83
- if (args.check) {
84
- console.log(`[changelog-archive --check] ${entries.length} live entries; keep ${args.keep}; ${toArchive.length} over budget`);
85
- process.exit(toArchive.length > 0 ? 1 : 0);
86
- }
87
-
88
- // Group entries-to-archive by major, merge with existing archive files, rewrite each.
89
- fs.mkdirSync(ARCHIVE_DIR, { recursive: true });
90
- // Entries archived FROM CHANGELOG.md drop 2 dirs into references/changelog-archive/, so
91
- // their skill-root-relative markdown links need `../../` prepended (the check-links depth
92
- // bug caught on the kit v3.11.0 first cut). Already-archived entries re-read below are
93
- // already corrected — reroot only the NEW ones, or the prefixes double up on re-runs.
94
- const reroot = (text) => text.replace(/\]\((?!\.\.?\/|\/|https?:|#)([^)]+)\)/g, '](../../$1)');
95
- const byMajor = {};
96
- for (const e of toArchive) (byMajor[e.major] ||= []).push({ ...e, text: reroot(e.text) });
97
-
98
- const written = [];
99
- // Touch every major that has either new entries OR an existing archive file (to keep them current).
100
- const majors = new Set([...Object.keys(byMajor), ...fs.readdirSync(ARCHIVE_DIR)
101
- .map((f) => (f.match(/^CHANGELOG-v(\d+)\.md$/) || [])[1]).filter(Boolean)]);
102
- for (const major of majors) {
103
- const file = path.join(ARCHIVE_DIR, `CHANGELOG-v${major}.md`);
104
- const merged = dedupe([...(byMajor[major] || []), ...readEntries(file)]);
105
- if (merged.length === 0) continue;
106
- const header = `# adia-ui-kit Changelog — v${major}.x archive\n\n` +
107
- `> Historical entries rolled out of the live [\`CHANGELOG.md\`](../../CHANGELOG.md), which keeps only the most recent ${args.keep}. Newest-first.\n`;
108
- fs.writeFileSync(file, `${header}\n${merged.map((e) => e.text).join('\n\n')}\n`);
109
- written.push({ major, file, count: merged.length, range: `${merged[merged.length - 1].version} … ${merged[0].version}` });
110
- }
111
- written.sort((a, b) => b.major - a.major);
112
-
113
- // Rebuild CHANGELOG.md: preamble + kept entries + archive index.
114
- let out = `${preamble}\n\n${keep.map((e) => e.text).join('\n\n')}\n`;
115
- if (written.length) {
116
- out += `\n## Archive\n\nOlder entries live in a per-major archive set (kept out of this file to hold its always-on cost down):\n\n`;
117
- for (const w of written) out += `- [\`references/changelog-archive/CHANGELOG-v${w.major}.md\`](references/changelog-archive/CHANGELOG-v${w.major}.md) — v${w.major}.x · ${w.count} entries (${w.range})\n`;
118
- }
119
- fs.writeFileSync(CHANGELOG, out);
120
-
121
- const kb = (p) => (fs.statSync(p).size / 1024).toFixed(0);
122
- console.log(`[changelog-archive] kept newest ${keep.length} in CHANGELOG.md (${kb(CHANGELOG)} KB); archived ${toArchive.length} entries:`);
123
- for (const w of written) console.log(` v${w.major}.x → ${path.relative(args.dir, w.file)} — ${w.count} entries (${w.range}), ${kb(w.file)} KB`);
124
- }
125
-
126
- main();