@magic-ingredients/tiny-brain-local 0.19.0 → 0.19.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.
@@ -19,5 +19,9 @@ export declare class QualityTool {
19
19
  private static handleCompare;
20
20
  private static handlePlan;
21
21
  private static handlePlanDetails;
22
+ private static handleDetectAnalyzers;
23
+ private static handleRunAnalyzers;
24
+ private static handleAssembleRun;
25
+ private static handleMergeResults;
22
26
  }
23
27
  //# sourceMappingURL=quality.tool.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"quality.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/quality/quality.tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAA0C,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AA4CrE;;GAEG;AACH,qBAAa,WAAW;IACtB,MAAM,CAAC,iBAAiB,IAAI,OAAO;WA2ItB,OAAO,CAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC;mBAsCD,UAAU;mBAqCV,aAAa;mBA2Bb,aAAa;mBA2Eb,aAAa;mBAsEb,UAAU;mBA0CV,iBAAiB;CA+DvC"}
1
+ {"version":3,"file":"quality.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/quality/quality.tool.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAA0C,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAyDrE;;GAEG;AACH,qBAAa,WAAW;IACtB,MAAM,CAAC,iBAAiB,IAAI,OAAO;WA+KtB,OAAO,CAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC;mBA8CD,UAAU;mBAqCV,aAAa;mBA2Bb,aAAa;mBA2Eb,aAAa;mBAsEb,UAAU;mBA0CV,iBAAiB;mBAgEjB,qBAAqB;mBA6BrB,kBAAkB;mBA+ClB,iBAAiB;IA8CtC,OAAO,CAAC,MAAM,CAAC,kBAAkB;CAoBlC"}
@@ -8,12 +8,15 @@ import { z } from 'zod';
8
8
  import { promises as fs } from 'fs';
9
9
  import path from 'path';
10
10
  import { createSuccessResult, createErrorResult } from '../index.js';
11
- import { QualityService, QualityIssueSchema, SaveQualityRunInputSchema, } from '@magic-ingredients/tiny-brain-core';
11
+ import { QualityService, AnalyzerDetectionService, AnalyzerExecutorService, AssemblyService, mergeResults, QualityIssueSchema, SaveQualityRunInputSchema, generateRunId, runIdToPath, } from '@magic-ingredients/tiny-brain-core';
12
12
  /**
13
13
  * Zod schema for QualityTool arguments
14
14
  */
15
15
  const QualityArgsSchema = z.object({
16
- operation: z.enum(['save', 'history', 'details', 'compare', 'plan', 'plan-details']),
16
+ operation: z.enum([
17
+ 'save', 'history', 'details', 'compare', 'plan', 'plan-details',
18
+ 'detect-analyzers', 'run-analyzers', 'merge-results', 'assemble-run',
19
+ ]),
17
20
  // For 'save' operation
18
21
  score: z.number().min(0).max(100).optional(),
19
22
  grade: z.enum(['A', 'B', 'C', 'D', 'F']).optional(),
@@ -38,6 +41,9 @@ const QualityArgsSchema = z.object({
38
41
  targetGrade: z.string().optional(),
39
42
  // For 'plan-details' operation
40
43
  planId: z.string().optional(),
44
+ // For 'merge-results' operation
45
+ analyzerIssues: z.array(QualityIssueSchema).optional(),
46
+ llmIssues: z.array(QualityIssueSchema).optional(),
41
47
  });
42
48
  /**
43
49
  * MCP Tool for quality analysis persistence
@@ -57,6 +63,10 @@ Persists and retrieves quality analysis results. Analysis is performed by agents
57
63
  • compare: Compare two runs to see new, resolved, and persistent issues
58
64
  • plan: Generate a Quality Improvement Plan from a saved run
59
65
  • plan-details: Retrieve a saved Quality Improvement Plan by ID
66
+ • detect-analyzers: Scan repo for configured static analyzers
67
+ • run-analyzers: Execute detected analyzers and return normalized issues
68
+ • merge-results: Merge analyzer + LLM issues with deduplication
69
+ • assemble-run: Read all intermediate files, merge, score, and save final report
60
70
 
61
71
  💡 WORKFLOW:
62
72
  1. Quality-coordinator agent performs analysis
@@ -69,7 +79,10 @@ Persists and retrieves quality analysis results. Analysis is performed by agents
69
79
  properties: {
70
80
  operation: {
71
81
  type: 'string',
72
- enum: ['save', 'history', 'details', 'compare', 'plan', 'plan-details'],
82
+ enum: [
83
+ 'save', 'history', 'details', 'compare', 'plan', 'plan-details',
84
+ 'detect-analyzers', 'run-analyzers', 'merge-results', 'assemble-run',
85
+ ],
73
86
  description: 'The quality operation to perform',
74
87
  },
75
88
  // For 'save' operation
@@ -176,6 +189,35 @@ Persists and retrieves quality analysis results. Analysis is performed by agents
176
189
  type: 'string',
177
190
  description: 'Plan ID to retrieve details for (required for plan-details)',
178
191
  },
192
+ // For 'merge-results' operation
193
+ analyzerIssues: {
194
+ type: 'array',
195
+ description: 'Array of analyzer issues (required for merge-results)',
196
+ items: {
197
+ type: 'object',
198
+ properties: {
199
+ category: { type: 'string' },
200
+ severity: { type: 'string' },
201
+ file: { type: 'string' },
202
+ message: { type: 'string' },
203
+ },
204
+ required: ['category', 'severity', 'file', 'message'],
205
+ },
206
+ },
207
+ llmIssues: {
208
+ type: 'array',
209
+ description: 'Array of LLM investigation issues (required for merge-results)',
210
+ items: {
211
+ type: 'object',
212
+ properties: {
213
+ category: { type: 'string' },
214
+ severity: { type: 'string' },
215
+ file: { type: 'string' },
216
+ message: { type: 'string' },
217
+ },
218
+ required: ['category', 'severity', 'file', 'message'],
219
+ },
220
+ },
179
221
  },
180
222
  required: ['operation'],
181
223
  },
@@ -202,6 +244,14 @@ Persists and retrieves quality analysis results. Analysis is performed by agents
202
244
  return await QualityTool.handlePlan(validatedArgs, service);
203
245
  case 'plan-details':
204
246
  return await QualityTool.handlePlanDetails(validatedArgs, context.repositoryRoot);
247
+ case 'detect-analyzers':
248
+ return await QualityTool.handleDetectAnalyzers(context.repositoryRoot);
249
+ case 'run-analyzers':
250
+ return await QualityTool.handleRunAnalyzers(context.repositoryRoot);
251
+ case 'merge-results':
252
+ return QualityTool.handleMergeResults(validatedArgs);
253
+ case 'assemble-run':
254
+ return await QualityTool.handleAssembleRun(validatedArgs, context.repositoryRoot);
205
255
  default:
206
256
  return createErrorResult(`Unknown operation: ${validatedArgs.operation}`);
207
257
  }
@@ -458,4 +508,109 @@ Persists and retrieves quality analysis results. Analysis is performed by agents
458
508
  return createErrorResult(`Improvement plan not found: ${args.planId}`);
459
509
  }
460
510
  }
511
+ static async handleDetectAnalyzers(repositoryRoot) {
512
+ const service = new AnalyzerDetectionService(repositoryRoot);
513
+ const analyzers = await service.detectAnalyzers();
514
+ if (analyzers.length === 0) {
515
+ return createSuccessResult('No static analyzers detected in this repository.\n\nSupported analyzers: ESLint, TypeScript, npm audit, RuboCop, ruff.');
516
+ }
517
+ const lines = [
518
+ `🔍 Detected ${analyzers.length} analyzer(s):`,
519
+ '',
520
+ ];
521
+ for (const analyzer of analyzers) {
522
+ lines.push(`- **${analyzer.name}** (${analyzer.analyzerId})`);
523
+ lines.push(` Configs: ${analyzer.configPaths.join(', ')}`);
524
+ lines.push(` Categories: ${analyzer.categories.join(', ')}`);
525
+ }
526
+ lines.push('');
527
+ lines.push(JSON.stringify(analyzers, null, 2));
528
+ return createSuccessResult(lines.join('\n'));
529
+ }
530
+ static async handleRunAnalyzers(repositoryRoot) {
531
+ const runId = generateRunId();
532
+ const runDir = path.join(repositoryRoot, 'docs', 'quality', 'runs', runIdToPath(runId));
533
+ const outputPath = path.join(runDir, 'analysis.json');
534
+ const outputDir = path.join(runDir, 'analysers');
535
+ const detectionService = new AnalyzerDetectionService(repositoryRoot);
536
+ const analyzers = await detectionService.detectAnalyzers();
537
+ const emptyResults = {
538
+ issues: [],
539
+ executions: [],
540
+ totalDurationMs: 0,
541
+ summary: { total: 0, succeeded: 0, failed: 0, timedOut: 0, skipped: 0 },
542
+ };
543
+ if (analyzers.length === 0) {
544
+ await fs.mkdir(runDir, { recursive: true });
545
+ await fs.writeFile(outputPath, JSON.stringify(emptyResults, null, 2), 'utf-8');
546
+ return createSuccessResult(`🔧 No analyzers detected. Empty results written to file.\n Run ID: ${runId}\n Output: ${outputPath}`);
547
+ }
548
+ const executorService = new AnalyzerExecutorService(repositoryRoot);
549
+ const results = await executorService.executeAnalyzers(analyzers, outputDir);
550
+ // Write merged/normalized result to the auto-generated run directory
551
+ await fs.mkdir(runDir, { recursive: true });
552
+ await fs.writeFile(outputPath, JSON.stringify(results, null, 2), 'utf-8');
553
+ const lines = [
554
+ `🔧 Executed ${results.summary.total} analyzer(s):`,
555
+ ` Succeeded: ${results.summary.succeeded}`,
556
+ ` Failed: ${results.summary.failed}`,
557
+ ` Total issues: ${results.issues.length}`,
558
+ ` Duration: ${results.totalDurationMs}ms`,
559
+ ` Run ID: ${runId}`,
560
+ ` Output: ${outputPath}`,
561
+ ` Raw files: ${outputDir}/`,
562
+ ];
563
+ return createSuccessResult(lines.join('\n'));
564
+ }
565
+ static async handleAssembleRun(args, repositoryRoot) {
566
+ if (!args.runId) {
567
+ return createErrorResult('runId is required for assemble-run operation (YYYY-MM-DD date prefix)');
568
+ }
569
+ const assemblyService = new AssemblyService(repositoryRoot);
570
+ const result = await assemblyService.assembleRun(args.runId, args.baseRunId);
571
+ const lines = [
572
+ '✅ Quality run assembled successfully!',
573
+ '',
574
+ `📊 Run ID: ${result.runId}`,
575
+ `📈 Score: ${result.score}/100 (Grade: ${result.grade})`,
576
+ `🔍 Issues: ${result.issueCount}`,
577
+ `📁 File: ${result.filePath}`,
578
+ ];
579
+ if (result.incremental) {
580
+ lines.push('');
581
+ lines.push('### Incremental Analysis');
582
+ lines.push(` Base run: ${result.baseRunId}`);
583
+ lines.push(` Files analyzed: ${result.filesAnalyzed}`);
584
+ lines.push(` Files carried forward: ${result.filesCarriedForward}`);
585
+ }
586
+ lines.push('');
587
+ lines.push('### Source Breakdown');
588
+ lines.push(` Analyzer: ${result.sourceBreakdown.analyzer}`);
589
+ lines.push(` Specialist: ${result.sourceBreakdown.llm}`);
590
+ lines.push(` Total: ${result.sourceBreakdown.total}`);
591
+ const categoryEntries = Object.entries(result.categoryBreakdown);
592
+ if (categoryEntries.length > 0) {
593
+ lines.push('');
594
+ lines.push('### Category Breakdown');
595
+ for (const [category, count] of categoryEntries) {
596
+ lines.push(` ${category}: ${count}`);
597
+ }
598
+ }
599
+ return createSuccessResult(lines.join('\n'));
600
+ }
601
+ static handleMergeResults(args) {
602
+ const analyzerIssues = args.analyzerIssues ?? [];
603
+ const llmIssues = args.llmIssues ?? [];
604
+ const result = mergeResults(analyzerIssues, llmIssues);
605
+ const lines = [
606
+ `🔀 Merged results:`,
607
+ ` Analyzer issues: ${result.sourceBreakdown.analyzer}`,
608
+ ` LLM issues: ${result.sourceBreakdown.llm}`,
609
+ ` Total (deduplicated): ${result.sourceBreakdown.total}`,
610
+ ` Duplicates removed: ${result.duplicatesRemoved}`,
611
+ '',
612
+ JSON.stringify(result, null, 2),
613
+ ];
614
+ return createSuccessResult(lines.join('\n'));
615
+ }
461
616
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magic-ingredients/tiny-brain-local",
3
- "version": "0.19.0",
3
+ "version": "0.19.1",
4
4
  "description": "MCP server for Tiny Brain AI assistant",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -31,7 +31,7 @@
31
31
  "dxt:init": "cd dxt && dxt init"
32
32
  },
33
33
  "dependencies": {
34
- "@magic-ingredients/tiny-brain-core": "^0.19.0",
34
+ "@magic-ingredients/tiny-brain-core": "^0.19.1",
35
35
  "@magic-ingredients/tiny-brain-dashboard": "file:../tiny-brain-dashboard",
36
36
  "@modelcontextprotocol/sdk": "^1.0.6",
37
37
  "chalk": "^5.3.0",