@adia-ai/adia-ui-factory 0.2.3 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/.claude-plugin/plugin.json +5 -7
  2. package/.mcp.json +1 -1
  3. package/CHANGELOG.md +27 -63
  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-lint +17 -2
  9. package/commands/adia-genui.md +5 -8
  10. package/commands/adia-migrate.md +5 -6
  11. package/commands/adia-orient.md +5 -10
  12. package/commands/adia-scaffold.md +7 -13
  13. package/commands/adia-verify.md +5 -6
  14. package/package.json +5 -6
  15. package/references/a2ui-mcp-tools.md +11 -8
  16. package/references/authoring-components.md +25 -1
  17. package/references/component-model.md +52 -4
  18. package/references/contracts/a2ui-mcp-surface.md +35 -0
  19. package/references/contracts/migration-guide-format.md +34 -0
  20. package/references/data-and-hydration.md +8 -5
  21. package/references/genui-a2ui.md +12 -6
  22. package/references/llm.md +22 -39
  23. package/references/migration.md +110 -36
  24. package/references/project-shapes.md +11 -13
  25. package/references/shell-admin.md +5 -2
  26. package/references/shell-chat.md +5 -3
  27. package/references/shell-editor.md +1 -1
  28. package/references/shell-embed.md +40 -26
  29. package/references/shell-simple.md +5 -2
  30. package/references/spa-architecture.md +28 -73
  31. package/references/ssr-integration.md +1 -1
  32. package/references/verification.md +8 -12
  33. package/skills/adia-compose/SKILL.md +52 -0
  34. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/Guidelines.md +3 -3
  35. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/components.md +3 -3
  36. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/styles.md +1 -1
  37. package/skills/{adia-ui-kit → adia-compose}/assets/figma-make/guidelines/tokens.md +3 -3
  38. package/skills/{adia-ui-kit → adia-compose}/assets/templates/FEEDBACK.template.md +7 -7
  39. package/skills/{adia-ui-kit → adia-compose}/assets/templates/RESPONSE.template.md +6 -7
  40. package/skills/adia-compose/references/composition-traps.md +58 -0
  41. package/skills/adia-compose/references/feedback-discipline.md +20 -0
  42. package/skills/adia-compose/references/spec-to-ui-reasoning.md +158 -0
  43. package/skills/adia-data/SKILL.md +78 -0
  44. package/skills/adia-genui/SKILL.md +83 -0
  45. package/skills/adia-host/SKILL.md +119 -0
  46. package/skills/adia-llm/SKILL.md +36 -0
  47. package/skills/adia-migrate/SKILL.md +76 -0
  48. package/skills/adia-orient/SKILL.md +123 -0
  49. package/skills/adia-project/SKILL.md +83 -0
  50. package/skills/adia-shells/SKILL.md +74 -0
  51. package/skills/adia-verify/SKILL.md +43 -0
  52. package/skills/adia-verify/references/verification.md +42 -0
  53. package/commands/adia-compose.md +0 -10
  54. package/commands/adia-wire.md +0 -13
  55. package/skills/adia-ui-compose/SKILL.md +0 -57
  56. package/skills/adia-ui-data/SKILL.md +0 -62
  57. package/skills/adia-ui-factory/SKILL.md +0 -113
  58. package/skills/adia-ui-genui/SKILL.md +0 -74
  59. package/skills/adia-ui-kit/CHANGELOG.md +0 -41
  60. package/skills/adia-ui-kit/SKILL.md +0 -442
  61. package/skills/adia-ui-kit/assets/team-scaffold/archive-README.md +0 -30
  62. package/skills/adia-ui-kit/assets/team-scaffold/drafts-README.md +0 -57
  63. package/skills/adia-ui-kit/assets/team-scaffold/inbox-README.md +0 -76
  64. package/skills/adia-ui-kit/assets/team-scaffold/outbox-README.md +0 -46
  65. package/skills/adia-ui-kit/assets/team-scaffold/team-root.README.md +0 -51
  66. package/skills/adia-ui-kit/assets/team-scaffold/tickets-README.md +0 -208
  67. package/skills/adia-ui-kit/assets/team-scaffold/tickets-REGISTRY.md +0 -81
  68. package/skills/adia-ui-kit/assets/templates/AGENTS.template.md +0 -128
  69. package/skills/adia-ui-kit/assets/templates/CLAUDE.template.md +0 -1
  70. package/skills/adia-ui-kit/assets/templates/figma-make-guidelines.md +0 -90
  71. package/skills/adia-ui-kit/evals/adversarial-content-trust.json +0 -83
  72. package/skills/adia-ui-kit/evals/evals.json +0 -55
  73. package/skills/adia-ui-kit/evals/routing-corpus.json +0 -121
  74. package/skills/adia-ui-kit/evals/teach-routing-cases.json +0 -92
  75. package/skills/adia-ui-kit/references/capabilities.md +0 -245
  76. package/skills/adia-ui-kit/references/catalog-lookup.md +0 -186
  77. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v1.md +0 -8
  78. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v2.md +0 -3585
  79. package/skills/adia-ui-kit/references/changelog-archive/CHANGELOG-v3.md +0 -1899
  80. package/skills/adia-ui-kit/references/common-gotchas-consumer.md +0 -522
  81. package/skills/adia-ui-kit/references/component-literacy.md +0 -291
  82. package/skills/adia-ui-kit/references/composition.md +0 -245
  83. package/skills/adia-ui-kit/references/correction-loop.md +0 -462
  84. package/skills/adia-ui-kit/references/css-policy.md +0 -343
  85. package/skills/adia-ui-kit/references/decision-model.md +0 -158
  86. package/skills/adia-ui-kit/references/diagnostic-output.md +0 -57
  87. package/skills/adia-ui-kit/references/docs-catalog.md +0 -239
  88. package/skills/adia-ui-kit/references/exclusions.md +0 -225
  89. package/skills/adia-ui-kit/references/feedback-authoring.md +0 -466
  90. package/skills/adia-ui-kit/references/figma-make.md +0 -244
  91. package/skills/adia-ui-kit/references/flows-auth.md +0 -307
  92. package/skills/adia-ui-kit/references/flows-content.md +0 -206
  93. package/skills/adia-ui-kit/references/flows-data.md +0 -348
  94. package/skills/adia-ui-kit/references/flows-interaction.md +0 -181
  95. package/skills/adia-ui-kit/references/flows-shells.md +0 -336
  96. package/skills/adia-ui-kit/references/gap-detection.md +0 -266
  97. package/skills/adia-ui-kit/references/harness.md +0 -284
  98. package/skills/adia-ui-kit/references/loading-protocol.md +0 -406
  99. package/skills/adia-ui-kit/references/local-link.md +0 -198
  100. package/skills/adia-ui-kit/references/mcp.md +0 -401
  101. package/skills/adia-ui-kit/references/migration.md +0 -116
  102. package/skills/adia-ui-kit/references/mission.md +0 -194
  103. package/skills/adia-ui-kit/references/mode-routing.md +0 -298
  104. package/skills/adia-ui-kit/references/patterns-agent.md +0 -223
  105. package/skills/adia-ui-kit/references/patterns-data.md +0 -1066
  106. package/skills/adia-ui-kit/references/patterns-forms.md +0 -223
  107. package/skills/adia-ui-kit/references/patterns-layout.md +0 -326
  108. package/skills/adia-ui-kit/references/patterns-recipes.md +0 -1416
  109. package/skills/adia-ui-kit/references/patterns-shells.md +0 -1097
  110. package/skills/adia-ui-kit/references/patterns-traits.md +0 -259
  111. package/skills/adia-ui-kit/references/patterns-wiring.md +0 -339
  112. package/skills/adia-ui-kit/references/pev-rationale.md +0 -137
  113. package/skills/adia-ui-kit/references/reasoning-gate.md +0 -146
  114. package/skills/adia-ui-kit/references/reasoning-methodologies.md +0 -112
  115. package/skills/adia-ui-kit/references/recon.md +0 -192
  116. package/skills/adia-ui-kit/references/rendering-model.md +0 -431
  117. package/skills/adia-ui-kit/references/response-authoring.md +0 -398
  118. package/skills/adia-ui-kit/references/session-log-schema.md +0 -203
  119. package/skills/adia-ui-kit/references/session-log-template.md +0 -118
  120. package/skills/adia-ui-kit/references/setup.md +0 -652
  121. package/skills/adia-ui-kit/references/skill-hygiene.md +0 -135
  122. package/skills/adia-ui-kit/references/spec-to-ui-reasoning.md +0 -394
  123. package/skills/adia-ui-kit/references/state.md +0 -229
  124. package/skills/adia-ui-kit/references/teach-protocol.md +0 -421
  125. package/skills/adia-ui-kit/references/theme.md +0 -142
  126. package/skills/adia-ui-kit/references/version-check.md +0 -69
  127. package/skills/adia-ui-kit/scripts/audit-decision-trace.mjs +0 -198
  128. package/skills/adia-ui-kit/scripts/audit-kit-roster.mjs +0 -366
  129. package/skills/adia-ui-kit/scripts/changelog-archive.mjs +0 -126
  130. package/skills/adia-ui-kit/scripts/check-adia-versions.py +0 -404
  131. package/skills/adia-ui-kit/scripts/run-skill-evals.mjs +0 -358
  132. package/skills/adia-ui-kit/scripts/validate-session-log.mjs +0 -242
  133. package/skills/adia-ui-kit/skill.json +0 -102
  134. package/skills/adia-ui-llm/SKILL.md +0 -51
  135. package/skills/adia-ui-migrate/SKILL.md +0 -64
  136. package/skills/adia-ui-migration/CHANGELOG.md +0 -47
  137. package/skills/adia-ui-migration/SKILL.md +0 -381
  138. package/skills/adia-ui-migration/skill.json +0 -29
  139. package/skills/adia-ui-project/SKILL.md +0 -77
  140. package/skills/adia-ui-shells/SKILL.md +0 -62
  141. package/skills/adia-ui-spa/SKILL.md +0 -52
  142. package/skills/adia-ui-ssr/SKILL.md +0 -52
  143. package/skills/adia-ui-verify/SKILL.md +0 -44
  144. /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 (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();