kodelyth-ecc 1.8.0 → 1.8.2

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 (91) hide show
  1. package/CLAUDE.md +1 -1
  2. package/VERSION +1 -1
  3. package/bin/kodelyth-ecc.js +54 -5
  4. package/install.ps1 +30 -13
  5. package/install.sh +11 -4
  6. package/package.json +1 -1
  7. package/rules/common/agent-intent-routing.md +96 -195
  8. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -47
  9. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  10. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -41
  11. package/.github/ISSUE_TEMPLATE/good_first_issue.md +0 -36
  12. package/.github/ISSUE_TEMPLATE/new_agent.md +0 -52
  13. package/.github/PULL_REQUEST_TEMPLATE.md +0 -53
  14. package/.github/workflows/ci.yml +0 -45
  15. package/.github/workflows/publish.yml +0 -60
  16. package/brand/concepts.svg +0 -109
  17. package/brand/convert.js +0 -161
  18. package/brand/favicon.svg +0 -7
  19. package/brand/fb-cover.svg +0 -34
  20. package/brand/fb-profile.svg +0 -21
  21. package/brand/kodelyth-dark.svg +0 -24
  22. package/brand/kodelyth-light.svg +0 -24
  23. package/brand/kodelyth-mark.svg +0 -20
  24. package/brand/package.json +0 -12
  25. package/docs/dashboard.md +0 -211
  26. package/docs/evolve.md +0 -303
  27. package/docs/mcp-clients.md +0 -167
  28. package/docs/mcp.md +0 -178
  29. package/docs/replay.md +0 -244
  30. package/docs/supply-chain.md +0 -207
  31. package/docs/swarm.md +0 -243
  32. package/skills/skill-comply/tests/test_grader.py +0 -137
  33. package/skills/skill-comply/tests/test_parser.py +0 -90
  34. package/social/card-agents.svg +0 -104
  35. package/social/card-install.svg +0 -58
  36. package/social/card-main.svg +0 -54
  37. package/social/facebook-v150.svg +0 -110
  38. package/social/fb-ad-main.svg +0 -128
  39. package/social/fb-post-features.svg +0 -118
  40. package/social/fb-post-launch.svg +0 -144
  41. package/social/fb-post-platforms.svg +0 -135
  42. package/social/github-social-preview.svg +0 -151
  43. package/social/hype-compound-learning.svg +0 -129
  44. package/social/hype-devil-mode.svg +0 -240
  45. package/social/hype-mcp-server.svg +0 -148
  46. package/social/hype-parallel-agents.svg +0 -162
  47. package/social/hype-stats-hero.svg +0 -138
  48. package/social/og-image.svg +0 -151
  49. package/social/readme-agents.svg +0 -138
  50. package/social/readme-hero.svg +0 -102
  51. package/social/section-agents.svg +0 -69
  52. package/social/section-author.svg +0 -66
  53. package/social/section-dashboard.svg +0 -71
  54. package/social/section-devil.svg +0 -66
  55. package/social/section-hooks.svg +0 -63
  56. package/social/section-install.svg +0 -54
  57. package/social/section-learning.svg +0 -64
  58. package/social/section-mcp.svg +0 -58
  59. package/social/section-memory.svg +0 -69
  60. package/social/section-parallel.svg +0 -84
  61. package/social/section-routing.svg +0 -63
  62. package/social/twitter-threads.md +0 -621
  63. package/social/x-card-agents-grid.svg +0 -120
  64. package/social/x-card-free.svg +0 -71
  65. package/social/x-card-hook.svg +0 -94
  66. package/tests/dashboard/data.test.js +0 -391
  67. package/tests/dashboard/server.test.js +0 -257
  68. package/tests/evolve/analyze.test.js +0 -169
  69. package/tests/evolve/proposals.test.js +0 -173
  70. package/tests/evolve/stats.test.js +0 -167
  71. package/tests/hooks/branch-name-check.test.js +0 -184
  72. package/tests/hooks/smart-suggest.test.js +0 -149
  73. package/tests/hooks/test-reminder.test.js +0 -181
  74. package/tests/mcp/catalog.test.js +0 -98
  75. package/tests/mcp/client.test.js +0 -113
  76. package/tests/mcp/resources-prompts.test.js +0 -70
  77. package/tests/mcp/tools.test.js +0 -159
  78. package/tests/memory/auto-recall.test.js +0 -132
  79. package/tests/memory/instincts.test.js +0 -258
  80. package/tests/memory/store.test.js +0 -203
  81. package/tests/replay/bundle.test.js +0 -181
  82. package/tests/replay/replay.test.js +0 -131
  83. package/tests/router/classify.test.js +0 -164
  84. package/tests/run-all.js +0 -55
  85. package/tests/safety/patterns.test.js +0 -94
  86. package/tests/safety/prompt-injection-guard.test.js +0 -111
  87. package/tests/safety/token-budget.test.js +0 -119
  88. package/tests/supply-chain/manifest.test.js +0 -147
  89. package/tests/supply-chain/sbom.test.js +0 -170
  90. package/tests/supply-chain/verify.test.js +0 -146
  91. package/tests/swarm/build-plan.test.js +0 -188
@@ -1,184 +0,0 @@
1
- 'use strict'
2
-
3
- /**
4
- * Tests for scripts/hooks/branch-name-check.js
5
- * Tests the pure logic functions directly.
6
- */
7
-
8
- const assert = require('node:assert/strict')
9
- const { test } = require('node:test')
10
-
11
- // ── Replicate pure functions ───────────────────────────────────────────────
12
-
13
- const VALID_PREFIXES = [
14
- 'feat', 'fix', 'chore', 'docs', 'refactor',
15
- 'test', 'perf', 'ci', 'hotfix', 'release',
16
- 'experiment', 'spike', 'wip',
17
- ]
18
-
19
- const BRANCH_REGEX = new RegExp(
20
- `^(${VALID_PREFIXES.join('|')})\\/[a-z0-9][a-z0-9-._/]{1,60}$`
21
- )
22
-
23
- const PROTECTED_BRANCHES = ['main', 'master', 'develop', 'staging', 'production']
24
-
25
- function isValid(name) {
26
- return BRANCH_REGEX.test(name)
27
- }
28
-
29
- function isProtected(name) {
30
- return PROTECTED_BRANCHES.includes(name)
31
- }
32
-
33
- function extractBranchName(command) {
34
- const checkoutMatch = command.match(/git\s+checkout\s+(?:-b|-B)\s+([^\s]+)/)
35
- if (checkoutMatch) return checkoutMatch[1]
36
-
37
- const switchMatch = command.match(/git\s+switch\s+(?:-c|-C)\s+([^\s]+)/)
38
- if (switchMatch) return switchMatch[1]
39
-
40
- return null
41
- }
42
-
43
- // ── isValid ────────────────────────────────────────────────────────────────
44
-
45
- test('isValid — accepts feat/add-auth', () => {
46
- assert.equal(isValid('feat/add-auth'), true)
47
- })
48
-
49
- test('isValid — accepts fix/null-pointer-checkout', () => {
50
- assert.equal(isValid('fix/null-pointer-checkout'), true)
51
- })
52
-
53
- test('isValid — accepts hotfix/payment-double-charge', () => {
54
- assert.equal(isValid('hotfix/payment-double-charge'), true)
55
- })
56
-
57
- test('isValid — accepts release/2.1.0', () => {
58
- assert.equal(isValid('release/2.1.0'), true)
59
- })
60
-
61
- test('isValid — accepts chore/upgrade-typescript-5', () => {
62
- assert.equal(isValid('chore/upgrade-typescript-5'), true)
63
- })
64
-
65
- test('isValid — accepts wip/experiments', () => {
66
- assert.equal(isValid('wip/experiments'), true)
67
- })
68
-
69
- test('isValid — rejects no-prefix branch', () => {
70
- assert.equal(isValid('my-feature'), false)
71
- })
72
-
73
- test('isValid — rejects uppercase type', () => {
74
- assert.equal(isValid('FEAT/add-auth'), false)
75
- })
76
-
77
- test('isValid — rejects unknown type prefix', () => {
78
- assert.equal(isValid('feature/add-auth'), false)
79
- })
80
-
81
- test('isValid — rejects empty description', () => {
82
- assert.equal(isValid('feat/'), false)
83
- })
84
-
85
- test('isValid — rejects description starting with dash', () => {
86
- assert.equal(isValid('feat/-bad-start'), false)
87
- })
88
-
89
- test('isValid — rejects bare branch name with no slash', () => {
90
- assert.equal(isValid('my-branch'), false)
91
- })
92
-
93
- // ── isProtected ────────────────────────────────────────────────────────────
94
-
95
- test('isProtected — main is protected', () => {
96
- assert.equal(isProtected('main'), true)
97
- })
98
-
99
- test('isProtected — master is protected', () => {
100
- assert.equal(isProtected('master'), true)
101
- })
102
-
103
- test('isProtected — staging is protected', () => {
104
- assert.equal(isProtected('staging'), true)
105
- })
106
-
107
- test('isProtected — feat/something is not protected', () => {
108
- assert.equal(isProtected('feat/something'), false)
109
- })
110
-
111
- // ── extractBranchName ──────────────────────────────────────────────────────
112
-
113
- test('extractBranchName — git checkout -b', () => {
114
- assert.equal(
115
- extractBranchName('git checkout -b feat/add-login'),
116
- 'feat/add-login'
117
- )
118
- })
119
-
120
- test('extractBranchName — git checkout -B', () => {
121
- assert.equal(
122
- extractBranchName('git checkout -B hotfix/urgent-fix'),
123
- 'hotfix/urgent-fix'
124
- )
125
- })
126
-
127
- test('extractBranchName — git switch -c', () => {
128
- assert.equal(
129
- extractBranchName('git switch -c fix/broken-button'),
130
- 'fix/broken-button'
131
- )
132
- })
133
-
134
- test('extractBranchName — git switch -C', () => {
135
- assert.equal(
136
- extractBranchName('git switch -C release/1.2.0'),
137
- 'release/1.2.0'
138
- )
139
- })
140
-
141
- test('extractBranchName — unrelated command returns null', () => {
142
- assert.equal(
143
- extractBranchName('git status'),
144
- null
145
- )
146
- })
147
-
148
- test('extractBranchName — git pull returns null', () => {
149
- assert.equal(
150
- extractBranchName('git pull origin main'),
151
- null
152
- )
153
- })
154
-
155
- test('extractBranchName — git commit returns null', () => {
156
- assert.equal(
157
- extractBranchName('git commit -m "fix: broken auth"'),
158
- null
159
- )
160
- })
161
-
162
- // ── End-to-end logic ──────────────────────────────────────────────────────
163
-
164
- test('should block — extracts invalid branch name from command', () => {
165
- const command = 'git checkout -b my-random-branch'
166
- const name = extractBranchName(command)
167
- assert.ok(name)
168
- assert.equal(isProtected(name), false)
169
- assert.equal(isValid(name), false)
170
- })
171
-
172
- test('should pass — extracts valid branch name from command', () => {
173
- const command = 'git checkout -b feat/user-authentication'
174
- const name = extractBranchName(command)
175
- assert.ok(name)
176
- assert.equal(isValid(name), true)
177
- })
178
-
179
- test('should pass — protected branch always passes', () => {
180
- const command = 'git checkout -b main'
181
- const name = extractBranchName(command)
182
- assert.ok(name)
183
- assert.equal(isProtected(name), true)
184
- })
@@ -1,149 +0,0 @@
1
- 'use strict'
2
-
3
- /**
4
- * Tests for scripts/hooks/smart-suggest.js
5
- * Tests signal detection logic.
6
- */
7
-
8
- const assert = require('node:assert/strict')
9
- const { test } = require('node:test')
10
-
11
- // ── Replicate detection logic ─────────────────────────────────────────────
12
-
13
- const SUGGESTION_RULES = [
14
- {
15
- signals: ['file_path', 'new_string', 'old_string', 'content'],
16
- keywords: ['.ts', '.tsx', '.js', '.py', '.go', '.rs', '.java', '.kt'],
17
- notKeywords: ['test', 'spec', '__test__'],
18
- suggestion: 'use code-reviewer',
19
- reason: 'Code was written — review before committing',
20
- },
21
- {
22
- signals: ['error', 'bug', 'crash', 'exception', 'undefined', 'null', 'failed', 'broken'],
23
- suggestion: 'use debug-detective',
24
- reason: 'A bug or error is present — trace it to root cause',
25
- },
26
- {
27
- signals: ['pull_request', 'gh pr create', 'created pr', 'pull request'],
28
- suggestion: 'use security-reviewer',
29
- reason: 'PR was created — run a security check before merge',
30
- },
31
- {
32
- signals: ['build succeeded', 'compiled successfully', 'build passed', 'tests passed'],
33
- suggestion: 'use e2e-runner',
34
- reason: 'Build passed — verify critical user flows with E2E tests',
35
- },
36
- {
37
- signals: ['route', 'endpoint', 'controller', 'handler', '/api/'],
38
- suggestion: 'use api-guardian',
39
- reason: 'API was modified — check for breaking changes',
40
- },
41
- {
42
- signals: ['i want to', 'i need to', 'how do i', 'help me build', 'implement'],
43
- suggestion: 'use pair-programmer',
44
- reason: 'Planning a feature — think it through before coding',
45
- },
46
- {
47
- signals: ['.tsx', '.jsx', 'component', '<button', '<form', '<input', 'onclick', 'onchange'],
48
- suggestion: 'use ux-reviewer',
49
- reason: 'UI was built — check UX and accessibility',
50
- },
51
- {
52
- signals: ['upgrade', 'migrate', 'version', 'breaking change', 'deprecated'],
53
- suggestion: 'use migration-guide',
54
- reason: 'Migration or upgrade detected — get a phased plan',
55
- },
56
- ]
57
-
58
- function detectSuggestion(content) {
59
- const lower = content.toLowerCase()
60
-
61
- for (const rule of SUGGESTION_RULES) {
62
- const hasSignal = (rule.signals || []).some(s => lower.includes(s.toLowerCase()))
63
- const hasKeyword = !rule.keywords || rule.keywords.some(k => lower.includes(k.toLowerCase()))
64
- const hasNoExclusion = !rule.notKeywords || !rule.notKeywords.some(k => lower.includes(k.toLowerCase()))
65
-
66
- if (hasSignal && hasKeyword && hasNoExclusion) {
67
- return { suggestion: rule.suggestion, reason: rule.reason }
68
- }
69
- }
70
-
71
- return null
72
- }
73
-
74
- // ── detectSuggestion ──────────────────────────────────────────────────────
75
-
76
- test('detects code edit → suggests code-reviewer', () => {
77
- const result = detectSuggestion('file_path: src/auth.ts new_string: ...')
78
- assert.ok(result)
79
- assert.equal(result.suggestion, 'use code-reviewer')
80
- })
81
-
82
- test('detects error → suggests debug-detective', () => {
83
- const result = detectSuggestion('TypeError: Cannot read property of undefined')
84
- assert.ok(result)
85
- assert.equal(result.suggestion, 'use debug-detective')
86
- })
87
-
88
- test('detects crash → suggests debug-detective', () => {
89
- const result = detectSuggestion('app crash on startup')
90
- assert.ok(result)
91
- assert.equal(result.suggestion, 'use debug-detective')
92
- })
93
-
94
- test('detects PR creation → suggests security-reviewer', () => {
95
- const result = detectSuggestion('gh pr create --title "feat: add auth"')
96
- assert.ok(result)
97
- assert.equal(result.suggestion, 'use security-reviewer')
98
- })
99
-
100
- test('detects build passed → suggests e2e-runner', () => {
101
- const result = detectSuggestion('build succeeded — all 24 tests passed')
102
- assert.ok(result)
103
- assert.equal(result.suggestion, 'use e2e-runner')
104
- })
105
-
106
- test('detects API route → suggests api-guardian', () => {
107
- const result = detectSuggestion('created new endpoint /api/users')
108
- assert.ok(result)
109
- assert.equal(result.suggestion, 'use api-guardian')
110
- })
111
-
112
- test('detects feature planning → suggests pair-programmer', () => {
113
- const result = detectSuggestion('i want to build a notification system')
114
- assert.ok(result)
115
- assert.equal(result.suggestion, 'use pair-programmer')
116
- })
117
-
118
- test('detects UI component → suggests ux-reviewer', () => {
119
- const result = detectSuggestion('created a new component with <button onClick...')
120
- assert.ok(result)
121
- assert.equal(result.suggestion, 'use ux-reviewer')
122
- })
123
-
124
- test('detects migration → suggests migration-guide', () => {
125
- const result = detectSuggestion('upgrade from Next.js 13 to 15 — breaking change')
126
- assert.ok(result)
127
- assert.equal(result.suggestion, 'use migration-guide')
128
- })
129
-
130
- test('returns null for unrelated content', () => {
131
- const result = detectSuggestion('hello world nothing special here')
132
- assert.equal(result, null)
133
- })
134
-
135
- test('does not suggest code-reviewer for test file edits', () => {
136
- const result = detectSuggestion('file_path: src/auth.test.ts new_string: ...')
137
- // test keyword exclusion means code-reviewer is skipped
138
- // but another rule might match — we just verify it is NOT code-reviewer
139
- if (result) {
140
- assert.notEqual(result.suggestion, 'use code-reviewer')
141
- }
142
- })
143
-
144
- test('returns first matching rule — error takes priority over API route', () => {
145
- const result = detectSuggestion('error in the route handler: exception thrown')
146
- assert.ok(result)
147
- // error rule comes before route rule in the list
148
- assert.equal(result.suggestion, 'use debug-detective')
149
- })
@@ -1,181 +0,0 @@
1
- 'use strict'
2
-
3
- /**
4
- * Tests for scripts/hooks/test-reminder.js
5
- * Tests the pure logic functions directly without spawning a process.
6
- */
7
-
8
- const assert = require('node:assert/strict')
9
- const { test } = require('node:test')
10
-
11
- // ── Extract pure functions for testing ────────────────────────────────────────
12
-
13
- const TEST_FILE_PATTERNS = [
14
- /\.(test|spec)\.(ts|tsx|js|jsx|mjs)$/,
15
- /\.(test|spec)\.py$/,
16
- /__tests__/,
17
- /\/tests?\//,
18
- /_test\.go$/,
19
- /_spec\.rb$/,
20
- /\.test\.rs$/,
21
- /Test\.java$/,
22
- /Spec\.kt$/,
23
- ]
24
-
25
- const CODE_FILE_PATTERNS = [
26
- /\.(ts|tsx|js|jsx|mjs)$/,
27
- /\.py$/,
28
- /\.go$/,
29
- /\.rs$/,
30
- /\.java$/,
31
- /\.kt$/,
32
- /\.rb$/,
33
- /\.cs$/,
34
- /\.cpp$|\.cc$|\.cxx$/,
35
- ]
36
-
37
- const IGNORED_PATHS = [
38
- /node_modules/,
39
- /\.git\//,
40
- /dist\//,
41
- /build\//,
42
- /coverage\//,
43
- /\.next\//,
44
- /\.nuxt\//,
45
- /migrations\//,
46
- /generated\//,
47
- ]
48
-
49
- function isTestFile(filePath) {
50
- return TEST_FILE_PATTERNS.some(p => p.test(filePath))
51
- }
52
-
53
- function isIgnored(filePath) {
54
- return IGNORED_PATHS.some(p => p.test(filePath))
55
- }
56
-
57
- function isCodeFile(filePath) {
58
- return CODE_FILE_PATTERNS.some(p => p.test(filePath)) && !isTestFile(filePath)
59
- }
60
-
61
- // ── isTestFile ─────────────────────────────────────────────────────────────
62
-
63
- test('isTestFile — detects .test.ts', () => {
64
- assert.equal(isTestFile('src/utils.test.ts'), true)
65
- })
66
-
67
- test('isTestFile — detects .spec.js', () => {
68
- assert.equal(isTestFile('src/api.spec.js'), true)
69
- })
70
-
71
- test('isTestFile — detects __tests__ directory', () => {
72
- assert.equal(isTestFile('src/__tests__/auth.ts'), true)
73
- })
74
-
75
- test('isTestFile — detects /tests/ directory', () => {
76
- assert.equal(isTestFile('tests/hook.test.js'), true)
77
- })
78
-
79
- test('isTestFile — detects _test.go', () => {
80
- assert.equal(isTestFile('server/handler_test.go'), true)
81
- })
82
-
83
- test('isTestFile — detects Test.java', () => {
84
- assert.equal(isTestFile('src/AuthServiceTest.java'), true)
85
- })
86
-
87
- test('isTestFile — detects Spec.kt', () => {
88
- assert.equal(isTestFile('src/UserSpec.kt'), true)
89
- })
90
-
91
- test('isTestFile — does not flag regular .ts file', () => {
92
- assert.equal(isTestFile('src/utils.ts'), false)
93
- })
94
-
95
- test('isTestFile — does not flag regular .py file', () => {
96
- assert.equal(isTestFile('src/service.py'), false)
97
- })
98
-
99
- // ── isCodeFile ─────────────────────────────────────────────────────────────
100
-
101
- test('isCodeFile — detects .ts', () => {
102
- assert.equal(isCodeFile('src/auth.ts'), true)
103
- })
104
-
105
- test('isCodeFile — detects .py', () => {
106
- assert.equal(isCodeFile('app/service.py'), true)
107
- })
108
-
109
- test('isCodeFile — detects .go', () => {
110
- assert.equal(isCodeFile('server/handler.go'), true)
111
- })
112
-
113
- test('isCodeFile — detects .rs', () => {
114
- assert.equal(isCodeFile('src/main.rs'), true)
115
- })
116
-
117
- test('isCodeFile — detects .java', () => {
118
- assert.equal(isCodeFile('src/UserService.java'), true)
119
- })
120
-
121
- test('isCodeFile — does not classify a test file as code', () => {
122
- assert.equal(isCodeFile('src/auth.test.ts'), false)
123
- })
124
-
125
- test('isCodeFile — does not classify .md as code', () => {
126
- assert.equal(isCodeFile('README.md'), false)
127
- })
128
-
129
- test('isCodeFile — does not classify .json as code', () => {
130
- assert.equal(isCodeFile('config.json'), false)
131
- })
132
-
133
- // ── isIgnored ──────────────────────────────────────────────────────────────
134
-
135
- test('isIgnored — ignores node_modules', () => {
136
- assert.equal(isIgnored('node_modules/lodash/index.js'), true)
137
- })
138
-
139
- test('isIgnored — ignores .next build output', () => {
140
- assert.equal(isIgnored('.next/server/app.js'), true)
141
- })
142
-
143
- test('isIgnored — ignores dist/', () => {
144
- assert.equal(isIgnored('dist/bundle.js'), true)
145
- })
146
-
147
- test('isIgnored — ignores generated/', () => {
148
- assert.equal(isIgnored('generated/graphql.ts'), true)
149
- })
150
-
151
- test('isIgnored — does not ignore src/', () => {
152
- assert.equal(isIgnored('src/auth.ts'), false)
153
- })
154
-
155
- // ── Logic: should reminder fire? ───────────────────────────────────────────
156
-
157
- test('reminder fires when code edited with no test touched', () => {
158
- const files = ['src/auth.ts']
159
- const codeFiles = files.filter(f => !isIgnored(f) && isCodeFile(f))
160
- const testFiles = files.filter(f => !isIgnored(f) && isTestFile(f))
161
- assert.equal(codeFiles.length > 0 && testFiles.length === 0, true)
162
- })
163
-
164
- test('reminder does not fire when test file also touched', () => {
165
- const files = ['src/auth.ts', 'src/auth.test.ts']
166
- const codeFiles = files.filter(f => !isIgnored(f) && isCodeFile(f))
167
- const testFiles = files.filter(f => !isIgnored(f) && isTestFile(f))
168
- assert.equal(codeFiles.length > 0 && testFiles.length === 0, false)
169
- })
170
-
171
- test('reminder does not fire when only markdown edited', () => {
172
- const files = ['README.md']
173
- const codeFiles = files.filter(f => !isIgnored(f) && isCodeFile(f))
174
- assert.equal(codeFiles.length, 0)
175
- })
176
-
177
- test('reminder does not fire for ignored paths', () => {
178
- const files = ['node_modules/express/index.js']
179
- const codeFiles = files.filter(f => !isIgnored(f) && isCodeFile(f))
180
- assert.equal(codeFiles.length, 0)
181
- })
@@ -1,98 +0,0 @@
1
- // Tests for scripts/mcp/catalog.js — read-only loader for ECC catalog files.
2
- 'use strict';
3
-
4
- const test = require('node:test');
5
- const assert = require('node:assert/strict');
6
-
7
- const catalog = require('../../scripts/mcp/catalog');
8
-
9
- test('parseFrontmatter parses simple key/value', () => {
10
- const { meta, body } = catalog.parseFrontmatter('---\nname: foo\ndescription: bar\n---\nhello');
11
- assert.equal(meta.name, 'foo');
12
- assert.equal(meta.description, 'bar');
13
- assert.equal(body, 'hello');
14
- });
15
-
16
- test('parseFrontmatter handles folded scalar (>)', () => {
17
- const raw = '---\nname: x\ndescription: >\n line one\n line two\n---\n# body';
18
- const { meta, body } = catalog.parseFrontmatter(raw);
19
- assert.equal(meta.name, 'x');
20
- assert.match(meta.description, /line one/);
21
- assert.match(meta.description, /line two/);
22
- assert.equal(body, '# body');
23
- });
24
-
25
- test('parseFrontmatter handles missing frontmatter', () => {
26
- const { meta, body } = catalog.parseFrontmatter('# just markdown');
27
- assert.deepEqual(meta, {});
28
- assert.equal(body, '# just markdown');
29
- });
30
-
31
- test('loadAgents finds at least one known agent', () => {
32
- const agents = catalog.loadAgents();
33
- assert.ok(Array.isArray(agents));
34
- assert.ok(agents.length >= 60, `expected >=60 agents, got ${agents.length}`);
35
- const dd = agents.find(a => a.name === 'debug-detective');
36
- assert.ok(dd, 'debug-detective agent must be present');
37
- assert.ok(dd.description.length > 0);
38
- assert.ok(dd.body.length > 100);
39
- });
40
-
41
- test('loadAgents includes the new devil-mode crew', () => {
42
- const agents = catalog.loadAgents();
43
- const names = new Set(agents.map(a => a.name));
44
- for (const expected of [
45
- 'prompt-injection-hunter',
46
- 'supply-chain-auditor',
47
- 'jailbreak-tester',
48
- 'backdoor-hunter',
49
- 'chaos-engineer',
50
- ]) {
51
- assert.ok(names.has(expected), `missing devil-mode agent: ${expected}`);
52
- }
53
- });
54
-
55
- test('loadSkills returns a populated list with descriptions', () => {
56
- const skills = catalog.loadSkills();
57
- assert.ok(skills.length >= 100, `expected >=100 skills, got ${skills.length}`);
58
- const ae = skills.find(s => s.name === 'agentic-engineering');
59
- assert.ok(ae, 'agentic-engineering skill must be present');
60
- assert.ok(ae.description.length > 0);
61
- });
62
-
63
- test('loadCommands returns commands and supports findCommand with leading slash', () => {
64
- const cmds = catalog.loadCommands();
65
- assert.ok(cmds.length >= 50);
66
- const found1 = catalog.findCommand('code-review');
67
- const found2 = catalog.findCommand('/code-review');
68
- assert.ok(found1 && found2 && found1.name === found2.name);
69
- });
70
-
71
- test('loadAllRules returns the intent routing rule', () => {
72
- const rules = catalog.loadAllRules();
73
- const r = rules.find(x => x.name === 'agent-intent-routing');
74
- assert.ok(r, 'agent-intent-routing rule must be present');
75
- assert.ok(r.body.length > 500);
76
- });
77
-
78
- test('loadBundles returns the three power bundles', () => {
79
- const bundles = catalog.loadBundles();
80
- const names = new Set(bundles.map(b => b.name));
81
- assert.ok(names.has('indie-hacker'));
82
- assert.ok(names.has('red-team'));
83
- assert.ok(names.has('enterprise'));
84
- });
85
-
86
- test('stats returns expected fields', () => {
87
- const s = catalog.stats();
88
- assert.ok(typeof s.agents === 'number' && s.agents > 0);
89
- assert.ok(typeof s.skills === 'number' && s.skills > 0);
90
- assert.ok(typeof s.commands === 'number' && s.commands > 0);
91
- assert.ok(typeof s.rules === 'number' && s.rules > 0);
92
- assert.ok(typeof s.bundles === 'number');
93
- assert.ok(typeof s.root === 'string');
94
- });
95
-
96
- test('findAgent returns null for unknown name', () => {
97
- assert.equal(catalog.findAgent('this-agent-does-not-exist'), null);
98
- });