knit-mcp 0.6.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.
@@ -0,0 +1,282 @@
1
+ // src/engine/scanner.ts
2
+ import { readFileSync, existsSync } from "fs";
3
+ import { join } from "path";
4
+ import { execSync } from "child_process";
5
+
6
+ // src/engine/agent-registry.ts
7
+ var VOLTAGENT_PINNED_SHA = "6f804f0cfab22fb62668855aa3d62ee3a1453077";
8
+ var VOLTAGENT_REF = process.env.ENGRAM_AGENT_REGISTRY_REF || VOLTAGENT_PINNED_SHA;
9
+ var VOLTAGENT_RAW_BASE = "https://raw.githubusercontent.com/VoltAgent/awesome-claude-code-subagents";
10
+ var AGENT_CATALOG = {
11
+ // 02 — Language specialists
12
+ "typescript-pro": { category: "02-language-specialists" },
13
+ "python-pro": { category: "02-language-specialists" },
14
+ "golang-pro": { category: "02-language-specialists" },
15
+ "rust-engineer": { category: "02-language-specialists" },
16
+ // 03 — Infrastructure
17
+ "security-engineer": { category: "03-infrastructure" },
18
+ "devops-engineer": { category: "03-infrastructure" },
19
+ // 04 — Quality & Security
20
+ "code-reviewer": { category: "04-quality-security" },
21
+ "qa-expert": { category: "04-quality-security" },
22
+ "debugger": { category: "04-quality-security" },
23
+ "architect-reviewer": { category: "04-quality-security" },
24
+ // 06 — Developer Experience
25
+ "build-engineer": { category: "06-developer-experience" }
26
+ };
27
+ var LANG_AGENTS = {
28
+ typescript: ["typescript-pro"],
29
+ javascript: ["typescript-pro"],
30
+ python: ["python-pro"],
31
+ go: ["golang-pro"],
32
+ rust: ["rust-engineer"]
33
+ // Java + others fall through to no lang-specialist; teams still get code-reviewer etc.
34
+ };
35
+ function agentsForRole(role, stack) {
36
+ const langSpecific = LANG_AGENTS[stack] || [];
37
+ let bare;
38
+ switch (role) {
39
+ case "core":
40
+ bare = uniq([...langSpecific, "code-reviewer", "architect-reviewer"]);
41
+ break;
42
+ case "security":
43
+ bare = uniq(["security-engineer", ...langSpecific, "code-reviewer"]);
44
+ break;
45
+ case "qa":
46
+ bare = uniq(["qa-expert", "debugger", "build-engineer"]);
47
+ break;
48
+ default:
49
+ bare = ["code-reviewer"];
50
+ }
51
+ return bare.map((n) => `knit-${n}`);
52
+ }
53
+ function knownAgents() {
54
+ return Object.keys(AGENT_CATALOG);
55
+ }
56
+ function categoryOf(name) {
57
+ return AGENT_CATALOG[name]?.category || null;
58
+ }
59
+ function rawAgentUrl(name, ref = VOLTAGENT_REF) {
60
+ const cat = categoryOf(name);
61
+ if (!cat) return null;
62
+ return `${VOLTAGENT_RAW_BASE}/${ref}/categories/${cat}/${name}.md`;
63
+ }
64
+ var BUNDLED_CORE_AGENTS = [
65
+ "code-reviewer",
66
+ "security-engineer",
67
+ "qa-expert",
68
+ "typescript-pro",
69
+ "python-pro",
70
+ "golang-pro"
71
+ ];
72
+ function isBundledCore(name) {
73
+ return BUNDLED_CORE_AGENTS.includes(name);
74
+ }
75
+ function uniq(arr) {
76
+ return Array.from(new Set(arr));
77
+ }
78
+
79
+ // src/engine/scanner.ts
80
+ function scanProject(rootPath) {
81
+ const stack = detectStack(rootPath);
82
+ return {
83
+ rootPath,
84
+ packageManager: detectPackageManager(rootPath),
85
+ stack,
86
+ domains: detectDomains(rootPath, stack.language),
87
+ hasExistingSetup: existsSync(join(rootPath, ".claude")),
88
+ hasExistingClaudeMd: existsSync(join(rootPath, "CLAUDE.md")),
89
+ git: detectGit(rootPath)
90
+ };
91
+ }
92
+ function detectPackageManager(root) {
93
+ if (existsSync(join(root, "bun.lockb")) || existsSync(join(root, "bun.lock"))) return "bun";
94
+ if (existsSync(join(root, "pnpm-lock.yaml"))) return "pnpm";
95
+ if (existsSync(join(root, "yarn.lock"))) return "yarn";
96
+ if (existsSync(join(root, "package-lock.json"))) return "npm";
97
+ if (existsSync(join(root, "package.json"))) return "npm";
98
+ return "unknown";
99
+ }
100
+ function detectStack(root) {
101
+ const stack = {
102
+ language: "unknown",
103
+ framework: null,
104
+ dependencies: [],
105
+ testFramework: null,
106
+ buildCommand: null,
107
+ lintCommand: null,
108
+ typecheckCommand: null
109
+ };
110
+ const pkgPath = join(root, "package.json");
111
+ if (existsSync(pkgPath)) {
112
+ let pkg;
113
+ try {
114
+ pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
115
+ } catch {
116
+ return stack;
117
+ }
118
+ const allDeps = {
119
+ ...pkg.dependencies,
120
+ ...pkg.devDependencies
121
+ };
122
+ if (allDeps.typescript || existsSync(join(root, "tsconfig.json"))) {
123
+ stack.language = "typescript";
124
+ stack.typecheckCommand = "tsc --noEmit";
125
+ } else {
126
+ stack.language = "javascript";
127
+ }
128
+ if (allDeps.next) stack.framework = "nextjs";
129
+ else if (allDeps.nuxt) stack.framework = "nuxt";
130
+ else if (allDeps.react) stack.framework = "react";
131
+ else if (allDeps.vue) stack.framework = "vue";
132
+ else if (allDeps.svelte || allDeps["@sveltejs/kit"]) stack.framework = "svelte";
133
+ else if (allDeps.express) stack.framework = "express";
134
+ else if (allDeps.fastify) stack.framework = "fastify";
135
+ else if (allDeps.hono) stack.framework = "hono";
136
+ stack.dependencies = Object.keys(allDeps).filter(
137
+ (d) => !d.startsWith("@types/") && !d.startsWith("eslint")
138
+ );
139
+ if (allDeps.vitest) stack.testFramework = "vitest";
140
+ else if (allDeps.jest) stack.testFramework = "jest";
141
+ else if (allDeps.mocha) stack.testFramework = "mocha";
142
+ else if (allDeps.playwright || allDeps["@playwright/test"]) stack.testFramework = "playwright";
143
+ const scripts = pkg.scripts || {};
144
+ if (scripts.build) stack.buildCommand = `${detectPackageManager(root)} run build`;
145
+ if (scripts.lint) stack.lintCommand = `${detectPackageManager(root)} run lint`;
146
+ if (scripts.typecheck) stack.typecheckCommand = `${detectPackageManager(root)} run typecheck`;
147
+ return stack;
148
+ }
149
+ if (existsSync(join(root, "pyproject.toml")) || existsSync(join(root, "requirements.txt"))) {
150
+ stack.language = "python";
151
+ if (existsSync(join(root, "pyproject.toml"))) {
152
+ const pyproject = readFileSync(join(root, "pyproject.toml"), "utf-8");
153
+ if (pyproject.includes("django")) stack.framework = "django";
154
+ else if (pyproject.includes("fastapi")) stack.framework = "fastapi";
155
+ else if (pyproject.includes("flask")) stack.framework = "flask";
156
+ if (pyproject.includes("pytest")) stack.testFramework = "pytest";
157
+ }
158
+ return stack;
159
+ }
160
+ if (existsSync(join(root, "go.mod"))) {
161
+ stack.language = "go";
162
+ stack.testFramework = "go test";
163
+ stack.buildCommand = "go build ./...";
164
+ return stack;
165
+ }
166
+ if (existsSync(join(root, "Cargo.toml"))) {
167
+ stack.language = "rust";
168
+ stack.testFramework = "cargo test";
169
+ stack.buildCommand = "cargo build";
170
+ return stack;
171
+ }
172
+ return stack;
173
+ }
174
+ function detectDomains(root, lang = "unknown") {
175
+ const domains = [];
176
+ const coreAgents = getAgentsForLanguage(lang, "core");
177
+ const securityAgents = getAgentsForLanguage(lang, "security");
178
+ const qaAgents = getAgentsForLanguage(lang, "qa");
179
+ const hasComponents = existsSync(join(root, "src", "components")) || existsSync(join(root, "components")) || existsSync(join(root, "pages")) || existsSync(join(root, "app")) || existsSync(join(root, "templates"));
180
+ if (hasComponents) {
181
+ domains.push({
182
+ name: "UI",
183
+ description: "Frontend components, pages, templates, and user-facing code",
184
+ filePatterns: ["components/**", "app/**/*.tsx", "src/components/**", "pages/**", "templates/**"],
185
+ agents: coreAgents
186
+ });
187
+ }
188
+ const hasApi = existsSync(join(root, "app", "api")) || existsSync(join(root, "src", "api")) || existsSync(join(root, "api")) || existsSync(join(root, "handlers")) || existsSync(join(root, "routes")) || existsSync(join(root, "controllers")) || existsSync(join(root, "cmd"));
189
+ if (hasApi) {
190
+ domains.push({
191
+ name: "API & Security",
192
+ description: "Route handlers, endpoints, authentication, authorization, input validation",
193
+ filePatterns: ["app/api/**", "src/api/**", "api/**", "handlers/**", "routes/**", "controllers/**", "cmd/**"],
194
+ agents: securityAgents
195
+ });
196
+ }
197
+ const hasSrc = existsSync(join(root, "src"));
198
+ const hasLib = existsSync(join(root, "lib"));
199
+ const hasPkg = existsSync(join(root, "pkg"));
200
+ const hasInternal = existsSync(join(root, "internal"));
201
+ if (hasSrc || hasLib || hasPkg || hasInternal) {
202
+ domains.push({
203
+ name: "Core Logic",
204
+ description: "Types, models, business rules, calculations, data transformations",
205
+ filePatterns: ["src/**", "lib/**", "pkg/**", "internal/**", "models/**"],
206
+ agents: coreAgents
207
+ });
208
+ }
209
+ const hasInfra = existsSync(join(root, "prisma")) || existsSync(join(root, "drizzle")) || existsSync(join(root, "migrations")) || existsSync(join(root, "docker-compose.yml")) || existsSync(join(root, "Dockerfile")) || existsSync(join(root, "terraform")) || existsSync(join(root, ".github"));
210
+ if (hasInfra) {
211
+ domains.push({
212
+ name: "Infrastructure",
213
+ description: "Database, migrations, Docker, CI/CD, deployment, external integrations",
214
+ filePatterns: ["prisma/**", "drizzle/**", "migrations/**", "Dockerfile*", "docker-compose*", ".github/**", "terraform/**"],
215
+ agents: ["code-reviewer", "performance-optimizer"]
216
+ });
217
+ }
218
+ const hasTests = existsSync(join(root, "tests")) || existsSync(join(root, "__tests__")) || existsSync(join(root, "test")) || existsSync(join(root, "spec"));
219
+ const hasInlineTests = lang === "go" || lang === "rust";
220
+ if (hasTests || hasInlineTests) {
221
+ domains.push({
222
+ name: "Quality Assurance",
223
+ description: "Tests, test coverage, build configs, CI/CD pipelines",
224
+ filePatterns: ["tests/**", "__tests__/**", "test/**", "spec/**", "**/*_test.go", "**/*.test.*"],
225
+ agents: qaAgents
226
+ });
227
+ }
228
+ if (domains.length === 0) {
229
+ domains.push({
230
+ name: "Core",
231
+ description: "Main application code",
232
+ filePatterns: ["src/**", "lib/**", "app/**", "**/*"],
233
+ agents: coreAgents
234
+ });
235
+ }
236
+ return domains;
237
+ }
238
+ function getAgentsForLanguage(lang, role) {
239
+ return agentsForRole(role, lang);
240
+ }
241
+ function detectGit(root) {
242
+ const isRepo = existsSync(join(root, ".git"));
243
+ let defaultBranch = null;
244
+ let hasRemote = false;
245
+ if (isRepo) {
246
+ try {
247
+ defaultBranch = execSync("git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null", {
248
+ cwd: root,
249
+ encoding: "utf-8"
250
+ }).trim().replace("refs/remotes/origin/", "");
251
+ } catch {
252
+ try {
253
+ execSync("git rev-parse --verify main 2>/dev/null", { cwd: root });
254
+ defaultBranch = "main";
255
+ } catch {
256
+ try {
257
+ execSync("git rev-parse --verify master 2>/dev/null", { cwd: root });
258
+ defaultBranch = "master";
259
+ } catch {
260
+ defaultBranch = "main";
261
+ }
262
+ }
263
+ }
264
+ try {
265
+ const remotes = execSync("git remote", { cwd: root, encoding: "utf-8" }).trim();
266
+ hasRemote = remotes.length > 0;
267
+ } catch {
268
+ hasRemote = false;
269
+ }
270
+ }
271
+ return { isRepo, defaultBranch, hasRemote };
272
+ }
273
+
274
+ export {
275
+ VOLTAGENT_PINNED_SHA,
276
+ VOLTAGENT_REF,
277
+ knownAgents,
278
+ categoryOf,
279
+ rawAgentUrl,
280
+ isBundledCore,
281
+ scanProject
282
+ };