tribunal-kit 5.8.2 → 5.8.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/ARCHITECTURE.md +12 -12
- package/.agent/agents/complexity-reviewer.md +53 -0
- package/.agent/agents/precedence-reviewer.md +12 -12
- package/.agent/agents/swarm-worker-registry.md +3 -3
- package/.agent/history/integrity_manifest.json +958 -0
- package/.agent/history/memory/.memory.idx +305 -1
- package/.agent/history/memory/MEMORY.md +21 -1
- package/.agent/rules/GEMINI.md +31 -28
- package/.agent/scripts/case_law_manager.js +3 -15
- package/.agent/scripts/context_broker.js +3 -1
- package/.agent/scripts/guardrail_engine.js +643 -0
- package/.agent/scripts/integrity_manifest.js +486 -0
- package/.agent/scripts/signal_detector.js +1 -1
- package/.agent/scripts/skill_evolution.js +3 -15
- package/.agent/scripts/skill_integrator.js +2 -12
- package/.agent/scripts/swarm_dispatcher.js +2 -12
- package/.agent/skills/12-principles-of-animation/SKILL.md +69 -0
- package/.agent/skills/60fps-animation/SKILL.md +78 -0
- package/.agent/skills/accessible-animation/SKILL.md +86 -0
- package/.agent/skills/adapt/SKILL.md +75 -0
- package/.agent/skills/animation-on-scroll/SKILL.md +110 -0
- package/.agent/skills/animation-systems/SKILL.md +69 -0
- package/.agent/skills/antfu-conventions/SKILL.md +74 -0
- package/.agent/skills/app-builder/SKILL.md +1 -1
- package/.agent/skills/apple-design/SKILL.md +108 -0
- package/.agent/skills/audit-and-fix/SKILL.md +83 -0
- package/.agent/skills/baseline-ui/SKILL.md +74 -0
- package/.agent/skills/better-colors/SKILL.md +124 -0
- package/.agent/skills/better-typography/SKILL.md +111 -0
- package/.agent/skills/better-ui/SKILL.md +102 -0
- package/.agent/skills/bolder/SKILL.md +68 -0
- package/.agent/skills/build-primitive/SKILL.md +70 -0
- package/.agent/skills/clarify/SKILL.md +75 -0
- package/.agent/skills/cobejs/SKILL.md +96 -0
- package/.agent/skills/codebase-design/SKILL.md +84 -0
- package/.agent/skills/colorize/SKILL.md +77 -0
- package/.agent/skills/compact-landing/SKILL.md +70 -0
- package/.agent/skills/company-logos/SKILL.md +73 -0
- package/.agent/skills/create-design-md/SKILL.md +104 -0
- package/.agent/skills/critique/SKILL.md +95 -0
- package/.agent/skills/delight/SKILL.md +65 -0
- package/.agent/skills/design-lab/SKILL.md +59 -0
- package/.agent/skills/diagnosing-bugs/SKILL.md +190 -0
- package/.agent/skills/distill/SKILL.md +65 -0
- package/.agent/skills/domain-modeling/SKILL.md +78 -0
- package/.agent/skills/fixing-accessibility/SKILL.md +98 -0
- package/.agent/skills/fixing-metadata/SKILL.md +97 -0
- package/.agent/skills/fixing-motion-performance/SKILL.md +96 -0
- package/.agent/skills/gpt-taste/SKILL.md +68 -0
- package/.agent/skills/harden/SKILL.md +74 -0
- package/.agent/skills/impeccable/SKILL.md +72 -0
- package/.agent/skills/improve-codebase-architecture/SKILL.md +67 -0
- package/.agent/skills/improve-ui/SKILL.md +83 -0
- package/.agent/skills/landing-page/SKILL.md +88 -0
- package/.agent/skills/lint-and-validate/SKILL.md +1 -2
- package/.agent/skills/local-first-architecture/SKILL.md +66 -0
- package/.agent/skills/lottie-animation/SKILL.md +82 -0
- package/.agent/skills/marquee-loop/SKILL.md +96 -0
- package/.agent/skills/masked-reveal/SKILL.md +88 -0
- package/.agent/skills/micro-interaction/SKILL.md +100 -0
- package/.agent/skills/morphing-icons/SKILL.md +85 -0
- package/.agent/skills/page-transition-animation/SKILL.md +108 -0
- package/.agent/skills/parallel-agents/SKILL.md +5 -5
- package/.agent/skills/polish/SKILL.md +63 -0
- package/.agent/skills/pricing-page/SKILL.md +68 -0
- package/.agent/skills/progressive-blur/SKILL.md +100 -0
- package/.agent/skills/project-idioms/SKILL.md +7 -7
- package/.agent/skills/quieter/SKILL.md +68 -0
- package/.agent/skills/react-doctor/SKILL.md +70 -0
- package/.agent/skills/redesign-skill/SKILL.md +65 -0
- package/.agent/skills/shape/SKILL.md +73 -0
- package/.agent/skills/soft-skill/SKILL.md +72 -0
- package/.agent/skills/sounds-on-the-web/SKILL.md +100 -0
- package/.agent/skills/svg-animation/SKILL.md +91 -0
- package/.agent/skills/swiss-design/SKILL.md +64 -0
- package/.agent/skills/taste-skill/SKILL.md +71 -0
- package/.agent/skills/tdd-workflow/SKILL.md +36 -165
- package/.agent/skills/test-result-analyzer/SKILL.md +1 -1
- package/.agent/skills/thermo-nuclear-code-quality-review/SKILL.md +82 -0
- package/.agent/skills/to-spring-or-not-to-spring/SKILL.md +66 -0
- package/.agent/skills/transitions-dev/SKILL.md +101 -0
- package/.agent/skills/typeset/SKILL.md +76 -0
- package/.agent/skills/ui-skills-root/SKILL.md +85 -0
- package/.agent/skills/web-quality-audit/SKILL.md +67 -0
- package/.agent/workflows/fix.md +2 -2
- package/.agent/workflows/preview.md +5 -5
- package/.agent/workflows/status.md +1 -1
- package/.agent/workflows/tribunal-full.md +3 -3
- package/.agent/workflows/tribunal-speed.md +2 -2
- package/README.md +348 -238
- package/bin/tribunal-kit.js +62 -1482
- package/dist/cli.js +13 -0
- package/dist/commands/align.js +1 -1
- package/dist/commands/compile.js +1 -1
- package/dist/commands/guardrail.js +275 -0
- package/dist/commands/init.js +10 -9
- package/dist/commands/optimize.js +318 -0
- package/dist/utils/version.js +1 -1
- package/package.json +10 -8
- package/scripts/fix-vbc.js +98 -0
- package/scripts/fix-vbc.ps1 +119 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* integrity_manifest.js — Tribunal Kit Integrity Manifest Compiler
|
|
4
|
+
* =================================================================
|
|
5
|
+
* Crawls the entire .agent/ directory and generates a machine-readable
|
|
6
|
+
* integrity_manifest.json — the single source of truth for every named
|
|
7
|
+
* reference in the system.
|
|
8
|
+
*
|
|
9
|
+
* This manifest is consumed by:
|
|
10
|
+
* - guardrail_engine.js (deterministic validation)
|
|
11
|
+
* - verify_all.js (pre-deploy checks)
|
|
12
|
+
* - context_broker.js (context assembly)
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* node .agent/scripts/integrity_manifest.js → Generate manifest
|
|
16
|
+
* node .agent/scripts/integrity_manifest.js --output json → Print to stdout
|
|
17
|
+
* node .agent/scripts/integrity_manifest.js --validate → Generate + validate
|
|
18
|
+
*
|
|
19
|
+
* Zero external dependencies. Node.js stdlib only.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
"use strict";
|
|
23
|
+
|
|
24
|
+
const fs = require("fs");
|
|
25
|
+
const path = require("path");
|
|
26
|
+
|
|
27
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
const AGENT_DIR_NAME = ".agent";
|
|
30
|
+
|
|
31
|
+
// ── YAML Frontmatter Parser (regex-based, zero deps) ─────────────────────────
|
|
32
|
+
|
|
33
|
+
function parseFrontmatter(content) {
|
|
34
|
+
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
35
|
+
if (!match) return {};
|
|
36
|
+
|
|
37
|
+
const yaml = {};
|
|
38
|
+
const lines = match[1].split(/\r?\n/);
|
|
39
|
+
for (const line of lines) {
|
|
40
|
+
const kv = line.match(/^(\w[\w-]*):\s*(.+)$/);
|
|
41
|
+
if (kv) {
|
|
42
|
+
yaml[kv[1].trim()] = kv[2].trim();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return yaml;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ── Directory Helpers ─────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
function safeReaddir(dir) {
|
|
51
|
+
try {
|
|
52
|
+
return fs.readdirSync(dir);
|
|
53
|
+
} catch {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function safeReadFile(filePath) {
|
|
59
|
+
try {
|
|
60
|
+
return fs.readFileSync(filePath, "utf8");
|
|
61
|
+
} catch {
|
|
62
|
+
return "";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── Agent Crawler ─────────────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
function crawlAgents(agentDir) {
|
|
69
|
+
const agentsDir = path.join(agentDir, "agents");
|
|
70
|
+
const files = safeReaddir(agentsDir).filter((f) => f.endsWith(".md"));
|
|
71
|
+
|
|
72
|
+
const reviewers = [];
|
|
73
|
+
const specialists = [];
|
|
74
|
+
const allAgents = [];
|
|
75
|
+
|
|
76
|
+
for (const file of files) {
|
|
77
|
+
const name = file.replace(/\.md$/, "");
|
|
78
|
+
allAgents.push(name);
|
|
79
|
+
|
|
80
|
+
// Classify: reviewer if name contains "reviewer", "auditor", or "tester"
|
|
81
|
+
// or if frontmatter has role: reviewer
|
|
82
|
+
const content = safeReadFile(path.join(agentsDir, file));
|
|
83
|
+
const fm = parseFrontmatter(content);
|
|
84
|
+
const isReviewer =
|
|
85
|
+
/reviewer|auditor|tester/i.test(name) ||
|
|
86
|
+
(fm.role && /reviewer/i.test(fm.role));
|
|
87
|
+
|
|
88
|
+
if (isReviewer) {
|
|
89
|
+
reviewers.push(name);
|
|
90
|
+
} else {
|
|
91
|
+
specialists.push(name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
total: allAgents.length,
|
|
97
|
+
reviewers: reviewers.sort(),
|
|
98
|
+
specialists: specialists.sort(),
|
|
99
|
+
reviewer_count: reviewers.length,
|
|
100
|
+
all: allAgents.sort(),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ── Skill Crawler ─────────────────────────────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
function crawlSkills(agentDir) {
|
|
107
|
+
const skillsDir = path.join(agentDir, "skills");
|
|
108
|
+
const entries = safeReaddir(skillsDir);
|
|
109
|
+
const names = [];
|
|
110
|
+
|
|
111
|
+
for (const entry of entries) {
|
|
112
|
+
const skillPath = path.join(skillsDir, entry, "SKILL.md");
|
|
113
|
+
if (fs.existsSync(skillPath)) {
|
|
114
|
+
const content = safeReadFile(skillPath);
|
|
115
|
+
const fm = parseFrontmatter(content);
|
|
116
|
+
names.push(fm.name || entry);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
total: names.length,
|
|
122
|
+
names: names.sort(),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ── Script Crawler ────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
function crawlScripts(agentDir) {
|
|
129
|
+
const scriptsDir = path.join(agentDir, "scripts");
|
|
130
|
+
const files = safeReaddir(scriptsDir).filter(
|
|
131
|
+
(f) => f.endsWith(".js") || f.endsWith(".py"),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const fileMap = {};
|
|
135
|
+
for (const file of files) {
|
|
136
|
+
const relPath = path.join(AGENT_DIR_NAME, "scripts", file);
|
|
137
|
+
fileMap[file] = relPath.replace(/\\/g, "/");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
total: files.length,
|
|
142
|
+
files: fileMap,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ── Workflow Crawler ──────────────────────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
function crawlWorkflows(agentDir) {
|
|
149
|
+
const workflowsDir = path.join(agentDir, "workflows");
|
|
150
|
+
const files = safeReaddir(workflowsDir).filter((f) => f.endsWith(".md"));
|
|
151
|
+
const names = files.map((f) => f.replace(/\.md$/, ""));
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
total: names.length,
|
|
155
|
+
names: names.sort(),
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ── Cross-Reference Extractor ─────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Scans a file's content for references to other .agent/ assets.
|
|
163
|
+
* Patterns detected:
|
|
164
|
+
* - agents/X.md, agents/X
|
|
165
|
+
* - skills/X/SKILL.md, skill name references
|
|
166
|
+
* - scripts/X.js, scripts/X.py
|
|
167
|
+
* - workflows/X.md
|
|
168
|
+
* - node .agent/scripts/X
|
|
169
|
+
*/
|
|
170
|
+
function extractCrossReferences(filePath, content, agentDir) {
|
|
171
|
+
const refs = [];
|
|
172
|
+
const relSource = path.relative(agentDir, filePath).replace(/\\/g, "/");
|
|
173
|
+
|
|
174
|
+
// Agent references: agents/name.md or `agent-name`
|
|
175
|
+
const agentRefRegex =
|
|
176
|
+
/(?:agents\/)([\w-]+)(?:\.md)?/g;
|
|
177
|
+
let m;
|
|
178
|
+
while ((m = agentRefRegex.exec(content)) !== null) {
|
|
179
|
+
const refFile = `agents/${m[1]}.md`;
|
|
180
|
+
const fullPath = path.join(agentDir, refFile);
|
|
181
|
+
refs.push({
|
|
182
|
+
source: relSource,
|
|
183
|
+
ref: refFile,
|
|
184
|
+
exists: fs.existsSync(fullPath),
|
|
185
|
+
type: "agent",
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Script references: scripts/name.js or scripts/name.py
|
|
190
|
+
const scriptRefRegex =
|
|
191
|
+
/(?:scripts\/)([\w_-]+)\.(js|py)/g;
|
|
192
|
+
while ((m = scriptRefRegex.exec(content)) !== null) {
|
|
193
|
+
const refFile = `scripts/${m[1]}.${m[2]}`;
|
|
194
|
+
const fullPath = path.join(agentDir, refFile);
|
|
195
|
+
refs.push({
|
|
196
|
+
source: relSource,
|
|
197
|
+
ref: refFile,
|
|
198
|
+
exists: fs.existsSync(fullPath),
|
|
199
|
+
type: "script",
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Workflow references: workflows/name.md
|
|
204
|
+
const workflowRefRegex = /(?:workflows\/)([\w-]+)(?:\.md)?/g;
|
|
205
|
+
while ((m = workflowRefRegex.exec(content)) !== null) {
|
|
206
|
+
const precedingText = content.substring(Math.max(0, m.index - 100), m.index);
|
|
207
|
+
if (precedingText.includes(".github/") || precedingText.includes("github.com/")) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const refFile = `workflows/${m[1]}.md`;
|
|
211
|
+
const fullPath = path.join(agentDir, refFile);
|
|
212
|
+
refs.push({
|
|
213
|
+
source: relSource,
|
|
214
|
+
ref: refFile,
|
|
215
|
+
exists: fs.existsSync(fullPath),
|
|
216
|
+
type: "workflow",
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Deduplicate
|
|
221
|
+
const seen = new Set();
|
|
222
|
+
return refs.filter((r) => {
|
|
223
|
+
const key = `${r.source}→${r.ref}`;
|
|
224
|
+
if (seen.has(key)) return false;
|
|
225
|
+
seen.add(key);
|
|
226
|
+
return true;
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ── Numeric Claim Extractor ───────────────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Finds numeric claims about asset counts in a file.
|
|
234
|
+
* Patterns: "N reviewers", "N agents", "N skills", "N workflows", "N scripts"
|
|
235
|
+
*/
|
|
236
|
+
function extractNumericClaims(filePath, content, agentDir, actualCounts) {
|
|
237
|
+
const claims = [];
|
|
238
|
+
const relSource = path.relative(agentDir, filePath).replace(/\\/g, "/");
|
|
239
|
+
const lines = content.split(/\r?\n/);
|
|
240
|
+
|
|
241
|
+
const claimPatterns = [
|
|
242
|
+
{ regex: /(\d+)[-\s]+(?:parallel\s+)?(?:reviewers?|agents?)/gi, entity: "reviewers" },
|
|
243
|
+
{ regex: /(\d+)\s*(?:specialist\s+)?agents?\b/gi, entity: "agents" },
|
|
244
|
+
{ regex: /(\d+)\s*(?:modular\s+)?skills?\b/gi, entity: "skills" },
|
|
245
|
+
{ regex: /(\d+)\s*(?:slash\s+)?workflows?\b/gi, entity: "workflows" },
|
|
246
|
+
{ regex: /(\d+)\s*(?:JS\s+)?scripts?\b/gi, entity: "scripts" },
|
|
247
|
+
];
|
|
248
|
+
|
|
249
|
+
for (let i = 0; i < lines.length; i++) {
|
|
250
|
+
const line = lines[i];
|
|
251
|
+
for (const { regex, entity } of claimPatterns) {
|
|
252
|
+
regex.lastIndex = 0;
|
|
253
|
+
let match;
|
|
254
|
+
while ((match = regex.exec(line)) !== null) {
|
|
255
|
+
const claimed = parseInt(match[1], 10);
|
|
256
|
+
// Skip tiny numbers (likely not count claims) and very large numbers
|
|
257
|
+
if (claimed < 3 || claimed > 500) continue;
|
|
258
|
+
|
|
259
|
+
const actual = actualCounts[entity];
|
|
260
|
+
if (actual !== undefined) {
|
|
261
|
+
claims.push({
|
|
262
|
+
source: `${relSource}:L${i + 1}`,
|
|
263
|
+
claim: `${claimed} ${entity}`,
|
|
264
|
+
actual,
|
|
265
|
+
valid: claimed === actual,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return claims;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// ── File Walker ───────────────────────────────────────────────────────────────
|
|
276
|
+
|
|
277
|
+
function walkMdFiles(dir, fileList = []) {
|
|
278
|
+
if (!fs.existsSync(dir)) return fileList;
|
|
279
|
+
|
|
280
|
+
const entries = safeReaddir(dir);
|
|
281
|
+
for (const entry of entries) {
|
|
282
|
+
const fullPath = path.join(dir, entry);
|
|
283
|
+
try {
|
|
284
|
+
const stat = fs.statSync(fullPath);
|
|
285
|
+
if (stat.isDirectory()) {
|
|
286
|
+
// Skip node_modules, .git, history, .backups, .shared
|
|
287
|
+
if (
|
|
288
|
+
["node_modules", ".git", "history", ".backups", ".shared"].includes(
|
|
289
|
+
entry,
|
|
290
|
+
)
|
|
291
|
+
) {
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
walkMdFiles(fullPath, fileList);
|
|
295
|
+
} else if (entry.endsWith(".md") || entry.endsWith(".json")) {
|
|
296
|
+
if (entry.startsWith("http") || entry.startsWith("#") || entry.startsWith("https://github.com/")) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
fileList.push(fullPath);
|
|
300
|
+
}
|
|
301
|
+
} catch {
|
|
302
|
+
// Skip files we can't stat
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return fileList;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ── Main: Generate Manifest ───────────────────────────────────────────────────
|
|
309
|
+
|
|
310
|
+
function generateManifest(projectRoot) {
|
|
311
|
+
const agentDir = path.join(projectRoot, AGENT_DIR_NAME);
|
|
312
|
+
|
|
313
|
+
if (!fs.existsSync(agentDir)) {
|
|
314
|
+
return {
|
|
315
|
+
error: `No ${AGENT_DIR_NAME}/ directory found at ${projectRoot}`,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Phase 1: Crawl all entity types
|
|
320
|
+
const agents = crawlAgents(agentDir);
|
|
321
|
+
const skills = crawlSkills(agentDir);
|
|
322
|
+
const scripts = crawlScripts(agentDir);
|
|
323
|
+
const workflows = crawlWorkflows(agentDir);
|
|
324
|
+
|
|
325
|
+
// Phase 2: Extract cross-references from all markdown files
|
|
326
|
+
const allFiles = walkMdFiles(agentDir);
|
|
327
|
+
const allRefs = [];
|
|
328
|
+
const allClaims = [];
|
|
329
|
+
|
|
330
|
+
const actualCounts = {
|
|
331
|
+
reviewers: agents.reviewer_count,
|
|
332
|
+
agents: agents.total,
|
|
333
|
+
skills: skills.total,
|
|
334
|
+
workflows: workflows.total,
|
|
335
|
+
scripts: scripts.total,
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
for (const file of allFiles) {
|
|
339
|
+
const content = safeReadFile(file);
|
|
340
|
+
if (!content) continue;
|
|
341
|
+
|
|
342
|
+
const refs = extractCrossReferences(file, content, agentDir);
|
|
343
|
+
allRefs.push(...refs);
|
|
344
|
+
|
|
345
|
+
const claims = extractNumericClaims(file, content, agentDir, actualCounts);
|
|
346
|
+
allClaims.push(...claims);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Phase 3: Deduplicate cross-references
|
|
350
|
+
const seenRefs = new Set();
|
|
351
|
+
const uniqueRefs = allRefs.filter((r) => {
|
|
352
|
+
const key = `${r.source}→${r.ref}`;
|
|
353
|
+
if (seenRefs.has(key)) return false;
|
|
354
|
+
seenRefs.add(key);
|
|
355
|
+
return true;
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// Phase 4: Build manifest
|
|
359
|
+
let version = "unknown";
|
|
360
|
+
try {
|
|
361
|
+
const pkg = JSON.parse(
|
|
362
|
+
safeReadFile(path.join(projectRoot, "package.json")) || "{}",
|
|
363
|
+
);
|
|
364
|
+
version = pkg.version || "unknown";
|
|
365
|
+
} catch {
|
|
366
|
+
// Ignore
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const manifest = {
|
|
370
|
+
version,
|
|
371
|
+
generated_at: new Date().toISOString(),
|
|
372
|
+
agents,
|
|
373
|
+
skills,
|
|
374
|
+
scripts,
|
|
375
|
+
workflows,
|
|
376
|
+
cross_references: uniqueRefs,
|
|
377
|
+
numeric_claims: allClaims,
|
|
378
|
+
integrity: {
|
|
379
|
+
phantom_references: uniqueRefs.filter((r) => !r.exists).length,
|
|
380
|
+
invalid_claims: allClaims.filter((c) => !c.valid).length,
|
|
381
|
+
total_references: uniqueRefs.length,
|
|
382
|
+
total_claims: allClaims.length,
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
return manifest;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// ── Persist Manifest ──────────────────────────────────────────────────────────
|
|
390
|
+
|
|
391
|
+
function saveManifest(manifest, projectRoot) {
|
|
392
|
+
const historyDir = path.join(projectRoot, AGENT_DIR_NAME, "history");
|
|
393
|
+
if (!fs.existsSync(historyDir)) {
|
|
394
|
+
fs.mkdirSync(historyDir, { recursive: true });
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const outputPath = path.join(historyDir, "integrity_manifest.json");
|
|
398
|
+
fs.writeFileSync(outputPath, JSON.stringify(manifest, null, 2), "utf8");
|
|
399
|
+
return outputPath;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ── CLI Entry Point ───────────────────────────────────────────────────────────
|
|
403
|
+
|
|
404
|
+
function main() {
|
|
405
|
+
const args = process.argv.slice(2);
|
|
406
|
+
const projectRoot = process.cwd();
|
|
407
|
+
const outputJson = args.includes("--output") && args.includes("json");
|
|
408
|
+
const validateOnly = args.includes("--validate");
|
|
409
|
+
|
|
410
|
+
const manifest = generateManifest(projectRoot);
|
|
411
|
+
|
|
412
|
+
if (manifest.error) {
|
|
413
|
+
console.error(`❌ ${manifest.error}`);
|
|
414
|
+
process.exit(1);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (outputJson) {
|
|
418
|
+
console.log(JSON.stringify(manifest, null, 2));
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Save to disk
|
|
423
|
+
const outputPath = saveManifest(manifest, projectRoot);
|
|
424
|
+
|
|
425
|
+
// Print summary
|
|
426
|
+
const { integrity } = manifest;
|
|
427
|
+
console.log(`\n🛡️ Integrity Manifest Generated`);
|
|
428
|
+
console.log(` Path: ${outputPath}`);
|
|
429
|
+
console.log(` ─────────────────────────────────`);
|
|
430
|
+
console.log(` Agents: ${manifest.agents.total} (${manifest.agents.reviewer_count} reviewers)`);
|
|
431
|
+
console.log(` Skills: ${manifest.skills.total}`);
|
|
432
|
+
console.log(` Scripts: ${manifest.scripts.total}`);
|
|
433
|
+
console.log(` Workflows: ${manifest.workflows.total}`);
|
|
434
|
+
console.log(` References: ${integrity.total_references}`);
|
|
435
|
+
console.log(` Claims: ${integrity.total_claims}`);
|
|
436
|
+
console.log(` ─────────────────────────────────`);
|
|
437
|
+
|
|
438
|
+
if (integrity.phantom_references > 0) {
|
|
439
|
+
console.log(` ⚠️ Phantom references: ${integrity.phantom_references}`);
|
|
440
|
+
const phantoms = manifest.cross_references.filter((r) => !r.exists);
|
|
441
|
+
for (const p of phantoms) {
|
|
442
|
+
console.log(` ❌ ${p.source} → ${p.ref}`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (integrity.invalid_claims > 0) {
|
|
447
|
+
console.log(` ⚠️ Invalid claims: ${integrity.invalid_claims}`);
|
|
448
|
+
const invalid = manifest.numeric_claims.filter((c) => !c.valid);
|
|
449
|
+
for (const c of invalid) {
|
|
450
|
+
console.log(` ❌ ${c.source}: says "${c.claim}" but actual is ${c.actual}`);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
if (
|
|
455
|
+
integrity.phantom_references === 0 &&
|
|
456
|
+
integrity.invalid_claims === 0
|
|
457
|
+
) {
|
|
458
|
+
console.log(` ✅ All references valid. All claims accurate.`);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
console.log();
|
|
462
|
+
|
|
463
|
+
if (validateOnly) {
|
|
464
|
+
const exitCode =
|
|
465
|
+
integrity.phantom_references > 0 || integrity.invalid_claims > 0 ? 1 : 0;
|
|
466
|
+
process.exit(exitCode);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// ── Exports ───────────────────────────────────────────────────────────────────
|
|
471
|
+
|
|
472
|
+
module.exports = {
|
|
473
|
+
generateManifest,
|
|
474
|
+
saveManifest,
|
|
475
|
+
parseFrontmatter,
|
|
476
|
+
crawlAgents,
|
|
477
|
+
crawlSkills,
|
|
478
|
+
crawlScripts,
|
|
479
|
+
crawlWorkflows,
|
|
480
|
+
extractCrossReferences,
|
|
481
|
+
extractNumericClaims,
|
|
482
|
+
};
|
|
483
|
+
|
|
484
|
+
if (require.main === module) {
|
|
485
|
+
main();
|
|
486
|
+
}
|
|
@@ -42,21 +42,9 @@ const {
|
|
|
42
42
|
RESET,
|
|
43
43
|
} = require("./_colors");
|
|
44
44
|
|
|
45
|
-
// ──
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const root = path.parse(current).root;
|
|
49
|
-
while (current !== root) {
|
|
50
|
-
const candidate = path.join(current, ".agent");
|
|
51
|
-
if (fs.existsSync(candidate) && fs.statSync(candidate).isDirectory())
|
|
52
|
-
return candidate;
|
|
53
|
-
current = path.dirname(current);
|
|
54
|
-
}
|
|
55
|
-
console.error(
|
|
56
|
-
`${RED}✖ Error: '.agent' directory not found. Please run 'npx tribunal-kit init' first.${RESET}`,
|
|
57
|
-
);
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
45
|
+
// ── Shared Utilities ──────────────────────────────────────────────────────────
|
|
46
|
+
const { findAgentDir } = require("./_utils");
|
|
47
|
+
|
|
60
48
|
|
|
61
49
|
const AGENT_DIR = findAgentDir();
|
|
62
50
|
const SKILL_DIR = path.join(AGENT_DIR, "skills", "project-idioms");
|
|
@@ -24,18 +24,8 @@ const { CYAN, GREEN, YELLOW, RED, BOLD, RESET } = require("./_colors");
|
|
|
24
24
|
|
|
25
25
|
const REPORT_FILE = "skill-integration-report.md";
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const root = path.parse(current).root;
|
|
30
|
-
while (current !== root) {
|
|
31
|
-
const agentDir = path.join(current, ".agent");
|
|
32
|
-
if (fs.existsSync(agentDir) && fs.statSync(agentDir).isDirectory()) {
|
|
33
|
-
return agentDir;
|
|
34
|
-
}
|
|
35
|
-
current = path.dirname(current);
|
|
36
|
-
}
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
27
|
+
const { findAgentDir } = require("./_utils");
|
|
28
|
+
|
|
39
29
|
|
|
40
30
|
function getAssociatedScript(skillDir, scriptsDir) {
|
|
41
31
|
/** Check if the skill has an explicit frontmatter script or an implicit script file. */
|
|
@@ -89,18 +89,8 @@ const MAX_GOAL_LENGTH = 200;
|
|
|
89
89
|
const MAX_CONTEXT_LENGTH = 800;
|
|
90
90
|
const MAX_WORKERS_PER_SWARM = 5;
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const root = path.parse(current).root;
|
|
95
|
-
while (current !== root) {
|
|
96
|
-
const agentDir = path.join(current, ".agent");
|
|
97
|
-
if (fs.existsSync(agentDir) && fs.statSync(agentDir).isDirectory()) {
|
|
98
|
-
return agentDir;
|
|
99
|
-
}
|
|
100
|
-
current = path.dirname(current);
|
|
101
|
-
}
|
|
102
|
-
return null;
|
|
103
|
-
}
|
|
92
|
+
const { findAgentDir } = require("./_utils");
|
|
93
|
+
|
|
104
94
|
|
|
105
95
|
// ─── Legacy mode: validate orchestrator micro-worker payloads ──────────────────
|
|
106
96
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: 12-principles-of-animation
|
|
3
|
+
description: Application of Disney's 12 Principles of Animation (Squash & Stretch, Anticipation, Staging, Follow Through, Slow In & Slow Out, Arc, Secondary Action, Timing, Exaggeration, Solid Drawing, Appeal) to modern web UI motion.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
last-updated: 2026-07-22
|
|
6
|
+
applies-to-model: gemini-3-6-flash, claude-3-7-sonnet
|
|
7
|
+
routing:
|
|
8
|
+
domain: UI Animation Theory & Disney Principles
|
|
9
|
+
tier: pro
|
|
10
|
+
co-requires: [emil-design-eng, motion-engineering, 60fps-animation]
|
|
11
|
+
trigger-signals:
|
|
12
|
+
strong: [12-principles-of-animation, Disney animation principles, squash and stretch UI, anticipation animation, staging UI motion, follow through web]
|
|
13
|
+
weak: [animation principles, ui animation theory]
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# 12 Principles of Animation — Web UI Motion Theory
|
|
17
|
+
|
|
18
|
+
Translate Disney's 12 classic principles of animation into modern CSS, Web API, and Framer Motion code patterns.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## The 6 Essential Web Principles
|
|
23
|
+
|
|
24
|
+
### 1. Squash & Stretch (Scale Elasticity)
|
|
25
|
+
- Compress elements slightly on impact (e.g. button press down `scale(0.97)`), then stretch slightly on release (`scale(1.02)` -> `scale(1)`).
|
|
26
|
+
- **Rule**: Preserve overall volume. If height decreases by 5%, width must expand by 5%.
|
|
27
|
+
|
|
28
|
+
### 2. Anticipation (Pre-Motion Cue)
|
|
29
|
+
- Before a major movement (e.g. modal sliding up), perform a micro-backwards movement (e.g. shift down `2px` for `40ms`) to prepare the user's eye.
|
|
30
|
+
|
|
31
|
+
### 3. Staging (Focus & Spatial Hierarchy)
|
|
32
|
+
- Direct user attention to one primary animation at a time. Never animate competing layout elements across different regions simultaneously.
|
|
33
|
+
|
|
34
|
+
### 4. Slow In & Slow Out (Easing Curves)
|
|
35
|
+
- Objects in nature start slow, accelerate, and decelerate gradually. Use strong ease-out curves (`cubic-bezier(0.16, 1, 0.3, 1)`) for UI entrances.
|
|
36
|
+
|
|
37
|
+
### 5. Arcs (Natural Curvilinear Trajectories)
|
|
38
|
+
- Human arms and physical objects move in curved arcs rather than mechanical straight lines. When moving elements across 2D space, use parabolic bezier curves or `offset-path`.
|
|
39
|
+
|
|
40
|
+
### 6. Follow Through & Overlapping Action
|
|
41
|
+
- Secondary elements (e.g. badge text inside a sliding card) lag slightly behind the main container (stagger delay 30ms - 50ms), creating organic physical realism.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 🤖 LLM-Specific Traps
|
|
46
|
+
|
|
47
|
+
1. **Extreme Distortion**: Applying 30% squash and stretch to text buttons, causing distorted unreadable font rendering. Keep UI squash/stretch subtle (max 2% - 4%).
|
|
48
|
+
2. **Mechanical Linear Motion**: Using `linear` easing for UI elements entering the screen.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 🏛️ Tribunal Integration (Anti-Hallucination)
|
|
53
|
+
|
|
54
|
+
**Active reviewers: `motion-reviewer` · `frontend-reviewer`**
|
|
55
|
+
|
|
56
|
+
### ✅ Pre-Flight Self-Audit
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
✅ Is squash & stretch volume-preserving ($Width \times Height \approx 1$)?
|
|
60
|
+
✅ Are entrance animations using custom `ease-out` curves?
|
|
61
|
+
✅ Is motion staged to lead user focus sequentially?
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 🛑 Verification-Before-Completion (VBC) Protocol
|
|
65
|
+
|
|
66
|
+
**CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
|
|
67
|
+
|
|
68
|
+
- ❌ **Forbidden:** Declaring a task complete because the output "looks correct."
|
|
69
|
+
- ✅ **Required:** You are explicitly forbidden from finalizing any task without providing **concrete evidence** (terminal output, passing tests, compile success, or equivalent proof) that your output works as intended.
|