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.
- package/.agent/ARCHITECTURE.md +5 -5
- package/.agent/history/integrity_manifest.json +1026 -0
- package/.agent/history/memory/.memory.idx +1217 -1
- package/.agent/history/memory/MEMORY.md +81 -1
- package/.agent/rules/GEMINI.md +1 -1
- package/.agent/scripts/_utils.js +28 -7
- package/.agent/scripts/case_law_manager.js +3 -15
- package/.agent/scripts/context_broker.js +3 -20
- package/.agent/scripts/guardrail_engine.js +643 -0
- package/.agent/scripts/integrity_manifest.js +492 -0
- package/.agent/scripts/marathon_harness.js +10 -1
- package/.agent/scripts/signal_detector.js +1 -1
- package/.agent/scripts/skill_evolution.js +39 -36
- package/.agent/scripts/skill_integrator.js +2 -12
- package/.agent/scripts/swarm_dispatcher.js +54 -5
- 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/api-patterns/scripts/__pycache__/api_validator.cpython-311.pyc +0 -0
- 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 +132 -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/database-design/scripts/__pycache__/schema_validator.cpython-311.pyc +0 -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 +100 -0
- package/.agent/skills/fixing-metadata/SKILL.md +97 -0
- package/.agent/skills/fixing-motion-performance/SKILL.md +96 -0
- package/.agent/skills/frontend-design/SKILL.md +80 -81
- package/.agent/skills/frontend-design/scripts/__pycache__/accessibility_checker.cpython-311.pyc +0 -0
- package/.agent/skills/frontend-design/scripts/__pycache__/ux_audit.cpython-311.pyc +0 -0
- package/.agent/skills/geo-fundamentals/scripts/__pycache__/geo_checker.cpython-311.pyc +0 -0
- package/.agent/skills/gpt-taste/SKILL.md +68 -0
- package/.agent/skills/harden/SKILL.md +74 -0
- package/.agent/skills/i18n-localization/scripts/__pycache__/i18n_checker.cpython-311.pyc +0 -0
- package/.agent/skills/impeccable/SKILL.md +74 -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 +0 -1
- package/.agent/skills/lint-and-validate/scripts/__pycache__/lint_runner.cpython-311.pyc +0 -0
- package/.agent/skills/lint-and-validate/scripts/__pycache__/type_coverage.cpython-311.pyc +0 -0
- 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/mobile-design/scripts/__pycache__/mobile_audit.cpython-311.pyc +0 -0
- package/.agent/skills/morphing-icons/SKILL.md +85 -0
- package/.agent/skills/motion-engineering/SKILL.md +66 -164
- package/.agent/skills/nextjs-react-expert/scripts/__pycache__/convert_rules.cpython-311.pyc +0 -0
- package/.agent/skills/nextjs-react-expert/scripts/__pycache__/react_performance_checker.cpython-311.pyc +0 -0
- package/.agent/skills/page-transition-animation/SKILL.md +108 -0
- package/.agent/skills/parallel-agents/SKILL.md +5 -5
- package/.agent/skills/performance-profiling/scripts/__pycache__/lighthouse_audit.cpython-311.pyc +0 -0
- 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/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/seo-fundamentals/scripts/__pycache__/seo_checker.cpython-311.pyc +0 -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/testing-patterns/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
- 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/vulnerability-scanner/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
- package/.agent/skills/web-quality-audit/SKILL.md +67 -0
- package/.agent/skills/webapp-testing/scripts/__pycache__/playwright_runner.cpython-311.pyc +0 -0
- package/.agent/workflows/generate.md +1 -1
- package/.agent/workflows/tribunal-full.md +2 -2
- package/.agent/workflows/tribunal-speed.md +2 -2
- package/CONTRIBUTING.md +1 -1
- package/README.md +349 -239
- package/bin/mcp-server.js +45 -45
- package/bin/tribunal-kit.js +62 -1482
- package/bin/wrapper.js +50 -25
- package/dist/cli.js +43 -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 +35 -10
- package/dist/commands/native.js +228 -0
- package/dist/commands/optimize.js +318 -0
- package/dist/commands/validate.js +67 -0
- package/dist/mcp/server.js +4 -138
- package/dist/utils/version.js +1 -1
- package/package.json +13 -10
- package/scripts/benchmark.js +42 -0
- package/scripts/fix-vbc.js +98 -0
- package/scripts/fix-vbc.ps1 +119 -0
- 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
|
-
|
|
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
|
}
|
|
@@ -42,28 +42,23 @@ const {
|
|
|
42
42
|
RESET,
|
|
43
43
|
} = require("./_colors");
|
|
44
44
|
|
|
45
|
-
// ──
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
221
|
-
|
|
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
|
-
|
|
362
|
-
|
|
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(
|
|
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
|
-
|
|
374
|
-
fs.
|
|
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
|
-
|
|
851
|
-
fs.
|
|
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
|
-
|
|
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(
|
|
953
|
+
console.log(fs.readFileSync(skillFile, "utf8"));
|
|
954
954
|
}
|
|
955
955
|
|
|
956
956
|
function cmdReset() {
|
|
957
|
-
|
|
958
|
-
|
|
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(
|
|
962
|
-
fs.unlinkSync(
|
|
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
|
|
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
|
-
|
|
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. */
|