@theihtisham/agent-shadow-brain 5.0.0 → 5.0.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.
@@ -0,0 +1,660 @@
1
+ // src/brain/auto-setup.ts — Zero-Config Auto-Setup Engine
2
+ // Detects project type, AI tools, languages, frameworks on install
3
+ // v5.0.1 — Infinite Intelligence Edition
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ /** AI tool config file patterns to detect */
7
+ const AI_TOOL_PATTERNS = [
8
+ { name: 'Claude Code', files: ['.claude/CLAUDE.md', '.claude/settings.json'], tool: 'claude-code' },
9
+ { name: 'Cursor', files: ['.cursorrules', '.cursor/rules', '.cursor/mcp.json'], tool: 'cursor' },
10
+ { name: 'Kilo Code', files: ['.kilocode/rules', '.kilocode/settings.json'], tool: 'kilo-code' },
11
+ { name: 'Cline', files: ['.clinerules', '.cline/settings.json'], tool: 'cline' },
12
+ { name: 'OpenCode', files: ['.opencode/rules', '.opencode/settings.json'], tool: 'opencode' },
13
+ { name: 'Codex', files: ['.codex/config.json', '.codex/rules'], tool: 'codex' },
14
+ { name: 'Aider', files: ['.aider.conf.yml', '.aiderignore'], tool: 'aider' },
15
+ { name: 'Windsurf', files: ['.windsurfrules', '.windsurf/rules'], tool: 'windsurf' },
16
+ ];
17
+ /** Framework detection from package.json dependencies */
18
+ const FRAMEWORK_DETECTORS = {
19
+ 'react': (d) => 'react' in d,
20
+ 'next.js': (d) => 'next' in d,
21
+ 'vue': (d) => 'vue' in d,
22
+ 'nuxt': (d) => 'nuxt' in d,
23
+ 'angular': (d) => '@angular/core' in d,
24
+ 'svelte': (d) => 'svelte' in d,
25
+ 'astro': (d) => 'astro' in d,
26
+ 'express': (d) => 'express' in d,
27
+ 'nestjs': (d) => '@nestjs/core' in d,
28
+ 'fastify': (d) => 'fastify' in d,
29
+ 'django': () => false, // detected by file existence
30
+ 'flask': () => false,
31
+ 'fastapi': () => false,
32
+ 'spring-boot': () => false,
33
+ 'rails': () => false,
34
+ 'gin': () => false,
35
+ 'actix': () => false,
36
+ 'flutter': (d) => 'flutter' in d || 'flutter_test' in d,
37
+ 'react-native': (d) => 'react-native' in d,
38
+ 'expo': (d) => 'expo' in d,
39
+ 'electron': (d) => 'electron' in d,
40
+ 'tauri': () => false,
41
+ 'three.js': (d) => 'three' in d,
42
+ 'unity': () => false,
43
+ 'unreal': () => false,
44
+ };
45
+ /** Language detection from file extensions */
46
+ const LANGUAGE_EXTENSIONS = {
47
+ 'typescript': ['.ts', '.tsx', '.mts', '.cts'],
48
+ 'javascript': ['.js', '.jsx', '.mjs', '.cjs'],
49
+ 'python': ['.py', '.pyi', '.ipynb'],
50
+ 'rust': ['.rs'],
51
+ 'go': ['.go'],
52
+ 'java': ['.java'],
53
+ 'kotlin': ['.kt', '.kts'],
54
+ 'swift': ['.swift'],
55
+ 'c#': ['.cs'],
56
+ 'c++': ['.cpp', '.cxx', '.cc', '.hpp'],
57
+ 'c': ['.c', '.h'],
58
+ 'ruby': ['.rb'],
59
+ 'php': ['.php'],
60
+ 'dart': ['.dart'],
61
+ 'scala': ['.scala'],
62
+ 'elixir': ['.ex', '.exs'],
63
+ 'haskell': ['.hs'],
64
+ 'lua': ['.lua'],
65
+ 'shell': ['.sh', '.bash', '.zsh'],
66
+ 'sql': ['.sql'],
67
+ 'html': ['.html', '.htm'],
68
+ 'css': ['.css', '.scss', '.sass', '.less'],
69
+ 'vue': ['.vue'],
70
+ 'svelte': ['.svelte'],
71
+ };
72
+ /** Test framework detection */
73
+ const TEST_DETECTORS = {
74
+ 'vitest': (d) => 'vitest' in d,
75
+ 'jest': (d) => 'jest' in d || '@jest/core' in d,
76
+ 'mocha': (d) => 'mocha' in d,
77
+ 'pytest': () => false,
78
+ 'unittest': () => false,
79
+ 'cypress': (d) => 'cypress' in d,
80
+ 'playwright': (d) => '@playwright/test' in d || 'playwright' in d,
81
+ 'testing-library': (d) => '@testing-library/react' in d || '@testing-library/vue' in d,
82
+ 'ava': (d) => 'ava' in d,
83
+ 'tap': (d) => 'tap' in d,
84
+ 'jasmine': (d) => 'jasmine' in d,
85
+ 'karma': (d) => 'karma' in d,
86
+ 'pytorch-test': () => false,
87
+ 'junit': () => false,
88
+ };
89
+ /** CI/CD detection */
90
+ const CICD_FILES = {
91
+ 'github-actions': ['.github/workflows'],
92
+ 'gitlab-ci': ['.gitlab-ci.yml'],
93
+ 'jenkins': ['Jenkinsfile'],
94
+ 'circleci': ['.circleci/config.yml'],
95
+ 'travis-ci': ['.travis.yml'],
96
+ 'azure-pipelines': ['azure-pipelines.yml', '.azure-pipelines.yml'],
97
+ 'bitbucket-pipelines': ['bitbucket-pipelines.yml'],
98
+ 'drone': ['.drone.yml'],
99
+ };
100
+ /**
101
+ * AutoSetup — zero-configuration project detection engine.
102
+ *
103
+ * Scans the project directory on install and automatically:
104
+ * 1. Detects languages, frameworks, build tools
105
+ * 2. Discovers AI coding tools already configured
106
+ * 3. Identifies test frameworks, linters, formatters
107
+ * 4. Checks for CI/CD, Git, Docker
108
+ * 5. Generates optimal BrainConfig with sensible defaults
109
+ * 6. Installs MCP integration for detected AI tools
110
+ */
111
+ export class AutoSetup {
112
+ constructor(projectDir) {
113
+ this.projectDir = projectDir || process.cwd();
114
+ }
115
+ /**
116
+ * Run full auto-detection and configuration.
117
+ * Call this from postinstall or `shadow-brain start`.
118
+ */
119
+ async detect() {
120
+ const languages = this.detectLanguages();
121
+ const packageJson = this.readPackageJson();
122
+ const deps = this.extractDependencies(packageJson);
123
+ const frameworks = this.detectFrameworks(deps);
124
+ const buildTools = this.detectBuildTools(deps, packageJson);
125
+ const packageManager = this.detectPackageManager();
126
+ const aiTools = this.detectAITools();
127
+ const testFrameworks = this.detectTestFrameworks(deps);
128
+ const linters = this.detectLinters();
129
+ const formatters = this.detectFormatters(deps);
130
+ const cicd = this.detectCICD();
131
+ const hasGit = this.detectGit();
132
+ const hasDocker = this.detectDocker();
133
+ const config = this.generateConfig({
134
+ languages,
135
+ frameworks,
136
+ buildTools,
137
+ packageManager,
138
+ aiTools,
139
+ testFrameworks,
140
+ hasGit,
141
+ hasDocker,
142
+ });
143
+ return {
144
+ projectDir: this.projectDir,
145
+ projectName: this.getProjectName(packageJson),
146
+ projectType: this.inferProjectType(frameworks, languages),
147
+ languages,
148
+ frameworks,
149
+ buildTools,
150
+ packageManager,
151
+ aiTools,
152
+ testFrameworks,
153
+ linters,
154
+ formatters,
155
+ cicd,
156
+ hasGit,
157
+ hasDocker,
158
+ config,
159
+ timestamp: new Date(),
160
+ };
161
+ }
162
+ /**
163
+ * Install MCP integration files for detected AI tools.
164
+ */
165
+ async installMCPForTools(result) {
166
+ const installed = [];
167
+ for (const tool of result.aiTools) {
168
+ if (!tool.detected)
169
+ continue;
170
+ try {
171
+ switch (tool.name) {
172
+ case 'Claude Code':
173
+ await this.installClaudeMCP();
174
+ installed.push('Claude Code');
175
+ break;
176
+ case 'Cursor':
177
+ await this.installCursorMCP();
178
+ installed.push('Cursor');
179
+ break;
180
+ case 'Kilo Code':
181
+ await this.installKiloCodeMCP();
182
+ installed.push('Kilo Code');
183
+ break;
184
+ case 'Cline':
185
+ await this.installClineMCP();
186
+ installed.push('Cline');
187
+ break;
188
+ }
189
+ }
190
+ catch {
191
+ // Non-blocking — tool may not support MCP injection
192
+ }
193
+ }
194
+ return installed;
195
+ }
196
+ /**
197
+ * Install git hooks for pre-commit/pre-push.
198
+ */
199
+ async installGitHooks() {
200
+ const gitDir = path.join(this.projectDir, '.git');
201
+ if (!fs.existsSync(gitDir))
202
+ return false;
203
+ const hooksDir = path.join(gitDir, 'hooks');
204
+ if (!fs.existsSync(hooksDir)) {
205
+ fs.mkdirSync(hooksDir, { recursive: true });
206
+ }
207
+ // Pre-commit hook
208
+ const preCommit = path.join(hooksDir, 'pre-commit');
209
+ const preCommitContent = `#!/bin/sh
210
+ # Agent Shadow Brain — Auto-installed pre-commit hook
211
+ npx shadow-brain scan --staged 2>/dev/null || true
212
+ `;
213
+ fs.writeFileSync(preCommit, preCommitContent, { mode: 0o755 });
214
+ // Pre-push hook
215
+ const prePush = path.join(hooksDir, 'pre-push');
216
+ const prePushContent = `#!/bin/sh
217
+ # Agent Shadow Brain — Auto-installed pre-push hook
218
+ npx shadow-brain health 2>/dev/null || true
219
+ `;
220
+ fs.writeFileSync(prePush, prePushContent, { mode: 0o755 });
221
+ return true;
222
+ }
223
+ /**
224
+ * Generate a summary string for CLI display.
225
+ */
226
+ getSummary(result) {
227
+ const lines = [];
228
+ lines.push(` Project: ${result.projectName} (${result.projectType})`);
229
+ lines.push(` Languages: ${result.languages.join(', ') || 'none detected'}`);
230
+ lines.push(` Frameworks: ${result.frameworks.join(', ') || 'none detected'}`);
231
+ lines.push(` Package Manager: ${result.packageManager}`);
232
+ lines.push(` Build Tools: ${result.buildTools.join(', ') || 'none detected'}`);
233
+ lines.push(` Test Frameworks: ${result.testFrameworks.join(', ') || 'none detected'}`);
234
+ lines.push(` AI Tools: ${result.aiTools.filter(t => t.detected).map(t => t.name).join(', ') || 'none detected'}`);
235
+ lines.push(` CI/CD: ${result.cicd.join(', ') || 'none detected'}`);
236
+ lines.push(` Git: ${result.hasGit ? 'yes' : 'no'}`);
237
+ lines.push(` Docker: ${result.hasDocker ? 'yes' : 'no'}`);
238
+ return lines.join('\n');
239
+ }
240
+ // ── Detection Methods ──────────────────────────────────────────────────────
241
+ detectLanguages() {
242
+ const detected = new Set();
243
+ const extensions = new Set();
244
+ const walkDir = (dir, depth = 0) => {
245
+ if (depth > 3)
246
+ return;
247
+ try {
248
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
249
+ for (const entry of entries) {
250
+ if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'dist' || entry.name === 'build' || entry.name === '.git')
251
+ continue;
252
+ const fullPath = path.join(dir, entry.name);
253
+ if (entry.isDirectory()) {
254
+ walkDir(fullPath, depth + 1);
255
+ }
256
+ else {
257
+ const ext = path.extname(entry.name).toLowerCase();
258
+ if (ext)
259
+ extensions.add(ext);
260
+ }
261
+ }
262
+ }
263
+ catch {
264
+ // Permission denied, skip
265
+ }
266
+ };
267
+ walkDir(this.projectDir);
268
+ for (const [language, exts] of Object.entries(LANGUAGE_EXTENSIONS)) {
269
+ for (const ext of exts) {
270
+ if (extensions.has(ext)) {
271
+ detected.add(language);
272
+ break;
273
+ }
274
+ }
275
+ }
276
+ return Array.from(detected);
277
+ }
278
+ readPackageJson() {
279
+ const pkgPath = path.join(this.projectDir, 'package.json');
280
+ if (!fs.existsSync(pkgPath))
281
+ return null;
282
+ try {
283
+ return JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
284
+ }
285
+ catch {
286
+ return null;
287
+ }
288
+ }
289
+ extractDependencies(pkg) {
290
+ if (!pkg)
291
+ return {};
292
+ const deps = {};
293
+ const depFields = ['dependencies', 'devDependencies', 'peerDependencies', 'optionalDependencies'];
294
+ for (const field of depFields) {
295
+ const fieldDeps = pkg[field];
296
+ if (fieldDeps && typeof fieldDeps === 'object') {
297
+ Object.assign(deps, fieldDeps);
298
+ }
299
+ }
300
+ return deps;
301
+ }
302
+ detectFrameworks(deps) {
303
+ const detected = [];
304
+ for (const [framework, detector] of Object.entries(FRAMEWORK_DETECTORS)) {
305
+ try {
306
+ if (detector(deps)) {
307
+ detected.push(framework);
308
+ }
309
+ }
310
+ catch {
311
+ // Skip
312
+ }
313
+ }
314
+ // File-based framework detection
315
+ if (fs.existsSync(path.join(this.projectDir, 'manage.py')))
316
+ detected.push('django');
317
+ if (fs.existsSync(path.join(this.projectDir, 'app.py')) || fs.existsSync(path.join(this.projectDir, 'requirements.txt'))) {
318
+ if (fs.existsSync(path.join(this.projectDir, 'requirements.txt'))) {
319
+ const reqs = fs.readFileSync(path.join(this.projectDir, 'requirements.txt'), 'utf-8').toLowerCase();
320
+ if (reqs.includes('flask'))
321
+ detected.push('flask');
322
+ if (reqs.includes('fastapi'))
323
+ detected.push('fastapi');
324
+ }
325
+ }
326
+ if (fs.existsSync(path.join(this.projectDir, 'pom.xml')))
327
+ detected.push('spring-boot');
328
+ if (fs.existsSync(path.join(this.projectDir, 'Gemfile')))
329
+ detected.push('rails');
330
+ if (fs.existsSync(path.join(this.projectDir, 'go.mod')))
331
+ detected.push('gin');
332
+ if (fs.existsSync(path.join(this.projectDir, 'Cargo.toml')))
333
+ detected.push('actix');
334
+ if (fs.existsSync(path.join(this.projectDir, 'src-tauri')))
335
+ detected.push('tauri');
336
+ if (fs.existsSync(path.join(this.projectDir, 'Assets')) && fs.existsSync(path.join(this.projectDir, 'ProjectSettings')))
337
+ detected.push('unity');
338
+ return [...new Set(detected)];
339
+ }
340
+ detectBuildTools(deps, pkg) {
341
+ const detected = new Set();
342
+ if (deps['webpack'])
343
+ detected.add('webpack');
344
+ if (deps['vite'])
345
+ detected.add('vite');
346
+ if (deps['esbuild'])
347
+ detected.add('esbuild');
348
+ if (deps['rollup'])
349
+ detected.add('rollup');
350
+ if (deps['parcel'])
351
+ detected.add('parcel');
352
+ if (deps['turbo'])
353
+ detected.add('turborepo');
354
+ if (deps['@nrwl/cli'] || deps['nx'])
355
+ detected.add('nx');
356
+ if (deps['typescript'])
357
+ detected.add('typescript');
358
+ if (deps['swc'] || deps['@swc/core'])
359
+ detected.add('swc');
360
+ if (deps['rspack'] || deps['@rspack/core'])
361
+ detected.add('rspack');
362
+ // From scripts
363
+ if (pkg && typeof pkg === 'object') {
364
+ const scripts = pkg.scripts;
365
+ if (scripts) {
366
+ for (const [, script] of Object.entries(scripts)) {
367
+ if (typeof script === 'string') {
368
+ if (script.includes('next build'))
369
+ detected.add('next.js-build');
370
+ if (script.includes('nuxt build'))
371
+ detected.add('nuxt-build');
372
+ if (script.includes('tsc'))
373
+ detected.add('tsc');
374
+ }
375
+ }
376
+ }
377
+ }
378
+ return Array.from(detected);
379
+ }
380
+ detectPackageManager() {
381
+ if (fs.existsSync(path.join(this.projectDir, 'pnpm-lock.yaml')))
382
+ return 'pnpm';
383
+ if (fs.existsSync(path.join(this.projectDir, 'bun.lockb')) || fs.existsSync(path.join(this.projectDir, 'bun.lock')))
384
+ return 'bun';
385
+ if (fs.existsSync(path.join(this.projectDir, 'yarn.lock')))
386
+ return 'yarn';
387
+ if (fs.existsSync(path.join(this.projectDir, 'package-lock.json')))
388
+ return 'npm';
389
+ return 'unknown';
390
+ }
391
+ detectAITools() {
392
+ const results = [];
393
+ for (const pattern of AI_TOOL_PATTERNS) {
394
+ let detected = false;
395
+ let foundPath = '';
396
+ for (const file of pattern.files) {
397
+ const fullPath = path.join(this.projectDir, file);
398
+ if (fs.existsSync(fullPath)) {
399
+ detected = true;
400
+ foundPath = file;
401
+ break;
402
+ }
403
+ }
404
+ results.push({ name: pattern.name, path: foundPath, detected });
405
+ }
406
+ return results;
407
+ }
408
+ detectTestFrameworks(deps) {
409
+ const detected = [];
410
+ for (const [framework, detector] of Object.entries(TEST_DETECTORS)) {
411
+ try {
412
+ if (detector(deps))
413
+ detected.push(framework);
414
+ }
415
+ catch {
416
+ // Skip
417
+ }
418
+ }
419
+ // File-based detection
420
+ if (fs.existsSync(path.join(this.projectDir, 'pytest.ini')) || fs.existsSync(path.join(this.projectDir, 'setup.cfg')))
421
+ detected.push('pytest');
422
+ if (fs.existsSync(path.join(this.projectDir, 'phpunit.xml')))
423
+ detected.push('phpunit');
424
+ if (fs.existsSync(path.join(this.projectDir, 'jest.config.js')) || fs.existsSync(path.join(this.projectDir, 'jest.config.ts'))) {
425
+ if (!detected.includes('jest'))
426
+ detected.push('jest');
427
+ }
428
+ if (fs.existsSync(path.join(this.projectDir, 'vitest.config.ts')) || fs.existsSync(path.join(this.projectDir, 'vitest.config.js'))) {
429
+ if (!detected.includes('vitest'))
430
+ detected.push('vitest');
431
+ }
432
+ return [...new Set(detected)];
433
+ }
434
+ detectLinters() {
435
+ const detected = [];
436
+ if (fs.existsSync(path.join(this.projectDir, '.eslintrc.js')) || fs.existsSync(path.join(this.projectDir, '.eslintrc.json')) || fs.existsSync(path.join(this.projectDir, '.eslintrc.yml')) || fs.existsSync(path.join(this.projectDir, 'eslint.config.js')) || fs.existsSync(path.join(this.projectDir, 'eslint.config.mjs')))
437
+ detected.push('eslint');
438
+ if (fs.existsSync(path.join(this.projectDir, '.pylintrc')) || fs.existsSync(path.join(this.projectDir, 'pyproject.toml')))
439
+ detected.push('pylint');
440
+ if (fs.existsSync(path.join(this.projectDir, '.flake8')))
441
+ detected.push('flake8');
442
+ if (fs.existsSync(path.join(this.projectDir, '.rubocop.yml')))
443
+ detected.push('rubocop');
444
+ if (fs.existsSync(path.join(this.projectDir, '.golangci.yml')) || fs.existsSync(path.join(this.projectDir, '.golangci.yaml')))
445
+ detected.push('golangci-lint');
446
+ if (fs.existsSync(path.join(this.projectDir, '.clippy.toml')))
447
+ detected.push('clippy');
448
+ return detected;
449
+ }
450
+ detectFormatters(deps) {
451
+ const detected = [];
452
+ if (deps['prettier'] || fs.existsSync(path.join(this.projectDir, '.prettierrc')) || fs.existsSync(path.join(this.projectDir, '.prettierrc.js')))
453
+ detected.push('prettier');
454
+ if (fs.existsSync(path.join(this.projectDir, '.black')) || fs.existsSync(path.join(this.projectDir, 'pyproject.toml')))
455
+ detected.push('black');
456
+ if (fs.existsSync(path.join(this.projectDir, '.rustfmt.toml')))
457
+ detected.push('rustfmt');
458
+ if (deps['@biomejs/biome'] || fs.existsSync(path.join(this.projectDir, 'biome.json')))
459
+ detected.push('biome');
460
+ return detected;
461
+ }
462
+ detectCICD() {
463
+ const detected = [];
464
+ for (const [platform, files] of Object.entries(CICD_FILES)) {
465
+ for (const file of files) {
466
+ if (fs.existsSync(path.join(this.projectDir, file))) {
467
+ detected.push(platform);
468
+ break;
469
+ }
470
+ }
471
+ }
472
+ return detected;
473
+ }
474
+ detectGit() {
475
+ return fs.existsSync(path.join(this.projectDir, '.git'));
476
+ }
477
+ detectDocker() {
478
+ return fs.existsSync(path.join(this.projectDir, 'Dockerfile')) || fs.existsSync(path.join(this.projectDir, 'docker-compose.yml')) || fs.existsSync(path.join(this.projectDir, 'docker-compose.yaml'));
479
+ }
480
+ // ── Config Generation ──────────────────────────────────────────────────────
481
+ generateConfig(ctx) {
482
+ const config = {
483
+ projectDir: this.projectDir,
484
+ };
485
+ // Store project name in config for reference
486
+ config.projectName = this.getProjectName(this.readPackageJson());
487
+ // Enable features based on detection
488
+ if (ctx.hasGit) {
489
+ config.gitEnabled = true;
490
+ }
491
+ // Set analysis depth based on project complexity
492
+ const complexity = ctx.frameworks.length + ctx.languages.length;
493
+ if (complexity > 5) {
494
+ config.analysisDepth = 'deep';
495
+ }
496
+ else if (complexity > 2) {
497
+ config.analysisDepth = 'standard';
498
+ }
499
+ else {
500
+ config.analysisDepth = 'quick';
501
+ }
502
+ return config;
503
+ }
504
+ inferProjectType(frameworks, languages) {
505
+ if (frameworks.includes('react-native') || frameworks.includes('expo') || frameworks.includes('flutter'))
506
+ return 'mobile';
507
+ if (frameworks.includes('electron') || frameworks.includes('tauri'))
508
+ return 'desktop';
509
+ if (frameworks.includes('unity'))
510
+ return 'game';
511
+ if (frameworks.includes('next.js') || frameworks.includes('nuxt') || frameworks.includes('astro') || frameworks.includes('angular') || frameworks.includes('svelte') || frameworks.includes('vue'))
512
+ return 'fullstack';
513
+ if (frameworks.includes('express') || frameworks.includes('nestjs') || frameworks.includes('fastify') || frameworks.includes('django') || frameworks.includes('flask') || frameworks.includes('fastapi') || frameworks.includes('spring-boot') || frameworks.includes('rails') || frameworks.includes('gin') || frameworks.includes('actix'))
514
+ return 'backend';
515
+ if (frameworks.includes('react') || frameworks.includes('vue') || frameworks.includes('angular') || frameworks.includes('svelte'))
516
+ return 'frontend';
517
+ if (frameworks.includes('three.js'))
518
+ return '3d-web';
519
+ if (languages.includes('rust') && !frameworks.length)
520
+ return 'systems';
521
+ if (languages.includes('go') && !frameworks.length)
522
+ return 'systems';
523
+ if (languages.includes('python') && !frameworks.length)
524
+ return 'data-science';
525
+ if (languages.includes('c++') || languages.includes('c'))
526
+ return 'systems';
527
+ return 'generic';
528
+ }
529
+ getProjectName(pkg) {
530
+ if (pkg && typeof pkg === 'object' && pkg.name && typeof pkg.name === 'string') {
531
+ return pkg.name.replace(/^@[^/]+\//, '');
532
+ }
533
+ return path.basename(this.projectDir);
534
+ }
535
+ // ── MCP Installation for AI Tools ──────────────────────────────────────────
536
+ async installClaudeMCP() {
537
+ const claudeDir = path.join(this.projectDir, '.claude');
538
+ const settingsPath = path.join(claudeDir, 'settings.json');
539
+ let settings = {};
540
+ if (fs.existsSync(settingsPath)) {
541
+ try {
542
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
543
+ }
544
+ catch {
545
+ settings = {};
546
+ }
547
+ }
548
+ const mcpServers = (settings.mcpServers || {});
549
+ mcpServers['shadow-brain'] = {
550
+ command: 'npx',
551
+ args: ['-y', '@theihtisham/agent-shadow-brain', 'mcp'],
552
+ };
553
+ settings.mcpServers = mcpServers;
554
+ if (!fs.existsSync(claudeDir)) {
555
+ fs.mkdirSync(claudeDir, { recursive: true });
556
+ }
557
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
558
+ }
559
+ async installCursorMCP() {
560
+ const cursorDir = path.join(this.projectDir, '.cursor');
561
+ const mcpPath = path.join(cursorDir, 'mcp.json');
562
+ let mcpConfig = {};
563
+ if (fs.existsSync(mcpPath)) {
564
+ try {
565
+ mcpConfig = JSON.parse(fs.readFileSync(mcpPath, 'utf-8'));
566
+ }
567
+ catch {
568
+ mcpConfig = {};
569
+ }
570
+ }
571
+ const mcpServers = (mcpConfig.mcpServers || {});
572
+ mcpServers['shadow-brain'] = {
573
+ command: 'npx',
574
+ args: ['-y', '@theihtisham/agent-shadow-brain', 'mcp'],
575
+ };
576
+ mcpConfig.mcpServers = mcpServers;
577
+ if (!fs.existsSync(cursorDir)) {
578
+ fs.mkdirSync(cursorDir, { recursive: true });
579
+ }
580
+ fs.writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2));
581
+ }
582
+ async installKiloCodeMCP() {
583
+ const kiloDir = path.join(this.projectDir, '.kilocode');
584
+ const settingsPath = path.join(kiloDir, 'settings.json');
585
+ let settings = {};
586
+ if (fs.existsSync(settingsPath)) {
587
+ try {
588
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
589
+ }
590
+ catch {
591
+ settings = {};
592
+ }
593
+ }
594
+ const mcpServers = (settings.mcpServers || {});
595
+ mcpServers['shadow-brain'] = {
596
+ command: 'npx',
597
+ args: ['-y', '@theihtisham/agent-shadow-brain', 'mcp'],
598
+ };
599
+ settings.mcpServers = mcpServers;
600
+ if (!fs.existsSync(kiloDir)) {
601
+ fs.mkdirSync(kiloDir, { recursive: true });
602
+ }
603
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
604
+ }
605
+ async installClineMCP() {
606
+ const clineDir = path.join(this.projectDir, '.cline');
607
+ const settingsPath = path.join(clineDir, 'settings.json');
608
+ let settings = {};
609
+ if (fs.existsSync(settingsPath)) {
610
+ try {
611
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
612
+ }
613
+ catch {
614
+ settings = {};
615
+ }
616
+ }
617
+ const mcpServers = (settings.mcpServers || {});
618
+ mcpServers['shadow-brain'] = {
619
+ command: 'npx',
620
+ args: ['-y', '@theihtisham/agent-shadow-brain', 'mcp'],
621
+ };
622
+ settings.mcpServers = mcpServers;
623
+ if (!fs.existsSync(clineDir)) {
624
+ fs.mkdirSync(clineDir, { recursive: true });
625
+ }
626
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
627
+ }
628
+ }
629
+ // ── Standalone postinstall runner ──────────────────────────────────────────
630
+ // Runs automatically after npm install to detect project and set up MCP
631
+ async function postinstall() {
632
+ try {
633
+ const setup = new AutoSetup();
634
+ const result = await setup.detect();
635
+ // Only install MCP for detected tools, silently
636
+ if (result.aiTools.some(t => t.detected)) {
637
+ await setup.installMCPForTools(result);
638
+ }
639
+ // Install git hooks if git repo detected
640
+ if (result.hasGit) {
641
+ await setup.installGitHooks();
642
+ }
643
+ // Output minimal setup info
644
+ console.log(`\n Shadow Brain v5.0.1 auto-configured for ${result.projectName} (${result.projectType})`);
645
+ if (result.aiTools.some(t => t.detected)) {
646
+ const tools = result.aiTools.filter(t => t.detected).map(t => t.name);
647
+ console.log(` AI tools detected: ${tools.join(', ')}`);
648
+ }
649
+ console.log(` Run: shadow-brain start\n`);
650
+ }
651
+ catch {
652
+ // Silent — never break npm install
653
+ }
654
+ }
655
+ // Run when executed directly (postinstall)
656
+ if (process.argv[1] &&
657
+ (process.argv[1].includes('auto-setup') || process.argv[1].includes('dist/brain/auto-setup'))) {
658
+ postinstall();
659
+ }
660
+ //# sourceMappingURL=auto-setup.js.map