tribunal-kit 5.8.3 → 5.8.5

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 (121) hide show
  1. package/.agent/ARCHITECTURE.md +5 -5
  2. package/.agent/history/integrity_manifest.json +1026 -0
  3. package/.agent/history/memory/.memory.idx +1217 -1
  4. package/.agent/history/memory/MEMORY.md +81 -1
  5. package/.agent/rules/GEMINI.md +1 -1
  6. package/.agent/scripts/_utils.js +28 -7
  7. package/.agent/scripts/case_law_manager.js +3 -15
  8. package/.agent/scripts/context_broker.js +3 -20
  9. package/.agent/scripts/guardrail_engine.js +643 -0
  10. package/.agent/scripts/integrity_manifest.js +492 -0
  11. package/.agent/scripts/marathon_harness.js +10 -1
  12. package/.agent/scripts/signal_detector.js +1 -1
  13. package/.agent/scripts/skill_evolution.js +39 -36
  14. package/.agent/scripts/skill_integrator.js +2 -12
  15. package/.agent/scripts/swarm_dispatcher.js +54 -5
  16. package/.agent/skills/12-principles-of-animation/SKILL.md +69 -0
  17. package/.agent/skills/60fps-animation/SKILL.md +78 -0
  18. package/.agent/skills/accessible-animation/SKILL.md +86 -0
  19. package/.agent/skills/adapt/SKILL.md +75 -0
  20. package/.agent/skills/animation-on-scroll/SKILL.md +110 -0
  21. package/.agent/skills/animation-systems/SKILL.md +69 -0
  22. package/.agent/skills/antfu-conventions/SKILL.md +74 -0
  23. package/.agent/skills/api-patterns/scripts/__pycache__/api_validator.cpython-311.pyc +0 -0
  24. package/.agent/skills/apple-design/SKILL.md +108 -0
  25. package/.agent/skills/audit-and-fix/SKILL.md +83 -0
  26. package/.agent/skills/baseline-ui/SKILL.md +74 -0
  27. package/.agent/skills/better-colors/SKILL.md +132 -0
  28. package/.agent/skills/better-typography/SKILL.md +111 -0
  29. package/.agent/skills/better-ui/SKILL.md +102 -0
  30. package/.agent/skills/bolder/SKILL.md +68 -0
  31. package/.agent/skills/build-primitive/SKILL.md +70 -0
  32. package/.agent/skills/clarify/SKILL.md +75 -0
  33. package/.agent/skills/cobejs/SKILL.md +96 -0
  34. package/.agent/skills/codebase-design/SKILL.md +84 -0
  35. package/.agent/skills/colorize/SKILL.md +77 -0
  36. package/.agent/skills/compact-landing/SKILL.md +70 -0
  37. package/.agent/skills/company-logos/SKILL.md +73 -0
  38. package/.agent/skills/create-design-md/SKILL.md +104 -0
  39. package/.agent/skills/critique/SKILL.md +95 -0
  40. package/.agent/skills/database-design/scripts/__pycache__/schema_validator.cpython-311.pyc +0 -0
  41. package/.agent/skills/delight/SKILL.md +65 -0
  42. package/.agent/skills/design-lab/SKILL.md +59 -0
  43. package/.agent/skills/diagnosing-bugs/SKILL.md +190 -0
  44. package/.agent/skills/distill/SKILL.md +65 -0
  45. package/.agent/skills/domain-modeling/SKILL.md +78 -0
  46. package/.agent/skills/fixing-accessibility/SKILL.md +100 -0
  47. package/.agent/skills/fixing-metadata/SKILL.md +97 -0
  48. package/.agent/skills/fixing-motion-performance/SKILL.md +96 -0
  49. package/.agent/skills/frontend-design/SKILL.md +80 -81
  50. package/.agent/skills/frontend-design/scripts/__pycache__/accessibility_checker.cpython-311.pyc +0 -0
  51. package/.agent/skills/frontend-design/scripts/__pycache__/ux_audit.cpython-311.pyc +0 -0
  52. package/.agent/skills/geo-fundamentals/scripts/__pycache__/geo_checker.cpython-311.pyc +0 -0
  53. package/.agent/skills/gpt-taste/SKILL.md +68 -0
  54. package/.agent/skills/harden/SKILL.md +74 -0
  55. package/.agent/skills/i18n-localization/scripts/__pycache__/i18n_checker.cpython-311.pyc +0 -0
  56. package/.agent/skills/impeccable/SKILL.md +74 -0
  57. package/.agent/skills/improve-codebase-architecture/SKILL.md +67 -0
  58. package/.agent/skills/improve-ui/SKILL.md +83 -0
  59. package/.agent/skills/landing-page/SKILL.md +88 -0
  60. package/.agent/skills/lint-and-validate/SKILL.md +0 -1
  61. package/.agent/skills/lint-and-validate/scripts/__pycache__/lint_runner.cpython-311.pyc +0 -0
  62. package/.agent/skills/lint-and-validate/scripts/__pycache__/type_coverage.cpython-311.pyc +0 -0
  63. package/.agent/skills/local-first-architecture/SKILL.md +66 -0
  64. package/.agent/skills/lottie-animation/SKILL.md +82 -0
  65. package/.agent/skills/marquee-loop/SKILL.md +96 -0
  66. package/.agent/skills/masked-reveal/SKILL.md +88 -0
  67. package/.agent/skills/micro-interaction/SKILL.md +100 -0
  68. package/.agent/skills/mobile-design/scripts/__pycache__/mobile_audit.cpython-311.pyc +0 -0
  69. package/.agent/skills/morphing-icons/SKILL.md +85 -0
  70. package/.agent/skills/motion-engineering/SKILL.md +66 -164
  71. package/.agent/skills/nextjs-react-expert/scripts/__pycache__/convert_rules.cpython-311.pyc +0 -0
  72. package/.agent/skills/nextjs-react-expert/scripts/__pycache__/react_performance_checker.cpython-311.pyc +0 -0
  73. package/.agent/skills/page-transition-animation/SKILL.md +108 -0
  74. package/.agent/skills/parallel-agents/SKILL.md +5 -5
  75. package/.agent/skills/performance-profiling/scripts/__pycache__/lighthouse_audit.cpython-311.pyc +0 -0
  76. package/.agent/skills/polish/SKILL.md +63 -0
  77. package/.agent/skills/pricing-page/SKILL.md +68 -0
  78. package/.agent/skills/progressive-blur/SKILL.md +100 -0
  79. package/.agent/skills/quieter/SKILL.md +68 -0
  80. package/.agent/skills/react-doctor/SKILL.md +70 -0
  81. package/.agent/skills/redesign-skill/SKILL.md +65 -0
  82. package/.agent/skills/seo-fundamentals/scripts/__pycache__/seo_checker.cpython-311.pyc +0 -0
  83. package/.agent/skills/shape/SKILL.md +73 -0
  84. package/.agent/skills/soft-skill/SKILL.md +72 -0
  85. package/.agent/skills/sounds-on-the-web/SKILL.md +100 -0
  86. package/.agent/skills/svg-animation/SKILL.md +91 -0
  87. package/.agent/skills/swiss-design/SKILL.md +64 -0
  88. package/.agent/skills/taste-skill/SKILL.md +71 -0
  89. package/.agent/skills/tdd-workflow/SKILL.md +36 -165
  90. package/.agent/skills/testing-patterns/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
  91. package/.agent/skills/thermo-nuclear-code-quality-review/SKILL.md +82 -0
  92. package/.agent/skills/to-spring-or-not-to-spring/SKILL.md +66 -0
  93. package/.agent/skills/transitions-dev/SKILL.md +101 -0
  94. package/.agent/skills/typeset/SKILL.md +76 -0
  95. package/.agent/skills/ui-skills-root/SKILL.md +85 -0
  96. package/.agent/skills/vulnerability-scanner/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
  97. package/.agent/skills/web-quality-audit/SKILL.md +67 -0
  98. package/.agent/skills/webapp-testing/scripts/__pycache__/playwright_runner.cpython-311.pyc +0 -0
  99. package/.agent/workflows/generate.md +1 -1
  100. package/.agent/workflows/tribunal-full.md +2 -2
  101. package/.agent/workflows/tribunal-speed.md +2 -2
  102. package/CONTRIBUTING.md +1 -1
  103. package/README.md +349 -239
  104. package/bin/mcp-server.js +45 -45
  105. package/bin/tribunal-kit.js +62 -1482
  106. package/bin/wrapper.js +50 -25
  107. package/dist/cli.js +43 -0
  108. package/dist/commands/align.js +1 -1
  109. package/dist/commands/compile.js +1 -1
  110. package/dist/commands/guardrail.js +275 -0
  111. package/dist/commands/init.js +35 -10
  112. package/dist/commands/native.js +228 -0
  113. package/dist/commands/optimize.js +318 -0
  114. package/dist/commands/validate.js +67 -0
  115. package/dist/mcp/server.js +4 -138
  116. package/dist/utils/version.js +1 -1
  117. package/package.json +13 -10
  118. package/scripts/benchmark.js +42 -0
  119. package/scripts/fix-vbc.js +98 -0
  120. package/scripts/fix-vbc.ps1 +119 -0
  121. package/scripts/sync-version.js +129 -76
@@ -0,0 +1,492 @@
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|throughput-optimizer/i.test(name) ||
86
+ (fm.role && /reviewer/i.test(fm.role));
87
+
88
+
89
+ if (isReviewer) {
90
+ reviewers.push(name);
91
+ } else {
92
+ specialists.push(name);
93
+ }
94
+ }
95
+
96
+ return {
97
+ total: allAgents.length,
98
+ reviewers: reviewers.sort(),
99
+ specialists: specialists.sort(),
100
+ reviewer_count: reviewers.length,
101
+ all: allAgents.sort(),
102
+ };
103
+ }
104
+
105
+ // ── Skill Crawler ─────────────────────────────────────────────────────────────
106
+
107
+ function crawlSkills(agentDir) {
108
+ const skillsDir = path.join(agentDir, "skills");
109
+ const entries = safeReaddir(skillsDir);
110
+ const names = [];
111
+
112
+ for (const entry of entries) {
113
+ const skillPath = path.join(skillsDir, entry, "SKILL.md");
114
+ if (fs.existsSync(skillPath)) {
115
+ const content = safeReadFile(skillPath);
116
+ const fm = parseFrontmatter(content);
117
+ names.push(fm.name || entry);
118
+ }
119
+ }
120
+
121
+ return {
122
+ total: names.length,
123
+ names: names.sort(),
124
+ };
125
+ }
126
+
127
+ // ── Script Crawler ────────────────────────────────────────────────────────────
128
+
129
+ function crawlScripts(agentDir) {
130
+ const scriptsDir = path.join(agentDir, "scripts");
131
+ const files = safeReaddir(scriptsDir).filter(
132
+ (f) => f.endsWith(".js") || f.endsWith(".py"),
133
+ );
134
+
135
+ const fileMap = {};
136
+ for (const file of files) {
137
+ const relPath = path.join(AGENT_DIR_NAME, "scripts", file);
138
+ fileMap[file] = relPath.replace(/\\/g, "/");
139
+ }
140
+
141
+ return {
142
+ total: files.length,
143
+ files: fileMap,
144
+ };
145
+ }
146
+
147
+ // ── Workflow Crawler ──────────────────────────────────────────────────────────
148
+
149
+ function crawlWorkflows(agentDir) {
150
+ const workflowsDir = path.join(agentDir, "workflows");
151
+ const files = safeReaddir(workflowsDir).filter((f) => f.endsWith(".md"));
152
+ const names = files.map((f) => f.replace(/\.md$/, ""));
153
+
154
+ return {
155
+ total: names.length,
156
+ names: names.sort(),
157
+ };
158
+ }
159
+
160
+ // ── Cross-Reference Extractor ─────────────────────────────────────────────────
161
+
162
+ /**
163
+ * Scans a file's content for references to other .agent/ assets.
164
+ * Patterns detected:
165
+ * - agents/X.md, agents/X
166
+ * - skills/X/SKILL.md, skill name references
167
+ * - scripts/X.js, scripts/X.py
168
+ * - workflows/X.md
169
+ * - node .agent/scripts/X
170
+ */
171
+ function extractCrossReferences(filePath, content, agentDir) {
172
+ const refs = [];
173
+ const relSource = path.relative(agentDir, filePath).replace(/\\/g, "/");
174
+
175
+ // Agent references: agents/name.md or `agent-name`
176
+ const agentRefRegex =
177
+ /(?:agents\/)([\w-]+)(?:\.md)?/g;
178
+ let m;
179
+ while ((m = agentRefRegex.exec(content)) !== null) {
180
+ const refFile = `agents/${m[1]}.md`;
181
+ const fullPath = path.join(agentDir, refFile);
182
+ refs.push({
183
+ source: relSource,
184
+ ref: refFile,
185
+ exists: fs.existsSync(fullPath),
186
+ type: "agent",
187
+ });
188
+ }
189
+
190
+ // Script references: scripts/name.js or scripts/name.py
191
+ const scriptRefRegex =
192
+ /(?:scripts\/)([\w_-]+)\.(js|py)/g;
193
+ while ((m = scriptRefRegex.exec(content)) !== null) {
194
+ const refFile = `scripts/${m[1]}.${m[2]}`;
195
+ const fullPath = path.join(agentDir, refFile);
196
+ refs.push({
197
+ source: relSource,
198
+ ref: refFile,
199
+ exists: fs.existsSync(fullPath),
200
+ type: "script",
201
+ });
202
+ }
203
+
204
+ // Workflow references: workflows/name.md
205
+ const workflowRefRegex = /(?:workflows\/)([\w-]+)(?:\.md)?/g;
206
+ while ((m = workflowRefRegex.exec(content)) !== null) {
207
+ const precedingText = content.substring(Math.max(0, m.index - 100), m.index);
208
+ if (precedingText.includes(".github/") || precedingText.includes("github.com/")) {
209
+ continue;
210
+ }
211
+ const refFile = `workflows/${m[1]}.md`;
212
+ const fullPath = path.join(agentDir, refFile);
213
+ refs.push({
214
+ source: relSource,
215
+ ref: refFile,
216
+ exists: fs.existsSync(fullPath),
217
+ type: "workflow",
218
+ });
219
+ }
220
+
221
+ // Deduplicate
222
+ const seen = new Set();
223
+ return refs.filter((r) => {
224
+ const key = `${r.source}→${r.ref}`;
225
+ if (seen.has(key)) return false;
226
+ seen.add(key);
227
+ return true;
228
+ });
229
+ }
230
+
231
+ // ── Numeric Claim Extractor ───────────────────────────────────────────────────
232
+
233
+ /**
234
+ * Finds numeric claims about asset counts in a file.
235
+ * Patterns: "N reviewers", "N agents", "N skills", "N workflows", "N scripts"
236
+ */
237
+ function extractNumericClaims(filePath, content, agentDir, actualCounts) {
238
+ const claims = [];
239
+ const relSource = path.relative(agentDir, filePath).replace(/\\/g, "/");
240
+ const lines = content.split(/\r?\n/);
241
+
242
+ const claimPatterns = [
243
+ {
244
+ regex: /(\d+)\s*(?:-\s*)?(?:(?:parallel|domain(?:-specific)?|tribunal|code)\s+)*reviewers?\b/gi,
245
+ entity: "reviewers",
246
+ },
247
+ { regex: /(\d+)\s*(?:specialist\s+)?agents?\b/gi, entity: "agents" },
248
+ { regex: /(\d+)\s*(?:modular\s+)?skills?\b/gi, entity: "skills" },
249
+ { regex: /(\d+)\s*(?:slash\s+)?workflows?\b/gi, entity: "workflows" },
250
+ { regex: /(\d+)\s*(?:JS\s+)?scripts?\b/gi, entity: "scripts" },
251
+ ];
252
+
253
+ for (let i = 0; i < lines.length; i++) {
254
+ const line = lines[i];
255
+ for (const { regex, entity } of claimPatterns) {
256
+ regex.lastIndex = 0;
257
+ let match;
258
+ while ((match = regex.exec(line)) !== null) {
259
+ const claimed = parseInt(match[1], 10);
260
+ // This manifest describes the full Tribunal payload, so small local
261
+ // counts (for example, "3 skills" in one skill group) are not global
262
+ // asset claims. Skip them along with implausibly large values.
263
+ if (claimed < 5 || claimed > 500) continue;
264
+
265
+ const actual = actualCounts[entity];
266
+ if (actual !== undefined) {
267
+ claims.push({
268
+ source: `${relSource}:L${i + 1}`,
269
+ claim: `${claimed} ${entity}`,
270
+ actual,
271
+ valid: claimed === actual,
272
+ });
273
+ }
274
+ }
275
+ }
276
+ }
277
+
278
+ return claims;
279
+ }
280
+
281
+ // ── File Walker ───────────────────────────────────────────────────────────────
282
+
283
+ function walkMdFiles(dir, fileList = []) {
284
+ if (!fs.existsSync(dir)) return fileList;
285
+
286
+ const entries = safeReaddir(dir);
287
+ for (const entry of entries) {
288
+ const fullPath = path.join(dir, entry);
289
+ try {
290
+ const stat = fs.statSync(fullPath);
291
+ if (stat.isDirectory()) {
292
+ // Skip node_modules, .git, history, .backups, .shared
293
+ if (
294
+ ["node_modules", ".git", "history", ".backups", ".shared"].includes(
295
+ entry,
296
+ )
297
+ ) {
298
+ continue;
299
+ }
300
+ walkMdFiles(fullPath, fileList);
301
+ } else if (entry.endsWith(".md") || entry.endsWith(".json")) {
302
+ if (entry.startsWith("http") || entry.startsWith("#") || entry.startsWith("https://github.com/")) {
303
+ continue;
304
+ }
305
+ fileList.push(fullPath);
306
+ }
307
+ } catch {
308
+ // Skip files we can't stat
309
+ }
310
+ }
311
+ return fileList;
312
+ }
313
+
314
+ // ── Main: Generate Manifest ───────────────────────────────────────────────────
315
+
316
+ function generateManifest(projectRoot) {
317
+ const agentDir = path.join(projectRoot, AGENT_DIR_NAME);
318
+
319
+ if (!fs.existsSync(agentDir)) {
320
+ return {
321
+ error: `No ${AGENT_DIR_NAME}/ directory found at ${projectRoot}`,
322
+ };
323
+ }
324
+
325
+ // Phase 1: Crawl all entity types
326
+ const agents = crawlAgents(agentDir);
327
+ const skills = crawlSkills(agentDir);
328
+ const scripts = crawlScripts(agentDir);
329
+ const workflows = crawlWorkflows(agentDir);
330
+
331
+ // Phase 2: Extract cross-references from all markdown files
332
+ const allFiles = walkMdFiles(agentDir);
333
+ const allRefs = [];
334
+ const allClaims = [];
335
+
336
+ const actualCounts = {
337
+ reviewers: agents.reviewer_count,
338
+ agents: agents.total,
339
+ skills: skills.total,
340
+ workflows: workflows.total,
341
+ scripts: scripts.total,
342
+ };
343
+
344
+ for (const file of allFiles) {
345
+ const content = safeReadFile(file);
346
+ if (!content) continue;
347
+
348
+ const refs = extractCrossReferences(file, content, agentDir);
349
+ allRefs.push(...refs);
350
+
351
+ const claims = extractNumericClaims(file, content, agentDir, actualCounts);
352
+ allClaims.push(...claims);
353
+ }
354
+
355
+ // Phase 3: Deduplicate cross-references
356
+ const seenRefs = new Set();
357
+ const uniqueRefs = allRefs.filter((r) => {
358
+ const key = `${r.source}→${r.ref}`;
359
+ if (seenRefs.has(key)) return false;
360
+ seenRefs.add(key);
361
+ return true;
362
+ });
363
+
364
+ // Phase 4: Build manifest
365
+ let version = "unknown";
366
+ try {
367
+ const pkg = JSON.parse(
368
+ safeReadFile(path.join(projectRoot, "package.json")) || "{}",
369
+ );
370
+ version = pkg.version || "unknown";
371
+ } catch {
372
+ // Ignore
373
+ }
374
+
375
+ const manifest = {
376
+ version,
377
+ generated_at: new Date().toISOString(),
378
+ agents,
379
+ skills,
380
+ scripts,
381
+ workflows,
382
+ cross_references: uniqueRefs,
383
+ numeric_claims: allClaims,
384
+ integrity: {
385
+ phantom_references: uniqueRefs.filter((r) => !r.exists).length,
386
+ invalid_claims: allClaims.filter((c) => !c.valid).length,
387
+ total_references: uniqueRefs.length,
388
+ total_claims: allClaims.length,
389
+ },
390
+ };
391
+
392
+ return manifest;
393
+ }
394
+
395
+ // ── Persist Manifest ──────────────────────────────────────────────────────────
396
+
397
+ function saveManifest(manifest, projectRoot) {
398
+ const historyDir = path.join(projectRoot, AGENT_DIR_NAME, "history");
399
+ if (!fs.existsSync(historyDir)) {
400
+ fs.mkdirSync(historyDir, { recursive: true });
401
+ }
402
+
403
+ const outputPath = path.join(historyDir, "integrity_manifest.json");
404
+ fs.writeFileSync(outputPath, JSON.stringify(manifest, null, 2), "utf8");
405
+ return outputPath;
406
+ }
407
+
408
+ // ── CLI Entry Point ───────────────────────────────────────────────────────────
409
+
410
+ function main() {
411
+ const args = process.argv.slice(2);
412
+ const projectRoot = process.cwd();
413
+ const outputJson = args.includes("--output") && args.includes("json");
414
+ const validateOnly = args.includes("--validate");
415
+
416
+ const manifest = generateManifest(projectRoot);
417
+
418
+ if (manifest.error) {
419
+ console.error(`❌ ${manifest.error}`);
420
+ process.exit(1);
421
+ }
422
+
423
+ if (outputJson) {
424
+ console.log(JSON.stringify(manifest, null, 2));
425
+ return;
426
+ }
427
+
428
+ // Save to disk
429
+ const outputPath = saveManifest(manifest, projectRoot);
430
+
431
+ // Print summary
432
+ const { integrity } = manifest;
433
+ console.log(`\n🛡️ Integrity Manifest Generated`);
434
+ console.log(` Path: ${outputPath}`);
435
+ console.log(` ─────────────────────────────────`);
436
+ console.log(` Agents: ${manifest.agents.total} (${manifest.agents.reviewer_count} reviewers)`);
437
+ console.log(` Skills: ${manifest.skills.total}`);
438
+ console.log(` Scripts: ${manifest.scripts.total}`);
439
+ console.log(` Workflows: ${manifest.workflows.total}`);
440
+ console.log(` References: ${integrity.total_references}`);
441
+ console.log(` Claims: ${integrity.total_claims}`);
442
+ console.log(` ─────────────────────────────────`);
443
+
444
+ if (integrity.phantom_references > 0) {
445
+ console.log(` ⚠️ Phantom references: ${integrity.phantom_references}`);
446
+ const phantoms = manifest.cross_references.filter((r) => !r.exists);
447
+ for (const p of phantoms) {
448
+ console.log(` ❌ ${p.source} → ${p.ref}`);
449
+ }
450
+ }
451
+
452
+ if (integrity.invalid_claims > 0) {
453
+ console.log(` ⚠️ Invalid claims: ${integrity.invalid_claims}`);
454
+ const invalid = manifest.numeric_claims.filter((c) => !c.valid);
455
+ for (const c of invalid) {
456
+ console.log(` ❌ ${c.source}: says "${c.claim}" but actual is ${c.actual}`);
457
+ }
458
+ }
459
+
460
+ if (
461
+ integrity.phantom_references === 0 &&
462
+ integrity.invalid_claims === 0
463
+ ) {
464
+ console.log(` ✅ All references valid. All claims accurate.`);
465
+ }
466
+
467
+ console.log();
468
+
469
+ if (validateOnly) {
470
+ const exitCode =
471
+ integrity.phantom_references > 0 || integrity.invalid_claims > 0 ? 1 : 0;
472
+ process.exit(exitCode);
473
+ }
474
+ }
475
+
476
+ // ── Exports ───────────────────────────────────────────────────────────────────
477
+
478
+ module.exports = {
479
+ generateManifest,
480
+ saveManifest,
481
+ parseFrontmatter,
482
+ crawlAgents,
483
+ crawlSkills,
484
+ crawlScripts,
485
+ crawlWorkflows,
486
+ extractCrossReferences,
487
+ extractNumericClaims,
488
+ };
489
+
490
+ if (require.main === module) {
491
+ main();
492
+ }
@@ -24,6 +24,7 @@
24
24
  const fs = require("fs");
25
25
  const path = require("path");
26
26
  const { execSync } = require("child_process");
27
+ const { findAgentDir } = require("./_utils");
27
28
 
28
29
  const {
29
30
  GREEN,
@@ -42,7 +43,11 @@ const {
42
43
  } = require("./_colors");
43
44
 
44
45
  // ── Paths ────────────────────────────────────────────────────────────────────
45
- const MARATHON_DIR = path.resolve(".agent", "history", "marathon");
46
+ function getMarathonDir(startDir = process.cwd()) {
47
+ return path.join(findAgentDir(startDir), "history", "marathon");
48
+ }
49
+
50
+ const MARATHON_DIR = getMarathonDir();
46
51
  const FEATURE_LIST_FILE = path.join(MARATHON_DIR, "feature_list.json");
47
52
  const PROGRESS_FILE = path.join(MARATHON_DIR, "progress.json");
48
53
  const ARCHIVE_DIR = path.join(MARATHON_DIR, "archive");
@@ -1049,6 +1054,10 @@ function main() {
1049
1054
  }
1050
1055
  }
1051
1056
 
1057
+ module.exports = {
1058
+ getMarathonDir,
1059
+ };
1060
+
1052
1061
  if (require.main === module) {
1053
1062
  main();
1054
1063
  }
@@ -15,7 +15,7 @@
15
15
 
16
16
  "use strict";
17
17
 
18
- const fs = require("fs");
18
+ const _fs = require("fs");
19
19
  const path = require("path");
20
20
 
21
21
  const SIGNAL_TYPES = {
@@ -42,28 +42,23 @@ const {
42
42
  RESET,
43
43
  } = require("./_colors");
44
44
 
45
- // ── Find .agent directory ─────────────────────────────────────────────────────
46
- function findAgentDir() {
47
- let current = path.resolve(process.cwd());
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);
45
+ // ── Shared Utilities ──────────────────────────────────────────────────────────
46
+ const { findAgentDir } = require("./_utils");
47
+
48
+
49
+ function getPaths(startDir = process.cwd()) {
50
+ const agentDir = findAgentDir(startDir);
51
+ const skillDir = path.join(agentDir, "skills", "project-idioms");
52
+ const historyDir = path.join(agentDir, "history", "skill-evolution");
53
+ return {
54
+ agentDir,
55
+ skillDir,
56
+ skillFile: path.join(skillDir, "SKILL.md"),
57
+ historyDir,
58
+ logFile: path.join(historyDir, "digest-log.json"),
59
+ };
59
60
  }
60
61
 
61
- const AGENT_DIR = findAgentDir();
62
- const SKILL_DIR = path.join(AGENT_DIR, "skills", "project-idioms");
63
- const SKILL_FILE = path.join(SKILL_DIR, "SKILL.md");
64
- const HISTORY_DIR = path.join(AGENT_DIR, "history", "skill-evolution");
65
- const LOG_FILE = path.join(HISTORY_DIR, "digest-log.json");
66
-
67
62
  // ── Architectural Weight Patterns ────────────────────────────────────────────
68
63
  const HIGH_WEIGHT_PATTERNS = [
69
64
  /\bclass\b/,
@@ -217,8 +212,9 @@ function countTokensEstimate(text) {
217
212
 
218
213
  // ── Idiom management ──────────────────────────────────────────────────────────
219
214
  function loadExistingIdioms() {
220
- if (!fs.existsSync(SKILL_FILE)) return [];
221
- const content = fs.readFileSync(SKILL_FILE, "utf8");
215
+ const { skillFile } = getPaths();
216
+ if (!fs.existsSync(skillFile)) return [];
217
+ const content = fs.readFileSync(skillFile, "utf8");
222
218
  const idioms = [];
223
219
  const pattern =
224
220
  /\|\s*(\d+)\s*\|\s*`([^`]+)`\s*\|\s*([^|]+)\|\s*([^|]+)\|\s*([^|]+)\|/g;
@@ -358,10 +354,11 @@ function parseLlmYamlResponse(response) {
358
354
 
359
355
  // ── Log helpers ────────────────────────────────────────────────────────────────
360
356
  function loadLog() {
361
- fs.mkdirSync(HISTORY_DIR, { recursive: true });
362
- if (fs.existsSync(LOG_FILE)) {
357
+ const { historyDir, logFile } = getPaths();
358
+ fs.mkdirSync(historyDir, { recursive: true });
359
+ if (fs.existsSync(logFile)) {
363
360
  try {
364
- return JSON.parse(fs.readFileSync(LOG_FILE, "utf8"));
361
+ return JSON.parse(fs.readFileSync(logFile, "utf8"));
365
362
  } catch {
366
363
  /* fallthrough */
367
364
  }
@@ -370,8 +367,9 @@ function loadLog() {
370
367
  }
371
368
 
372
369
  function saveLog(log) {
373
- fs.mkdirSync(HISTORY_DIR, { recursive: true });
374
- fs.writeFileSync(LOG_FILE, JSON.stringify(log, null, 2), "utf8");
370
+ const { historyDir, logFile } = getPaths();
371
+ fs.mkdirSync(historyDir, { recursive: true });
372
+ fs.writeFileSync(logFile, JSON.stringify(log, null, 2), "utf8");
375
373
  }
376
374
 
377
375
  // ── Auto-LLM API Integration ─────────────────────────────────────────────────
@@ -847,8 +845,9 @@ async function cmdDigest(args) {
847
845
  if (added > 0) {
848
846
  log.total_idioms = merged.length;
849
847
  const skillMd = renderSkillMd(merged, (log.cycles || []).length + 1);
850
- fs.mkdirSync(SKILL_DIR, { recursive: true });
851
- fs.writeFileSync(SKILL_FILE, skillMd, "utf8");
848
+ const { skillDir, skillFile } = getPaths();
849
+ fs.mkdirSync(skillDir, { recursive: true });
850
+ fs.writeFileSync(skillFile, skillMd, "utf8");
852
851
  console.log(` ${GREEN}✔ ${added} new idiom(s) added to SKILL.md${RESET}`);
853
852
  } else {
854
853
  console.log(` ${DIM} No new unique idioms added to SKILL.md.${RESET}`);
@@ -944,22 +943,24 @@ async function cmdDigest(args) {
944
943
  }
945
944
 
946
945
  function cmdShow() {
947
- if (!fs.existsSync(SKILL_FILE)) {
946
+ const { skillFile } = getPaths();
947
+ if (!fs.existsSync(skillFile)) {
948
948
  console.log(
949
949
  `${YELLOW}No project-idioms skill found. Run 'digest' first.${RESET}`,
950
950
  );
951
951
  return;
952
952
  }
953
- console.log(fs.readFileSync(SKILL_FILE, "utf8"));
953
+ console.log(fs.readFileSync(skillFile, "utf8"));
954
954
  }
955
955
 
956
956
  function cmdReset() {
957
- if (fs.existsSync(SKILL_FILE)) {
958
- fs.unlinkSync(SKILL_FILE);
957
+ const { skillFile, logFile } = getPaths();
958
+ if (fs.existsSync(skillFile)) {
959
+ fs.unlinkSync(skillFile);
959
960
  console.log(`${GREEN}✔ project-idioms/SKILL.md deleted.${RESET}`);
960
961
  }
961
- if (fs.existsSync(LOG_FILE)) {
962
- fs.unlinkSync(LOG_FILE);
962
+ if (fs.existsSync(logFile)) {
963
+ fs.unlinkSync(logFile);
963
964
  console.log(`${GREEN}✔ Digest log cleared.${RESET}`);
964
965
  }
965
966
  console.log(`${DIM}Run 'digest' to start a fresh evolution cycle.${RESET}`);
@@ -970,7 +971,8 @@ function cmdStatus() {
970
971
  const cycles = log.cycles || [];
971
972
  const totalSaved = log.total_tokens_saved || 0;
972
973
  const totalIdioms = log.total_idioms || 0;
973
- const idiomsExist = fs.existsSync(SKILL_FILE);
974
+ const { skillFile } = getPaths();
975
+ const idiomsExist = fs.existsSync(skillFile);
974
976
 
975
977
  console.log(
976
978
  `\n${BOLD}${CYAN}━━━ Skill Evolution Status ━━━━━━━━━━━━━━━━━━━━━━━━${RESET}`,
@@ -1045,6 +1047,7 @@ module.exports = {
1045
1047
  architecturalWeight,
1046
1048
  parseLlmYamlResponse,
1047
1049
  loadExistingIdioms,
1050
+ getPaths,
1048
1051
  };
1049
1052
 
1050
1053
  if (require.main === module) {
@@ -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
- function findAgentDir(startPathStr) {
28
- let current = path.resolve(startPathStr);
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. */