kodelyth-ecc 1.5.10 → 1.7.1

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 (273) hide show
  1. package/.github/workflows/publish.yml +19 -0
  2. package/AGENTS.md +1 -1
  3. package/CHANGELOG.md +475 -0
  4. package/CLAUDE.md +48 -16
  5. package/README.md +285 -81
  6. package/VERSION +1 -1
  7. package/actions/ecc-review/README.md +243 -0
  8. package/actions/ecc-review/action.yml +158 -0
  9. package/actions/ecc-review/post-comment.js +171 -0
  10. package/actions/ecc-review/run-review.js +285 -0
  11. package/agents/backdoor-hunter.md +260 -0
  12. package/agents/chaos-engineer.md +251 -0
  13. package/agents/code-stealer-detector.md +228 -0
  14. package/agents/jailbreak-tester.md +222 -0
  15. package/agents/license-violation-finder.md +212 -0
  16. package/agents/prompt-injection-hunter.md +126 -0
  17. package/agents/secret-hunter.md +209 -0
  18. package/agents/supply-chain-auditor.md +195 -0
  19. package/bin/kodelyth-ecc.js +875 -1
  20. package/bundles/enterprise.md +172 -0
  21. package/bundles/indie-hacker.md +106 -0
  22. package/bundles/red-team.md +138 -0
  23. package/cat +0 -0
  24. package/commands/dashboard.md +67 -0
  25. package/commands/devil-mode.md +121 -0
  26. package/commands/memory-evolve.md +71 -0
  27. package/commands/replay.md +61 -0
  28. package/commands/route-model.md +48 -0
  29. package/commands/swarm.md +68 -0
  30. package/commands/verify-supply-chain.md +59 -0
  31. package/docs/dashboard.md +211 -0
  32. package/docs/evolve.md +303 -0
  33. package/docs/mcp-clients.md +167 -0
  34. package/docs/mcp.md +178 -0
  35. package/docs/replay.md +244 -0
  36. package/docs/supply-chain.md +207 -0
  37. package/docs/swarm.md +243 -0
  38. package/hooks/hooks.json +52 -0
  39. package/hooks/memory/auto-recall.js +29 -1
  40. package/hooks/safety/README.md +124 -0
  41. package/hooks/safety/lib/patterns.js +179 -0
  42. package/hooks/safety/prompt-injection-guard.js +179 -0
  43. package/hooks/safety/token-budget.js +229 -0
  44. package/install.ps1 +199 -2
  45. package/install.sh +285 -3
  46. package/package.json +30 -4
  47. package/rules/common/agent-intent-routing.md +182 -0
  48. package/rules/common/cost-aware-model-routing.md +152 -0
  49. package/scripts/dashboard/data.js +353 -0
  50. package/scripts/dashboard/server.js +324 -0
  51. package/scripts/dashboard/static/index.html +645 -0
  52. package/scripts/evolve/analyze.js +303 -0
  53. package/scripts/evolve/proposals.js +162 -0
  54. package/scripts/evolve/stats.js +219 -0
  55. package/scripts/mcp/catalog.js +244 -0
  56. package/scripts/mcp/client.js +181 -0
  57. package/scripts/mcp/prompts.js +133 -0
  58. package/scripts/mcp/resources.js +94 -0
  59. package/scripts/mcp/server.js +153 -0
  60. package/scripts/mcp/tools.js +465 -0
  61. package/scripts/replay/bundle.js +191 -0
  62. package/scripts/replay/replay.js +107 -0
  63. package/scripts/router/classify.js +232 -0
  64. package/scripts/supply-chain/manifest.js +155 -0
  65. package/scripts/supply-chain/sbom.js +202 -0
  66. package/scripts/supply-chain/verify.js +102 -0
  67. package/scripts/swarm/build-plan.js +193 -0
  68. package/skills/cost-aware-model-routing/SKILL.md +153 -0
  69. package/skills/kodelyth-quickstart/SKILL.md +7 -0
  70. package/skills/observability-dashboard/SKILL.md +119 -0
  71. package/skills/self-evolving-memory/SKILL.md +175 -0
  72. package/skills/session-replay/SKILL.md +199 -0
  73. package/skills/supply-chain-verification/SKILL.md +201 -0
  74. package/skills/swarm-orchestrator/SKILL.md +177 -0
  75. package/social/card-install.svg +1 -1
  76. package/social/facebook-group/POST.md +121 -0
  77. package/social/facebook-group/fb-1-3am-debug.png +0 -0
  78. package/social/facebook-group/fb-1-3am-debug.svg +97 -0
  79. package/social/facebook-group/fb-2-cpu-upgrade.png +0 -0
  80. package/social/facebook-group/fb-2-cpu-upgrade.svg +132 -0
  81. package/social/facebook-group/fb-3-before-after.png +0 -0
  82. package/social/facebook-group/fb-3-before-after.svg +94 -0
  83. package/social/facebook-v150.svg +6 -6
  84. package/social/github-social-preview.svg +119 -100
  85. package/social/readme-hero.svg +12 -12
  86. package/social/section-agents.svg +57 -0
  87. package/social/section-author.svg +54 -0
  88. package/social/section-dashboard.svg +59 -0
  89. package/social/section-devil.svg +54 -0
  90. package/social/section-hooks.svg +51 -0
  91. package/social/section-install.svg +42 -0
  92. package/social/section-learning.svg +52 -0
  93. package/social/section-mcp.svg +46 -0
  94. package/social/section-memory.svg +57 -0
  95. package/social/section-parallel.svg +72 -0
  96. package/social/section-routing.svg +51 -0
  97. package/social/x-card-agents-grid.svg +6 -6
  98. package/tests/dashboard/data.test.js +235 -0
  99. package/tests/dashboard/server.test.js +240 -0
  100. package/tests/evolve/analyze.test.js +169 -0
  101. package/tests/evolve/proposals.test.js +173 -0
  102. package/tests/evolve/stats.test.js +167 -0
  103. package/tests/mcp/catalog.test.js +98 -0
  104. package/tests/mcp/client.test.js +113 -0
  105. package/tests/mcp/resources-prompts.test.js +70 -0
  106. package/tests/mcp/tools.test.js +159 -0
  107. package/tests/replay/bundle.test.js +181 -0
  108. package/tests/replay/replay.test.js +131 -0
  109. package/tests/router/classify.test.js +164 -0
  110. package/tests/safety/patterns.test.js +94 -0
  111. package/tests/safety/prompt-injection-guard.test.js +111 -0
  112. package/tests/safety/token-budget.test.js +119 -0
  113. package/tests/supply-chain/manifest.test.js +147 -0
  114. package/tests/supply-chain/sbom.test.js +170 -0
  115. package/tests/supply-chain/verify.test.js +146 -0
  116. package/tests/swarm/build-plan.test.js +188 -0
  117. package/wiki/Agent-Reference.md +58 -7
  118. package/wiki/FAQ.md +204 -7
  119. package/wiki/Home.md +104 -29
  120. package/wiki/Hook-Reference.md +1 -1
  121. package/wiki/Installation-Guide.md +109 -6
  122. package/wiki/Platform-Support.md +189 -72
  123. package/wiki/Skill-Reference.md +101 -6
  124. package/.agent/rules/README.md +0 -108
  125. package/.agent/rules/agents.md +0 -94
  126. package/.agent/rules/code-review.md +0 -124
  127. package/.agent/rules/coding-style.md +0 -122
  128. package/.agent/rules/design-quality.md +0 -63
  129. package/.agent/rules/development-workflow.md +0 -44
  130. package/.agent/rules/git-mastery.md +0 -366
  131. package/.agent/rules/git-workflow.md +0 -24
  132. package/.agent/rules/hooks.md +0 -25
  133. package/.agent/rules/kodelyth-always-on.md +0 -63
  134. package/.agent/rules/kodelyth-quickstart.md +0 -207
  135. package/.agent/rules/observability.md +0 -390
  136. package/.agent/rules/patterns.md +0 -50
  137. package/.agent/rules/performance.md +0 -178
  138. package/.agent/rules/security.md +0 -58
  139. package/.agent/rules/smart-debug.md +0 -241
  140. package/.agent/rules/soul.md +0 -32
  141. package/.agent/rules/testing.md +0 -46
  142. package/.agent/skills/api-guardian.md +0 -279
  143. package/.agent/skills/architect.md +0 -211
  144. package/.agent/skills/build-error-resolver.md +0 -114
  145. package/.agent/skills/chief-of-staff.md +0 -151
  146. package/.agent/skills/code-architect.md +0 -71
  147. package/.agent/skills/code-explorer.md +0 -69
  148. package/.agent/skills/code-reviewer.md +0 -237
  149. package/.agent/skills/code-simplifier.md +0 -47
  150. package/.agent/skills/comment-analyzer.md +0 -45
  151. package/.agent/skills/conversation-analyzer.md +0 -52
  152. package/.agent/skills/cpp-build-resolver.md +0 -90
  153. package/.agent/skills/cpp-reviewer.md +0 -72
  154. package/.agent/skills/csharp-reviewer.md +0 -101
  155. package/.agent/skills/dart-build-resolver.md +0 -201
  156. package/.agent/skills/database-reviewer.md +0 -91
  157. package/.agent/skills/debug-detective.md +0 -409
  158. package/.agent/skills/doc-updater.md +0 -107
  159. package/.agent/skills/docs-lookup.md +0 -68
  160. package/.agent/skills/e2e-runner.md +0 -107
  161. package/.agent/skills/flutter-reviewer.md +0 -243
  162. package/.agent/skills/gan-evaluator.md +0 -209
  163. package/.agent/skills/gan-generator.md +0 -131
  164. package/.agent/skills/gan-planner.md +0 -99
  165. package/.agent/skills/go-build-resolver.md +0 -94
  166. package/.agent/skills/go-reviewer.md +0 -76
  167. package/.agent/skills/harness-optimizer.md +0 -35
  168. package/.agent/skills/healthcare-reviewer.md +0 -83
  169. package/.agent/skills/java-build-resolver.md +0 -153
  170. package/.agent/skills/java-reviewer.md +0 -92
  171. package/.agent/skills/kodelyth-advisor.md +0 -172
  172. package/.agent/skills/kotlin-build-resolver.md +0 -118
  173. package/.agent/skills/kotlin-reviewer.md +0 -159
  174. package/.agent/skills/loop-operator.md +0 -36
  175. package/.agent/skills/migration-guide.md +0 -368
  176. package/.agent/skills/opensource-forker.md +0 -198
  177. package/.agent/skills/opensource-packager.md +0 -249
  178. package/.agent/skills/opensource-sanitizer.md +0 -188
  179. package/.agent/skills/pair-programmer.md +0 -260
  180. package/.agent/skills/performance-optimizer.md +0 -446
  181. package/.agent/skills/planner.md +0 -212
  182. package/.agent/skills/pr-test-analyzer.md +0 -45
  183. package/.agent/skills/python-reviewer.md +0 -98
  184. package/.agent/skills/pytorch-build-resolver.md +0 -120
  185. package/.agent/skills/refactor-cleaner.md +0 -85
  186. package/.agent/skills/rust-build-resolver.md +0 -148
  187. package/.agent/skills/rust-reviewer.md +0 -94
  188. package/.agent/skills/security-reviewer.md +0 -108
  189. package/.agent/skills/seo-specialist.md +0 -62
  190. package/.agent/skills/silent-failure-hunter.md +0 -50
  191. package/.agent/skills/tdd-guide.md +0 -91
  192. package/.agent/skills/type-design-analyzer.md +0 -41
  193. package/.agent/skills/typescript-reviewer.md +0 -112
  194. package/.agent/skills/ux-reviewer.md +0 -483
  195. package/.agent/workflows/agent-sort.md +0 -23
  196. package/.agent/workflows/aside.md +0 -164
  197. package/.agent/workflows/build-fix.md +0 -62
  198. package/.agent/workflows/checkpoint.md +0 -74
  199. package/.agent/workflows/claw.md +0 -23
  200. package/.agent/workflows/code-review.md +0 -289
  201. package/.agent/workflows/context-budget.md +0 -23
  202. package/.agent/workflows/cpp-build.md +0 -173
  203. package/.agent/workflows/cpp-review.md +0 -132
  204. package/.agent/workflows/cpp-test.md +0 -251
  205. package/.agent/workflows/devfleet.md +0 -23
  206. package/.agent/workflows/docs.md +0 -23
  207. package/.agent/workflows/e2e.md +0 -268
  208. package/.agent/workflows/eval.md +0 -23
  209. package/.agent/workflows/evolve.md +0 -178
  210. package/.agent/workflows/feature-dev.md +0 -49
  211. package/.agent/workflows/flutter-build.md +0 -164
  212. package/.agent/workflows/flutter-review.md +0 -116
  213. package/.agent/workflows/flutter-test.md +0 -144
  214. package/.agent/workflows/gan-build.md +0 -99
  215. package/.agent/workflows/gan-design.md +0 -35
  216. package/.agent/workflows/go-build.md +0 -183
  217. package/.agent/workflows/go-review.md +0 -148
  218. package/.agent/workflows/go-test.md +0 -268
  219. package/.agent/workflows/gradle-build.md +0 -70
  220. package/.agent/workflows/harness-audit.md +0 -73
  221. package/.agent/workflows/hookify-configure.md +0 -14
  222. package/.agent/workflows/hookify-help.md +0 -46
  223. package/.agent/workflows/hookify-list.md +0 -21
  224. package/.agent/workflows/hookify.md +0 -50
  225. package/.agent/workflows/instinct-export.md +0 -66
  226. package/.agent/workflows/instinct-import.md +0 -114
  227. package/.agent/workflows/instinct-status.md +0 -59
  228. package/.agent/workflows/jira.md +0 -106
  229. package/.agent/workflows/kotlin-build.md +0 -174
  230. package/.agent/workflows/kotlin-review.md +0 -140
  231. package/.agent/workflows/kotlin-test.md +0 -312
  232. package/.agent/workflows/learn-eval.md +0 -116
  233. package/.agent/workflows/learn.md +0 -70
  234. package/.agent/workflows/loop-start.md +0 -32
  235. package/.agent/workflows/loop-status.md +0 -24
  236. package/.agent/workflows/model-route.md +0 -26
  237. package/.agent/workflows/multi-backend.md +0 -158
  238. package/.agent/workflows/multi-execute.md +0 -315
  239. package/.agent/workflows/multi-frontend.md +0 -158
  240. package/.agent/workflows/multi-plan.md +0 -268
  241. package/.agent/workflows/multi-workflow.md +0 -191
  242. package/.agent/workflows/orchestrate.md +0 -135
  243. package/.agent/workflows/plan.md +0 -117
  244. package/.agent/workflows/pm2.md +0 -272
  245. package/.agent/workflows/projects.md +0 -39
  246. package/.agent/workflows/promote.md +0 -41
  247. package/.agent/workflows/prompt-optimize.md +0 -23
  248. package/.agent/workflows/prp-commit.md +0 -112
  249. package/.agent/workflows/prp-implement.md +0 -385
  250. package/.agent/workflows/prp-plan.md +0 -502
  251. package/.agent/workflows/prp-pr.md +0 -184
  252. package/.agent/workflows/prp-prd.md +0 -447
  253. package/.agent/workflows/prune.md +0 -31
  254. package/.agent/workflows/python-review.md +0 -297
  255. package/.agent/workflows/quality-gate.md +0 -29
  256. package/.agent/workflows/refactor-clean.md +0 -80
  257. package/.agent/workflows/resume-session.md +0 -156
  258. package/.agent/workflows/review-pr.md +0 -37
  259. package/.agent/workflows/rules-distill.md +0 -20
  260. package/.agent/workflows/rust-build.md +0 -187
  261. package/.agent/workflows/rust-review.md +0 -142
  262. package/.agent/workflows/rust-test.md +0 -308
  263. package/.agent/workflows/santa-loop.md +0 -175
  264. package/.agent/workflows/save-session.md +0 -275
  265. package/.agent/workflows/sessions.md +0 -333
  266. package/.agent/workflows/setup-pm.md +0 -80
  267. package/.agent/workflows/skill-create.md +0 -174
  268. package/.agent/workflows/skill-health.md +0 -54
  269. package/.agent/workflows/tdd.md +0 -231
  270. package/.agent/workflows/test-coverage.md +0 -69
  271. package/.agent/workflows/update-codemaps.md +0 -72
  272. package/.agent/workflows/update-docs.md +0 -84
  273. package/.agent/workflows/verify.md +0 -23
@@ -0,0 +1,153 @@
1
+ #!/usr/bin/env node
2
+ // =============================================================================
3
+ // Kodelyth ECC — MCP Server
4
+ // Universal adapter that exposes ECC agents, skills, commands, rules, and
5
+ // the local memory store to any MCP-compatible client (Claude Desktop,
6
+ // LangGraph, AutoGen, CrewAI, OpenAI Agents SDK, etc.).
7
+ //
8
+ // Transport: stdio (JSON-RPC over stdin/stdout).
9
+ // Run via:
10
+ // npx kodelyth-ecc mcp
11
+ // node scripts/mcp/server.js
12
+ // =============================================================================
13
+
14
+ 'use strict';
15
+
16
+ const path = require('path');
17
+
18
+ const TOOLS = require('./tools');
19
+ const PROMPTS = require('./prompts');
20
+ const RESOURCES = require('./resources');
21
+ const catalog = require('./catalog');
22
+
23
+ // ── Lazy SDK load with friendly install hint ─────────────────────────────────
24
+ function loadSdk() {
25
+ try {
26
+ return {
27
+ Server: require('@modelcontextprotocol/sdk/server/index.js').Server,
28
+ StdioServerTransport: require('@modelcontextprotocol/sdk/server/stdio.js').StdioServerTransport,
29
+ schemas: require('@modelcontextprotocol/sdk/types.js'),
30
+ };
31
+ } catch (e) {
32
+ process.stderr.write(
33
+ '\n❌ Kodelyth MCP server requires `@modelcontextprotocol/sdk`.\n' +
34
+ ' Install it once with:\n\n' +
35
+ ' npm install @modelcontextprotocol/sdk\n\n' +
36
+ ' Or, if you ran via npx, the dependency should auto-install on next run.\n' +
37
+ ` Underlying error: ${e.message}\n\n`
38
+ );
39
+ process.exit(1);
40
+ }
41
+ }
42
+
43
+ // ── Read package metadata for server identity ────────────────────────────────
44
+ function loadIdentity() {
45
+ try {
46
+ const pkg = require(path.join(catalog.ROOT, 'package.json'));
47
+ return { name: 'kodelyth-ecc', version: pkg.version || '0.0.0' };
48
+ } catch {
49
+ return { name: 'kodelyth-ecc', version: '0.0.0' };
50
+ }
51
+ }
52
+
53
+ // ── Main ─────────────────────────────────────────────────────────────────────
54
+ async function main() {
55
+ const sdk = loadSdk();
56
+ const { Server, StdioServerTransport, schemas } = sdk;
57
+ const {
58
+ ListToolsRequestSchema,
59
+ CallToolRequestSchema,
60
+ ListResourcesRequestSchema,
61
+ ReadResourceRequestSchema,
62
+ ListPromptsRequestSchema,
63
+ GetPromptRequestSchema,
64
+ } = schemas;
65
+
66
+ const identity = loadIdentity();
67
+
68
+ const server = new Server(
69
+ { name: identity.name, version: identity.version },
70
+ {
71
+ capabilities: {
72
+ tools: {},
73
+ resources: {},
74
+ prompts: {},
75
+ },
76
+ }
77
+ );
78
+
79
+ // ── Tools ──────────────────────────────────────────────────────────────────
80
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
81
+ tools: TOOLS.TOOL_DEFINITIONS.map(t => ({
82
+ name: t.name,
83
+ description: t.description,
84
+ inputSchema: t.inputSchema,
85
+ })),
86
+ }));
87
+
88
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
89
+ const { name, arguments: args } = req.params || {};
90
+ const handler = TOOLS.TOOL_HANDLERS[name];
91
+ if (!handler) {
92
+ return {
93
+ content: [{ type: 'text', text: `Unknown tool: ${name}` }],
94
+ isError: true,
95
+ };
96
+ }
97
+ try {
98
+ return handler(args || {});
99
+ } catch (e) {
100
+ return {
101
+ content: [{ type: 'text', text: `Tool ${name} failed: ${e.message}` }],
102
+ isError: true,
103
+ };
104
+ }
105
+ });
106
+
107
+ // ── Resources ──────────────────────────────────────────────────────────────
108
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
109
+ resources: RESOURCES.buildList(),
110
+ }));
111
+
112
+ server.setRequestHandler(ReadResourceRequestSchema, async (req) => {
113
+ const uri = req.params && req.params.uri;
114
+ return RESOURCES.readResource(uri);
115
+ });
116
+
117
+ // ── Prompts ────────────────────────────────────────────────────────────────
118
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
119
+ prompts: PROMPTS.PROMPT_DEFINITIONS.map(p => ({
120
+ name: p.name,
121
+ description: p.description,
122
+ arguments: p.arguments || [],
123
+ })),
124
+ }));
125
+
126
+ server.setRequestHandler(GetPromptRequestSchema, async (req) => {
127
+ const name = req.params && req.params.name;
128
+ const handler = PROMPTS.PROMPT_HANDLERS[name];
129
+ if (!handler) throw new Error(`Unknown prompt: ${name}`);
130
+ return handler(req.params.arguments || {});
131
+ });
132
+
133
+ // ── Boot ───────────────────────────────────────────────────────────────────
134
+ const transport = new StdioServerTransport();
135
+ await server.connect(transport);
136
+
137
+ // Stay alive until stdio closes.
138
+ process.stderr.write(
139
+ `[kodelyth-mcp] ready · ${identity.version} · ` +
140
+ `${TOOLS.TOOL_DEFINITIONS.length} tools · ` +
141
+ `${PROMPTS.PROMPT_DEFINITIONS.length} prompts · ` +
142
+ `${RESOURCES.buildList().length} resources\n`
143
+ );
144
+ }
145
+
146
+ if (require.main === module) {
147
+ main().catch(e => {
148
+ process.stderr.write(`[kodelyth-mcp] fatal: ${e.stack || e.message}\n`);
149
+ process.exit(1);
150
+ });
151
+ }
152
+
153
+ module.exports = { main };
@@ -0,0 +1,465 @@
1
+ // =============================================================================
2
+ // Kodelyth ECC — MCP Tools
3
+ // Pure-function tool handlers. No SDK imports here so we can unit-test directly.
4
+ // Each handler: (args) => { content: [{type, text}], isError?: bool }
5
+ // =============================================================================
6
+
7
+ 'use strict';
8
+
9
+ const path = require('path');
10
+ const catalog = require('./catalog');
11
+ const memoryStore = require('../memory/store');
12
+
13
+ // ── Token utilities (shared with route_intent) ───────────────────────────────
14
+ const STOP = new Set([
15
+ 'the','a','an','and','or','but','if','then','else','for','to','of','in','on',
16
+ 'is','are','was','were','be','been','being','have','has','had','do','does',
17
+ 'did','will','would','should','can','could','may','might','must','this','that',
18
+ 'these','those','it','its','as','at','by','from','with','about','i','you','we',
19
+ 'they','he','she','my','your','our','their','use','using','used','help','need',
20
+ 'want','make','please','some','any','all','very','really','just','also','here',
21
+ 'there','when','what','which','who','how','why','code','codebase','project',
22
+ ]);
23
+
24
+ function tokens(text) {
25
+ return String(text || '')
26
+ .toLowerCase()
27
+ .replace(/[^a-z0-9_\-/.\s]/g, ' ')
28
+ .split(/\s+/)
29
+ .filter(t => t.length >= 2 && t.length <= 40 && !STOP.has(t));
30
+ }
31
+
32
+ function jaccard(a, b) {
33
+ if (!a.size || !b.size) return 0;
34
+ let inter = 0;
35
+ for (const x of a) if (b.has(x)) inter++;
36
+ const union = a.size + b.size - inter;
37
+ return union ? inter / union : 0;
38
+ }
39
+
40
+ // ── Wrap result text in MCP content shape ────────────────────────────────────
41
+ function ok(text) { return { content: [{ type: 'text', text }] }; }
42
+ function err(text) { return { content: [{ type: 'text', text }], isError: true }; }
43
+
44
+ function toJSON(obj) { return ok(JSON.stringify(obj, null, 2)); }
45
+
46
+ // =============================================================================
47
+ // TOOL: route_intent
48
+ // =============================================================================
49
+ // Suggests the best ECC agent for a user message. Uses simple token overlap
50
+ // against agent name + description. This is intentionally thin — the heavy
51
+ // routing rule lives in rules/common/agent-intent-routing.md (also exposed
52
+ // as an MCP prompt). Clients should layer their own LLM judgment on top.
53
+ function tool_route_intent({ message, top_k = 3 } = {}) {
54
+ if (!message || typeof message !== 'string') {
55
+ return err('route_intent: `message` is required (string).');
56
+ }
57
+ const k = Math.max(1, Math.min(20, Number(top_k) || 3));
58
+
59
+ const queryTokens = new Set(tokens(message));
60
+ if (queryTokens.size === 0) {
61
+ return err('route_intent: message had no usable tokens after normalization.');
62
+ }
63
+
64
+ const agents = catalog.loadAgents();
65
+ const scored = agents.map(a => {
66
+ const haystack = `${a.name} ${a.description}`;
67
+ const aTokens = new Set(tokens(haystack));
68
+ // Boost name-match heavily — direct mention of an agent name should dominate.
69
+ const nameTokens = new Set(tokens(a.name));
70
+ const nameHits = [...queryTokens].filter(t => nameTokens.has(t)).length;
71
+ const score = jaccard(queryTokens, aTokens) + nameHits * 0.5;
72
+ return { agent: a.name, score, description: a.description.slice(0, 200) };
73
+ })
74
+ .filter(r => r.score > 0)
75
+ .sort((a, b) => b.score - a.score)
76
+ .slice(0, k);
77
+
78
+ return toJSON({
79
+ message,
80
+ suggestions: scored,
81
+ routing_rule: 'rules/common/agent-intent-routing.md',
82
+ note: 'These are token-overlap suggestions only. Read the full routing rule (exposed as MCP prompt `routing-rule`) for tier-based intent matching.',
83
+ });
84
+ }
85
+
86
+ // =============================================================================
87
+ // TOOL: list_agents / list_skills / list_commands / list_rules / list_bundles
88
+ // =============================================================================
89
+ function tool_list_agents() {
90
+ const agents = catalog.loadAgents().map(a => ({
91
+ name: a.name,
92
+ description: a.description,
93
+ relpath: a.relpath,
94
+ }));
95
+ return toJSON({ count: agents.length, agents });
96
+ }
97
+
98
+ function tool_list_skills() {
99
+ const skills = catalog.loadSkills().map(s => ({
100
+ name: s.name,
101
+ description: s.description,
102
+ origin: s.origin,
103
+ relpath: s.relpath,
104
+ }));
105
+ return toJSON({ count: skills.length, skills });
106
+ }
107
+
108
+ function tool_list_commands() {
109
+ const cmds = catalog.loadCommands().map(c => ({
110
+ name: c.name,
111
+ description: c.description,
112
+ argument_hint: c.argumentHint,
113
+ relpath: c.relpath,
114
+ }));
115
+ return toJSON({ count: cmds.length, commands: cmds });
116
+ }
117
+
118
+ function tool_list_rules() {
119
+ const rules = catalog.loadAllRules().map(r => ({
120
+ name: r.name,
121
+ relpath: r.relpath,
122
+ bytes: r.body.length,
123
+ }));
124
+ return toJSON({ count: rules.length, rules });
125
+ }
126
+
127
+ function tool_list_bundles() {
128
+ const bundles = catalog.loadBundles().map(b => ({
129
+ name: b.name,
130
+ relpath: b.relpath,
131
+ bytes: b.body.length,
132
+ }));
133
+ return toJSON({ count: bundles.length, bundles });
134
+ }
135
+
136
+ // =============================================================================
137
+ // TOOL: get_agent / get_skill / get_command / get_rule / get_bundle
138
+ // =============================================================================
139
+ function tool_get_agent({ name } = {}) {
140
+ if (!name) return err('get_agent: `name` is required.');
141
+ const a = catalog.findAgent(name);
142
+ if (!a) return err(`get_agent: agent "${name}" not found.`);
143
+ return ok(`# Agent: ${a.name}\n\n**Description:** ${a.description}\n\n---\n\n${a.body}`);
144
+ }
145
+
146
+ function tool_get_skill({ name } = {}) {
147
+ if (!name) return err('get_skill: `name` is required.');
148
+ const s = catalog.findSkill(name);
149
+ if (!s) return err(`get_skill: skill "${name}" not found.`);
150
+ return ok(`# Skill: ${s.name}\n\n**Description:** ${s.description}\n\n---\n\n${s.body}`);
151
+ }
152
+
153
+ function tool_get_command({ name } = {}) {
154
+ if (!name) return err('get_command: `name` is required.');
155
+ const c = catalog.findCommand(name);
156
+ if (!c) return err(`get_command: command "${name}" not found.`);
157
+ const hint = c.argumentHint ? `\n**Arguments:** ${c.argumentHint}` : '';
158
+ return ok(`# Command: /${c.name}\n\n**Description:** ${c.description}${hint}\n\n---\n\n${c.body}`);
159
+ }
160
+
161
+ function tool_get_rule({ name } = {}) {
162
+ if (!name) return err('get_rule: `name` is required.');
163
+ const r = catalog.loadRule(name);
164
+ if (!r) return err(`get_rule: rule "${name}" not found.`);
165
+ return ok(r.body);
166
+ }
167
+
168
+ function tool_get_bundle({ name } = {}) {
169
+ if (!name) return err('get_bundle: `name` is required.');
170
+ const b = catalog.loadBundles().find(x => x.name === name);
171
+ if (!b) return err(`get_bundle: bundle "${name}" not found.`);
172
+ return ok(b.body);
173
+ }
174
+
175
+ // =============================================================================
176
+ // TOOL: recall_memory
177
+ // =============================================================================
178
+ function tool_recall_memory({ query, project_root, limit = 5 } = {}) {
179
+ if (!query || typeof query !== 'string') {
180
+ return err('recall_memory: `query` is required (string).');
181
+ }
182
+ const lim = Math.max(1, Math.min(50, Number(limit) || 5));
183
+ let results;
184
+ if (project_root) {
185
+ results = memoryStore.recallForProject(project_root, query, { limit: lim });
186
+ } else {
187
+ results = memoryStore.recall(query, { limit: lim });
188
+ }
189
+ return toJSON({
190
+ query,
191
+ project_root: project_root || null,
192
+ count: results.length,
193
+ memories: results.map(m => ({
194
+ id: m.id,
195
+ problem: m.problem,
196
+ approach: m.approach,
197
+ tags: m.tags || [],
198
+ language: m.language || null,
199
+ project_path: m.project_path || null,
200
+ score: m.score || 0,
201
+ captured_at: m.captured_at || null,
202
+ })),
203
+ });
204
+ }
205
+
206
+ // =============================================================================
207
+ // TOOL: capture_memory
208
+ // =============================================================================
209
+ function tool_capture_memory({
210
+ problem, approach, tags = [], language = null, project_root = null,
211
+ files = [], gotchas = [],
212
+ } = {}) {
213
+ if (!problem || !approach) {
214
+ return err('capture_memory: both `problem` and `approach` are required.');
215
+ }
216
+ try {
217
+ const memory = memoryStore.capture({
218
+ problem,
219
+ approach,
220
+ tags: Array.isArray(tags) ? tags : String(tags).split(',').map(t => t.trim()).filter(Boolean),
221
+ language: language || null,
222
+ project: project_root || null,
223
+ files: Array.isArray(files) ? files : [],
224
+ gotchas: Array.isArray(gotchas) ? gotchas : [],
225
+ source: 'mcp',
226
+ });
227
+ return toJSON({
228
+ ok: true,
229
+ id: memory.id,
230
+ problem: memory.problem,
231
+ captured_at: memory.captured_at,
232
+ });
233
+ } catch (e) {
234
+ return err(`capture_memory: ${e.message}`);
235
+ }
236
+ }
237
+
238
+ // =============================================================================
239
+ // TOOL: memory_stats
240
+ // =============================================================================
241
+ function tool_memory_stats() {
242
+ try {
243
+ return toJSON(memoryStore.stats());
244
+ } catch (e) {
245
+ return err(`memory_stats: ${e.message}`);
246
+ }
247
+ }
248
+
249
+ // =============================================================================
250
+ // TOOL: catalog_stats
251
+ // =============================================================================
252
+ function tool_catalog_stats() {
253
+ return toJSON(catalog.stats());
254
+ }
255
+
256
+ // =============================================================================
257
+ // TOOL: audit_skill_match
258
+ // =============================================================================
259
+ // Given a description of work, surface skills whose description or body
260
+ // overlaps. Useful for clients deciding which skills to attach as context.
261
+ function tool_audit_skill_match({ task, top_k = 5 } = {}) {
262
+ if (!task || typeof task !== 'string') {
263
+ return err('audit_skill_match: `task` is required (string).');
264
+ }
265
+ const k = Math.max(1, Math.min(20, Number(top_k) || 5));
266
+ const queryTokens = new Set(tokens(task));
267
+ if (queryTokens.size === 0) {
268
+ return err('audit_skill_match: task had no usable tokens after normalization.');
269
+ }
270
+ const skills = catalog.loadSkills();
271
+ const scored = skills.map(s => {
272
+ // Cap body slice to keep matching cheap on large skill files.
273
+ const haystack = `${s.name} ${s.description} ${s.body.slice(0, 2000)}`;
274
+ const sTokens = new Set(tokens(haystack));
275
+ const score = jaccard(queryTokens, sTokens);
276
+ return { skill: s.name, score, description: s.description.slice(0, 200) };
277
+ })
278
+ .filter(r => r.score > 0)
279
+ .sort((a, b) => b.score - a.score)
280
+ .slice(0, k);
281
+
282
+ return toJSON({ task, suggestions: scored });
283
+ }
284
+
285
+ // =============================================================================
286
+ // Tool definitions (exported for SDK registration)
287
+ // =============================================================================
288
+ const TOOL_DEFINITIONS = [
289
+ {
290
+ name: 'route_intent',
291
+ description: 'Suggest the best ECC agent for a user message via token-overlap scoring. Read the full routing rule via the `routing-rule` MCP prompt for tier-based intent matching.',
292
+ inputSchema: {
293
+ type: 'object',
294
+ properties: {
295
+ message: { type: 'string', description: 'The user message or task description.' },
296
+ top_k: { type: 'integer', description: 'How many agents to return (1-20, default 3).', default: 3 },
297
+ },
298
+ required: ['message'],
299
+ },
300
+ handler: tool_route_intent,
301
+ },
302
+ {
303
+ name: 'list_agents',
304
+ description: 'List all ECC agents (name, description, relative path).',
305
+ inputSchema: { type: 'object', properties: {} },
306
+ handler: tool_list_agents,
307
+ },
308
+ {
309
+ name: 'list_skills',
310
+ description: 'List all ECC skills (name, description, origin, relative path).',
311
+ inputSchema: { type: 'object', properties: {} },
312
+ handler: tool_list_skills,
313
+ },
314
+ {
315
+ name: 'list_commands',
316
+ description: 'List all ECC slash commands.',
317
+ inputSchema: { type: 'object', properties: {} },
318
+ handler: tool_list_commands,
319
+ },
320
+ {
321
+ name: 'list_rules',
322
+ description: 'List all ECC rule files in rules/common/.',
323
+ inputSchema: { type: 'object', properties: {} },
324
+ handler: tool_list_rules,
325
+ },
326
+ {
327
+ name: 'list_bundles',
328
+ description: 'List ECC power bundles (indie-hacker, red-team, enterprise).',
329
+ inputSchema: { type: 'object', properties: {} },
330
+ handler: tool_list_bundles,
331
+ },
332
+ {
333
+ name: 'get_agent',
334
+ description: 'Fetch the full markdown body of a single agent.',
335
+ inputSchema: {
336
+ type: 'object',
337
+ properties: { name: { type: 'string', description: 'Agent name (e.g. "debug-detective").' } },
338
+ required: ['name'],
339
+ },
340
+ handler: tool_get_agent,
341
+ },
342
+ {
343
+ name: 'get_skill',
344
+ description: 'Fetch the full markdown body of a single skill.',
345
+ inputSchema: {
346
+ type: 'object',
347
+ properties: { name: { type: 'string', description: 'Skill name (e.g. "agentic-engineering").' } },
348
+ required: ['name'],
349
+ },
350
+ handler: tool_get_skill,
351
+ },
352
+ {
353
+ name: 'get_command',
354
+ description: 'Fetch the full markdown body of a single slash command.',
355
+ inputSchema: {
356
+ type: 'object',
357
+ properties: { name: { type: 'string', description: 'Command name with or without leading slash (e.g. "code-review" or "/code-review").' } },
358
+ required: ['name'],
359
+ },
360
+ handler: tool_get_command,
361
+ },
362
+ {
363
+ name: 'get_rule',
364
+ description: 'Fetch the full markdown body of a single ECC rule file.',
365
+ inputSchema: {
366
+ type: 'object',
367
+ properties: { name: { type: 'string', description: 'Rule name (e.g. "agent-intent-routing").' } },
368
+ required: ['name'],
369
+ },
370
+ handler: tool_get_rule,
371
+ },
372
+ {
373
+ name: 'get_bundle',
374
+ description: 'Fetch the cheat sheet for a power bundle (indie-hacker, red-team, enterprise).',
375
+ inputSchema: {
376
+ type: 'object',
377
+ properties: { name: { type: 'string', description: 'Bundle name (e.g. "indie-hacker").' } },
378
+ required: ['name'],
379
+ },
380
+ handler: tool_get_bundle,
381
+ },
382
+ {
383
+ name: 'recall_memory',
384
+ description: 'BM25 search across the local Kodelyth memory store. Returns top-N matching memories.',
385
+ inputSchema: {
386
+ type: 'object',
387
+ properties: {
388
+ query: { type: 'string', description: 'Search query.' },
389
+ project_root: { type: 'string', description: 'Optional absolute path to scope to a project first.' },
390
+ limit: { type: 'integer', description: 'Max results (1-50, default 5).', default: 5 },
391
+ },
392
+ required: ['query'],
393
+ },
394
+ handler: tool_recall_memory,
395
+ },
396
+ {
397
+ name: 'capture_memory',
398
+ description: 'Append a new memory entry to the local Kodelyth memory store.',
399
+ inputSchema: {
400
+ type: 'object',
401
+ properties: {
402
+ problem: { type: 'string', description: 'Short problem description.' },
403
+ approach: { type: 'string', description: 'How the problem was solved or the lesson learned.' },
404
+ tags: { type: 'array', items: { type: 'string' }, description: 'Optional tags.' },
405
+ language: { type: 'string', description: 'Optional primary language (e.g. "typescript").' },
406
+ project_root: { type: 'string', description: 'Optional project absolute path.' },
407
+ files: { type: 'array', items: { type: 'string' }, description: 'Optional related files.' },
408
+ gotchas: { type: 'array', items: { type: 'string' }, description: 'Optional gotchas.' },
409
+ },
410
+ required: ['problem', 'approach'],
411
+ },
412
+ handler: tool_capture_memory,
413
+ },
414
+ {
415
+ name: 'memory_stats',
416
+ description: 'Summary of the local Kodelyth memory store (counts, projects, etc.).',
417
+ inputSchema: { type: 'object', properties: {} },
418
+ handler: tool_memory_stats,
419
+ },
420
+ {
421
+ name: 'catalog_stats',
422
+ description: 'Summary of how many ECC agents, skills, commands, rules, and bundles are loaded.',
423
+ inputSchema: { type: 'object', properties: {} },
424
+ handler: tool_catalog_stats,
425
+ },
426
+ {
427
+ name: 'audit_skill_match',
428
+ description: 'Suggest ECC skills whose description/body overlap a task. Use to decide which skills to attach as context.',
429
+ inputSchema: {
430
+ type: 'object',
431
+ properties: {
432
+ task: { type: 'string', description: 'The task or work description.' },
433
+ top_k: { type: 'integer', description: 'Max skills to return (1-20, default 5).', default: 5 },
434
+ },
435
+ required: ['task'],
436
+ },
437
+ handler: tool_audit_skill_match,
438
+ },
439
+ ];
440
+
441
+ const TOOL_HANDLERS = Object.fromEntries(
442
+ TOOL_DEFINITIONS.map(t => [t.name, t.handler])
443
+ );
444
+
445
+ module.exports = {
446
+ TOOL_DEFINITIONS,
447
+ TOOL_HANDLERS,
448
+ // Exposed individually for tests:
449
+ tool_route_intent,
450
+ tool_list_agents,
451
+ tool_list_skills,
452
+ tool_list_commands,
453
+ tool_list_rules,
454
+ tool_list_bundles,
455
+ tool_get_agent,
456
+ tool_get_skill,
457
+ tool_get_command,
458
+ tool_get_rule,
459
+ tool_get_bundle,
460
+ tool_recall_memory,
461
+ tool_capture_memory,
462
+ tool_memory_stats,
463
+ tool_catalog_stats,
464
+ tool_audit_skill_match,
465
+ };