tribunal-kit 4.3.1 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/history/architecture-explorer.html +352 -0
- package/.agent/history/architecture-graph.yaml +109 -0
- package/.agent/history/graph-cache.json +215 -0
- package/.agent/history/snapshots/migrate_refs.js.json +11 -0
- package/.agent/history/snapshots/scripts__changelog.js.json +12 -0
- package/.agent/history/snapshots/scripts__sync-version.js.json +11 -0
- package/.agent/history/snapshots/scripts__validate-payload.js.json +11 -0
- package/.agent/history/snapshots/test__integration__bridges.test.js.json +13 -0
- package/.agent/history/snapshots/test__integration__init.test.js.json +13 -0
- package/.agent/history/snapshots/test__integration__routing.test.js.json +11 -0
- package/.agent/history/snapshots/test__integration__swarm_dispatcher.test.js.json +13 -0
- package/.agent/history/snapshots/test__integration__wave2.test.js.json +13 -0
- package/.agent/history/snapshots/test__unit__args.test.js.json +10 -0
- package/.agent/history/snapshots/test__unit__case_law_manager.test.js.json +10 -0
- package/.agent/history/snapshots/test__unit__copyDir.test.js.json +13 -0
- package/.agent/history/snapshots/test__unit__graph_tools.test.js.json +11 -0
- package/.agent/history/snapshots/test__unit__selfInstall.test.js.json +13 -0
- package/.agent/history/snapshots/test__unit__semver.test.js.json +10 -0
- package/.agent/history/snapshots/test__unit__swarm_dispatcher.test.js.json +11 -0
- package/.agent/scripts/dependency_analyzer.js +1 -1
- package/.agent/scripts/graph_builder.js +311 -199
- package/.agent/scripts/graph_visualizer.js +384 -0
- package/.agent/scripts/mutation_runner.js +280 -0
- package/.agent/skills/knowledge-graph/SKILL.md +52 -36
- package/.agent/skills/testing-patterns/SKILL.md +19 -2
- package/.agent/skills/ui-ux-pro-max/SKILL.md +562 -125
- package/bin/tribunal-kit.js +129 -1
- package/package.json +1 -1
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "migrate_refs.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"fs": [],
|
|
7
|
+
"path": []
|
|
8
|
+
},
|
|
9
|
+
"dependents": [],
|
|
10
|
+
"content": "const fs = require('fs');\nconst path = require('path');\n\nconst filesToUpdate = [\n \".agent/workflows/swarm.md\",\n \".agent/workflows/session.md\",\n \".agent/rules/GEMINI.md\",\n \".agent/ARCHITECTURE.md\",\n \".agent/agents/swarm-worker-contracts.md\",\n \"AGENT_FLOW.md\",\n \"CHANGELOG.md\"\n];\n\nlet totalChanges = 0;\n\nfor (const filepath of filesToUpdate) {\n if (!fs.existsSync(filepath)) continue;\n let content = fs.readFileSync(filepath, 'utf8');\n \n // Explicit python calls\n content = content.replace(/python \\.agent\\/scripts\\/swarm_dispatcher\\.py/g, 'node .agent/scripts/swarm_dispatcher.js');\n content = content.replace(/python \\.agent\\/scripts\\/session_manager\\.py/g, 'node .agent/scripts/session_manager.js');\n content = content.replace(/python \\.agent\\/scripts\\/minify_context\\.py/g, 'node .agent/scripts/minify_context.js');\n content = content.replace(/python \\.agent\\/scripts\\/test_swarm_dispatcher\\.py/g, 'npx jest test/integration/swarm_dispatcher.test.js');\n \n // General filename references\n content = content.replace(/swarm_dispatcher\\.py/g, 'swarm_dispatcher.js');\n content = content.replace(/session_manager\\.py/g, 'session_manager.js');\n content = content.replace(/minify_context\\.py/g, 'minify_context.js');\n content = content.replace(/test_swarm_dispatcher\\.py/g, 'swarm_dispatcher.test.js');\n\n fs.writeFileSync(filepath, content, 'utf8');\n totalChanges++;\n}\nconsole.log(`Updated ${totalChanges} files.`);\n"
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "scripts/changelog.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"child_process": [],
|
|
7
|
+
"fs": [],
|
|
8
|
+
"path": []
|
|
9
|
+
},
|
|
10
|
+
"dependents": [],
|
|
11
|
+
"content": "#!/usr/bin/env node\n/**\n * changelog.js — Auto-generate CHANGELOG from git history\n * \n * Categorizes commits by conventional commit type:\n * feat: → ✨ Features\n * fix: → 🐛 Bug Fixes\n * perf: → ⚡ Performance\n * docs: → 📝 Documentation\n * test: → ✅ Tests\n * refactor: → ♻️ Refactors\n * chore: → 🔧 Chores\n * BREAKING: → 💥 Breaking Changes\n * \n * Usage:\n * node scripts/changelog.js → Generate full changelog\n * node scripts/changelog.js --preview → Preview unreleased changes\n * node scripts/changelog.js --since v4.1.0 → Changes since a specific tag\n */\n\n'use strict';\n\nconst { execSync } = require('child_process');\nconst fs = require('fs');\nconst path = require('path');\n\nconst PKG = require(path.resolve(__dirname, '..', 'package.json'));\nconst CHANGELOG_PATH = path.resolve(__dirname, '..', 'CHANGELOG.md');\n\n// ── Commit Categories ────────────────────────────────────\nconst CATEGORIES = {\n feat: { emoji: '✨', title: 'Features' },\n fix: { emoji: '🐛', title: 'Bug Fixes' },\n perf: { emoji: '⚡', title: 'Performance' },\n docs: { emoji: '📝', title: 'Documentation' },\n test: { emoji: '✅', title: 'Tests' },\n refactor: { emoji: '♻️', title: 'Refactors' },\n chore: { emoji: '🔧', title: 'Chores' },\n ci: { emoji: '🏗️', title: 'CI/CD' },\n style: { emoji: '🎨', title: 'Style' },\n breaking: { emoji: '💥', title: 'Breaking Changes' },\n};\n\n// ── Git Helpers ──────────────────────────────────────────\nfunction git(cmd) {\n try {\n return execSync(`git ${cmd}`, { encoding: 'utf8', timeout: 10000 }).trim();\n } catch {\n return '';\n }\n}\n\nfunction getLatestTag() {\n return git('describe --tags --abbrev=0 2>nul') || git('describe --tags --abbrev=0 2>/dev/null') || '';\n}\n\nfunction getCommits(since) {\n const range = since ? `${since}..HEAD` : 'HEAD';\n const format = '--format=\"%H||%s||%an||%ai\"';\n const raw = git(`log ${range} ${format} --no-merges`);\n if (!raw) return [];\n\n return raw.split('\\n').filter(Boolean).map(line => {\n const [hash, subject, author, date] = line.split('||');\n return { hash: hash?.slice(0, 7), subject, author, date: date?.slice(0, 10) };\n });\n}\n\nfunction categorize(subject) {\n const lower = subject.toLowerCase();\n\n // Check for BREAKING CHANGE\n if (lower.includes('breaking') || lower.includes('!:')) {\n return 'breaking';\n }\n\n // Match conventional commit prefix\n const match = subject.match(/^(\\w+)(?:\\(.+?\\))?:\\s*/);\n if (match) {\n const type = match[1].toLowerCase();\n if (CATEGORIES[type]) return type;\n }\n\n // Heuristic fallback\n if (lower.includes('fix') || lower.includes('bug')) return 'fix';\n if (lower.includes('add') || lower.includes('new') || lower.includes('feat')) return 'feat';\n if (lower.includes('doc') || lower.includes('readme')) return 'docs';\n if (lower.includes('test')) return 'test';\n if (lower.includes('refactor') || lower.includes('clean')) return 'refactor';\n if (lower.includes('perf') || lower.includes('optim')) return 'perf';\n if (lower.includes('ci') || lower.includes('workflow')) return 'ci';\n\n return 'chore';\n}\n\n// ── Changelog Generation ─────────────────────────────────\nfunction generateChangelog(commits, version, date) {\n const grouped = {};\n for (const commit of commits) {\n const cat = categorize(commit.subject);\n if (!grouped[cat]) grouped[cat] = [];\n // Strip conventional prefix for cleaner display\n const clean = commit.subject.replace(/^\\w+(\\(.+?\\))?:\\s*/, '');\n grouped[cat].push({ ...commit, clean });\n }\n\n let md = `## [${version}] — ${date}\\n\\n`;\n\n // Breaking changes first\n const order = ['breaking', 'feat', 'fix', 'perf', 'refactor', 'docs', 'test', 'ci', 'style', 'chore'];\n for (const cat of order) {\n if (!grouped[cat] || grouped[cat].length === 0) continue;\n const { emoji, title } = CATEGORIES[cat];\n md += `### ${emoji} ${title}\\n\\n`;\n for (const c of grouped[cat]) {\n md += `- ${c.clean} (\\`${c.hash}\\`)\\n`;\n }\n md += '\\n';\n }\n\n return md;\n}\n\n// ── Main ─────────────────────────────────────────────────\nfunction main() {\n const args = process.argv.slice(2);\n const isPreview = args.includes('--preview');\n const sinceIdx = args.indexOf('--since');\n const sinceTag = sinceIdx !== -1 ? args[sinceIdx + 1] : null;\n\n const since = sinceTag || getLatestTag();\n const commits = getCommits(since);\n\n if (commits.length === 0) {\n console.log(' ℹ️ No new commits found since', since || 'beginning');\n process.exit(0);\n }\n\n const today = new Date().toISOString().slice(0, 10);\n const version = isPreview ? 'Unreleased' : PKG.version;\n\n const changelog = generateChangelog(commits, version, today);\n\n if (isPreview) {\n console.log('\\n 📋 Changelog Preview\\n ' + '─'.repeat(40) + '\\n');\n console.log(changelog);\n console.log(` 📊 ${commits.length} commits since ${since || 'initial commit'}`);\n return;\n }\n\n // Write or prepend to CHANGELOG.md\n const header = `# Changelog\\n\\nAll notable changes to Tribunal Kit are documented here.\\nFormat follows [Keep a Changelog](https://keepachangelog.com/).\\n\\n`;\n\n let existing = '';\n if (fs.existsSync(CHANGELOG_PATH)) {\n existing = fs.readFileSync(CHANGELOG_PATH, 'utf8');\n // Remove existing header\n existing = existing.replace(/^# Changelog[\\s\\S]*?(?=## )/, '');\n }\n\n const full = header + changelog + existing;\n fs.writeFileSync(CHANGELOG_PATH, full, 'utf8');\n\n console.log(` ✔ CHANGELOG.md updated — v${version} (${commits.length} commits)`);\n}\n\nmain();\n"
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "scripts/sync-version.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"fs": [],
|
|
7
|
+
"path": []
|
|
8
|
+
},
|
|
9
|
+
"dependents": [],
|
|
10
|
+
"content": "#!/usr/bin/env node\n/**\n * sync-version.js — Version Sync for Tribunal Kit\n * \n * Reads the version and counts from package.json and the .agent/ directory,\n * then updates all stale references across documentation files.\n * \n * Run manually or as a preversion npm script:\n * node scripts/sync-version.js\n */\n\nconst fs = require('fs');\nconst path = require('path');\n\nconst ROOT = path.resolve(__dirname, '..');\nconst PKG = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));\n\n// Count actual installed items\nfunction countItems(dir) {\n const fullPath = path.join(ROOT, '.agent', dir);\n if (!fs.existsSync(fullPath)) return '?';\n return fs.readdirSync(fullPath).filter(f => !f.startsWith('.')).length;\n}\n\nconst version = PKG.version;\nconst agents = countItems('agents');\nconst skills = countItems('skills');\nconst workflows = countItems('workflows');\nconst scripts = countItems('scripts');\n\nconsole.log(`\\n 📊 Tribunal Kit v${version} — Actual Counts`);\nconsole.log(` ──────────────────────────────────────`);\nconsole.log(` Agents: ${agents}`);\nconsole.log(` Skills: ${skills}`);\nconsole.log(` Workflows: ${workflows}`);\nconsole.log(` Scripts: ${scripts}`);\nconsole.log();\n\n// Files to check for stale numbers\nconst FILES_TO_CHECK = [\n 'README.md',\n 'AGENT_FLOW.md',\n '.agent/ARCHITECTURE.md',\n];\n\nlet staleFound = 0;\n\nfor (const relPath of FILES_TO_CHECK) {\n const filePath = path.join(ROOT, relPath);\n if (!fs.existsSync(filePath)) continue;\n\n const content = fs.readFileSync(filePath, 'utf8');\n \n // Check for common stale patterns\n const checks = [\n { regex: /(\\d+)\\s*(specialist\\s+)?agents/gi, expected: agents, label: 'agents' },\n { regex: /(\\d+)\\s*skill\\s*modules/gi, expected: skills, label: 'skills' },\n { regex: /(\\d+)\\s*slash\\s*command/gi, expected: workflows, label: 'workflows' },\n ];\n\n for (const check of checks) {\n let match;\n while ((match = check.regex.exec(content)) !== null) {\n const found = parseInt(match[1]);\n if (found !== check.expected && found > 5) { // ignore tiny numbers\n staleFound++;\n const line = content.substring(0, match.index).split('\\n').length;\n console.log(` ⚠️ ${relPath}:${line} — says ${found} ${check.label}, actual is ${check.expected}`);\n }\n }\n }\n}\n\nif (staleFound === 0) {\n console.log(` ✅ All counts are in sync across ${FILES_TO_CHECK.length} files.`);\n} else {\n console.log(`\\n ❌ Found ${staleFound} stale reference(s). Update manually or run the sync tool.`);\n process.exit(1);\n}\n\nconsole.log();\n"
|
|
11
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "scripts/validate-payload.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"fs": [],
|
|
7
|
+
"path": []
|
|
8
|
+
},
|
|
9
|
+
"dependents": [],
|
|
10
|
+
"content": "const fs = require('fs');\r\nconst path = require('path');\r\n\r\n// Simple Markdown frontmatter and Tribunal header validator\r\nfunction validateMarkdownFile(filePath) {\r\n const content = fs.readFileSync(filePath, 'utf8');\r\n let errors = [];\r\n\r\n // Check for frontmatter\r\n if (content.startsWith('---')) {\r\n const parts = content.split('---');\r\n if (parts.length < 3) {\r\n errors.push('Malformed YAML frontmatter (missing closing ---)');\r\n }\r\n }\r\n\r\n // If it's a skill, check for mandatory headers\r\n if (filePath.includes('/skills/') || filePath.includes('\\\\skills\\\\')) {\r\n // Exclude templates and documentation that aren't strict skills\r\n if (!filePath.includes('SKILL.md')) return errors;\r\n \r\n if (!content.includes('Pre-Flight Checklist') && !content.includes('Pre-Flight')) {\r\n errors.push('Missing mandatory header/section: Pre-Flight Checklist');\r\n }\r\n if (!content.includes('VBC Protocol') && !content.includes('VBC')) {\r\n errors.push('Missing mandatory header/section: VBC Protocol');\r\n }\r\n }\r\n\r\n return errors;\r\n}\r\n\r\nfunction walkDir(dir, callback) {\r\n fs.readdirSync(dir).forEach(f => {\r\n let dirPath = path.join(dir, f);\r\n let isDirectory = fs.statSync(dirPath).isDirectory();\r\n isDirectory ? walkDir(dirPath, callback) : callback(path.join(dir, f));\r\n });\r\n}\r\n\r\nfunction run() {\r\n const agentDir = path.join(__dirname, '..', '.agent');\r\n if (!fs.existsSync(agentDir)) {\r\n console.error('No .agent directory found.');\r\n process.exit(1);\r\n }\r\n\r\n let hasErrors = false;\r\n let checkedCount = 0;\r\n \r\n console.log('Validating .agent payload...');\r\n \r\n walkDir(agentDir, function(filePath) {\r\n if (filePath.endsWith('.md')) {\r\n checkedCount++;\r\n const errors = validateMarkdownFile(filePath);\r\n if (errors.length > 0) {\r\n console.error(`\\x1b[31m[FAIL]\\x1b[0m ${filePath}`);\r\n errors.forEach(e => console.error(` - ${e}`));\r\n hasErrors = true;\r\n }\r\n }\r\n });\r\n\r\n if (hasErrors) {\r\n console.error(`\\nPayload validation failed. Checked ${checkedCount} files.`);\r\n process.exit(1);\r\n } else {\r\n console.log(`\\x1b[32mPayload validation passed.\\x1b[0m Checked ${checkedCount} files.`);\r\n }\r\n}\r\n\r\nrun();\r\n"
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/integration/bridges.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"child_process": [],
|
|
7
|
+
"path": [],
|
|
8
|
+
"fs": [],
|
|
9
|
+
"os": []
|
|
10
|
+
},
|
|
11
|
+
"dependents": [],
|
|
12
|
+
"content": "'use strict';\n\nconst { spawnSync } = require('child_process');\nconst path = require('path');\nconst fs = require('fs');\nconst os = require('os');\n\nconst CLI = path.resolve(__dirname, '../../bin/tribunal-kit.js');\n\nfunction runCLI(args = [], opts = {}) {\n return spawnSync(process.execPath, [CLI, ...args], {\n encoding : 'utf8',\n timeout : 30000,\n env : { ...process.env, TK_SKIP_UPDATE_CHECK: '1' },\n ...opts,\n });\n}\n\ndescribe('IDE Bridge File Generation', () => {\n let tmpDir;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tk-bridge-'));\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n test('init creates .cursorrules', () => {\n runCLI(['init', '--path', tmpDir, '--skip-update-check']);\n const cursorRules = path.join(tmpDir, '.cursorrules');\n expect(fs.existsSync(cursorRules)).toBe(true);\n const content = fs.readFileSync(cursorRules, 'utf8');\n expect(content).toContain('Tribunal');\n expect(content).toContain('Anti-Hallucination');\n });\n\n test('init creates .windsurfrules', () => {\n runCLI(['init', '--path', tmpDir, '--skip-update-check']);\n const windsurfRules = path.join(tmpDir, '.windsurfrules');\n expect(fs.existsSync(windsurfRules)).toBe(true);\n const content = fs.readFileSync(windsurfRules, 'utf8');\n expect(content).toContain('Windsurf Bridge');\n expect(content).toContain('Tribunal');\n });\n\n test('init creates .gemini/settings.json', () => {\n runCLI(['init', '--path', tmpDir, '--skip-update-check']);\n const geminiSettings = path.join(tmpDir, '.gemini', 'settings.json');\n expect(fs.existsSync(geminiSettings)).toBe(true);\n const json = JSON.parse(fs.readFileSync(geminiSettings, 'utf8'));\n expect(json.rules).toBeDefined();\n expect(json.rules[0].path).toContain('.agent/rules/GEMINI.md');\n });\n\n test('init creates .github/copilot-instructions.md', () => {\n runCLI(['init', '--path', tmpDir, '--skip-update-check']);\n const copilot = path.join(tmpDir, '.github', 'copilot-instructions.md');\n expect(fs.existsSync(copilot)).toBe(true);\n const content = fs.readFileSync(copilot, 'utf8');\n expect(content).toContain('Copilot Bridge');\n });\n\n test('bridge files are NOT overwritten on re-init', () => {\n runCLI(['init', '--path', tmpDir, '--skip-update-check']);\n const cursorRules = path.join(tmpDir, '.cursorrules');\n fs.writeFileSync(cursorRules, '# My custom rules\\nDo not touch');\n runCLI(['init', '--path', tmpDir, '--force', '--skip-update-check']);\n const content = fs.readFileSync(cursorRules, 'utf8');\n expect(content).toBe('# My custom rules\\nDo not touch');\n });\n\n test('all bridge files contain the routing table', () => {\n runCLI(['init', '--path', tmpDir, '--skip-update-check']);\n const content = fs.readFileSync(path.join(tmpDir, '.cursorrules'), 'utf8');\n expect(content).toContain('backend-specialist');\n expect(content).toContain('frontend-specialist');\n });\n\n test('init --dry-run does NOT create bridge files', () => {\n runCLI(['init', '--path', tmpDir, '--dry-run', '--skip-update-check']);\n expect(fs.existsSync(path.join(tmpDir, '.cursorrules'))).toBe(false);\n });\n});\n"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/integration/init.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"child_process": [],
|
|
7
|
+
"path": [],
|
|
8
|
+
"fs": [],
|
|
9
|
+
"os": []
|
|
10
|
+
},
|
|
11
|
+
"dependents": [],
|
|
12
|
+
"content": "'use strict';\r\n\r\nconst { spawnSync } = require('child_process');\r\nconst path = require('path');\r\nconst fs = require('fs');\r\nconst os = require('os');\r\n\r\nconst CLI = path.resolve(__dirname, '../../bin/tribunal-kit.js');\r\n\r\nfunction runCLI(args = [], opts = {}) {\r\n return spawnSync(process.execPath, [CLI, ...args], {\r\n encoding : 'utf8',\r\n timeout : 15000,\r\n env : { ...process.env, TK_SKIP_UPDATE_CHECK: '1' },\r\n ...opts,\r\n });\r\n}\r\n\r\ndescribe('tribunal-kit init command', () => {\r\n let tmpDir;\r\n\r\n beforeEach(() => {\r\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tk-init-test-'));\r\n });\r\n\r\n afterEach(() => {\r\n fs.rmSync(tmpDir, { recursive: true, force: true });\r\n });\r\n\r\n test('init --path installs .agent/ and exits 0', () => {\r\n const result = runCLI(['init', '--path', tmpDir, '--skip-update-check']);\r\n expect(result.status).toBe(0);\r\n expect(fs.existsSync(path.join(tmpDir, '.agent'))).toBe(true);\r\n });\r\n\r\n test('init --dry-run writes NO files and exits 0', () => {\r\n const dryDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tk-dry-'));\r\n fs.rmSync(dryDir, { recursive: true, force: true });\r\n fs.mkdirSync(dryDir);\r\n const result = runCLI(['init', '--path', dryDir, '--dry-run', '--skip-update-check']);\r\n expect(result.status).toBe(0);\r\n expect(fs.existsSync(path.join(dryDir, '.agent'))).toBe(false);\r\n fs.rmSync(dryDir, { recursive: true, force: true });\r\n });\r\n\r\n test('init warns and exits 0 when .agent already exists (no --force)', () => {\r\n // Pre-create the .agent dir\r\n fs.mkdirSync(path.join(tmpDir, '.agent'));\r\n const result = runCLI(['init', '--path', tmpDir, '--skip-update-check']);\r\n expect(result.status).toBe(0);\r\n // Should warn about existing install\r\n expect(result.stdout).toMatch(/already exists|--force/i);\r\n });\r\n\r\n test('init --quiet suppresses main output and exits 0', () => {\r\n const result = runCLI(['init', '--path', tmpDir, '--quiet', '--skip-update-check']);\r\n expect(result.status).toBe(0);\r\n // --quiet suppresses the big banner/card but dim() may still emit\r\n // a version line; what matters is the .agent/ folder was installed\r\n expect(fs.existsSync(path.join(tmpDir, '.agent'))).toBe(true);\r\n });\r\n\r\n test('init --force overwrites existing .agent/ and exits 0', () => {\r\n // First install\r\n runCLI(['init', '--path', tmpDir, '--skip-update-check']);\r\n // Write a marker inside a known-cleaned subdir (agents/)\r\n const marker = path.join(tmpDir, '.agent', 'agents', '__test_marker__.md');\r\n fs.writeFileSync(marker, 'marker');\r\n // Force reinstall\r\n const result = runCLI(['init', '--path', tmpDir, '--force', '--skip-update-check']);\r\n expect(result.status).toBe(0);\r\n // The agents/ subdir is wiped and rebuilt, so the extra marker is gone\r\n expect(fs.existsSync(marker)).toBe(false);\r\n });\r\n});\r\n\r\ndescribe('tribunal-kit status command', () => {\r\n let tmpDir;\r\n\r\n beforeEach(() => {\r\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tk-status-test-'));\r\n });\r\n\r\n afterEach(() => {\r\n fs.rmSync(tmpDir, { recursive: true, force: true });\r\n });\r\n\r\n test('status exits 0 when not installed', () => {\r\n const result = runCLI(['status', '--path', tmpDir]);\r\n expect(result.status).toBe(0);\r\n expect(result.stdout).toMatch(/not installed/i);\r\n });\r\n\r\n test('status exits 0 and shows counts when installed', () => {\r\n runCLI(['init', '--path', tmpDir, '--skip-update-check']);\r\n const result = runCLI(['status', '--path', tmpDir]);\r\n expect(result.status).toBe(0);\r\n expect(result.stdout).toMatch(/installed/i);\r\n });\r\n});\r\n\r\ndescribe('tribunal-kit help / unknown commands', () => {\r\n test('--help exits 0 and shows usage', () => {\r\n const result = runCLI(['--help']);\r\n expect(result.status).toBe(0);\r\n expect(result.stdout).toMatch(/init|update|status/i);\r\n });\r\n\r\n test('unknown command exits 1', () => {\r\n const result = runCLI(['totally-unknown-command']);\r\n expect(result.status).toBe(1);\r\n expect(result.stderr).toMatch(/unknown command/i);\r\n });\r\n});\r\n"
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/integration/routing.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"path": [],
|
|
7
|
+
"fs": []
|
|
8
|
+
},
|
|
9
|
+
"dependents": [],
|
|
10
|
+
"content": "'use strict';\r\n\r\nconst path = require('path');\r\nconst fs = require('fs');\r\n\r\nconst AGENT_DIR = path.resolve(__dirname, '../../.agent');\r\nconst WORKFLOWS_DIR = path.join(AGENT_DIR, 'workflows');\r\nconst AGENTS_DIR = path.join(AGENT_DIR, 'agents');\r\nconst SKILLS_DIR = path.join(AGENT_DIR, 'skills');\r\n\r\n// Expected slash commands per plan (31 workflows)\r\nconst EXPECTED_WORKFLOWS = [\r\n 'api-tester', 'audit', 'brainstorm', 'changelog', 'create',\r\n 'debug', 'deploy', 'enhance', 'fix', 'generate', 'migrate',\r\n 'orchestrate', 'performance-benchmarker', 'plan', 'preview',\r\n 'refactor', 'review', 'review-ai', 'session', 'status',\r\n 'strengthen-skills', 'swarm', 'test', 'tribunal-backend',\r\n 'tribunal-database', 'tribunal-frontend', 'tribunal-full',\r\n 'tribunal-mobile', 'tribunal-performance', 'tribunal-speed', 'ui-ux-pro-max',\r\n];\r\n\r\ndescribe('Workflow file integrity', () => {\r\n test('.agent/workflows/ directory exists', () => {\r\n expect(fs.existsSync(WORKFLOWS_DIR)).toBe(true);\r\n });\r\n\r\n test('all 31 expected workflow files are present and non-empty', () => {\r\n const missing = [];\r\n const empty = [];\r\n\r\n for (const name of EXPECTED_WORKFLOWS) {\r\n const filePath = path.join(WORKFLOWS_DIR, `${name}.md`);\r\n if (!fs.existsSync(filePath)) {\r\n missing.push(name);\r\n } else {\r\n const size = fs.statSync(filePath).size;\r\n if (size === 0) empty.push(name);\r\n }\r\n }\r\n\r\n expect(missing).toEqual([]);\r\n expect(empty).toEqual([]);\r\n });\r\n\r\n test('no workflow file is a duplicate of another', () => {\r\n const files = fs.readdirSync(WORKFLOWS_DIR).filter(f => f.endsWith('.md'));\r\n const contents = files.map(f =>\r\n fs.readFileSync(path.join(WORKFLOWS_DIR, f), 'utf8').trim()\r\n );\r\n const unique = new Set(contents);\r\n expect(unique.size).toBe(contents.length);\r\n });\r\n});\r\n\r\ndescribe('Agent file integrity', () => {\r\n test('.agent/agents/ directory exists', () => {\r\n expect(fs.existsSync(AGENTS_DIR)).toBe(true);\r\n });\r\n\r\n test('at least 30 agent files are present', () => {\r\n const files = fs.readdirSync(AGENTS_DIR).filter(f => f.endsWith('.md'));\r\n expect(files.length).toBeGreaterThanOrEqual(30);\r\n });\r\n\r\n test('all agent files are non-empty', () => {\r\n const files = fs.readdirSync(AGENTS_DIR).filter(f => f.endsWith('.md'));\r\n const empty = files.filter(f => fs.statSync(path.join(AGENTS_DIR, f)).size === 0);\r\n expect(empty).toEqual([]);\r\n });\r\n});\r\n\r\ndescribe('Skills directory integrity', () => {\r\n test('.agent/skills/ directory exists', () => {\r\n expect(fs.existsSync(SKILLS_DIR)).toBe(true);\r\n });\r\n\r\n test('at least 50 skill entries are present', () => {\r\n const entries = fs.readdirSync(SKILLS_DIR);\r\n // Each skill is a directory containing a SKILL.md\r\n const skillDirs = entries.filter(e =>\r\n fs.statSync(path.join(SKILLS_DIR, e)).isDirectory()\r\n );\r\n expect(skillDirs.length).toBeGreaterThanOrEqual(50);\r\n });\r\n\r\n test('every skill directory contains a SKILL.md', () => {\r\n const entries = fs.readdirSync(SKILLS_DIR);\r\n const skillDirs = entries.filter(e =>\r\n fs.statSync(path.join(SKILLS_DIR, e)).isDirectory()\r\n );\r\n const missing = skillDirs.filter(dir =>\r\n !fs.existsSync(path.join(SKILLS_DIR, dir, 'SKILL.md'))\r\n );\r\n expect(missing).toEqual([]);\r\n });\r\n});\r\n"
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/integration/swarm_dispatcher.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"path": [],
|
|
7
|
+
"fs": [],
|
|
8
|
+
"os": [],
|
|
9
|
+
"../../.agent/scripts/swarm_dispatcher.js": []
|
|
10
|
+
},
|
|
11
|
+
"dependents": [],
|
|
12
|
+
"content": "const path = require('path');\nconst fs = require('fs');\nconst os = require('os');\nconst {\n findAgentDir,\n validatePayload,\n buildWorkerPrompts,\n validateWorkerRequest,\n validateWorkerResult,\n validateSwarmPayload\n} = require('../../.agent/scripts/swarm_dispatcher.js');\n\ndescribe('swarm_dispatcher.js legacy mode', () => {\n let tmpDir;\n\n beforeEach(() => {\n tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarm-test-'));\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n test('findAgentDir finds .agent directory', () => {\n const agentDir = path.join(tmpDir, '.agent');\n fs.mkdirSync(agentDir);\n const subDir = path.join(tmpDir, 'src', 'deep', 'folder');\n fs.mkdirSync(subDir, { recursive: true });\n\n const result = findAgentDir(subDir);\n expect(result).not.toBeNull();\n expect(path.resolve(result)).toBe(path.resolve(agentDir));\n });\n\n test('findAgentDir returns null if not found', () => {\n const subDir = path.join(tmpDir, 'src', 'deep');\n fs.mkdirSync(subDir, { recursive: true });\n\n expect(findAgentDir(subDir)).toBeNull();\n });\n\n test('validatePayload valid', () => {\n const agentsDir = path.join(tmpDir, 'agents');\n fs.mkdirSync(agentsDir);\n fs.writeFileSync(path.join(agentsDir, 'test_agent.md'), '');\n\n const workspace = path.join(tmpDir, 'workspace');\n fs.mkdirSync(workspace);\n fs.writeFileSync(path.join(workspace, 'file1.txt'), '');\n\n const payload = {\n dispatch_micro_workers: [\n {\n target_agent: \"test_agent\",\n files_attached: [\"file1.txt\"]\n }\n ]\n };\n\n expect(validatePayload(payload, workspace, agentsDir)).toBe(true);\n });\n\n test('validatePayload missing workers', () => {\n expect(validatePayload({}, tmpDir, tmpDir)).toBe(false);\n });\n\n test('validatePayload files not a list', () => {\n const agentsDir = path.join(tmpDir, 'agents');\n fs.mkdirSync(agentsDir);\n fs.writeFileSync(path.join(agentsDir, 'test_agent.md'), '');\n\n const payload = {\n dispatch_micro_workers: [\n {\n target_agent: \"test_agent\",\n files_attached: \"a single file string\"\n }\n ]\n };\n\n expect(validatePayload(payload, tmpDir, agentsDir)).toBe(false);\n });\n\n test('validateSwarmPayload validates WorkerRequest correctly', () => {\n const agentsDir = path.join(tmpDir, 'agents');\n fs.mkdirSync(agentsDir);\n fs.writeFileSync(path.join(agentsDir, 'research.md'), '');\n\n const req = {\n task_id: \"t123\",\n type: \"research\",\n agent: \"research\",\n goal: \"Find the root cause\",\n context: \"We have a bug here.\",\n max_retries: 2\n };\n\n const errors = validateWorkerRequest(req, 0, agentsDir);\n expect(errors).toHaveLength(0);\n\n expect(validateSwarmPayload([req], agentsDir)).toBe(true);\n });\n});\n"
|
|
13
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/integration/wave2.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"fs": [],
|
|
7
|
+
"../../.agent/scripts/skill_integrator.js": [],
|
|
8
|
+
"../../.agent/scripts/skill_evolution.js": [],
|
|
9
|
+
"../../.agent/scripts/case_law_manager.js": []
|
|
10
|
+
},
|
|
11
|
+
"dependents": [],
|
|
12
|
+
"content": "const { getAssociatedScript } = require('../../.agent/scripts/skill_integrator.js');\nconst { architecturalWeight, semanticDelta } = require('../../.agent/scripts/skill_evolution.js');\nconst { parseCaseId, findAgentDir } = require('../../.agent/scripts/case_law_manager.js');\n\ndescribe('Wave 2 Scripts (Evolving Brain)', () => {\n describe('skill_integrator.js', () => {\n it('should correctly identify implicit JS scripts', () => {\n // we mock fs internally if we wanted to true unit test, but for now just check exports\n expect(typeof getAssociatedScript).toBe('function');\n });\n });\n\n describe('skill_evolution.js', () => {\n it('should assign higher architectural weight to classes and interfaces', () => {\n expect(architecturalWeight('class User {')).toBeGreaterThanOrEqual(2);\n expect(architecturalWeight('interface Data {')).toBeGreaterThanOrEqual(2);\n });\n\n it('should assign 0 weight to noise patterns like comments and imports', () => {\n expect(architecturalWeight('// this is a comment')).toBe(0);\n expect(architecturalWeight('import { foo } from \"fs\";')).toBe(0);\n expect(architecturalWeight('')).toBe(0);\n });\n\n it('should successfully filter a semantic delta', () => {\n const rawDiff = `\n@@ -1,3 +1,4 @@\n import { foo } from 'fs';\n+import { baz } from 'fs';\n class Foo {\n- // old comment\n+ // new comment\n+ export class Baz {}\n }\n `.trim();\n\n const delta = semanticDelta(rawDiff, 2);\n // It gets kept because the hunk contains 'export class Baz {}'\n expect(delta).toContain('import { baz }');\n expect(delta).toContain('export class Baz {}');\n });\n });\n\n describe('case_law_manager.js', () => {\n it('should extract tags', () => {\n const { extractTags } = require('../../.agent/scripts/case_law_manager.js');\n expect(typeof extractTags).toBe('function');\n expect(extractTags('hello world and foo bar')).toContain('hello');\n expect(extractTags('hello world and foo bar')).not.toContain('and');\n });\n it('should find agent diff', () => {\n expect(typeof findAgentDir).toBe('function');\n });\n });\n});\n"
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/unit/args.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"../../bin/tribunal-kit": []
|
|
7
|
+
},
|
|
8
|
+
"dependents": [],
|
|
9
|
+
"content": "'use strict';\r\n\r\nconst { parseArgs } = require('../../bin/tribunal-kit');\r\n\r\ndescribe('parseArgs', () => {\r\n test('parses \"init\" command', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js', 'init']);\r\n expect(result.command).toBe('init');\r\n expect(result.flags).toEqual({});\r\n });\r\n\r\n test('parses \"update\" command', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js', 'update']);\r\n expect(result.command).toBe('update');\r\n });\r\n\r\n test('parses \"status\" command', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js', 'status']);\r\n expect(result.command).toBe('status');\r\n });\r\n\r\n test('returns null command when no args given', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js']);\r\n expect(result.command).toBeNull();\r\n });\r\n\r\n test('parses --force flag', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js', 'init', '--force']);\r\n expect(result.flags.force).toBe(true);\r\n });\r\n\r\n test('parses --quiet flag', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js', 'init', '--quiet']);\r\n expect(result.flags.quiet).toBe(true);\r\n });\r\n\r\n test('parses --dry-run flag', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js', 'init', '--dry-run']);\r\n expect(result.flags.dryRun).toBe(true);\r\n });\r\n\r\n test('parses --skip-update-check flag', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js', 'init', '--skip-update-check']);\r\n expect(result.flags.skipUpdateCheck).toBe(true);\r\n });\r\n\r\n test('parses --path=<value> flag', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js', 'init', '--path=./my-app']);\r\n expect(result.flags.path).toBe('./my-app');\r\n });\r\n\r\n test('parses --path <value> flag (space separated)', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js', 'init', '--path', './my-app']);\r\n expect(result.flags.path).toBe('./my-app');\r\n });\r\n\r\n test('ignores unknown flags gracefully', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js', 'init', '--unknown-flag']);\r\n expect(result.command).toBe('init');\r\n // Unknown flags don't throw, just get silently ignored\r\n expect(result.flags.unknownFlag).toBeUndefined();\r\n });\r\n\r\n test('multiple flags together', () => {\r\n const result = parseArgs(['node', 'tribunal-kit.js', 'init', '--force', '--quiet', '--dry-run']);\r\n expect(result.flags.force).toBe(true);\r\n expect(result.flags.quiet).toBe(true);\r\n expect(result.flags.dryRun).toBe(true);\r\n });\r\n});\r\n"
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/unit/case_law_manager.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"../../.agent/scripts/case_law_manager": []
|
|
7
|
+
},
|
|
8
|
+
"dependents": [],
|
|
9
|
+
"content": "const {\r\n semanticDelta,\r\n extractTags,\r\n isNoiseRejection,\r\n isTrivialLine\r\n} = require('../../.agent/scripts/case_law_manager');\r\n\r\ndescribe('case_law_manager.js', () => {\r\n describe('Noise Filter', () => {\r\n it('should identify noise rejections', () => {\r\n expect(isNoiseRejection('Please fix formatting')).toBe(true);\r\n expect(isNoiseRejection('Trailing whitespace detected')).toBe(true);\r\n expect(isNoiseRejection('Run prettier')).toBe(true);\r\n });\r\n\r\n it('should allow valid rejections', () => {\r\n expect(isNoiseRejection('Missing SQL injection protection')).toBe(false);\r\n expect(isNoiseRejection('Type error in React component')).toBe(false);\r\n });\r\n });\r\n\r\n describe('Semantic Delta', () => {\r\n it('should identify trivial lines', () => {\r\n expect(isTrivialLine('// just a comment')).toBe(true);\r\n expect(isTrivialLine('import { foo } from \"bar\"')).toBe(true);\r\n expect(isTrivialLine('const a = 1;')).toBe(false);\r\n });\r\n\r\n it('should filter trivial changes from diff', () => {\r\n const diff = `--- file.js\\n+++ file.js\\n-import a from 'a'\\n+// a comment\\n+const a = 1;`;\r\n const delta = semanticDelta(diff);\r\n expect(delta).toContain('+const a = 1;');\r\n expect(delta).not.toContain('// a comment');\r\n expect(delta).not.toContain('import a');\r\n });\r\n });\r\n\r\n describe('Extract Tags', () => {\r\n it('should extract meaningful tags and ignore stop words', () => {\r\n const tags = extractTags('The React component has a missing key prop');\r\n expect(tags).toContain('react');\r\n expect(tags).toContain('component');\r\n expect(tags).toContain('missing');\r\n expect(tags).toContain('key');\r\n expect(tags).toContain('prop');\r\n expect(tags).not.toContain('the');\r\n expect(tags).not.toContain('has');\r\n expect(tags).not.toContain('a');\r\n });\r\n });\r\n});\r\n"
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/unit/copyDir.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"fs": [],
|
|
7
|
+
"path": [],
|
|
8
|
+
"os": [],
|
|
9
|
+
"../../bin/tribunal-kit": []
|
|
10
|
+
},
|
|
11
|
+
"dependents": [],
|
|
12
|
+
"content": "'use strict';\r\n\r\nconst fs = require('fs');\r\nconst path = require('path');\r\nconst os = require('os');\r\nconst { copyDir, countDir } = require('../../bin/tribunal-kit');\r\n\r\nfunction makeTempDir(prefix = 'tk-test-') {\r\n return fs.mkdtempSync(path.join(os.tmpdir(), prefix));\r\n}\r\n\r\ndescribe('countDir', () => {\r\n let tmpDir;\r\n\r\n beforeEach(() => {\r\n tmpDir = makeTempDir();\r\n });\r\n\r\n afterEach(() => {\r\n fs.rmSync(tmpDir, { recursive: true, force: true });\r\n });\r\n\r\n test('returns 0 for empty directory', () => {\r\n expect(countDir(tmpDir)).toBe(0);\r\n });\r\n\r\n test('counts files at top level', () => {\r\n fs.writeFileSync(path.join(tmpDir, 'a.txt'), 'a');\r\n fs.writeFileSync(path.join(tmpDir, 'b.txt'), 'b');\r\n expect(countDir(tmpDir)).toBe(2);\r\n });\r\n\r\n test('counts files recursively in subdirectories', () => {\r\n const sub = path.join(tmpDir, 'sub');\r\n fs.mkdirSync(sub);\r\n fs.writeFileSync(path.join(tmpDir, 'top.txt'), 'top');\r\n fs.writeFileSync(path.join(sub, 'nested.txt'), 'nested');\r\n expect(countDir(tmpDir)).toBe(2);\r\n });\r\n});\r\n\r\ndescribe('copyDir', () => {\r\n let srcDir;\r\n let destDir;\r\n\r\n beforeEach(() => {\r\n srcDir = makeTempDir('tk-src-');\r\n destDir = makeTempDir('tk-dest-');\r\n // Remove destDir so copyDir can create it fresh\r\n fs.rmSync(destDir, { recursive: true, force: true });\r\n });\r\n\r\n afterEach(() => {\r\n fs.rmSync(srcDir, { recursive: true, force: true });\r\n fs.rmSync(destDir, { recursive: true, force: true });\r\n });\r\n\r\n test('copies files to destination', () => {\r\n fs.writeFileSync(path.join(srcDir, 'hello.txt'), 'hello');\r\n copyDir(srcDir, destDir);\r\n expect(fs.existsSync(path.join(destDir, 'hello.txt'))).toBe(true);\r\n expect(fs.readFileSync(path.join(destDir, 'hello.txt'), 'utf8')).toBe('hello');\r\n });\r\n\r\n test('copies nested directories recursively', () => {\r\n const sub = path.join(srcDir, 'sub');\r\n fs.mkdirSync(sub);\r\n fs.writeFileSync(path.join(sub, 'nested.txt'), 'nested');\r\n copyDir(srcDir, destDir);\r\n expect(fs.existsSync(path.join(destDir, 'sub', 'nested.txt'))).toBe(true);\r\n });\r\n\r\n test('returns total file count copied', () => {\r\n fs.writeFileSync(path.join(srcDir, 'a.txt'), 'a');\r\n fs.writeFileSync(path.join(srcDir, 'b.txt'), 'b');\r\n const count = copyDir(srcDir, destDir);\r\n expect(count).toBe(2);\r\n });\r\n\r\n test('dry-run does NOT create destination files', () => {\r\n fs.writeFileSync(path.join(srcDir, 'hello.txt'), 'hello');\r\n const destDryRun = path.join(os.tmpdir(), 'tk-dryrun-' + Date.now());\r\n copyDir(srcDir, destDryRun, true);\r\n expect(fs.existsSync(destDryRun)).toBe(false);\r\n });\r\n\r\n test('dry-run still returns correct file count', () => {\r\n fs.writeFileSync(path.join(srcDir, 'a.txt'), 'a');\r\n fs.writeFileSync(path.join(srcDir, 'b.txt'), 'b');\r\n const destDryRun = path.join(os.tmpdir(), 'tk-dryrun2-' + Date.now());\r\n const count = copyDir(srcDir, destDryRun, true);\r\n expect(count).toBe(2);\r\n });\r\n});\r\n"
|
|
13
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/unit/graph_tools.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"fs": [],
|
|
7
|
+
"path": []
|
|
8
|
+
},
|
|
9
|
+
"dependents": [],
|
|
10
|
+
"content": "const fs = require('fs');\r\nconst path = require('path');\r\n\r\ndescribe('Knowledge Graph Tools', () => {\r\n describe('graph_builder.js', () => {\r\n it('should exist and be executable', () => {\r\n const builderPath = path.join(__dirname, '../../.agent/scripts/graph_builder.js');\r\n expect(fs.existsSync(builderPath)).toBe(true);\r\n const content = fs.readFileSync(builderPath, 'utf8');\r\n expect(content).toContain('function main()');\r\n expect(content).toContain('function walkDir');\r\n expect(content).toContain('isExcluded');\r\n });\r\n });\r\n\r\n describe('graph_zoom.js', () => {\r\n it('should exist and be executable', () => {\r\n const zoomPath = path.join(__dirname, '../../.agent/scripts/graph_zoom.js');\r\n expect(fs.existsSync(zoomPath)).toBe(true);\r\n const content = fs.readFileSync(zoomPath, 'utf8');\r\n expect(content).toContain('function extractSkeleton');\r\n // Ensure fallback logic is present\r\n expect(content).toContain('RAW FILE FALLBACK');\r\n });\r\n });\r\n});\r\n"
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/unit/selfInstall.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"path": [],
|
|
7
|
+
"fs": [],
|
|
8
|
+
"os": [],
|
|
9
|
+
"../../bin/tribunal-kit": []
|
|
10
|
+
},
|
|
11
|
+
"dependents": [],
|
|
12
|
+
"content": "'use strict';\r\n\r\nconst path = require('path');\r\nconst fs = require('fs');\r\nconst os = require('os');\r\nconst { isSelfInstall } = require('../../bin/tribunal-kit');\r\n\r\ndescribe('isSelfInstall', () => {\r\n test('returns true when target is the kit root (package root)', () => {\r\n // The kit root is one level above bin/\r\n const kitRoot = path.resolve(__dirname, '../../');\r\n expect(isSelfInstall(kitRoot)).toBe(true);\r\n });\r\n\r\n test('returns false for an unrelated directory', () => {\r\n expect(isSelfInstall(os.tmpdir())).toBe(false);\r\n });\r\n\r\n test('returns true when target package.json has name \"tribunal-kit\"', () => {\r\n // Create a temp dir with a fake package.json mimicking the kit\r\n const fakeKitDir = fs.mkdtempSync(path.join(os.tmpdir(), 'fake-tk-'));\r\n fs.writeFileSync(\r\n path.join(fakeKitDir, 'package.json'),\r\n JSON.stringify({ name: 'tribunal-kit', version: '1.0.0' })\r\n );\r\n expect(isSelfInstall(fakeKitDir)).toBe(true);\r\n fs.rmSync(fakeKitDir, { recursive: true, force: true });\r\n });\r\n\r\n test('returns false when target package.json has a different name', () => {\r\n const fakeDir = fs.mkdtempSync(path.join(os.tmpdir(), 'other-pkg-'));\r\n fs.writeFileSync(\r\n path.join(fakeDir, 'package.json'),\r\n JSON.stringify({ name: 'my-other-project', version: '1.0.0' })\r\n );\r\n expect(isSelfInstall(fakeDir)).toBe(false);\r\n fs.rmSync(fakeDir, { recursive: true, force: true });\r\n });\r\n\r\n test('returns false when target has no package.json', () => {\r\n const emptyDir = fs.mkdtempSync(path.join(os.tmpdir(), 'no-pkg-'));\r\n expect(isSelfInstall(emptyDir)).toBe(false);\r\n fs.rmSync(emptyDir, { recursive: true, force: true });\r\n });\r\n});\r\n"
|
|
13
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/unit/semver.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"../../bin/tribunal-kit": []
|
|
7
|
+
},
|
|
8
|
+
"dependents": [],
|
|
9
|
+
"content": "'use strict';\r\n\r\nconst { compareSemver } = require('../../bin/tribunal-kit');\r\n\r\ndescribe('compareSemver', () => {\r\n test('returns 1 when a > b (patch)', () => {\r\n expect(compareSemver('2.4.2', '2.4.1')).toBe(1);\r\n });\r\n\r\n test('returns 1 when a > b (minor)', () => {\r\n expect(compareSemver('2.5.0', '2.4.9')).toBe(1);\r\n });\r\n\r\n test('returns 1 when a > b (major)', () => {\r\n expect(compareSemver('3.0.0', '2.9.9')).toBe(1);\r\n });\r\n\r\n test('returns 0 when a === b', () => {\r\n expect(compareSemver('2.4.2', '2.4.2')).toBe(0);\r\n });\r\n\r\n test('returns -1 when a < b (patch)', () => {\r\n expect(compareSemver('2.4.1', '2.4.2')).toBe(-1);\r\n });\r\n\r\n test('returns -1 when a < b (minor)', () => {\r\n expect(compareSemver('2.4.9', '2.5.0')).toBe(-1);\r\n });\r\n\r\n test('strips leading \"v\" prefix from a', () => {\r\n expect(compareSemver('v2.4.2', '2.4.2')).toBe(0);\r\n });\r\n\r\n test('strips leading \"v\" prefix from b', () => {\r\n expect(compareSemver('2.4.2', 'v2.4.2')).toBe(0);\r\n });\r\n\r\n test('strips leading \"v\" from both', () => {\r\n expect(compareSemver('v2.4.3', 'v2.4.2')).toBe(1);\r\n });\r\n\r\n test('handles missing patch segment (treats as 0)', () => {\r\n expect(compareSemver('2.4', '2.4.0')).toBe(0);\r\n });\r\n});\r\n"
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"file": "test/unit/swarm_dispatcher.test.js",
|
|
3
|
+
"riskScore": "Low",
|
|
4
|
+
"blastRadius": 0,
|
|
5
|
+
"imports": {
|
|
6
|
+
"path": [],
|
|
7
|
+
"fs": []
|
|
8
|
+
},
|
|
9
|
+
"dependents": [],
|
|
10
|
+
"content": "const path = require('path');\r\nconst fs = require('fs');\r\n\r\ndescribe('swarm_dispatcher.js', () => {\r\n it('should be a valid javascript file', () => {\r\n const filePath = path.join(__dirname, '../../.agent/scripts/swarm_dispatcher.js');\r\n expect(fs.existsSync(filePath)).toBe(true);\r\n const content = fs.readFileSync(filePath, 'utf8');\r\n expect(content.includes('module.exports')).toBeTruthy();\r\n });\r\n \r\n // As swarm_dispatcher heavily relies on filesystem operations and child_process,\r\n // we would use jest.mock('child_process') and jest.mock('fs') for deeper testing.\r\n // This serves as the foundation for the swarm test suite.\r\n});\r\n"
|
|
11
|
+
}
|
|
@@ -23,7 +23,7 @@ const { spawnSync } = require('child_process');
|
|
|
23
23
|
const { RED, GREEN, YELLOW, BLUE, BOLD, RESET } = require('./colors.js');
|
|
24
24
|
|
|
25
25
|
const SOURCE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"]);
|
|
26
|
-
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", ".next", ".agent", "__pycache__"]);
|
|
26
|
+
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", ".next", ".agent", "__pycache__", "test", "tests", "__tests__"]);
|
|
27
27
|
|
|
28
28
|
const NODE_BUILTINS = new Set([
|
|
29
29
|
"fs", "path", "os", "crypto", "http", "https", "url", "util",
|