@hailer/mcp 0.1.13 → 0.1.15

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,42 @@
1
+ ---
2
+ name: agent-lars-code-inspector
3
+ description: LSP-powered code intelligence - finds bugs, dead code, unused imports, and navigates codebases.\n\n<example>\nuser: "Find bugs in src/App.tsx"\nassistant: {"status":"success","result":{"dead_code":[{"line":50,"symbol":"setColorMode"}],"unused_imports":[]},"summary":"Found 1 dead code issue"}\n</example>
4
+ model: haiku
5
+ tools: LSP, Read, Glob
6
+ ---
7
+
8
+ <identity>
9
+ I am Lars. LSP only. Minimal output. JSON only.
10
+ </identity>
11
+
12
+ <handles>
13
+ - Find unused variables/imports
14
+ - Find dead code
15
+ - Type errors
16
+ - Duplicate declarations
17
+ </handles>
18
+
19
+ <rules>
20
+ 1. **LSP ONLY** - Use LSP operations, never tsc/eslint.
21
+ 2. **MINIMAL OUTPUT** - No intermediate results. Final JSON only.
22
+ 3. **NO PROSE** - Output JSON, then STOP immediately.
23
+ </rules>
24
+
25
+ <workflow>
26
+ 1. Use `documentSymbol` to list symbols in file
27
+ 2. Use `findReferences` on key symbols to check if used
28
+ 3. Check LSP diagnostics in response for errors
29
+ 4. Return compact JSON
30
+ </workflow>
31
+
32
+ <protocol>
33
+ Output JSON only. No explanations. No tool output dumps.
34
+ {
35
+ "status": "success|error",
36
+ "result": {
37
+ "dead_code": [{"line":0,"symbol":""}],
38
+ "type_errors": [{"line":0,"msg":""}]
39
+ },
40
+ "summary": "max 30 chars"
41
+ }
42
+ </protocol>
package/CLAUDE.md CHANGED
@@ -47,6 +47,7 @@ Agents return JSON. You interpret it for the user.
47
47
  | Activity CRUD | `agent-dmitri-activity-crud` | haiku |
48
48
  | Discussions/chat | `agent-yevgeni-discussions` | haiku |
49
49
  | Config audit | `agent-bjorn-config-audit` | haiku |
50
+ | LSP/code bugs | `agent-lars-code-inspector` | haiku |
50
51
  | Workflows, fields, phases | `agent-helga-workflow-config` | sonnet |
51
52
  | SQL insights | `agent-viktor-sql-insights` | sonnet |
52
53
  | Function fields | `agent-alejandro-function-fields` | sonnet |
@@ -18,6 +18,7 @@ export declare class GiuseppeBot {
18
18
  private ai;
19
19
  private git;
20
20
  private files;
21
+ private lsp;
21
22
  private appsRegistry;
22
23
  constructor(userContext: UserContext, config: BugReportsConfig, monitor: BugMonitor);
23
24
  /**
@@ -20,6 +20,7 @@ const app_scaffold_1 = require("../../mcp/tools/app-scaffold");
20
20
  const giuseppe_ai_1 = require("./giuseppe-ai");
21
21
  const giuseppe_git_1 = require("./giuseppe-git");
22
22
  const giuseppe_files_1 = require("./giuseppe-files");
23
+ const giuseppe_lsp_1 = require("./giuseppe-lsp");
23
24
  const logger = (0, logger_1.createLogger)({ component: 'giuseppe-bot' });
24
25
  // Magic words for approval flow
25
26
  const MAGIC_WORD_APPROVED = 'approved';
@@ -44,6 +45,7 @@ class GiuseppeBot {
44
45
  ai;
45
46
  git;
46
47
  files;
48
+ lsp;
47
49
  appsRegistry = new Map();
48
50
  constructor(userContext, config, monitor) {
49
51
  this.userContext = userContext;
@@ -54,6 +56,7 @@ class GiuseppeBot {
54
56
  this.ai = new giuseppe_ai_1.GiuseppeAI(apiKey);
55
57
  this.git = new giuseppe_git_1.GiuseppeGit();
56
58
  this.files = new giuseppe_files_1.GiuseppeFiles(process.env.DEV_APPS_PATH);
59
+ this.lsp = new giuseppe_lsp_1.GiuseppeLsp();
57
60
  // Load apps registry from config
58
61
  if (config.appsRegistry) {
59
62
  for (const [appId, entry] of Object.entries(config.appsRegistry)) {
@@ -236,6 +239,12 @@ class GiuseppeBot {
236
239
  }
237
240
  result.log?.push(`Found app project: ${app.projectPath}`);
238
241
  await this.reportProgress(bug, `📁 Found app project: ${app.name}`);
242
+ // 1.5. Run LSP analysis to understand current codebase state
243
+ const preAnalysis = await this.lsp.analyzeProject(app.projectPath);
244
+ if (preAnalysis.issues.length > 0) {
245
+ result.log?.push(`Pre-fix LSP: ${preAnalysis.summary}`);
246
+ await this.reportProgress(bug, `🔎 Codebase analysis: ${preAnalysis.summary}`);
247
+ }
239
248
  // 2. Analyze and generate fix
240
249
  const allFiles = await this.files.scanSourceFiles(app.projectPath);
241
250
  const fixPlan = await this.ai.analyzeAndPlanFix(bug, app, allFiles, (paths) => this.files.readSelectedFiles(app.projectPath, paths));
@@ -283,6 +292,12 @@ class GiuseppeBot {
283
292
  await this.reportProgress(bug, `✏️ Applied fixes to ${applyResult.files.length} file(s)`);
284
293
  const buildResult = await this.buildAndTest(app);
285
294
  if (buildResult.success) {
295
+ // Run LSP analysis to check for remaining issues
296
+ const lspResult = await this.lsp.validateFix(app.projectPath);
297
+ if (!lspResult.valid) {
298
+ result.log?.push(`LSP found ${lspResult.errors.length} issue(s)`);
299
+ await this.reportProgress(bug, `⚠️ Build passed but LSP found issues:\n${lspResult.errors.slice(0, 3).join('\n')}`);
300
+ }
286
301
  fixSuccess = true;
287
302
  result.log?.push(`Build successful (attempt ${attempt})`);
288
303
  await this.reportProgress(bug, '✅ Build successful');
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Giuseppe LSP Module - Code analysis via TypeScript compiler
3
+ */
4
+ export interface CodeIssue {
5
+ file: string;
6
+ line: number;
7
+ column: number;
8
+ message: string;
9
+ code: string;
10
+ severity: 'error' | 'warning';
11
+ }
12
+ export interface AnalysisResult {
13
+ success: boolean;
14
+ issues: CodeIssue[];
15
+ summary: string;
16
+ }
17
+ export declare class GiuseppeLsp {
18
+ /**
19
+ * Analyze a project for TypeScript errors
20
+ */
21
+ analyzeProject(projectPath: string): Promise<AnalysisResult>;
22
+ /**
23
+ * Analyze a single file
24
+ */
25
+ analyzeFile(filePath: string): Promise<AnalysisResult>;
26
+ /**
27
+ * Check if code compiles after a fix
28
+ */
29
+ validateFix(projectPath: string): Promise<{
30
+ valid: boolean;
31
+ errors: string[];
32
+ }>;
33
+ /**
34
+ * Find unused exports/variables in a file
35
+ */
36
+ findDeadCode(projectPath: string): Promise<CodeIssue[]>;
37
+ /**
38
+ * Parse tsc output into structured issues
39
+ */
40
+ private parseTscOutput;
41
+ }
42
+ //# sourceMappingURL=giuseppe-lsp.d.ts.map
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+ /**
3
+ * Giuseppe LSP Module - Code analysis via TypeScript compiler
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.GiuseppeLsp = void 0;
40
+ const child_process_1 = require("child_process");
41
+ const path = __importStar(require("path"));
42
+ const logger_1 = require("../../lib/logger");
43
+ const logger = (0, logger_1.createLogger)({ component: 'giuseppe-lsp' });
44
+ class GiuseppeLsp {
45
+ /**
46
+ * Analyze a project for TypeScript errors
47
+ */
48
+ async analyzeProject(projectPath) {
49
+ try {
50
+ logger.info(`Analyzing project: ${projectPath}`);
51
+ const result = (0, child_process_1.execSync)('npx tsc --noEmit --pretty false 2>&1 || true', {
52
+ cwd: projectPath,
53
+ encoding: 'utf-8',
54
+ maxBuffer: 10 * 1024 * 1024
55
+ });
56
+ const issues = this.parseTscOutput(result, projectPath);
57
+ return {
58
+ success: issues.filter(i => i.severity === 'error').length === 0,
59
+ issues,
60
+ summary: issues.length === 0
61
+ ? 'No issues found'
62
+ : `Found ${issues.length} issue(s)`
63
+ };
64
+ }
65
+ catch (error) {
66
+ logger.error('Analysis failed', { error });
67
+ return {
68
+ success: false,
69
+ issues: [],
70
+ summary: `Analysis failed: ${error}`
71
+ };
72
+ }
73
+ }
74
+ /**
75
+ * Analyze a single file
76
+ */
77
+ async analyzeFile(filePath) {
78
+ const projectPath = path.dirname(filePath);
79
+ const result = await this.analyzeProject(projectPath);
80
+ const fileName = path.basename(filePath);
81
+ result.issues = result.issues.filter(i => i.file.endsWith(fileName));
82
+ result.summary = result.issues.length === 0
83
+ ? `No issues in ${fileName}`
84
+ : `Found ${result.issues.length} issue(s) in ${fileName}`;
85
+ return result;
86
+ }
87
+ /**
88
+ * Check if code compiles after a fix
89
+ */
90
+ async validateFix(projectPath) {
91
+ const result = await this.analyzeProject(projectPath);
92
+ const errors = result.issues
93
+ .filter(i => i.severity === 'error')
94
+ .map(i => `${i.file}:${i.line} - ${i.message}`);
95
+ return {
96
+ valid: errors.length === 0,
97
+ errors
98
+ };
99
+ }
100
+ /**
101
+ * Find unused exports/variables in a file
102
+ */
103
+ async findDeadCode(projectPath) {
104
+ try {
105
+ const result = (0, child_process_1.execSync)('npx tsc --noEmit --noUnusedLocals --noUnusedParameters --pretty false 2>&1 || true', {
106
+ cwd: projectPath,
107
+ encoding: 'utf-8',
108
+ maxBuffer: 10 * 1024 * 1024
109
+ });
110
+ return this.parseTscOutput(result, projectPath)
111
+ .filter(i => i.message.includes('declared but') || i.message.includes('never used'));
112
+ }
113
+ catch (error) {
114
+ logger.error('Dead code analysis failed', { error });
115
+ return [];
116
+ }
117
+ }
118
+ /**
119
+ * Parse tsc output into structured issues
120
+ */
121
+ parseTscOutput(output, basePath) {
122
+ const issues = [];
123
+ const lines = output.split('\n');
124
+ // Pattern: src/file.ts(10,5): error TS2304: Cannot find name 'foo'.
125
+ const pattern = /^(.+?)\((\d+),(\d+)\):\s*(error|warning)\s+(TS\d+):\s*(.+)$/;
126
+ for (const line of lines) {
127
+ const match = line.match(pattern);
128
+ if (match) {
129
+ issues.push({
130
+ file: path.resolve(basePath, match[1]),
131
+ line: parseInt(match[2], 10),
132
+ column: parseInt(match[3], 10),
133
+ severity: match[4],
134
+ code: match[5],
135
+ message: match[6]
136
+ });
137
+ }
138
+ }
139
+ return issues;
140
+ }
141
+ }
142
+ exports.GiuseppeLsp = GiuseppeLsp;
143
+ //# sourceMappingURL=giuseppe-lsp.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hailer/mcp",
3
- "version": "0.1.13",
3
+ "version": "0.1.15",
4
4
  "config": {
5
5
  "docker": {
6
6
  "registry": "registry.gitlab.com/hailer-repos/hailer-mcp"
@@ -42,6 +42,7 @@
42
42
  "form-data": "^4.0.4",
43
43
  "openai": "^5.5.1",
44
44
  "sharp": "^0.34.5",
45
+ "typescript-language-server": "^5.1.3",
45
46
  "zod": "^3.24.1"
46
47
  },
47
48
  "devDependencies": {
@@ -54,8 +55,7 @@
54
55
  "eslint": "^9.29.0",
55
56
  "globals": "^16.2.0",
56
57
  "tsx": "^4.19.2",
57
- "typescript": "^5",
58
- "typescript-language-server": "^5.1.3"
58
+ "typescript": "^5"
59
59
  },
60
60
  "bin": {
61
61
  "hailer-mcp": "dist/cli.js"