tribunal-kit 7.0.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.agent/config/claude.json +18 -0
  2. package/.agent/config/plugin.json +102 -0
  3. package/.agent/config/slash-commands.json +103 -0
  4. package/.agent/config/specialist-registry.json +415 -0
  5. package/.agent/config/system-prompt.md +191 -0
  6. package/.agent/history/architecture-graph.yaml +302 -3
  7. package/.agent/history/graph-cache.json +515 -45
  8. package/.agent/history/memory.db +0 -0
  9. package/.agent/history/snapshots/bin__adapter-install.js.json +13 -0
  10. package/.agent/history/snapshots/bin__global-store.js.json +15 -0
  11. package/.agent/history/snapshots/bin__mcp-server.js.json +19 -0
  12. package/.agent/history/snapshots/bin__proxy-server.js.json +20 -0
  13. package/.agent/history/snapshots/bin__spawn-agent.js.json +12 -0
  14. package/.agent/history/snapshots/bin__tk-proxy.js.json +21 -0
  15. package/.agent/history/snapshots/bin__tribunal-kit.js.json +9 -7
  16. package/.agent/history/snapshots/bin__wrapper.js.json +14 -0
  17. package/.agent/history/snapshots/eslint.config.js.json +1 -2
  18. package/.agent/history/snapshots/scripts__benchmark.js.json +14 -0
  19. package/.agent/history/snapshots/scripts__changelog.js.json +1 -2
  20. package/.agent/history/snapshots/scripts__fix-vbc.js.json +11 -0
  21. package/.agent/history/snapshots/scripts__stress_benchmark.js.json +15 -0
  22. package/.agent/history/snapshots/scripts__sync-version.js.json +1 -2
  23. package/.agent/history/snapshots/scripts__validate-payload.js.json +1 -2
  24. package/.agent/history/snapshots/scripts__visual_audit.js.json +11 -0
  25. package/.agent/history/snapshots/test__integration__bridges.test.js.json +1 -2
  26. package/.agent/history/snapshots/test__integration__graceful_degradation.test.js.json +12 -0
  27. package/.agent/history/snapshots/test__integration__init.test.js.json +1 -2
  28. package/.agent/history/snapshots/test__integration__minimal_change_pipeline.test.js.json +13 -0
  29. package/.agent/history/snapshots/test__integration__parallel_tribunal.test.js.json +12 -0
  30. package/.agent/history/snapshots/test__integration__routing.test.js.json +1 -2
  31. package/.agent/history/snapshots/test__integration__swarm_dispatcher.test.js.json +1 -2
  32. package/.agent/history/snapshots/test__integration__sync_status.test.js.json +13 -0
  33. package/.agent/history/snapshots/test__integration__wave2.test.js.json +1 -2
  34. package/.agent/history/snapshots/test__integration__wrapper.test.js.json +13 -0
  35. package/.agent/history/snapshots/test__unit__align.test.js.json +10 -0
  36. package/.agent/history/snapshots/test__unit__args.test.js.json +3 -3
  37. package/.agent/history/snapshots/test__unit__audit_release.test.js.json +16 -0
  38. package/.agent/history/snapshots/test__unit__case_law_manager.test.js.json +1 -2
  39. package/.agent/history/snapshots/test__unit__cicd_validator.test.js.json +12 -0
  40. package/.agent/history/snapshots/test__unit__compile.test.js.json +10 -0
  41. package/.agent/history/snapshots/test__unit__context_broker.test.js.json +1 -2
  42. package/.agent/history/snapshots/test__unit__contract_engine.test.js.json +13 -0
  43. package/.agent/history/snapshots/test__unit__copyDir.test.js.json +3 -3
  44. package/.agent/history/snapshots/test__unit__graph_tools.test.js.json +1 -2
  45. package/.agent/history/snapshots/test__unit__guardrail_engine.test.js.json +10 -0
  46. package/.agent/history/snapshots/test__unit__impact_classifier.test.js.json +12 -0
  47. package/.agent/history/snapshots/test__unit__init.test.js.json +14 -0
  48. package/.agent/history/snapshots/test__unit__inner_loop_validator.test.js.json +1 -2
  49. package/.agent/history/snapshots/test__unit__integrity_manifest.test.js.json +13 -0
  50. package/.agent/history/snapshots/test__unit__learn.test.js.json +11 -0
  51. package/.agent/history/snapshots/test__unit__marathon.test.js.json +22 -0
  52. package/.agent/history/snapshots/test__unit__mcp_server.test.js.json +16 -0
  53. package/.agent/history/snapshots/test__unit__memory.test.js.json +13 -0
  54. package/.agent/history/snapshots/test__unit__minimal_change.test.js.json +10 -0
  55. package/.agent/history/snapshots/test__unit__native.test.js.json +10 -0
  56. package/.agent/history/snapshots/test__unit__optimize.test.js.json +13 -0
  57. package/.agent/history/snapshots/test__unit__path_resolution.test.js.json +14 -0
  58. package/.agent/history/snapshots/test__unit__production_readiness_evidence.test.js.json +21 -0
  59. package/.agent/history/snapshots/test__unit__selfInstall.test.js.json +3 -3
  60. package/.agent/history/snapshots/test__unit__semver.test.js.json +3 -3
  61. package/.agent/history/snapshots/test__unit__skill_evolution.test.js.json +11 -0
  62. package/.agent/history/snapshots/test__unit__stress.test.js.json +15 -0
  63. package/.agent/history/snapshots/test__unit__swarm_dispatcher.test.js.json +3 -3
  64. package/.agent/history/snapshots/test__unit__utils.test.js.json +10 -0
  65. package/.agent/scripts/ast_context_loader.js +137 -0
  66. package/.agent/scripts/memory_engine.js +581 -0
  67. package/.agent/scripts/payload_schemas.js +146 -0
  68. package/.agent/scripts/prompt_compiler.js +59 -11
  69. package/.agent/scripts/swarm_dispatcher.js +295 -67
  70. package/.agent/scripts/token_budget_broker.js +117 -6
  71. package/.claude/CLAUDE.md +442 -0
  72. package/README.md +22 -12
  73. package/SECURITY.md +3 -3
  74. package/bin/adapter-install.js +240 -0
  75. package/bin/global-store.js +48 -0
  76. package/bin/mcp-server.js +568 -252
  77. package/bin/proxy-server.js +113 -0
  78. package/bin/spawn-agent.js +39 -0
  79. package/bin/tk-proxy.js +34 -0
  80. package/dist/commands/init.js +8 -51
  81. package/dist/commands/status.js +61 -36
  82. package/dist/commands/validate.js +22 -31
  83. package/dist/tui/banner.js +77 -0
  84. package/dist/tui/index.js +21 -0
  85. package/dist/tui/reviewer-grid.js +90 -0
  86. package/dist/tui/shimmer.js +91 -0
  87. package/dist/tui/theme.js +130 -0
  88. package/dist/tui/tree.js +93 -0
  89. package/dist/tui/wizard.js +148 -0
  90. package/dist/utils/helpers.js +5 -41
  91. package/package.json +27 -10
  92. package/scripts/build-graph.js +71 -0
  93. package/.agent/history/snapshots/migrate_refs.js.json +0 -11
  94. package/.agent/scripts/compile_router.py +0 -5
  95. package/.agent/scripts/migrate_skills_frontmatter.py +0 -5
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "file": "test/unit/context_broker.test.js",
3
- "hash": "5fa154b5885a89376a937bd4825b1ba5643c92f6",
4
3
  "riskScore": "Low",
5
4
  "blastRadius": 0,
6
5
  "imports": {
7
6
  "../../.agent/scripts/context_broker": []
8
7
  },
9
8
  "dependents": [],
10
- "content": "/**\n * context_broker.test.js — Unit tests for the Context Density Broker\n * Tests: tokenize, scoreSkill, selectSkills\n */\n\nconst {\n scoreSkill,\n selectSkills,\n tokenize\n} = require('../../.agent/scripts/context_broker');\n\ndescribe('context_broker.js', () => {\n\n // ── tokenize ──────────────────────────────────────────────────────────\n\n describe('tokenize()', () => {\n it('should extract words 3+ characters, lowercased', () => {\n const tokens = tokenize('Build a JWT authentication API');\n expect(tokens).toContain('build');\n expect(tokens).toContain('jwt');\n expect(tokens).toContain('authentication');\n expect(tokens).toContain('api');\n });\n\n it('should ignore 1-2 character words', () => {\n const tokens = tokenize('I am ok to go');\n // \"ok\" is only 2 chars, \"am\" is 2 chars, \"I\" is 1 char, \"to\" is 2 chars, \"go\" is 2 chars\n expect(tokens).toEqual([]);\n });\n\n it('should handle empty string', () => {\n expect(tokenize('')).toEqual([]);\n });\n\n it('should handle underscored identifiers', () => {\n const tokens = tokenize('use_case my_component');\n expect(tokens).toContain('use_case');\n expect(tokens).toContain('my_component');\n });\n });\n\n // ── scoreSkill ────────────────────────────────────────────────────────\n\n describe('scoreSkill()', () => {\n const makeSkill = (name, description = '') => ({\n name,\n description,\n content: ''\n });\n\n it('should give higher scores for domain-matched skills', () => {\n const taskTokens = tokenize('Build a React component with hooks');\n const reactSkill = makeSkill('react-specialist', 'React 19+ specialist. Components, hooks, state.');\n const sqlSkill = makeSkill('sql-pro', 'Advanced SQL queries and optimization');\n\n const reactScore = scoreSkill(reactSkill, 'Build a React component with hooks', [], taskTokens);\n const sqlScore = scoreSkill(sqlSkill, 'Build a React component with hooks', [], taskTokens);\n\n expect(reactScore).toBeGreaterThan(sqlScore);\n });\n\n it('should boost score for file extension affinity', () => {\n const taskTokens = tokenize('Fix the login page');\n const reactSkill = makeSkill('react-specialist', 'React components and hooks');\n\n const withoutExt = scoreSkill(reactSkill, 'Fix the login page', [], taskTokens);\n const withExt = scoreSkill(reactSkill, 'Fix the login page', ['.tsx'], taskTokens);\n\n expect(withExt).toBeGreaterThanOrEqual(withoutExt);\n });\n\n it('should return 0 or low score for completely unrelated skills', () => {\n const taskTokens = tokenize('Deploy to Kubernetes');\n const gameSkill = makeSkill('game-design-expert', 'Game design and player experience');\n\n const score = scoreSkill(gameSkill, 'Deploy to Kubernetes', [], taskTokens);\n expect(score).toBeLessThanOrEqual(1);\n });\n\n it('should boost baseline skills', () => {\n const taskTokens = tokenize('random unrelated task');\n // clean-code is a baseline skill\n const cleanCode = makeSkill('clean-code', 'Clean code mastery');\n\n const score = scoreSkill(cleanCode, 'random unrelated task', [], taskTokens);\n // Baseline skills get +1 regardless of match\n expect(score).toBeGreaterThanOrEqual(1);\n });\n });\n\n // ── selectSkills ──────────────────────────────────────────────────────\n\n describe('selectSkills()', () => {\n // Create minimal mock skills array for testing\n const mockSkills = [\n { name: 'react-specialist', description: 'React 19+ specialist. Components, hooks, state management.', content: '' },\n { name: 'sql-pro', description: 'SQL queries, PostgreSQL, MySQL, optimization', content: '' },\n { name: 'clean-code', description: 'Clean code mastery. Naming, SOLID, refactoring.', content: '' },\n { name: 'testing-patterns', description: 'Testing mastery. Jest, Vitest, Playwright, mocking.', content: '' },\n { name: 'vulnerability-scanner', description: 'Security vulnerability analysis. OWASP, injection.', content: '' },\n ];\n\n it('should return essential, supplementary, and available arrays', () => {\n const result = selectSkills('Build a React component', [], 'large', mockSkills);\n\n expect(result).toHaveProperty('essential');\n expect(result).toHaveProperty('supplementary');\n expect(result).toHaveProperty('available');\n expect(result).toHaveProperty('scores');\n expect(Array.isArray(result.essential)).toBe(true);\n expect(Array.isArray(result.supplementary)).toBe(true);\n expect(Array.isArray(result.available)).toBe(true);\n });\n\n it('should prioritize relevant skills in essential tier', () => {\n const result = selectSkills('Write a SQL query for user analytics', [], 'large', mockSkills);\n const essentialNames = result.essential.map(s => s.name);\n // sql-pro should be ranked highly for a SQL task\n if (essentialNames.length > 0) {\n expect(essentialNames).toContain('sql-pro');\n }\n });\n\n it('should limit essential to 6 for small models', () => {\n const result = selectSkills('Build something', [], 'small', mockSkills);\n expect(result.essential.length).toBeLessThanOrEqual(6);\n // Small model should have empty supplementary\n expect(result.supplementary).toEqual([]);\n });\n\n it('should collapse supplementary into available for small models', () => {\n const result = selectSkills('Build a full-stack app', [], 'small', mockSkills);\n expect(result.supplementary).toEqual([]);\n // Available should contain things that would have been supplementary\n expect(result.available.length).toBeGreaterThanOrEqual(0);\n });\n\n it('should limit essential to 10 for large models', () => {\n const result = selectSkills('Build everything imaginable', [], 'large', mockSkills);\n expect(result.essential.length).toBeLessThanOrEqual(10);\n });\n\n it('should return a scores Map', () => {\n const result = selectSkills('Test something', [], 'large', mockSkills);\n expect(result.scores).toBeInstanceOf(Map);\n expect(result.scores.size).toBe(mockSkills.length);\n });\n });\n});\n"
9
+ "content": "/**\n * context_broker.test.js — Unit tests for the Context Density Broker\n * Tests: tokenize, scoreSkill, selectSkills\n */\n\nconst { scoreSkill, selectSkills, tokenize } = require('../../.agent/scripts/context_broker');\n\ndescribe('context_broker.js', () => {\n // ── tokenize ──────────────────────────────────────────────────────────\n\n describe('tokenize()', () => {\n it('should extract words 3+ characters, lowercased', () => {\n const tokens = tokenize('Build a JWT authentication API');\n expect(tokens).toContain('build');\n expect(tokens).toContain('jwt');\n expect(tokens).toContain('authentication');\n expect(tokens).toContain('api');\n });\n\n it('should ignore 1-2 character words', () => {\n const tokens = tokenize('I am ok to go');\n // \"ok\" is only 2 chars, \"am\" is 2 chars, \"I\" is 1 char, \"to\" is 2 chars, \"go\" is 2 chars\n expect(tokens).toEqual([]);\n });\n\n it('should handle empty string', () => {\n expect(tokenize('')).toEqual([]);\n });\n\n it('should handle underscored identifiers', () => {\n const tokens = tokenize('use_case my_component');\n expect(tokens).toContain('use_case');\n expect(tokens).toContain('my_component');\n });\n });\n\n // ── scoreSkill ────────────────────────────────────────────────────────\n\n describe('scoreSkill()', () => {\n const makeSkill = (name, description = '') => ({\n name,\n description,\n content: '',\n });\n\n it('should give higher scores for domain-matched skills', () => {\n const taskTokens = tokenize('Build a React component with hooks');\n const reactSkill = makeSkill(\n 'react-specialist',\n 'React 19+ specialist. Components, hooks, state.',\n );\n const sqlSkill = makeSkill('sql-pro', 'Advanced SQL queries and optimization');\n\n const reactScore = scoreSkill(\n reactSkill,\n 'Build a React component with hooks',\n [],\n taskTokens,\n );\n const sqlScore = scoreSkill(sqlSkill, 'Build a React component with hooks', [], taskTokens);\n\n expect(reactScore).toBeGreaterThan(sqlScore);\n });\n\n it('should boost score for file extension affinity', () => {\n const taskTokens = tokenize('Fix the login page');\n const reactSkill = makeSkill('react-specialist', 'React components and hooks');\n\n const withoutExt = scoreSkill(reactSkill, 'Fix the login page', [], taskTokens);\n const withExt = scoreSkill(reactSkill, 'Fix the login page', ['.tsx'], taskTokens);\n\n expect(withExt).toBeGreaterThanOrEqual(withoutExt);\n });\n\n it('should return 0 or low score for completely unrelated skills', () => {\n const taskTokens = tokenize('Deploy to Kubernetes');\n const gameSkill = makeSkill('game-design-expert', 'Game design and player experience');\n\n const score = scoreSkill(gameSkill, 'Deploy to Kubernetes', [], taskTokens);\n expect(score).toBeLessThanOrEqual(1);\n });\n\n it('should boost baseline skills', () => {\n const taskTokens = tokenize('random unrelated task');\n // clean-code is a baseline skill\n const cleanCode = makeSkill('clean-code', 'Clean code mastery');\n\n const score = scoreSkill(cleanCode, 'random unrelated task', [], taskTokens);\n // Baseline skills get +1 regardless of match\n expect(score).toBeGreaterThanOrEqual(1);\n });\n });\n\n // ── selectSkills ──────────────────────────────────────────────────────\n\n describe('selectSkills()', () => {\n // Create minimal mock skills array for testing\n const mockSkills = [\n {\n name: 'react-specialist',\n description: 'React 19+ specialist. Components, hooks, state management.',\n content: '',\n },\n {\n name: 'sql-pro',\n description: 'SQL queries, PostgreSQL, MySQL, optimization',\n content: '',\n },\n {\n name: 'clean-code',\n description: 'Clean code mastery. Naming, SOLID, refactoring.',\n content: '',\n },\n {\n name: 'testing-patterns',\n description: 'Testing mastery. Jest, Vitest, Playwright, mocking.',\n content: '',\n },\n {\n name: 'vulnerability-scanner',\n description: 'Security vulnerability analysis. OWASP, injection.',\n content: '',\n },\n ];\n\n it('should return essential, supplementary, and available arrays', () => {\n const result = selectSkills('Build a React component', [], 'large', mockSkills);\n\n expect(result).toHaveProperty('essential');\n expect(result).toHaveProperty('supplementary');\n expect(result).toHaveProperty('available');\n expect(result).toHaveProperty('scores');\n expect(Array.isArray(result.essential)).toBe(true);\n expect(Array.isArray(result.supplementary)).toBe(true);\n expect(Array.isArray(result.available)).toBe(true);\n });\n\n it('should prioritize relevant skills in essential tier', () => {\n const result = selectSkills('Write a SQL query for user analytics', [], 'large', mockSkills);\n const essentialNames = result.essential.map(s => s.name);\n // sql-pro should be ranked highly for a SQL task\n if (essentialNames.length > 0) {\n expect(essentialNames).toContain('sql-pro');\n }\n });\n\n it('should limit essential to 6 for small models', () => {\n const result = selectSkills('Build something', [], 'small', mockSkills);\n expect(result.essential.length).toBeLessThanOrEqual(6);\n // Small model should have empty supplementary\n expect(result.supplementary).toEqual([]);\n });\n\n it('should collapse supplementary into available for small models', () => {\n const result = selectSkills('Build a full-stack app', [], 'small', mockSkills);\n expect(result.supplementary).toEqual([]);\n // Available should contain things that would have been supplementary\n expect(result.available.length).toBeGreaterThanOrEqual(0);\n });\n\n it('should limit essential to 10 for large models', () => {\n const result = selectSkills('Build everything imaginable', [], 'large', mockSkills);\n expect(result.essential.length).toBeLessThanOrEqual(10);\n });\n\n it('should return a scores Map', () => {\n const result = selectSkills('Test something', [], 'large', mockSkills);\n expect(result.scores).toBeInstanceOf(Map);\n expect(result.scores.size).toBe(mockSkills.length);\n });\n });\n});\n"
11
10
  }
@@ -0,0 +1,13 @@
1
+ {
2
+ "file": "test/unit/contract_engine.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "fs": [],
7
+ "path": [],
8
+ "../../.agent/scripts/contract_engine": [],
9
+ "../../.agent/scripts/trace_engine": []
10
+ },
11
+ "dependents": [],
12
+ "content": "'use strict';\n\nconst fs = require('fs');\nconst path = require('path');\nconst {\n parseYamlContract,\n matchGlob,\n evaluateContract,\n loadContracts: _loadContracts,\n verifyWorkspace,\n} = require('../../.agent/scripts/contract_engine');\n\nconst {\n captureSnapshot,\n listSnapshots,\n replaySnapshot,\n} = require('../../.agent/scripts/trace_engine');\n\ndescribe('Contract Engine (YAML parsing & rule evaluation)', () => {\n test('parseYamlContract parses schema fields correctly', () => {\n const yaml = `\nname: \"No console.log\"\ndescription: \"Block debug logs\"\nscope: \"src/**/*.js\"\nexclude: \"**/*.test.js\"\nwhen: file_modified\nseverity: block\nmust_not:\n - pattern: \"console.log\"\n message: \"No logs in src\"\nmust:\n - pattern: \"use strict\"\n message: \"Must be strict\"\n`;\n const parsed = parseYamlContract(yaml);\n expect(parsed.name).toBe('No console.log');\n expect(parsed.description).toBe('Block debug logs');\n expect(parsed.scope).toBe('src/**/*.js');\n expect(parsed.exclude).toBe('**/*.test.js');\n expect(parsed.severity).toBe('block');\n expect(parsed.must_not).toHaveLength(1);\n expect(parsed.must_not[0].pattern).toBe('console.log');\n expect(parsed.must).toHaveLength(1);\n expect(parsed.must[0].pattern).toBe('use strict');\n });\n\n test('matchGlob handles standard and globstar patterns', () => {\n expect(matchGlob('src/api/user.ts', 'src/**/*.ts')).toBe(true);\n expect(matchGlob('test/user.test.ts', '**/*.test.ts')).toBe(true);\n expect(matchGlob('src/api/user.ts', 'src/**/*.js')).toBe(false);\n });\n\n test('evaluateContract detects must_not violations', () => {\n const contract = {\n name: 'No console.log',\n scope: 'src/**/*.js',\n severity: 'warn',\n must_not: [\n {\n pattern: 'console.log',\n message: 'No debug logging allowed',\n },\n ],\n };\n\n const fileContent = `function test() {\\n console.log(\"hello\");\\n}`;\n const violations = evaluateContract(contract, 'src/index.js', fileContent);\n expect(violations).toHaveLength(1);\n expect(violations[0].line).toBe(2);\n expect(violations[0].message).toBe('No debug logging allowed');\n });\n\n test('evaluateContract evaluates regex must rules', () => {\n const contract = {\n name: 'Strict Mode Required',\n scope: 'src/**/*.js',\n severity: 'block',\n must: [\n {\n pattern: 'regex:use strict',\n message: 'Missing strict mode header',\n },\n ],\n };\n\n const badContent = `function test() {}`;\n const violations = evaluateContract(contract, 'src/index.js', badContent);\n expect(violations).toHaveLength(1);\n\n const goodContent = `\"use strict\";\\nfunction test() {}`;\n const clean = evaluateContract(contract, 'src/index.js', goodContent);\n expect(clean).toHaveLength(0);\n });\n\n test('verifyWorkspace verifies workspace contracts against target files', () => {\n const projectRoot = process.cwd();\n const result = verifyWorkspace(projectRoot, [path.join(projectRoot, 'dist', 'cli.js')]);\n\n expect(result.contracts_loaded).toBeGreaterThanOrEqual(1);\n expect(result.files_checked).toBe(1);\n expect(typeof result.blocked).toBe('boolean');\n });\n});\n\ndescribe('Trace Engine (Failure Snapshots & Replay)', () => {\n const tmpDir = path.join(__dirname, '../../scratch/test_trace_env');\n\n beforeAll(() => {\n if (!fs.existsSync(tmpDir)) {\n fs.mkdirSync(tmpDir, { recursive: true });\n }\n });\n\n afterAll(() => {\n try {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n } catch {\n // Cleanup\n }\n });\n\n test('captureSnapshot & listSnapshots & replaySnapshot lifecycle', () => {\n const violation = {\n contract: 'No console.log in production code',\n rule: 'console.log',\n file: 'src/api/user.ts',\n line: 15,\n snippet: \"console.log('test')\",\n message: 'Forbidden pattern found',\n severity: 'block',\n };\n\n const captured = captureSnapshot(tmpDir, violation);\n expect(captured.id).toBeDefined();\n expect(captured.contract).toBe('No console.log in production code');\n\n const snapshots = listSnapshots(tmpDir);\n expect(snapshots.length).toBeGreaterThanOrEqual(1);\n\n const replayed = replaySnapshot(tmpDir, captured.id);\n expect(replayed).not.toBeNull();\n expect(replayed.target_file).toBe('src/api/user.ts');\n });\n});\n"
13
+ }
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "file": "test/unit/copyDir.test.js",
3
- "hash": "1f3111ef91650adcb45b728779000c03b9bdcb3f",
4
3
  "riskScore": "Low",
5
4
  "blastRadius": 0,
6
5
  "imports": {
@@ -15,9 +14,10 @@
15
14
  "isSelfInstall",
16
15
  "CORE_AGENTS",
17
16
  "CORE_SKILLS",
18
- "generateIDEBridges"
17
+ "generateIDEBridges",
18
+ "cmdMarathon"
19
19
  ]
20
20
  },
21
21
  "dependents": [],
22
- "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"
22
+ "content": "'use strict';\n\nconst fs = require('fs');\nconst path = require('path');\nconst os = require('os');\nconst { copyDir, countDir } = require('../../bin/tribunal-kit');\n\nfunction makeTempDir(prefix = 'tk-test-') {\n return fs.mkdtempSync(path.join(os.tmpdir(), prefix));\n}\n\ndescribe('countDir', () => {\n let tmpDir;\n\n beforeEach(() => {\n tmpDir = makeTempDir();\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n test('returns 0 for empty directory', async () => {\n expect(await countDir(tmpDir)).toBe(0);\n });\n\n test('counts files at top level', async () => {\n fs.writeFileSync(path.join(tmpDir, 'a.txt'), 'a');\n fs.writeFileSync(path.join(tmpDir, 'b.txt'), 'b');\n expect(await countDir(tmpDir)).toBe(2);\n });\n\n test('counts files recursively in subdirectories', async () => {\n const sub = path.join(tmpDir, 'sub');\n fs.mkdirSync(sub);\n fs.writeFileSync(path.join(tmpDir, 'top.txt'), 'top');\n fs.writeFileSync(path.join(sub, 'nested.txt'), 'nested');\n expect(await countDir(tmpDir)).toBe(2);\n });\n});\n\ndescribe('copyDir', () => {\n let srcDir;\n let destDir;\n\n beforeEach(() => {\n srcDir = makeTempDir('tk-src-');\n destDir = makeTempDir('tk-dest-');\n // Remove destDir so copyDir can create it fresh\n fs.rmSync(destDir, { recursive: true, force: true });\n });\n\n afterEach(() => {\n fs.rmSync(srcDir, { recursive: true, force: true });\n fs.rmSync(destDir, { recursive: true, force: true });\n });\n\n test('copies files to destination', async () => {\n fs.writeFileSync(path.join(srcDir, 'hello.txt'), 'hello');\n await copyDir(srcDir, destDir);\n expect(fs.existsSync(path.join(destDir, 'hello.txt'))).toBe(true);\n expect(fs.readFileSync(path.join(destDir, 'hello.txt'), 'utf8')).toBe('hello');\n });\n\n test('copies nested directories recursively', async () => {\n const sub = path.join(srcDir, 'sub');\n fs.mkdirSync(sub);\n fs.writeFileSync(path.join(sub, 'nested.txt'), 'nested');\n await copyDir(srcDir, destDir);\n expect(fs.existsSync(path.join(destDir, 'sub', 'nested.txt'))).toBe(true);\n });\n\n test('returns total file count copied', async () => {\n fs.writeFileSync(path.join(srcDir, 'a.txt'), 'a');\n fs.writeFileSync(path.join(srcDir, 'b.txt'), 'b');\n const count = await copyDir(srcDir, destDir);\n expect(count).toBe(2);\n });\n\n test('dry-run does NOT create destination files', async () => {\n fs.writeFileSync(path.join(srcDir, 'hello.txt'), 'hello');\n const destDryRun = path.join(os.tmpdir(), 'tk-dryrun-' + Date.now());\n await copyDir(srcDir, destDryRun, true);\n expect(fs.existsSync(destDryRun)).toBe(false);\n });\n\n test('dry-run still returns correct file count', async () => {\n fs.writeFileSync(path.join(srcDir, 'a.txt'), 'a');\n fs.writeFileSync(path.join(srcDir, 'b.txt'), 'b');\n const destDryRun = path.join(os.tmpdir(), 'tk-dryrun2-' + Date.now());\n const count = await copyDir(srcDir, destDryRun, true);\n expect(count).toBe(2);\n });\n});\n"
23
23
  }
@@ -1,6 +1,5 @@
1
1
  {
2
2
  "file": "test/unit/graph_tools.test.js",
3
- "hash": "08b5b30f66169e34f86a9b8ad9aa842e0917f218",
4
3
  "riskScore": "Low",
5
4
  "blastRadius": 0,
6
5
  "imports": {
@@ -8,5 +7,5 @@
8
7
  "path": []
9
8
  },
10
9
  "dependents": [],
11
- "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"
10
+ "content": "const fs = require('fs');\nconst path = require('path');\n\ndescribe('Knowledge Graph Tools', () => {\n describe('graph_builder.js', () => {\n it('should exist and be executable', () => {\n const builderPath = path.join(__dirname, '../../.agent/scripts/graph_builder.js');\n expect(fs.existsSync(builderPath)).toBe(true);\n const content = fs.readFileSync(builderPath, 'utf8');\n expect(content).toContain('function main()');\n expect(content).toContain('function walkDir');\n expect(content).toContain('isExcluded');\n });\n });\n\n describe('graph_zoom.js', () => {\n it('should exist and be executable', () => {\n const zoomPath = path.join(__dirname, '../../.agent/scripts/graph_zoom.js');\n expect(fs.existsSync(zoomPath)).toBe(true);\n const content = fs.readFileSync(zoomPath, 'utf8');\n expect(content).toContain('function extractSkeleton');\n // Ensure fallback logic is present\n expect(content).toContain('RAW FILE FALLBACK');\n });\n });\n});\n"
12
11
  }
@@ -0,0 +1,10 @@
1
+ {
2
+ "file": "test/unit/guardrail_engine.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "../../.agent/scripts/guardrail_engine": []
7
+ },
8
+ "dependents": [],
9
+ "content": "'use strict';\n\nconst {\n validate,\n _ruleFileExists,\n ruleScriptExtension,\n ruleReviewerCount,\n _ruleSkillExists,\n _ruleAgentExists,\n ruleUnresolvedVerify,\n ruleNumericConsistency,\n _ruleImportPhantom,\n} = require('../../.agent/scripts/guardrail_engine');\n\ndescribe('ruleScriptExtension', () => {\n const manifest = {\n scripts: {\n files: {\n 'verify_all.js': '.agent/scripts/verify_all.js',\n 'checklist.js': '.agent/scripts/checklist.js',\n },\n },\n };\n\n test('passes on correct .js extension', () => {\n const content = 'We run scripts/verify_all.js to verify.';\n const violations = ruleScriptExtension(content, manifest);\n expect(violations.length).toBe(0);\n });\n\n test('warns and suggests fix on incorrect .py extension', () => {\n const content = 'We run scripts/verify_all.py to verify.';\n const violations = ruleScriptExtension(content, manifest);\n expect(violations.length).toBe(1);\n expect(violations[0].severity).toBe('warning');\n expect(violations[0].autoFixable).toBe(true);\n expect(violations[0].fix.replace).toBe('scripts/verify_all.js');\n });\n});\n\ndescribe('ruleReviewerCount', () => {\n const manifest = {\n agents: {\n reviewer_count: 20,\n },\n };\n\n test('passes on matching reviewer count', () => {\n const content = 'We support 20 reviewers in the pipeline.';\n const violations = ruleReviewerCount(content, manifest);\n expect(violations.length).toBe(0);\n });\n\n test('warns and suggests fix on mismatched reviewer count', () => {\n const content = 'We support 19 reviewers in the pipeline.';\n const violations = ruleReviewerCount(content, manifest);\n expect(violations.length).toBe(1);\n expect(violations[0].severity).toBe('warning');\n expect(violations[0].fix.replace).toContain('20 reviewers');\n });\n\n test('does not reinterpret an agent total as a reviewer total', () => {\n const violations = ruleReviewerCount('The payload includes 44 specialist agents.', manifest);\n expect(violations).toEqual([]);\n });\n\n test('recognizes qualified reviewer-count claims', () => {\n const violations = ruleReviewerCount(\n 'The pipeline has 19 parallel Tribunal code reviewers.',\n manifest,\n );\n expect(violations).toHaveLength(1);\n expect(violations[0].message).toContain('Claims 19 reviewers');\n });\n});\n\ndescribe('ruleUnresolvedVerify', () => {\n test('passes if no VERIFY tags exist', () => {\n const content = 'const x = 42; // some comment';\n const violations = ruleUnresolvedVerify(content);\n expect(violations.length).toBe(0);\n });\n\n test('fails critical if VERIFY tags are present', () => {\n const content = 'const x = 42; // VERIFY: this logic is correct';\n const violations = ruleUnresolvedVerify(content);\n expect(violations.length).toBe(1);\n expect(violations[0].severity).toBe('critical');\n expect(violations[0].message).toContain('this logic is correct');\n });\n});\n\ndescribe('ruleNumericConsistency', () => {\n test('passes if claims match manifest', () => {\n const manifest = {\n numeric_claims: [{ source: 'file.md:L1', claim: '20 reviewers', actual: 20, valid: true }],\n };\n const violations = ruleNumericConsistency('', manifest);\n expect(violations.length).toBe(0);\n });\n\n test('warns on mismatched claims', () => {\n const manifest = {\n numeric_claims: [{ source: 'file.md:L1', claim: '19 reviewers', actual: 20, valid: false }],\n };\n const violations = ruleNumericConsistency('', manifest);\n expect(violations.length).toBe(1);\n expect(violations[0].severity).toBe('warning');\n expect(violations[0].fix.replace).toBe('20 reviewers');\n });\n});\n\ndescribe('validate main wrapper', () => {\n const manifest = {\n agents: { reviewer_count: 20 },\n scripts: { files: { 'verify_all.js': '.agent/scripts/verify_all.js' } },\n };\n\n test('aggregates pass result', () => {\n const content = 'Clean text. 20 reviewers. scripts/verify_all.js';\n const result = validate(content, manifest, { rules: ['reviewer-count', 'script-extension'] });\n expect(result.passed).toBe(true);\n expect(result.violations.length).toBe(0);\n });\n\n test('reports critical errors', () => {\n const content = 'Code with // VERIFY: error';\n const result = validate(content, manifest, { rules: ['unresolved-verify'] });\n expect(result.passed).toBe(false);\n expect(result.violations.some(v => v.severity === 'critical')).toBe(true);\n });\n\n test('applies auto-fixes when requested', () => {\n const content = 'We have 19 reviewers and run scripts/verify_all.py';\n const result = validate(content, manifest, {\n rules: ['reviewer-count', 'script-extension'],\n autoFix: true,\n });\n expect(result.autoFixedContent).toBe('We have 20 reviewers and run scripts/verify_all.js');\n });\n});\n"
10
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "file": "test/unit/impact_classifier.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "../../.agent/scripts/impact_classifier": [],
7
+ "../../.agent/scripts/socratic_gate_policy": [],
8
+ "../../.agent/scripts/token_budget_broker": []
9
+ },
10
+ "dependents": [],
11
+ "content": "/**\n * impact_classifier.test.js — Unit tests for Adaptive Governance Impact Classifier\n */\n\n'use strict';\n\nconst { classifyImpact } = require('../../.agent/scripts/impact_classifier');\nconst { evaluateSocraticGate } = require('../../.agent/scripts/socratic_gate_policy');\nconst { getTokenBudget } = require('../../.agent/scripts/token_budget_broker');\n\ndescribe('Adaptive Governance Impact Classifier', () => {\n test('Classifies pure CSS / typo edit as Tier 0 Fast-Pass', () => {\n const res = classifyImpact({\n files: ['src/components/Button.css'],\n task: 'Fix typo in button hover style comment',\n lineCount: 3,\n });\n\n expect(res.tier).toBe(0);\n expect(res.fastPass).toBe(true);\n expect(res.maxReviewers).toBe(0);\n expect(res.requireGate).toBe(false);\n });\n\n test('Classifies single component logic fix as Tier 1 Express Pass', () => {\n const res = classifyImpact({\n files: ['src/components/Header.tsx'],\n task: 'Update toggle state logic',\n lineCount: 15,\n });\n\n expect(res.tier).toBe(1);\n expect(res.fastPass).toBe(false);\n expect(res.maxReviewers).toBe(1);\n expect(res.requireGate).toBe(false);\n });\n\n test('Classifies auth/security files as Tier 3 Full Gauntlet', () => {\n const res = classifyImpact({\n files: ['src/auth/jwt_verifier.ts'],\n task: 'Update session token expiration check',\n lineCount: 8,\n });\n\n expect(res.tier).toBe(3);\n expect(res.maxReviewers).toBe(8);\n expect(res.requireGate).toBe(true);\n });\n});\n\ndescribe('Adaptive Socratic Gate Policy', () => {\n test('Bypasses Socratic gate for Tier 0 and Tier 1', () => {\n expect(evaluateSocraticGate({ tier: 0 }).shouldBlock).toBe(false);\n expect(evaluateSocraticGate({ tier: 1 }).shouldBlock).toBe(false);\n });\n\n test('Enforces gate for Tier 3 or high ambiguity Tier 2', () => {\n expect(evaluateSocraticGate({ tier: 3 }).shouldBlock).toBe(true);\n expect(evaluateSocraticGate({ tier: 2, ambiguityScore: 0.8 }).shouldBlock).toBe(true);\n });\n\n test('Respects --no-gate or --express override flags', () => {\n expect(evaluateSocraticGate({ tier: 3, flags: { 'no-gate': true } }).shouldBlock).toBe(false);\n expect(evaluateSocraticGate({ tier: 3, flags: { express: true } }).shouldBlock).toBe(false);\n });\n});\n\ndescribe('Token Budget Broker', () => {\n test('Caps token budget according to Impact Tier', () => {\n expect(getTokenBudget(0).maxTokens).toBe(0);\n expect(getTokenBudget(1).maxTokens).toBe(2000);\n expect(getTokenBudget(2).maxTokens).toBe(8000);\n expect(getTokenBudget(3).maxTokens).toBe(32000);\n });\n});\n"
12
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "file": "test/unit/init.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "fs": [],
7
+ "path": [],
8
+ "os": [],
9
+ "../../dist/commands/init": [],
10
+ "../../dist/utils/fs": []
11
+ },
12
+ "dependents": [],
13
+ "content": "'use strict';\n\nconst fs = require('fs');\nconst path = require('path');\nconst os = require('os');\n\n// Test against the modular dist/ init command\nconst { cmdInit, generateIDEBridges } = require('../../dist/commands/init');\nconst { isSelfInstall, copyDir, countDir } = require('../../dist/utils/fs');\n\nfunction makeTempDir(prefix = 'tk-init-test-') {\n return fs.mkdtempSync(path.join(os.tmpdir(), prefix));\n}\n\n/**\n * Create a minimal fake .agent/ source directory that mirrors the real structure.\n * This allows cmdInit to run without needing the full 400+ file payload.\n */\nfunction _createFakeAgentSource(dir) {\n const agentsDir = path.join(dir, 'agents');\n const skillsDir = path.join(dir, 'skills', 'clean-code');\n const workflowsDir = path.join(dir, 'workflows');\n const scriptsDir = path.join(dir, 'scripts');\n const rulesDir = path.join(dir, 'rules');\n\n fs.mkdirSync(agentsDir, { recursive: true });\n fs.mkdirSync(skillsDir, { recursive: true });\n fs.mkdirSync(workflowsDir, { recursive: true });\n fs.mkdirSync(scriptsDir, { recursive: true });\n fs.mkdirSync(rulesDir, { recursive: true });\n\n // Create a few agent files (some core, some non-core)\n fs.writeFileSync(\n path.join(agentsDir, 'backend-specialist.md'),\n '# Backend Specialist\\nCore agent.',\n );\n fs.writeFileSync(\n path.join(agentsDir, 'frontend-specialist.md'),\n '# Frontend Specialist\\nCore agent.',\n );\n fs.writeFileSync(path.join(agentsDir, 'game-developer.md'), '# Game Developer\\nNon-core agent.');\n\n // Create a skill\n fs.writeFileSync(\n path.join(skillsDir, 'SKILL.md'),\n '---\\nname: clean-code\\ndescription: Clean code mastery.\\n---\\n# Clean Code',\n );\n\n // Create a workflow\n fs.writeFileSync(path.join(workflowsDir, 'create.md'), '# /create\\nCreate workflow.');\n\n // Create a script\n fs.writeFileSync(path.join(scriptsDir, 'checklist.js'), '// checklist script');\n\n // Create rules\n fs.writeFileSync(path.join(rulesDir, 'GEMINI.md'), '# Rules\\nMaster rules file.');\n\n return dir;\n}\n\ndescribe('dist/commands/init — cmdInit', () => {\n let tmpTarget;\n let _originalCwd;\n let mockConsoleLog;\n let mockConsoleError;\n let mockExit;\n\n beforeEach(() => {\n tmpTarget = makeTempDir();\n _originalCwd = process.cwd();\n mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => {});\n mockConsoleError = jest.spyOn(console, 'error').mockImplementation(() => {});\n mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {\n throw new Error('process.exit called');\n });\n });\n\n afterEach(() => {\n mockConsoleLog.mockRestore();\n mockConsoleError.mockRestore();\n mockExit.mockRestore();\n try {\n fs.rmSync(tmpTarget, { recursive: true, force: true });\n } catch {\n // ignore cleanup errors\n }\n });\n\n test('self-install guard prevents init inside tribunal-kit package itself', async () => {\n // Point --path to the kit's own root\n const kitRoot = path.resolve(__dirname, '../../');\n const flags = { path: kitRoot };\n\n await expect(cmdInit(flags, true)).rejects.toThrow('process.exit called');\n expect(mockExit).toHaveBeenCalledWith(1);\n });\n\n test('exits when target directory does not exist', async () => {\n const nonExistent = path.join(tmpTarget, 'does-not-exist');\n const flags = { path: nonExistent };\n\n await expect(cmdInit(flags, true)).rejects.toThrow('process.exit called');\n expect(mockExit).toHaveBeenCalledWith(1);\n });\n\n test('exits gracefully when .agent/ already exists without --force', async () => {\n // Pre-create .agent/ in the target\n fs.mkdirSync(path.join(tmpTarget, '.agent'), { recursive: true });\n const flags = { path: tmpTarget };\n\n await expect(cmdInit(flags, true)).rejects.toThrow('process.exit called');\n expect(mockExit).toHaveBeenCalledWith(0);\n });\n\n test('dry-run does NOT create .agent/ in target', async () => {\n const flags = { path: tmpTarget, dryRun: true };\n\n await cmdInit(flags, true);\n\n // .agent should NOT exist in the target\n const agentDir = path.join(tmpTarget, '.agent');\n // In dry-run, copyDir doesn't create files but the directory structure\n // may be created by the history dir logic. The key assertion is that\n // no actual agent/skill files are copied.\n const agentsDir = path.join(agentDir, 'agents');\n if (fs.existsSync(agentsDir)) {\n const files = fs.readdirSync(agentsDir);\n expect(files.length).toBe(0);\n }\n });\n\n test('full init copies .agent/ into target directory', async () => {\n const flags = { path: tmpTarget };\n\n await cmdInit(flags, true);\n\n const agentDir = path.join(tmpTarget, '.agent');\n expect(fs.existsSync(agentDir)).toBe(true);\n\n // Check that key subdirectories were created\n expect(fs.existsSync(path.join(agentDir, 'agents'))).toBe(true);\n expect(fs.existsSync(path.join(agentDir, 'skills'))).toBe(true);\n expect(fs.existsSync(path.join(agentDir, 'workflows'))).toBe(true);\n expect(fs.existsSync(path.join(agentDir, 'scripts'))).toBe(true);\n\n // Check history dirs were created\n expect(fs.existsSync(path.join(agentDir, 'history', 'case-law', 'cases', '.gitkeep'))).toBe(\n true,\n );\n expect(fs.existsSync(path.join(agentDir, 'history', 'skill-evolution', '.gitkeep'))).toBe(true);\n });\n\n test('init with --force over existing .agent/ succeeds', async () => {\n // First init\n const flags = { path: tmpTarget };\n await cmdInit(flags, true);\n\n // Second init with --force\n const forceFlags = { path: tmpTarget, force: true };\n await cmdInit(forceFlags, true);\n\n // Should still have a valid .agent/\n const agentDir = path.join(tmpTarget, '.agent');\n expect(fs.existsSync(agentDir)).toBe(true);\n expect(fs.existsSync(path.join(agentDir, 'agents'))).toBe(true);\n });\n});\n\ndescribe('dist/utils/fs — isSelfInstall', () => {\n test('returns true when target matches kitRoot path', () => {\n const kitRoot = path.resolve(__dirname, '../../');\n expect(isSelfInstall(kitRoot, 'tribunal-kit', kitRoot)).toBe(true);\n });\n\n test('returns false for unrelated directory', () => {\n const kitRoot = path.resolve(__dirname, '../../');\n expect(isSelfInstall(os.tmpdir(), 'tribunal-kit', kitRoot)).toBe(false);\n });\n\n test('returns true when target package.json name matches', () => {\n const fakeDir = makeTempDir('tk-self-');\n const kitRoot = '/some/other/path';\n fs.writeFileSync(path.join(fakeDir, 'package.json'), JSON.stringify({ name: 'tribunal-kit' }));\n expect(isSelfInstall(fakeDir, 'tribunal-kit', kitRoot)).toBe(true);\n fs.rmSync(fakeDir, { recursive: true, force: true });\n });\n\n test('returns false when target package.json name differs', () => {\n const fakeDir = makeTempDir('tk-other-');\n const kitRoot = '/some/other/path';\n fs.writeFileSync(path.join(fakeDir, 'package.json'), JSON.stringify({ name: 'other-package' }));\n expect(isSelfInstall(fakeDir, 'tribunal-kit', kitRoot)).toBe(false);\n fs.rmSync(fakeDir, { recursive: true, force: true });\n });\n\n test('returns false when package.json is invalid JSON', () => {\n const fakeDir = makeTempDir('tk-bad-json-');\n const kitRoot = '/some/other/path';\n fs.writeFileSync(path.join(fakeDir, 'package.json'), 'NOT VALID JSON');\n expect(isSelfInstall(fakeDir, 'tribunal-kit', kitRoot)).toBe(false);\n fs.rmSync(fakeDir, { recursive: true, force: true });\n });\n});\n\ndescribe('dist/utils/fs — copyDir', () => {\n let srcDir;\n let destDir;\n\n beforeEach(() => {\n srcDir = makeTempDir('tk-cp-src-');\n destDir = path.join(os.tmpdir(), `tk-cp-dest-${Date.now()}`);\n });\n\n afterEach(() => {\n try {\n fs.rmSync(srcDir, { recursive: true, force: true });\n } catch {}\n try {\n fs.rmSync(destDir, { recursive: true, force: true });\n } catch {}\n });\n\n test('copies files to destination', async () => {\n fs.writeFileSync(path.join(srcDir, 'test.txt'), 'hello');\n const count = await copyDir(srcDir, destDir);\n expect(count).toBe(1);\n expect(fs.readFileSync(path.join(destDir, 'test.txt'), 'utf8')).toBe('hello');\n });\n\n test('copies nested directories', async () => {\n fs.mkdirSync(path.join(srcDir, 'sub'), { recursive: true });\n fs.writeFileSync(path.join(srcDir, 'sub', 'nested.txt'), 'nested');\n const count = await copyDir(srcDir, destDir);\n expect(count).toBe(1);\n expect(fs.readFileSync(path.join(destDir, 'sub', 'nested.txt'), 'utf8')).toBe('nested');\n });\n\n test('dry-run returns count but creates no files', async () => {\n fs.writeFileSync(path.join(srcDir, 'a.txt'), 'a');\n fs.writeFileSync(path.join(srcDir, 'b.txt'), 'b');\n const count = await copyDir(srcDir, destDir, true);\n expect(count).toBe(2);\n // destDir should not have the files (it may have the dir created)\n expect(fs.existsSync(path.join(destDir, 'a.txt'))).toBe(false);\n });\n\n test('filter function excludes matching files', async () => {\n fs.writeFileSync(path.join(srcDir, 'keep.txt'), 'keep');\n fs.writeFileSync(path.join(srcDir, 'skip.txt'), 'skip');\n const filter = name => name !== 'skip.txt';\n const count = await copyDir(srcDir, destDir, false, filter);\n expect(count).toBe(1);\n expect(fs.existsSync(path.join(destDir, 'keep.txt'))).toBe(true);\n expect(fs.existsSync(path.join(destDir, 'skip.txt'))).toBe(false);\n });\n});\n\ndescribe('dist/utils/fs — countDir', () => {\n let tmpDir;\n\n beforeEach(() => {\n tmpDir = makeTempDir('tk-count-');\n });\n\n afterEach(() => {\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n test('returns 0 for empty directory', async () => {\n expect(await countDir(tmpDir)).toBe(0);\n });\n\n test('counts top-level files', async () => {\n fs.writeFileSync(path.join(tmpDir, 'a.txt'), 'a');\n fs.writeFileSync(path.join(tmpDir, 'b.txt'), 'b');\n expect(await countDir(tmpDir)).toBe(2);\n });\n\n test('counts recursively', async () => {\n fs.mkdirSync(path.join(tmpDir, 'sub'));\n fs.writeFileSync(path.join(tmpDir, 'top.txt'), 'top');\n fs.writeFileSync(path.join(tmpDir, 'sub', 'nested.txt'), 'nested');\n expect(await countDir(tmpDir)).toBe(2);\n });\n});\n\ndescribe('generateIDEBridges', () => {\n let tmpDir;\n let agentDir;\n let mockConsoleLog;\n\n beforeEach(() => {\n tmpDir = makeTempDir('tk-bridge-');\n agentDir = path.join(tmpDir, '.agent');\n fs.mkdirSync(path.join(agentDir, 'rules'), { recursive: true });\n fs.writeFileSync(path.join(agentDir, 'rules', 'GEMINI.md'), '# Rules\\nTest rules.');\n mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => {});\n });\n\n afterEach(() => {\n mockConsoleLog.mockRestore();\n fs.rmSync(tmpDir, { recursive: true, force: true });\n });\n\n test('generates IDE bridge files in target directory', async () => {\n await generateIDEBridges(tmpDir, agentDir, false);\n\n // Check that bridge files were created\n const files = fs.readdirSync(tmpDir);\n const bridgeFiles = files.filter(\n f =>\n f === '.cursorrules' ||\n f === '.windsurfrules' ||\n f === '.gemini' ||\n f === '.github' ||\n f === '.claude',\n );\n expect(bridgeFiles.length).toBeGreaterThan(0);\n });\n});\n"
14
+ }
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "file": "test/unit/inner_loop_validator.test.js",
3
- "hash": "48487187be036a1448dcb2089cb17d01258f964f",
4
3
  "riskScore": "Low",
5
4
  "blastRadius": 0,
6
5
  "imports": {
7
6
  "../../.agent/scripts/inner_loop_validator": []
8
7
  },
9
8
  "dependents": [],
10
- "content": "/**\n * inner_loop_validator.test.js — Unit tests for the Inner-Loop Validator\n * Tests: scanCode, computeVerdict, buildSelfHealingInstructions, validate\n */\n\nconst {\n scanCode,\n computeVerdict,\n buildSelfHealingInstructions,\n validate\n} = require('../../.agent/scripts/inner_loop_validator');\n\ndescribe('inner_loop_validator.js', () => {\n\n // ── scanCode ──────────────────────────────────────────────────────────\n\n describe('scanCode()', () => {\n it('should return empty array for clean code', () => {\n const findings = scanCode('const x = 1;\\nconst y = x + 2;\\n');\n expect(findings).toEqual([]);\n });\n\n it('should detect eval() as high severity', () => {\n const findings = scanCode('const result = eval(userInput);');\n const evalFinding = findings.find(f => f.category === 'Code Injection');\n expect(evalFinding).toBeDefined();\n expect(evalFinding.severity).toBe('high');\n expect(evalFinding.line).toBe(1);\n expect(evalFinding.source).toBe('security_scan');\n });\n\n it('should detect innerHTML as high severity XSS', () => {\n const findings = scanCode('el.innerHTML = userInput;');\n const xssFinding = findings.find(f => f.category === 'XSS');\n expect(xssFinding).toBeDefined();\n expect(xssFinding.severity).toBe('high');\n });\n\n it('should detect hardcoded passwords as critical', () => {\n const findings = scanCode('const password = \"supersecret123\";');\n const secretFinding = findings.find(f => f.category === 'Hardcoded Secret');\n expect(secretFinding).toBeDefined();\n expect(secretFinding.severity).toBe('critical');\n });\n\n it('should detect empty catch blocks as medium severity', () => {\n const findings = scanCode('try { foo(); } catch (e) {}');\n const catchFinding = findings.find(f => f.category === 'Error Handling');\n expect(catchFinding).toBeDefined();\n expect(catchFinding.severity).toBe('medium');\n });\n\n it('should detect throw string as low severity', () => {\n const findings = scanCode('throw \"something went wrong\";');\n const throwFinding = findings.find(f => f.category === 'Error Quality');\n expect(throwFinding).toBeDefined();\n expect(throwFinding.severity).toBe('low');\n });\n\n it('should skip lines that are pure comments', () => {\n const code = '// eval(dangerousInput);\\nconst safe = 1;';\n const findings = scanCode(code);\n // eval is in a comment, should not be flagged\n const evalFinding = findings.find(f => f.category === 'Code Injection');\n expect(evalFinding).toBeUndefined();\n });\n\n it('should report correct line numbers', () => {\n const code = 'const a = 1;\\nconst b = 2;\\nconst r = eval(\"bad\");';\n const findings = scanCode(code);\n const evalFinding = findings.find(f => f.category === 'Code Injection');\n expect(evalFinding).toBeDefined();\n expect(evalFinding.line).toBe(3);\n });\n\n it('should detect VERIFY flags as low/informational', () => {\n const findings = scanCode('const x = someApi(); // VERIFY: does this exist?');\n const verifyFinding = findings.find(f => f.category === 'Verification Flag');\n expect(verifyFinding).toBeDefined();\n expect(verifyFinding.severity).toBe('low');\n });\n\n it('should detect browser globals in Node context', () => {\n const findings = scanCode('const w = window.innerWidth;');\n const browserFinding = findings.find(f => f.category === 'Environment Check');\n expect(browserFinding).toBeDefined();\n });\n });\n\n // ── computeVerdict ────────────────────────────────────────────────────\n\n describe('computeVerdict()', () => {\n it('should return APPROVED for no findings', () => {\n const result = computeVerdict([]);\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n });\n\n it('should return APPROVED for low-only findings', () => {\n const findings = [\n { severity: 'low', category: 'Error Quality', line: 1, message: 'test' }\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n });\n\n it('should return WARNING for medium findings', () => {\n const findings = [\n { severity: 'medium', category: 'Error Handling', line: 1, message: 'test' }\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('WARNING');\n expect(result.passed).toBe(true);\n });\n\n it('should return REJECTED for high findings', () => {\n const findings = [\n { severity: 'high', category: 'Code Injection', line: 1, message: 'test' }\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('REJECTED');\n expect(result.passed).toBe(false);\n });\n\n it('should return REJECTED for critical findings', () => {\n const findings = [\n { severity: 'critical', category: 'Hardcoded Secret', line: 1, message: 'test' }\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('REJECTED');\n expect(result.passed).toBe(false);\n });\n\n it('should use worst severity when mixed findings exist', () => {\n const findings = [\n { severity: 'low', category: 'Info', line: 1, message: 'ok' },\n { severity: 'critical', category: 'Secret', line: 2, message: 'bad' },\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('REJECTED');\n expect(result.passed).toBe(false);\n });\n\n it('should not let Verification Flags block approval', () => {\n const findings = [\n { severity: 'low', category: 'Verification Flag', line: 1, message: 'verify' }\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n });\n });\n\n // ── buildSelfHealingInstructions ───────────────────────────────────────\n\n describe('buildSelfHealingInstructions()', () => {\n it('should return null when no blocking findings', () => {\n const result = buildSelfHealingInstructions([\n { severity: 'low', category: 'Info', line: 1, message: 'test', fix: 'do nothing' }\n ]);\n expect(result).toBeNull();\n });\n\n it('should return null for empty array', () => {\n expect(buildSelfHealingInstructions([])).toBeNull();\n });\n\n it('should generate instructions for critical findings', () => {\n const result = buildSelfHealingInstructions([\n { severity: 'critical', category: 'Hardcoded Secret', line: 5, message: 'password hardcoded', fix: 'Use env vars' }\n ]);\n expect(result).toContain('CRITICAL');\n expect(result).toContain('Line 5');\n expect(result).toContain('Hardcoded Secret');\n expect(result).toContain('Use env vars');\n });\n\n it('should generate instructions for high findings', () => {\n const result = buildSelfHealingInstructions([\n { severity: 'high', category: 'Code Injection', line: 3, message: 'eval used', fix: 'Remove eval' }\n ]);\n expect(result).toContain('HIGH');\n expect(result).toContain('Line 3');\n });\n\n it('should skip medium findings in instructions', () => {\n const result = buildSelfHealingInstructions([\n { severity: 'medium', category: 'Error Handling', line: 1, message: 'empty catch', fix: 'Add handler' }\n ]);\n expect(result).toBeNull();\n });\n });\n\n // ── validate (integration of scan + verdict + healing) ────────────────\n\n describe('validate()', () => {\n it('should return APPROVED for clean code', () => {\n const result = validate('const x = 1;\\nconst y = x + 2;');\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n expect(result.issues).toEqual([]);\n expect(result.self_healing_instructions).toBeNull();\n });\n\n it('should return REJECTED for code with eval', () => {\n const result = validate('const x = eval(input);');\n expect(result.verdict).toBe('REJECTED');\n expect(result.passed).toBe(false);\n expect(result.issues.length).toBeGreaterThan(0);\n expect(result.self_healing_instructions).not.toBeNull();\n });\n\n it('should handle empty/null input gracefully', () => {\n const result = validate('');\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n expect(result.summary).toContain('No code provided');\n });\n\n it('should handle null input', () => {\n const result = validate(null);\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n });\n\n it('should include meta with line count and timestamp', () => {\n const result = validate('const a = 1;\\nconst b = 2;\\nconst c = 3;');\n expect(result.meta).toBeDefined();\n expect(result.meta.lines_scanned).toBe(3);\n expect(result.meta.lang).toBe('js');\n expect(result.meta.timestamp).toBeDefined();\n });\n\n it('should sort issues by severity (critical first)', () => {\n const code = [\n 'throw \"bad\";', // low: Error Quality\n 'const password = \"secret123\";', // critical: Hardcoded Secret\n 'const x = eval(input);', // high: Code Injection\n ].join('\\n');\n const result = validate(code);\n const severities = result.issues.map(i => i.severity);\n const rankOrder = { critical: 0, high: 1, medium: 2, low: 3 };\n for (let i = 1; i < severities.length; i++) {\n expect(rankOrder[severities[i]]).toBeGreaterThanOrEqual(rankOrder[severities[i - 1]]);\n }\n });\n\n it('should generate summary with issue counts', () => {\n const result = validate('const password = \"secret\";');\n expect(result.summary).toBeTruthy();\n expect(result.summary).toContain('critical');\n });\n });\n});\n"
9
+ "content": "/**\n * inner_loop_validator.test.js — Unit tests for the Inner-Loop Validator\n * Tests: scanCode, computeVerdict, buildSelfHealingInstructions, validate\n */\n\nconst {\n scanCode,\n computeVerdict,\n buildSelfHealingInstructions,\n validate,\n} = require('../../.agent/scripts/inner_loop_validator');\n\ndescribe('inner_loop_validator.js', () => {\n // ── scanCode ──────────────────────────────────────────────────────────\n\n describe('scanCode()', () => {\n it('should return empty array for clean code', () => {\n const findings = scanCode('const x = 1;\\nconst y = x + 2;\\n');\n expect(findings).toEqual([]);\n });\n\n it('should detect eval() as high severity', () => {\n const findings = scanCode('const result = eval(userInput);');\n const evalFinding = findings.find(f => f.category === 'Code Injection');\n expect(evalFinding).toBeDefined();\n expect(evalFinding.severity).toBe('high');\n expect(evalFinding.line).toBe(1);\n expect(evalFinding.source).toBe('security_scan');\n });\n\n it('should detect innerHTML as high severity XSS', () => {\n const findings = scanCode('el.innerHTML = userInput;');\n const xssFinding = findings.find(f => f.category === 'XSS');\n expect(xssFinding).toBeDefined();\n expect(xssFinding.severity).toBe('high');\n });\n\n it('should detect hardcoded passwords as critical', () => {\n const findings = scanCode('const password = \"supersecret123\";');\n const secretFinding = findings.find(f => f.category === 'Hardcoded Secret');\n expect(secretFinding).toBeDefined();\n expect(secretFinding.severity).toBe('critical');\n });\n\n it('should detect empty catch blocks as medium severity', () => {\n const findings = scanCode('try { foo(); } catch (e) {}');\n const catchFinding = findings.find(f => f.category === 'Error Handling');\n expect(catchFinding).toBeDefined();\n expect(catchFinding.severity).toBe('medium');\n });\n\n it('should detect throw string as low severity', () => {\n const findings = scanCode('throw \"something went wrong\";');\n const throwFinding = findings.find(f => f.category === 'Error Quality');\n expect(throwFinding).toBeDefined();\n expect(throwFinding.severity).toBe('low');\n });\n\n it('should skip lines that are pure comments', () => {\n const code = '// eval(dangerousInput);\\nconst safe = 1;';\n const findings = scanCode(code);\n // eval is in a comment, should not be flagged\n const evalFinding = findings.find(f => f.category === 'Code Injection');\n expect(evalFinding).toBeUndefined();\n });\n\n it('should report correct line numbers', () => {\n const code = 'const a = 1;\\nconst b = 2;\\nconst r = eval(\"bad\");';\n const findings = scanCode(code);\n const evalFinding = findings.find(f => f.category === 'Code Injection');\n expect(evalFinding).toBeDefined();\n expect(evalFinding.line).toBe(3);\n });\n\n it('should detect VERIFY flags as low/informational', () => {\n const findings = scanCode('const x = someApi(); // VERIFY: does this exist?');\n const verifyFinding = findings.find(f => f.category === 'Verification Flag');\n expect(verifyFinding).toBeDefined();\n expect(verifyFinding.severity).toBe('low');\n });\n\n it('should detect browser globals in Node context', () => {\n const findings = scanCode('const w = window.innerWidth;');\n const browserFinding = findings.find(f => f.category === 'Environment Check');\n expect(browserFinding).toBeDefined();\n });\n });\n\n // ── computeVerdict ────────────────────────────────────────────────────\n\n describe('computeVerdict()', () => {\n it('should return APPROVED for no findings', () => {\n const result = computeVerdict([]);\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n });\n\n it('should return APPROVED for low-only findings', () => {\n const findings = [\n {\n severity: 'low',\n category: 'Error Quality',\n line: 1,\n message: 'test',\n },\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n });\n\n it('should return WARNING for medium findings', () => {\n const findings = [\n {\n severity: 'medium',\n category: 'Error Handling',\n line: 1,\n message: 'test',\n },\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('WARNING');\n expect(result.passed).toBe(true);\n });\n\n it('should return REJECTED for high findings', () => {\n const findings = [\n {\n severity: 'high',\n category: 'Code Injection',\n line: 1,\n message: 'test',\n },\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('REJECTED');\n expect(result.passed).toBe(false);\n });\n\n it('should return REJECTED for critical findings', () => {\n const findings = [\n {\n severity: 'critical',\n category: 'Hardcoded Secret',\n line: 1,\n message: 'test',\n },\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('REJECTED');\n expect(result.passed).toBe(false);\n });\n\n it('should use worst severity when mixed findings exist', () => {\n const findings = [\n { severity: 'low', category: 'Info', line: 1, message: 'ok' },\n { severity: 'critical', category: 'Secret', line: 2, message: 'bad' },\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('REJECTED');\n expect(result.passed).toBe(false);\n });\n\n it('should not let Verification Flags block approval', () => {\n const findings = [\n {\n severity: 'low',\n category: 'Verification Flag',\n line: 1,\n message: 'verify',\n },\n ];\n const result = computeVerdict(findings);\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n });\n });\n\n // ── buildSelfHealingInstructions ───────────────────────────────────────\n\n describe('buildSelfHealingInstructions()', () => {\n it('should return null when no blocking findings', () => {\n const result = buildSelfHealingInstructions([\n {\n severity: 'low',\n category: 'Info',\n line: 1,\n message: 'test',\n fix: 'do nothing',\n },\n ]);\n expect(result).toBeNull();\n });\n\n it('should return null for empty array', () => {\n expect(buildSelfHealingInstructions([])).toBeNull();\n });\n\n it('should generate instructions for critical findings', () => {\n const result = buildSelfHealingInstructions([\n {\n severity: 'critical',\n category: 'Hardcoded Secret',\n line: 5,\n message: 'password hardcoded',\n fix: 'Use env vars',\n },\n ]);\n expect(result).toContain('CRITICAL');\n expect(result).toContain('Line 5');\n expect(result).toContain('Hardcoded Secret');\n expect(result).toContain('Use env vars');\n });\n\n it('should generate instructions for high findings', () => {\n const result = buildSelfHealingInstructions([\n {\n severity: 'high',\n category: 'Code Injection',\n line: 3,\n message: 'eval used',\n fix: 'Remove eval',\n },\n ]);\n expect(result).toContain('HIGH');\n expect(result).toContain('Line 3');\n });\n\n it('should skip medium findings in instructions', () => {\n const result = buildSelfHealingInstructions([\n {\n severity: 'medium',\n category: 'Error Handling',\n line: 1,\n message: 'empty catch',\n fix: 'Add handler',\n },\n ]);\n expect(result).toBeNull();\n });\n });\n\n // ── validate (integration of scan + verdict + healing) ────────────────\n\n describe('validate()', () => {\n it('should return APPROVED for clean code', () => {\n const result = validate('const x = 1;\\nconst y = x + 2;');\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n expect(result.issues).toEqual([]);\n expect(result.self_healing_instructions).toBeNull();\n });\n\n it('should return REJECTED for code with eval', () => {\n const result = validate('const x = eval(input);');\n expect(result.verdict).toBe('REJECTED');\n expect(result.passed).toBe(false);\n expect(result.issues.length).toBeGreaterThan(0);\n expect(result.self_healing_instructions).not.toBeNull();\n });\n\n it('should handle empty/null input gracefully', () => {\n const result = validate('');\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n expect(result.summary).toContain('No code provided');\n });\n\n it('should handle null input', () => {\n const result = validate(null);\n expect(result.verdict).toBe('APPROVED');\n expect(result.passed).toBe(true);\n });\n\n it('should include meta with line count and timestamp', () => {\n const result = validate('const a = 1;\\nconst b = 2;\\nconst c = 3;');\n expect(result.meta).toBeDefined();\n expect(result.meta.lines_scanned).toBe(3);\n expect(result.meta.lang).toBe('js');\n expect(result.meta.timestamp).toBeDefined();\n });\n\n it('should sort issues by severity (critical first)', () => {\n const code = [\n 'throw \"bad\";', // low: Error Quality\n 'const password = \"secret123\";', // critical: Hardcoded Secret\n 'const x = eval(input);', // high: Code Injection\n ].join('\\n');\n const result = validate(code);\n const severities = result.issues.map(i => i.severity);\n const rankOrder = { critical: 0, high: 1, medium: 2, low: 3 };\n for (let i = 1; i < severities.length; i++) {\n expect(rankOrder[severities[i]]).toBeGreaterThanOrEqual(rankOrder[severities[i - 1]]);\n }\n });\n\n it('should generate summary with issue counts', () => {\n const result = validate('const password = \"secret\";');\n expect(result.summary).toBeTruthy();\n expect(result.summary).toContain('critical');\n });\n });\n});\n"
11
10
  }
@@ -0,0 +1,13 @@
1
+ {
2
+ "file": "test/unit/integrity_manifest.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "fs": [],
7
+ "path": [],
8
+ "os": [],
9
+ "../../.agent/scripts/integrity_manifest": []
10
+ },
11
+ "dependents": [],
12
+ "content": "'use strict';\n\nconst fs = require('fs');\nconst path = require('path');\nconst os = require('os');\nconst {\n _generateManifest,\n parseFrontmatter,\n crawlAgents,\n crawlSkills,\n crawlScripts,\n crawlWorkflows,\n extractCrossReferences,\n extractNumericClaims,\n} = require('../../.agent/scripts/integrity_manifest');\n\nfunction makeTempDir(prefix = 'tk-manifest-test-') {\n return fs.mkdtempSync(path.join(os.tmpdir(), prefix));\n}\n\ndescribe('parseFrontmatter', () => {\n test('parses valid frontmatter correctly', () => {\n const input =\n '---\\nname: test-skill\\ndescription: A test skill\\nversion: 1.0.0\\n---\\nBody content here';\n const result = parseFrontmatter(input);\n expect(result.name).toBe('test-skill');\n expect(result.description).toBe('A test skill');\n expect(result.version).toBe('1.0.0');\n });\n\n test('returns empty object if no frontmatter found', () => {\n const input = 'Body content without frontmatter';\n expect(parseFrontmatter(input)).toEqual({});\n });\n});\n\ndescribe('integrity manifest crawlers', () => {\n let tmpProject;\n let agentDir;\n\n beforeEach(() => {\n tmpProject = makeTempDir();\n agentDir = path.join(tmpProject, '.agent');\n fs.mkdirSync(agentDir);\n });\n\n afterEach(() => {\n fs.rmSync(tmpProject, { recursive: true, force: true });\n });\n\n test('crawlAgents extracts and classifies reviewers and specialists', () => {\n const agentsDir = path.join(agentDir, 'agents');\n fs.mkdirSync(agentsDir);\n\n // Write a reviewer agent\n fs.writeFileSync(\n path.join(agentsDir, 'logic-reviewer.md'),\n '---\\nrole: reviewer\\n---\\nReviewer content',\n );\n // Write a specialist agent\n fs.writeFileSync(\n path.join(agentsDir, 'backend-specialist.md'),\n '---\\nrole: specialist\\n---\\nSpecialist content',\n );\n\n const result = crawlAgents(agentDir);\n expect(result.total).toBe(2);\n expect(result.reviewers).toContain('logic-reviewer');\n expect(result.specialists).toContain('backend-specialist');\n expect(result.reviewer_count).toBe(1);\n });\n\n test('crawlSkills extracts skill names from frontmatter name field', () => {\n const skillsDir = path.join(agentDir, 'skills');\n fs.mkdirSync(skillsDir);\n const testSkillDir = path.join(skillsDir, 'test-skill-folder');\n fs.mkdirSync(testSkillDir);\n\n fs.writeFileSync(\n path.join(testSkillDir, 'SKILL.md'),\n '---\\nname: canonical-skill-name\\n---\\nContent',\n );\n\n const result = crawlSkills(agentDir);\n expect(result.total).toBe(1);\n expect(result.names).toContain('canonical-skill-name');\n });\n\n test('crawlScripts extracts script files', () => {\n const scriptsDir = path.join(agentDir, 'scripts');\n fs.mkdirSync(scriptsDir);\n\n fs.writeFileSync(path.join(scriptsDir, 'test_script.js'), \"console.log('test')\");\n fs.writeFileSync(path.join(scriptsDir, 'python_script.py'), \"print('test')\");\n\n const result = crawlScripts(agentDir);\n expect(result.total).toBe(2);\n expect(result.files['test_script.js']).toBe('.agent/scripts/test_script.js');\n expect(result.files['python_script.py']).toBe('.agent/scripts/python_script.py');\n });\n\n test('crawlWorkflows extracts workflow names', () => {\n const workflowsDir = path.join(agentDir, 'workflows');\n fs.mkdirSync(workflowsDir);\n\n fs.writeFileSync(path.join(workflowsDir, 'generate.md'), 'Generate content');\n\n const result = crawlWorkflows(agentDir);\n expect(result.total).toBe(1);\n expect(result.names).toContain('generate');\n });\n});\n\ndescribe('cross-references and claims extraction', () => {\n test('extractCrossReferences finds agent and script references', () => {\n const content = `\n Please refer to agents/logic-reviewer.md and scripts/verify_all.js.\n Also check workflows/generate.md.\n `;\n const refs = extractCrossReferences('test.md', content, '/fake/agent/dir');\n expect(refs.length).toBe(3);\n expect(refs.map(r => r.ref)).toContain('agents/logic-reviewer.md');\n expect(refs.map(r => r.ref)).toContain('scripts/verify_all.js');\n expect(refs.map(r => r.ref)).toContain('workflows/generate.md');\n });\n\n test('extractNumericClaims finds and validates claims in text', () => {\n const content = `\n We run 20 parallel reviewers.\n There are 43 specialist agents in total.\n `;\n const actualCounts = {\n reviewers: 20,\n agents: 44,\n };\n const claims = extractNumericClaims('test.md', content, '/fake/agent/dir', actualCounts);\n expect(claims.length).toBe(2);\n expect(claims.find(c => c.claim.includes('reviewers')).valid).toBe(true);\n expect(claims.find(c => c.claim.includes('agents')).valid).toBe(false);\n });\n\n test('keeps agent totals separate and ignores local small skill groups', () => {\n const content = 'The payload has 44 specialist agents. This category has 3 skills.';\n const claims = extractNumericClaims('test.md', content, '/fake/agent/dir', {\n agents: 44,\n reviewers: 19,\n skills: 168,\n });\n expect(claims).toEqual([\n expect.objectContaining({ claim: '44 agents', actual: 44, valid: true }),\n ]);\n });\n});\n"
13
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "file": "test/unit/learn.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "../../dist/commands/learn": [],
7
+ "../../dist/utils/helpers": []
8
+ },
9
+ "dependents": [],
10
+ "content": "'use strict';\n\nconst { cmdLearn } = require('../../dist/commands/learn');\nconst { getKitAgent: _getKitAgent } = require('../../dist/utils/helpers');\n\ndescribe('cmdLearn command', () => {\n test('exports cmdLearn function', () => {\n expect(typeof cmdLearn).toBe('function');\n });\n\n test('runs without crashing in quiet mode when target dir has no git diff', async () => {\n const flags = { quiet: true, head: false };\n // Should resolve cleanly\n await expect(cmdLearn(flags, true)).resolves.not.toThrow();\n });\n});\n"
11
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "file": "test/unit/marathon.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "fs": [],
7
+ "child_process": [],
8
+ "../../bin/tribunal-kit": [
9
+ "parseArgs",
10
+ "compareSemver",
11
+ "copyDir",
12
+ "countDir",
13
+ "isSelfInstall",
14
+ "CORE_AGENTS",
15
+ "CORE_SKILLS",
16
+ "generateIDEBridges",
17
+ "cmdMarathon"
18
+ ]
19
+ },
20
+ "dependents": [],
21
+ "content": "'use strict';\n\nconst fs = require('fs');\nconst { spawn } = require('child_process');\nconst { cmdMarathon } = require('../../bin/tribunal-kit');\n\njest.mock('fs');\njest.mock('child_process');\n\ndescribe('cmdMarathon', () => {\n let mockExit;\n let mockConsoleError;\n let mockConsoleLog;\n\n beforeEach(() => {\n jest.clearAllMocks();\n mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {});\n mockConsoleError = jest.spyOn(console, 'error').mockImplementation(() => {});\n mockConsoleLog = jest.spyOn(console, 'log').mockImplementation(() => {});\n });\n\n afterEach(() => {\n mockExit.mockRestore();\n mockConsoleError.mockRestore();\n mockConsoleLog.mockRestore();\n });\n\n test('exits if .agent/ is not found', async () => {\n fs.existsSync.mockReturnValue(false);\n const flags = { path: './dummy' };\n\n await cmdMarathon(flags);\n\n expect(fs.existsSync).toHaveBeenCalled();\n expect(mockConsoleError).toHaveBeenCalledWith(expect.stringContaining('.agent/ not found'));\n expect(mockExit).toHaveBeenCalledWith(1);\n });\n\n test('prints help when args are empty', async () => {\n fs.existsSync.mockReturnValue(true);\n const originalArgv = process.argv;\n process.argv = ['node', 'tribunal-kit.js', 'marathon']; // no extra args\n\n const flags = { path: './dummy' };\n await cmdMarathon(flags);\n\n expect(mockConsoleLog).toHaveBeenCalledWith(expect.stringContaining('Marathon'));\n expect(spawn).not.toHaveBeenCalled();\n\n process.argv = originalArgv;\n });\n\n test('executes marathon_harness.js with provided args', async () => {\n fs.existsSync.mockReturnValue(true);\n const originalArgv = process.argv;\n process.argv = ['node', 'tribunal-kit.js', 'marathon', 'status'];\n\n // Mock spawn to simulate a successful child process\n const mockChild = {\n on: jest.fn((event, cb) => {\n if (event === 'close') {\n setImmediate(() => cb(0));\n }\n }),\n };\n spawn.mockReturnValue(mockChild);\n\n const flags = { path: './dummy' };\n await cmdMarathon(flags);\n\n expect(spawn).toHaveBeenCalledWith(\n expect.stringContaining('marathon_harness.js'),\n [],\n expect.objectContaining({ cwd: expect.any(String), shell: true }),\n );\n expect(mockExit).not.toHaveBeenCalled();\n\n process.argv = originalArgv;\n });\n\n test('catches execution errors gracefully and exits 1', async () => {\n fs.existsSync.mockReturnValue(true);\n const originalArgv = process.argv;\n process.argv = ['node', 'tribunal-kit.js', 'marathon', 'status'];\n\n // Mock spawn to simulate a failing child process\n const mockChild = {\n on: jest.fn((event, cb) => {\n if (event === 'close') {\n setImmediate(() => cb(1));\n }\n }),\n };\n spawn.mockReturnValue(mockChild);\n\n const flags = { path: './dummy' };\n await cmdMarathon(flags);\n\n expect(spawn).toHaveBeenCalled();\n expect(mockExit).toHaveBeenCalledWith(1);\n\n process.argv = originalArgv;\n });\n});\n"
22
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "file": "test/unit/mcp_server.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "path": [],
7
+ "fs": [],
8
+ "../../bin/mcp-server": [
9
+ "handleRequest",
10
+ "stripBoilerplate",
11
+ "runTribunalAudit"
12
+ ]
13
+ },
14
+ "dependents": [],
15
+ "content": "'use strict';\n\n// Set environment to test so that mcp-server exports handleRequest and stripBoilerplate\nprocess.env.NODE_ENV = 'test';\n\nconst _path = require('path');\nconst _fs = require('fs');\nconst { handleRequest, stripBoilerplate, runTribunalAudit } = require('../../bin/mcp-server');\n\ndescribe('MCP Server Boilerplate Stripper', () => {\n test('strips standard duplicate boilerplate blocks from text', async () => {\n const rawText = `## Core Rules\n- Rule 1\n- Rule 2\n\nAI coding assistants often fall into specific bad habits when dealing with this domain. These are strictly forbidden:\n1. Over-engineering\n2. Hallucinated libraries\n\n## Pre-Flight Checklist\n- Check files\n- Check imports\n\n## VBC Protocol (Verification-Before-Completion)\nVerify everything.\n`;\n\n const expected = `## Core Rules\n- Rule 1\n- Rule 2`;\n\n expect(stripBoilerplate(rawText)).toBe(expected);\n });\n\n test('handles text without boilerplate gracefully', async () => {\n const rawText = 'Just normal instructions.';\n expect(stripBoilerplate(rawText)).toBe('Just normal instructions.');\n });\n\n test('returns empty string for empty input', async () => {\n expect(stripBoilerplate('')).toBe('');\n expect(stripBoilerplate(null)).toBeNull();\n });\n});\n\ndescribe('MCP Server handleRequest', () => {\n test('handles initialize request correctly', async () => {\n const req = {\n jsonrpc: '2.0',\n id: 1,\n method: 'initialize',\n params: {},\n };\n const result = await handleRequest(req);\n expect(result.protocolVersion).toBe('2025-03-26');\n expect(result.serverInfo.name).toBe('tribunal-kit-mcp');\n });\n\n test('lists tools including get_sparse_context', async () => {\n const req = {\n jsonrpc: '2.0',\n id: 2,\n method: 'tools/list',\n params: {},\n };\n const result = await handleRequest(req);\n expect(result.tools).toBeDefined();\n\n const getSparseContextTool = result.tools.find(t => t.name === 'get_sparse_context');\n expect(getSparseContextTool).toBeDefined();\n expect(getSparseContextTool.inputSchema.required).toContain('task');\n });\n\n test('uses the in-process manifest audit rather than schema validation', async () => {\n const text = runTribunalAudit();\n expect(text).toContain('Tribunal audit complete.');\n expect(text).toMatch(/Agents: \\d+ \\(\\d+ reviewers\\)/);\n\n const result = await handleRequest({\n jsonrpc: '2.0',\n id: 20,\n method: 'tools/call',\n params: { name: 'run_tribunal_audit', arguments: {} },\n });\n expect(result.content[0].text).toContain('Tribunal audit');\n });\n\n test('get_sparse_context throws error if task is missing', async () => {\n const req = {\n jsonrpc: '2.0',\n id: 3,\n method: 'tools/call',\n params: {\n name: 'get_sparse_context',\n arguments: {},\n },\n };\n await expect(handleRequest(req)).rejects.toThrow();\n });\n\n test('get_sparse_context returns sparse context prompt', async () => {\n const req = {\n jsonrpc: '2.0',\n id: 4,\n method: 'tools/call',\n params: {\n name: 'get_sparse_context',\n arguments: {\n task: 'Build JWT authentication API with Hono',\n files: ['src/auth.ts'],\n model: 'large',\n },\n },\n };\n\n // We mock process.cwd or make sure .agent/ exists\n // The test runs from the project root where .agent/ actually exists!\n const result = await handleRequest(req);\n expect(result.content).toBeDefined();\n expect(result.content[0].type).toBe('text');\n\n const text = result.content[0].text;\n expect(text).toContain('Tribunal Context Broker');\n expect(text).toContain('Task: Build JWT authentication API with Hono');\n // Ensure duplicate boilerplate is stripped from the returned prompt\n expect(text).not.toContain('AI coding assistants often fall into specific bad habits');\n });\n\n test('get_tribunal_skill strips boilerplate', async () => {\n const req = {\n jsonrpc: '2.0',\n id: 5,\n method: 'tools/call',\n params: {\n name: 'get_tribunal_skill',\n arguments: {\n name: 'clean-code',\n },\n },\n };\n\n const result = await handleRequest(req);\n expect(result.content).toBeDefined();\n expect(result.content[0].type).toBe('text');\n\n const text = result.content[0].text;\n expect(text).not.toContain('AI coding assistants often fall into specific bad habits');\n expect(text).not.toContain('VBC Protocol');\n });\n});\n"
16
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "file": "test/unit/memory.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "path": [],
7
+ "fs": [],
8
+ "os": [],
9
+ "../../dist/commands/memory": []
10
+ },
11
+ "dependents": [],
12
+ "content": "'use strict';\n\nconst path = require('path');\nconst fs = require('fs');\nconst os = require('os');\n\n/**\n * Tribunal Memory Engine — Unit Tests\n *\n * Tests the JS fallback implementation of the 4-Type Taxonomy Memory Engine.\n * Covers: store validation, recall with budget constraints, garbage collection,\n * scoring priorities, MEMORY.md projection, and anti-hallucination guards.\n */\n\n// ── Test Helpers ────────────────────────────────────────────────────────────\n\nfunction createTempAgent() {\n const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tk-memory-test-'));\n const agentDir = path.join(tmpDir, '.agent');\n const memoryDir = path.join(agentDir, 'history', 'memory');\n fs.mkdirSync(memoryDir, { recursive: true });\n // Seed empty index\n fs.writeFileSync(\n path.join(memoryDir, '.memory.idx'),\n JSON.stringify({ version: 1, entries: [], next_id: 1 }, null, 2),\n 'utf8',\n );\n return agentDir;\n}\n\nfunction cleanupTempAgent(agentDir) {\n const tmpDir = path.dirname(agentDir);\n fs.rmSync(tmpDir, { recursive: true, force: true });\n}\n\n// Import the memory module's internal functions\nconst {\n _memoryStore,\n _memoryRecall,\n _memoryGc,\n _memoryStats,\n _generateProjection,\n} = require('../../dist/commands/memory');\n\n// ── Store Tests ─────────────────────────────────────────────────────────────\n\ndescribe('Memory Store', () => {\n let agentDir;\n\n beforeEach(() => {\n agentDir = createTempAgent();\n });\n\n afterEach(() => {\n cleanupTempAgent(agentDir);\n });\n\n test('stores a semantic memory and returns ID', () => {\n const result = _memoryStore(\n agentDir,\n 'semantic',\n 'Project uses PostgreSQL',\n ['db', 'orm'],\n null,\n );\n expect(result.id).toBe(1);\n expect(result.token_estimate).toBeGreaterThan(0);\n });\n\n test('increments IDs sequentially', () => {\n const r1 = _memoryStore(agentDir, 'semantic', 'First memory', ['test'], null);\n const r2 = _memoryStore(agentDir, 'procedural', 'Second memory', ['test'], null);\n expect(r1.id).toBe(1);\n expect(r2.id).toBe(2);\n });\n\n test('rejects invalid memory type', () => {\n expect(() => {\n _memoryStore(agentDir, 'super_semantic', 'Hallucinated type', [], null);\n }).toThrow(/Invalid memory type/);\n });\n\n test('rejects empty content', () => {\n expect(() => {\n _memoryStore(agentDir, 'semantic', '', [], null);\n }).toThrow(/cannot be empty/);\n });\n\n test('rejects whitespace-only content', () => {\n expect(() => {\n _memoryStore(agentDir, 'semantic', ' ', [], null);\n }).toThrow(/cannot be empty/);\n });\n\n test('accepts all four valid types', () => {\n const types = ['episodic', 'semantic', 'procedural', 'working'];\n for (const type of types) {\n const result = _memoryStore(agentDir, type, `Memory of type ${type}`, [type], null);\n expect(result.id).toBeGreaterThan(0);\n }\n });\n\n test('persists to disk', () => {\n _memoryStore(agentDir, 'semantic', 'Persistent test', ['test'], null);\n const indexPath = path.join(agentDir, 'history', 'memory', '.memory.idx');\n const index = JSON.parse(fs.readFileSync(indexPath, 'utf8'));\n expect(index.entries).toHaveLength(1);\n expect(index.entries[0].content).toBe('Persistent test');\n expect(index.entries[0].memory_type).toBe('semantic');\n });\n\n test('filters empty tags', () => {\n _memoryStore(agentDir, 'semantic', 'Tag test', ['valid', '', ' ', 'also-valid'], null);\n const indexPath = path.join(agentDir, 'history', 'memory', '.memory.idx');\n const index = JSON.parse(fs.readFileSync(indexPath, 'utf8'));\n // Only non-empty tags should be stored\n const tags = index.entries[0].tags;\n expect(tags).not.toContain('');\n });\n});\n\n// ── Recall Tests ────────────────────────────────────────────────────────────\n\ndescribe('Memory Recall', () => {\n let agentDir;\n\n beforeEach(() => {\n agentDir = createTempAgent();\n _memoryStore(agentDir, 'semantic', 'Project uses PostgreSQL database', ['db', 'orm'], null);\n _memoryStore(agentDir, 'procedural', 'Deploy with vercel --prod', ['deploy', 'ci'], null);\n _memoryStore(\n agentDir,\n 'episodic',\n 'User rejected Redux for Zustand',\n ['state', 'decision'],\n null,\n );\n _memoryStore(agentDir, 'working', 'Debugging auth middleware', ['auth', 'debug'], null);\n });\n\n afterEach(() => {\n cleanupTempAgent(agentDir);\n });\n\n test('returns matching entries', () => {\n const { results } = _memoryRecall(agentDir, 'database', 2000);\n expect(results.length).toBeGreaterThan(0);\n expect(results[0].content).toContain('PostgreSQL');\n });\n\n test('returns empty for non-matching query', () => {\n const { results } = _memoryRecall(agentDir, 'kubernetes', 2000);\n expect(results).toHaveLength(0);\n });\n\n test('enforces budget constraint', () => {\n // Budget of 1 token should return 0 or very limited results\n const { tokens_used } = _memoryRecall(agentDir, 'database', 1);\n // Even 1 entry would exceed 1 token budget\n expect(tokens_used).toBeLessThanOrEqual(1);\n });\n\n test('recalls by tag match', () => {\n const { results } = _memoryRecall(agentDir, 'auth', 2000);\n expect(results.length).toBeGreaterThan(0);\n });\n\n test('ranks semantic higher than working', () => {\n // Store two entries with same content but different types\n _memoryStore(agentDir, 'semantic', 'ranking test content alpha', ['rank'], null);\n _memoryStore(agentDir, 'working', 'ranking test content alpha', ['rank'], null);\n\n const { results } = _memoryRecall(agentDir, 'ranking test content alpha', 2000);\n expect(results.length).toBeGreaterThanOrEqual(2);\n // First result should be semantic (higher priority)\n const semanticIdx = results.findIndex(\n r => r.memory_type === 'semantic' && r.content.includes('ranking'),\n );\n const workingIdx = results.findIndex(\n r => r.memory_type === 'working' && r.content.includes('ranking'),\n );\n if (semanticIdx !== -1 && workingIdx !== -1) {\n expect(semanticIdx).toBeLessThan(workingIdx);\n }\n });\n\n test('updates access count on recall', () => {\n _memoryRecall(agentDir, 'database', 2000);\n _memoryRecall(agentDir, 'database', 2000);\n const indexPath = path.join(agentDir, 'history', 'memory', '.memory.idx');\n const index = JSON.parse(fs.readFileSync(indexPath, 'utf8'));\n const dbEntry = index.entries.find(e => e.content.includes('PostgreSQL'));\n expect(dbEntry.access_count).toBeGreaterThanOrEqual(2);\n });\n});\n\n// ── Garbage Collection Tests ────────────────────────────────────────────────\n\ndescribe('Memory GC', () => {\n let agentDir;\n\n beforeEach(() => {\n agentDir = createTempAgent();\n });\n\n afterEach(() => {\n cleanupTempAgent(agentDir);\n });\n\n test('removes all working entries', () => {\n _memoryStore(agentDir, 'working', 'temp1', [], null);\n _memoryStore(agentDir, 'working', 'temp2', [], null);\n _memoryStore(agentDir, 'semantic', 'permanent', [], null);\n\n const result = _memoryGc(agentDir);\n expect(result.working_removed).toBe(2);\n expect(result.after).toBe(1);\n });\n\n test('preserves semantic and procedural entries', () => {\n _memoryStore(agentDir, 'semantic', 'fact', [], null);\n _memoryStore(agentDir, 'procedural', 'recipe', [], null);\n\n const result = _memoryGc(agentDir);\n expect(result.working_removed).toBe(0);\n expect(result.episodic_removed).toBe(0);\n expect(result.after).toBe(2);\n });\n\n test('regenerates MEMORY.md after GC', () => {\n _memoryStore(agentDir, 'working', 'temp', [], null);\n _memoryGc(agentDir);\n\n const projPath = path.join(agentDir, 'history', 'memory', 'MEMORY.md');\n expect(fs.existsSync(projPath)).toBe(true);\n const content = fs.readFileSync(projPath, 'utf8');\n expect(content).toContain('Tribunal Memory Index');\n });\n});\n\n// ── Stats Tests ─────────────────────────────────────────────────────────────\n\ndescribe('Memory Stats', () => {\n let agentDir;\n\n beforeEach(() => {\n agentDir = createTempAgent();\n });\n\n afterEach(() => {\n cleanupTempAgent(agentDir);\n });\n\n test('returns correct counts by type', () => {\n _memoryStore(agentDir, 'semantic', 'fact', [], null);\n _memoryStore(agentDir, 'procedural', 'recipe', [], null);\n _memoryStore(agentDir, 'episodic', 'event', [], null);\n _memoryStore(agentDir, 'working', 'scratch', [], null);\n\n const stats = _memoryStats(agentDir);\n expect(stats.total).toBe(4);\n expect(stats.semantic).toBe(1);\n expect(stats.procedural).toBe(1);\n expect(stats.episodic).toBe(1);\n expect(stats.working).toBe(1);\n expect(stats.capacity).toBe(500);\n });\n\n test('counts total tokens', () => {\n _memoryStore(agentDir, 'semantic', 'This is a memory with some tokens', [], null);\n const stats = _memoryStats(agentDir);\n expect(stats.total_tokens).toBeGreaterThan(0);\n });\n\n test('returns zeros for empty index', () => {\n const stats = _memoryStats(agentDir);\n expect(stats.total).toBe(0);\n expect(stats.total_tokens).toBe(0);\n });\n});\n\n// ── Projection Tests ────────────────────────────────────────────────────────\n\ndescribe('Memory Projection (MEMORY.md)', () => {\n let agentDir;\n\n beforeEach(() => {\n agentDir = createTempAgent();\n });\n\n afterEach(() => {\n cleanupTempAgent(agentDir);\n });\n\n test('generates MEMORY.md with sections for each type', () => {\n _memoryStore(agentDir, 'semantic', 'Uses PostgreSQL', ['db'], null);\n _memoryStore(agentDir, 'procedural', 'Deploy with vercel', ['deploy'], null);\n\n const projPath = _generateProjection(agentDir);\n const content = fs.readFileSync(projPath, 'utf8');\n\n expect(content).toContain('SEMANTIC');\n expect(content).toContain('PROCEDURAL');\n expect(content).toContain('Uses PostgreSQL');\n expect(content).toContain('Deploy with vercel');\n });\n\n test('shows hint for empty index', () => {\n const projPath = _generateProjection(agentDir);\n const content = fs.readFileSync(projPath, 'utf8');\n expect(content).toContain('No memories recorded yet');\n });\n\n test('escapes pipe characters in content', () => {\n _memoryStore(agentDir, 'semantic', 'Use a|b pattern', [], null);\n const projPath = _generateProjection(agentDir);\n const content = fs.readFileSync(projPath, 'utf8');\n // Pipes should be escaped so they don't break the markdown table\n expect(content).toContain('a\\\\|b');\n });\n});\n\n// ── Anti-Hallucination Tests ────────────────────────────────────────────────\n\ndescribe('Anti-Hallucination Guards', () => {\n let agentDir;\n\n beforeEach(() => {\n agentDir = createTempAgent();\n });\n\n afterEach(() => {\n cleanupTempAgent(agentDir);\n });\n\n test(\"rejects hallucinated memory type 'cognitive'\", () => {\n expect(() => _memoryStore(agentDir, 'cognitive', 'test', [], null)).toThrow();\n });\n\n test(\"rejects hallucinated memory type 'long_term'\", () => {\n expect(() => _memoryStore(agentDir, 'long_term', 'test', [], null)).toThrow();\n });\n\n test(\"rejects hallucinated memory type 'super_semantic'\", () => {\n expect(() => _memoryStore(agentDir, 'super_semantic', 'test', [], null)).toThrow();\n });\n\n test('only accepts exactly 4 types', () => {\n const validTypes = ['episodic', 'semantic', 'procedural', 'working'];\n const invalidTypes = [\n 'cognitive',\n 'long_term',\n 'flash',\n 'permanent',\n 'temporary',\n 'cache',\n 'vector',\n ];\n\n for (const type of validTypes) {\n expect(() => _memoryStore(agentDir, type, `Valid ${type}`, [], null)).not.toThrow();\n }\n\n for (const type of invalidTypes) {\n expect(() => _memoryStore(agentDir, type, `Invalid ${type}`, [], null)).toThrow();\n }\n });\n});\n"
13
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "file": "test/unit/minimal_change.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "../../.agent/scripts/minimal_change_engine": []
7
+ },
8
+ "dependents": [],
9
+ "content": "const {\n DECISION_HIERARCHY,\n COMPLEXITY_FLAG_TYPES,\n STRICTNESS_CONFIGS: _STRICTNESS_CONFIGS,\n calculateChangeBudget,\n calculateMinimalityScore,\n detectComplexityFlags: _detectComplexityFlags,\n classifyProposal,\n evaluateMinimalChange,\n} = require('../../.agent/scripts/minimal_change_engine');\n\ndescribe('minimal_change_engine.js', () => {\n describe('Decision Hierarchy Order', () => {\n it('should define exact 7-level evaluation order', () => {\n expect(DECISION_HIERARCHY).toEqual([\n 'NO_CHANGE',\n 'REUSE',\n 'CONFIGURE',\n 'DELETE',\n 'MODIFY',\n 'EXTEND',\n 'CREATE',\n ]);\n });\n });\n\n describe('Complexity Flag Definitions', () => {\n it('should define all 14 standardized complexity flag types', () => {\n expect(COMPLEXITY_FLAG_TYPES).toHaveLength(14);\n expect(COMPLEXITY_FLAG_TYPES).toContain('UNNECESSARY_ABSTRACTION');\n expect(COMPLEXITY_FLAG_TYPES).toContain('DUPLICATE_FUNCTIONALITY');\n expect(COMPLEXITY_FLAG_TYPES).toContain('DEPENDENCY_BLOAT');\n expect(COMPLEXITY_FLAG_TYPES).toContain('FILE_PROLIFERATION');\n });\n });\n\n describe('Change Budget Calculation', () => {\n it('should default footprint values gracefully', () => {\n const budget = calculateChangeBudget();\n expect(budget.files_added).toBe(0);\n expect(budget.files_modified).toBe(0);\n expect(budget.dependencies_added).toBe(0);\n });\n\n it('should aggregate explicit proposal metrics accurately', () => {\n const budget = calculateChangeBudget({\n files_added: 2,\n files_modified: 3,\n dependencies_added: 1,\n new_abstractions: 1,\n estimated_lines_added: 45,\n });\n expect(budget.files_added).toBe(2);\n expect(budget.files_modified).toBe(3);\n expect(budget.dependencies_added).toBe(1);\n expect(budget.new_abstractions).toBe(1);\n });\n });\n\n describe('Minimality Scoring (0-100)', () => {\n it('should award high score for small modification and code reuse', () => {\n const budget = calculateChangeBudget({ files_modified: 1, estimated_lines_added: 10 });\n const score = calculateMinimalityScore(budget, 'MODIFY', []);\n expect(score).toBeGreaterThanOrEqual(90);\n });\n\n it('should penalize file proliferation and unnecessary dependencies', () => {\n const budget = calculateChangeBudget({\n files_added: 5,\n dependencies_added: 2,\n new_abstractions: 3,\n });\n const score = calculateMinimalityScore(budget, 'CREATE', [\n { severity: 'high' },\n { severity: 'high' },\n ]);\n expect(score).toBeLessThan(60);\n });\n });\n\n describe('Classification Engine', () => {\n it('should classify no-op change as NO_CHANGE', () => {\n const classification = classifyProposal(\n 'task',\n { files_added: 0, files_modified: 0, files_deleted: 0 },\n [],\n );\n expect(classification).toBe('NO_CHANGE');\n });\n\n it('should classify existing match as REUSE', () => {\n const classification = classifyProposal('task', { files_added: 0, files_modified: 0 }, [\n { name: 'existingUtil' },\n ]);\n expect(classification).toBe('REUSE');\n });\n\n it('should classify single file tweak as MODIFY', () => {\n const classification = classifyProposal('task', { files_added: 0, files_modified: 1 }, []);\n expect(classification).toBe('MODIFY');\n });\n });\n\n describe('Full Evaluation Pipeline', () => {\n it('should evaluate minimal change proposal and return structured audit', () => {\n const result = evaluateMinimalChange(\n 'add request retry',\n { files_modified: 1, estimated_lines_added: 15 },\n { mode: 'balanced' },\n );\n expect(result).toHaveProperty('minimality_classification');\n expect(result).toHaveProperty('minimality_score');\n expect(result).toHaveProperty('change_budget');\n expect(result).toHaveProperty('complexity_flags');\n expect(result.passed).toBe(true);\n });\n });\n});\n"
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "file": "test/unit/native.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "../../dist/commands/native": []
7
+ },
8
+ "dependents": [],
9
+ "content": "'use strict';\n\nconst {\n cmdMinContext: _cmdMinContext,\n cmdDagSchedule,\n cmdContextCompress: _cmdContextCompress,\n cmdOptimizeStep: _cmdOptimizeStep,\n cmdImpactTier,\n} = require('../../dist/commands/native');\n\ndescribe('native commands JS fallbacks', () => {\n test('cmdDagSchedule computes concurrent execution waves using topological sort', () => {\n const tasksJson = JSON.stringify([\n { id: 'task-1', dependencies: [] },\n { id: 'task-2', dependencies: ['task-1'] },\n { id: 'task-3', dependencies: ['task-1'] },\n { id: 'task-4', dependencies: ['task-2', 'task-3'] },\n ]);\n const processArgs = ['node', 'tk', 'dag-schedule', '--tasks', tasksJson];\n const success = cmdDagSchedule(processArgs, true);\n expect(success).toBe(true);\n });\n\n test('cmdImpactTier correctly classifies impact tier 0 for trivial changes', () => {\n const processArgs = ['node', 'tk', 'impact-tier', '--files', '', '--lines', '2'];\n const success = cmdImpactTier(processArgs, true);\n expect(success).toBe(true);\n });\n\n test('cmdImpactTier elevates to tier 3 for security or auth files', () => {\n const processArgs = [\n 'node',\n 'tk',\n 'impact-tier',\n '--files',\n 'src/auth.ts',\n '--lines',\n '10',\n '--task',\n 'update JWT',\n ];\n const success = cmdImpactTier(processArgs, true);\n expect(success).toBe(true);\n });\n});\n"
10
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "file": "test/unit/optimize.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "../../dist/commands/optimize": [],
7
+ "fs": [],
8
+ "path": [],
9
+ "child_process": []
10
+ },
11
+ "dependents": [],
12
+ "content": "'use strict';\n\nconst { cmdOptimizeSkill } = require('../../dist/commands/optimize');\nconst fs = require('fs');\nconst _path = require('path');\nconst _child_process = require('child_process');\n\njest.mock('fs');\njest.mock('child_process');\n\ndescribe('cmdOptimizeSkill', () => {\n let originalEnv;\n\n beforeEach(() => {\n originalEnv = { ...process.env };\n jest.clearAllMocks();\n });\n\n afterEach(() => {\n process.env = originalEnv;\n });\n\n test('fails immediately if no agent directory is present', async () => {\n fs.existsSync.mockReturnValue(false);\n\n const mockExit = jest.spyOn(process, 'exit').mockImplementation(code => {\n throw new Error(`process.exit called with: ${code}`);\n });\n\n await expect(cmdOptimizeSkill({ target: 'project-idioms' }, [])).rejects.toThrow(\n 'process.exit called with: 1',\n );\n mockExit.mockRestore();\n });\n\n test('fails if no LLM key is found in environment', async () => {\n // Mock agent dir exists\n fs.existsSync.mockImplementation(p => {\n if (p.includes('.agent')) return true;\n return false;\n });\n\n // Clear LLM env keys\n delete process.env.GEMINI_API_KEY;\n delete process.env.OPENAI_API_KEY;\n delete process.env.ANTHROPIC_API_KEY;\n\n const mockExit = jest.spyOn(process, 'exit').mockImplementation(code => {\n throw new Error(`process.exit called with: ${code}`);\n });\n\n await expect(cmdOptimizeSkill({ target: 'project-idioms' }, [])).rejects.toThrow(\n 'process.exit called with: 1',\n );\n mockExit.mockRestore();\n });\n\n test('fails if core binary is not found', async () => {\n // Mock agent dir exists\n fs.existsSync.mockImplementation(p => {\n if (p.includes('.agent')) return true;\n // Core binary does not exist\n return false;\n });\n\n process.env.GEMINI_API_KEY = 'test-key';\n\n const mockExit = jest.spyOn(process, 'exit').mockImplementation(code => {\n throw new Error(`process.exit called with: ${code}`);\n });\n\n await expect(cmdOptimizeSkill({ target: 'project-idioms' }, [])).rejects.toThrow(\n 'process.exit called with: 1',\n );\n mockExit.mockRestore();\n });\n});\n"
13
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "file": "test/unit/path_resolution.test.js",
3
+ "riskScore": "Low",
4
+ "blastRadius": 0,
5
+ "imports": {
6
+ "fs": [],
7
+ "os": [],
8
+ "path": [],
9
+ "child_process": [],
10
+ "../../.agent/scripts/marathon_harness": []
11
+ },
12
+ "dependents": [],
13
+ "content": "'use strict';\n\nconst fs = require('fs');\nconst os = require('os');\nconst path = require('path');\nconst { spawnSync } = require('child_process');\nconst { getMarathonDir } = require('../../.agent/scripts/marathon_harness');\n\ndescribe('agent path resolution', () => {\n test('marathon state resolves from the nearest .agent directory', () => {\n const nestedPath = path.join(process.cwd(), 'test', 'unit', 'nested');\n expect(getMarathonDir(nestedPath)).toBe(\n path.join(process.cwd(), '.agent', 'history', 'marathon'),\n );\n });\n\n test('skill evolution can be imported outside an initialized workspace', () => {\n const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tk-skill-import-'));\n const script = path.resolve(__dirname, '../../.agent/scripts/skill_evolution.js');\n try {\n const result = spawnSync(\n process.execPath,\n [\n '-e',\n \"process.chdir(process.argv[1]); require(process.argv[2]); console.log('loaded')\",\n tempDir,\n script,\n ],\n { encoding: 'utf8' },\n );\n expect(result.status).toBe(0);\n expect(result.stdout).toContain('loaded');\n } finally {\n fs.rmSync(tempDir, { recursive: true, force: true });\n }\n });\n});\n"
14
+ }