@magic-ingredients/tiny-brain-local 0.19.0 → 0.20.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.
- package/dist/core/mcp-server.d.ts.map +1 -1
- package/dist/core/mcp-server.js +10 -19
- package/dist/services/analyse-service.d.ts +19 -0
- package/dist/services/analyse-service.d.ts.map +1 -1
- package/dist/services/analyse-service.js +177 -4
- package/dist/services/repo-service.d.ts.map +1 -1
- package/dist/services/repo-service.js +3 -1
- package/dist/tools/persona/as.tool.js +2 -2
- package/dist/tools/quality/quality.tool.d.ts +5 -0
- package/dist/tools/quality/quality.tool.d.ts.map +1 -1
- package/dist/tools/quality/quality.tool.js +205 -4
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../../src/core/mcp-server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH,OAAO,KAAK,EAA4B,QAAQ,EAAkC,MAAM,oCAAoC,CAAC;AAE7H,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAQ5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IAEF,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;KACrB,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,sBAAsB,CAA0D;IACxF,OAAO,CAAC,mBAAmB,CAA2G;IACtI,OAAO,CAAC,UAAU,CAA6E;IAC/F,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,SAAS,CAAC,CAAY;IAC9B,OAAO,CAAC,gBAAgB,CAAC,CAAmB;gBAEhC,MAAM,GAAE,SAAc;IAwFlC,OAAO,CAAC,aAAa;IAarB;;OAEG;IACH,SAAS,IAAI,SAAS;IAItB;;OAEG;IACH,mBAAmB,IAAI,gBAAgB,GAAG,SAAS;IAInD;;OAEG;IACH,oBAAoB,IAAI,iBAAiB,GAAG,IAAI;IAIhD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBjC;;;;;;;OAOG;YACW,sBAAsB;IA+DpC;;OAEG;YACW,iBAAiB;IAW/B;;;OAGG;YACW,oBAAoB;IA6BlC;;OAEG;YACW,oBAAoB;
|
|
1
|
+
{"version":3,"file":"mcp-server.d.ts","sourceRoot":"","sources":["../../src/core/mcp-server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AA0BH,OAAO,KAAK,EAA4B,QAAQ,EAAkC,MAAM,oCAAoC,CAAC;AAE7H,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAQ5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAE9E;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,OAAO,CAAC,EAAE;QACR,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IAEF,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,QAAQ,CAAC;KACrB,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,MAAM,CAAa;IAC3B,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,sBAAsB,CAA0D;IACxF,OAAO,CAAC,mBAAmB,CAA2G;IACtI,OAAO,CAAC,UAAU,CAA6E;IAC/F,OAAO,CAAC,iBAAiB,CAAkC;IAC3D,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,SAAS,CAAC,CAAY;IAC9B,OAAO,CAAC,gBAAgB,CAAC,CAAmB;gBAEhC,MAAM,GAAE,SAAc;IAwFlC,OAAO,CAAC,aAAa;IAarB;;OAEG;IACH,SAAS,IAAI,SAAS;IAItB;;OAEG;IACH,mBAAmB,IAAI,gBAAgB,GAAG,SAAS;IAInD;;OAEG;IACH,oBAAoB,IAAI,iBAAiB,GAAG,IAAI;IAIhD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBjC;;;;;;;OAOG;YACW,sBAAsB;IA+DpC;;OAEG;YACW,iBAAiB;IAW/B;;;OAGG;YACW,oBAAoB;IA6BlC;;OAEG;YACW,oBAAoB;IAwGlC;;;;;;OAMG;YACW,yBAAyB;IA+CvC;;OAEG;YACW,kBAAkB;IAsBhC;;OAEG;YACW,wBAAwB;IA8BtC;;OAEG;YACW,mBAAmB;IA8BjC;;OAEG;IACG,OAAO,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5C;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;YAiBjB,eAAe;YAMf,cAAc;YA+Fd,iBAAiB;YAMjB,eAAe;YAqEf,iBAAiB;IAS/B;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAoB7B;;OAEG;IACH,OAAO,CAAC,oBAAoB;CAkD7B"}
|
package/dist/core/mcp-server.js
CHANGED
|
@@ -8,7 +8,7 @@ import { existsSync, cpSync, readdirSync, readFileSync, writeFileSync, mkdirSync
|
|
|
8
8
|
import { execSync } from 'child_process';
|
|
9
9
|
import { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js';
|
|
10
10
|
import { ListToolsRequestSchema, CallToolRequestSchema, ListPromptsRequestSchema, GetPromptRequestSchema, SetLevelRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
|
|
11
|
-
import { isDevelopment as checkIsDevelopment } from '@magic-ingredients/tiny-brain-core';
|
|
11
|
+
import { isDevelopment as checkIsDevelopment, detectClaudeCliStatus } from '@magic-ingredients/tiny-brain-core';
|
|
12
12
|
import { AuthTokenService } from '../services/remote/auth-token-service.js';
|
|
13
13
|
import { CredentialStorageService } from '../services/credential-storage.service.js';
|
|
14
14
|
import { SystemPersonaService } from '../services/remote/system-persona-service.js';
|
|
@@ -276,12 +276,11 @@ export class MCPServer {
|
|
|
276
276
|
hasClientSecret: !!this.config.account?.clientSecret,
|
|
277
277
|
configKeys: Object.keys(this.config)
|
|
278
278
|
});
|
|
279
|
-
// Create credential service to check for credentials
|
|
279
|
+
// Create credential service to check for credentials
|
|
280
280
|
const credentialService = new CredentialStorageService();
|
|
281
|
-
// Check
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
const getLlmApiKey = () => credentialService.getLlmApiKey();
|
|
281
|
+
// Check Claude CLI availability (for dashboard to disable/enable skill invocation buttons)
|
|
282
|
+
const cliStatus = await detectClaudeCliStatus(this.logger);
|
|
283
|
+
const hasClaudeCli = cliStatus.authenticated;
|
|
285
284
|
// Check for remote auth credentials
|
|
286
285
|
if (!this.config.account?.clientId || !this.config.account?.clientSecret) {
|
|
287
286
|
this.logger.info('No remote credentials configured in MCP config, checking tiny-brain CLI config...');
|
|
@@ -298,13 +297,11 @@ export class MCPServer {
|
|
|
298
297
|
}
|
|
299
298
|
else {
|
|
300
299
|
this.logger.info('No credentials found in CLI config either, running in local-only mode');
|
|
301
|
-
// Still set libraryAuth with hasLlmApiKey for dashboard
|
|
302
300
|
this.baseContext.libraryAuth = {
|
|
303
301
|
clientId: undefined,
|
|
304
302
|
hasStoredSecret: false,
|
|
305
|
-
|
|
303
|
+
hasClaudeCli,
|
|
306
304
|
token: undefined,
|
|
307
|
-
getLlmApiKey
|
|
308
305
|
};
|
|
309
306
|
return;
|
|
310
307
|
}
|
|
@@ -312,13 +309,11 @@ export class MCPServer {
|
|
|
312
309
|
catch (error) {
|
|
313
310
|
this.logger.warn('Error reading CLI config:', error);
|
|
314
311
|
this.logger.info('Running in local-only mode');
|
|
315
|
-
// Still set libraryAuth with hasLlmApiKey for dashboard
|
|
316
312
|
this.baseContext.libraryAuth = {
|
|
317
313
|
clientId: undefined,
|
|
318
314
|
hasStoredSecret: false,
|
|
319
|
-
|
|
315
|
+
hasClaudeCli,
|
|
320
316
|
token: undefined,
|
|
321
|
-
getLlmApiKey
|
|
322
317
|
};
|
|
323
318
|
return;
|
|
324
319
|
}
|
|
@@ -343,9 +338,8 @@ export class MCPServer {
|
|
|
343
338
|
this.baseContext.libraryAuth = {
|
|
344
339
|
clientId: this.config.account?.clientId,
|
|
345
340
|
hasStoredSecret: true,
|
|
346
|
-
|
|
341
|
+
hasClaudeCli,
|
|
347
342
|
token: token.token,
|
|
348
|
-
getLlmApiKey
|
|
349
343
|
};
|
|
350
344
|
}
|
|
351
345
|
else {
|
|
@@ -356,22 +350,19 @@ export class MCPServer {
|
|
|
356
350
|
this.baseContext.libraryAuth = {
|
|
357
351
|
clientId: this.config.account?.clientId,
|
|
358
352
|
hasStoredSecret: !!this.config.account?.clientSecret,
|
|
359
|
-
|
|
353
|
+
hasClaudeCli,
|
|
360
354
|
token: undefined,
|
|
361
|
-
getLlmApiKey
|
|
362
355
|
};
|
|
363
356
|
}
|
|
364
357
|
}
|
|
365
358
|
catch (error) {
|
|
366
359
|
this.logger.error('Unexpected error during remote authentication', error);
|
|
367
360
|
// Continue in local mode - don't fail the entire initialization
|
|
368
|
-
// Still set libraryAuth with hasLlmApiKey for dashboard
|
|
369
361
|
this.baseContext.libraryAuth = {
|
|
370
362
|
clientId: this.config.account?.clientId,
|
|
371
363
|
hasStoredSecret: !!this.config.account?.clientSecret,
|
|
372
|
-
|
|
364
|
+
hasClaudeCli,
|
|
373
365
|
token: undefined,
|
|
374
|
-
getLlmApiKey
|
|
375
366
|
};
|
|
376
367
|
}
|
|
377
368
|
}
|
|
@@ -29,6 +29,7 @@ export interface AnalyseResult {
|
|
|
29
29
|
};
|
|
30
30
|
skillPermissionsAdded?: string[];
|
|
31
31
|
writtenTechContexts?: string[];
|
|
32
|
+
agentsMdStatus?: 'created' | 'updated' | 'up-to-date';
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
35
|
* Service for managing repository analysis
|
|
@@ -45,6 +46,20 @@ export declare class AnalyseService {
|
|
|
45
46
|
* Perform repository analysis with unified behavior for initialized/uninitialized repos
|
|
46
47
|
*/
|
|
47
48
|
performAnalysis(options?: AnalyseOptions): Promise<AnalyseResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Generate AGENTS.md file in the repository root
|
|
51
|
+
* For monorepos, also generates per-package AGENTS.md files
|
|
52
|
+
* Uses AgentsMdService from tiny-brain-core
|
|
53
|
+
*/
|
|
54
|
+
private generateAgentsMd;
|
|
55
|
+
/**
|
|
56
|
+
* Read package.json from each workspace directory to collect names and descriptions
|
|
57
|
+
*/
|
|
58
|
+
private collectPackageDescriptions;
|
|
59
|
+
/**
|
|
60
|
+
* Generate AGENTS.md for each workspace package in a monorepo
|
|
61
|
+
*/
|
|
62
|
+
private generatePerPackageAgentsMd;
|
|
48
63
|
/**
|
|
49
64
|
* Register repository in dashboard for visibility
|
|
50
65
|
* Called after successful analysis regardless of invocation method
|
|
@@ -95,6 +110,10 @@ export declare class AnalyseService {
|
|
|
95
110
|
* Format analysis output for display
|
|
96
111
|
*/
|
|
97
112
|
formatAnalysisOutput(result: AnalyseResult): string;
|
|
113
|
+
/**
|
|
114
|
+
* Append rich metadata sections to output lines
|
|
115
|
+
*/
|
|
116
|
+
private appendRichMetadataOutput;
|
|
98
117
|
/**
|
|
99
118
|
* Compare two tech stacks and return differences
|
|
100
119
|
* Handles null previous analysis (first run)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyse-service.d.ts","sourceRoot":"","sources":["../../src/services/analyse-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAEL,KAAK,YAAY,EAEjB,KAAK,aAAa,
|
|
1
|
+
{"version":3,"file":"analyse-service.d.ts","sourceRoot":"","sources":["../../src/services/analyse-service.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAEL,KAAK,YAAY,EAEjB,KAAK,aAAa,EAQnB,MAAM,oCAAoC,CAAC;AAS5C,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAGD,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,YAAY,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE;QACR,cAAc,EAAE,MAAM,EAAE,CAAC;QACzB,gBAAgB,EAAE,MAAM,EAAE,CAAC;KAC5B,CAAC;IACF,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,cAAc,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,YAAY,CAAC;CACvD;AAGD;;;GAGG;AACH,qBAAa,cAAc;IAMb,OAAO,CAAC,OAAO;IAL3B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,kBAAkB,CAAqB;gBAE3B,OAAO,EAAE,cAAc;IAO3C;;OAEG;IACG,eAAe,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC;IAyK3E;;;;OAIG;YACW,gBAAgB;IAmD9B;;OAEG;YACW,0BAA0B;IAyBxC;;OAEG;YACW,0BAA0B;IA+BxC;;;OAGG;YACW,kBAAkB;IAoBhC;;OAEG;YACW,eAAe;IAc7B;;;OAGG;YACW,sBAAsB;IAwBpC;;;OAGG;YACW,sBAAsB;IAwBpC;;;OAGG;YACW,0BAA0B;IAwBxC;;;OAGG;YACW,wBAAwB;IAwBtC;;;;OAIG;YACW,kBAAkB;IA+EhC;;;;OAIG;YACW,sBAAsB;IAiEpC;;;OAGG;YACW,yBAAyB;IAkCvC;;OAEG;IACH,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM;IAgInD;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA2ChC;;;OAGG;IACH,iBAAiB,CAAC,gBAAgB,EAAE,YAAY,GAAG,IAAI,EAAE,eAAe,EAAE,YAAY,GAAG,aAAa;IA8BtG;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IAY5B;;;;;OAKG;IACH,OAAO,CAAC,eAAe;CAWxB"}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Handles repository tech stack analysis.
|
|
5
5
|
* Focused solely on analysis - no context file management.
|
|
6
6
|
*/
|
|
7
|
-
import { analyseRepository, formatTechStackChanges, detectTechStackChanges, ConfigService, RepoConfigService, LibraryClient, TechContextService, } from '@magic-ingredients/tiny-brain-core';
|
|
7
|
+
import { analyseRepository, formatTechStackChanges, detectTechStackChanges, ConfigService, RepoConfigService, LibraryClient, TechContextService, AgentsMdService, } from '@magic-ingredients/tiny-brain-core';
|
|
8
8
|
import { RepoService } from './repo-service.js';
|
|
9
9
|
import { fileURLToPath } from 'url';
|
|
10
10
|
import { dirname, join } from 'path';
|
|
@@ -60,6 +60,13 @@ export class AnalyseService {
|
|
|
60
60
|
primaryLanguage: currentAnalysis.primaryLanguage,
|
|
61
61
|
documentationPattern: currentAnalysis.documentationPattern,
|
|
62
62
|
documentationLocations: currentAnalysis.documentationLocations,
|
|
63
|
+
// Rich metadata
|
|
64
|
+
packageManager: currentAnalysis.packageManager,
|
|
65
|
+
scripts: currentAnalysis.scripts,
|
|
66
|
+
linting: currentAnalysis.linting,
|
|
67
|
+
monorepo: currentAnalysis.monorepo,
|
|
68
|
+
projectName: currentAnalysis.projectName,
|
|
69
|
+
sourceDirectories: currentAnalysis.sourceDirectories,
|
|
63
70
|
};
|
|
64
71
|
const stackChanged = await this.techContextService.hasStackChanged(analysisInput);
|
|
65
72
|
const existingAnalysis = await this.techContextService.readAnalysis();
|
|
@@ -79,6 +86,12 @@ export class AnalyseService {
|
|
|
79
86
|
if (!dryRun) {
|
|
80
87
|
await this.techContextService.syncAgents(enableAgentic);
|
|
81
88
|
}
|
|
89
|
+
// Generate AGENTS.md (if config allows)
|
|
90
|
+
let agentsMdStatus;
|
|
91
|
+
const manageAgentsMd = await this.configService.isManageAgentsMdEnabled();
|
|
92
|
+
if (!dryRun && manageAgentsMd) {
|
|
93
|
+
agentsMdStatus = await this.generateAgentsMd(currentAnalysis);
|
|
94
|
+
}
|
|
82
95
|
// Write workflow sections to CLAUDE.md based on config flags (SDD, TDD)
|
|
83
96
|
if (!dryRun) {
|
|
84
97
|
await this.repoService.writeWorkflowSections({ contextPath });
|
|
@@ -105,7 +118,8 @@ export class AnalyseService {
|
|
|
105
118
|
contextBlockVersion,
|
|
106
119
|
contextBlockUpdated,
|
|
107
120
|
skillPermissionsAdded: skillPermissionsAdded.length > 0 ? skillPermissionsAdded : undefined,
|
|
108
|
-
writtenTechContexts
|
|
121
|
+
writtenTechContexts,
|
|
122
|
+
agentsMdStatus,
|
|
109
123
|
};
|
|
110
124
|
}
|
|
111
125
|
if (!stackChanged) {
|
|
@@ -125,7 +139,8 @@ export class AnalyseService {
|
|
|
125
139
|
contextBlockVersion,
|
|
126
140
|
contextBlockUpdated,
|
|
127
141
|
skillPermissionsAdded: skillPermissionsAdded.length > 0 ? skillPermissionsAdded : undefined,
|
|
128
|
-
writtenTechContexts
|
|
142
|
+
writtenTechContexts,
|
|
143
|
+
agentsMdStatus,
|
|
129
144
|
};
|
|
130
145
|
}
|
|
131
146
|
// Changes detected - compute diff for display purposes
|
|
@@ -163,9 +178,108 @@ export class AnalyseService {
|
|
|
163
178
|
techStackRemoved: techStackDiff.removed
|
|
164
179
|
},
|
|
165
180
|
skillPermissionsAdded: skillPermissionsAdded.length > 0 ? skillPermissionsAdded : undefined,
|
|
166
|
-
writtenTechContexts
|
|
181
|
+
writtenTechContexts,
|
|
182
|
+
agentsMdStatus,
|
|
167
183
|
};
|
|
168
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Generate AGENTS.md file in the repository root
|
|
187
|
+
* For monorepos, also generates per-package AGENTS.md files
|
|
188
|
+
* Uses AgentsMdService from tiny-brain-core
|
|
189
|
+
*/
|
|
190
|
+
async generateAgentsMd(analysis) {
|
|
191
|
+
const repoRoot = process.cwd();
|
|
192
|
+
const agentsMdService = new AgentsMdService(repoRoot);
|
|
193
|
+
const agentsMdPath = join(repoRoot, 'AGENTS.md');
|
|
194
|
+
// Read existing AGENTS.md if present
|
|
195
|
+
let existingContent;
|
|
196
|
+
try {
|
|
197
|
+
existingContent = await readFile(agentsMdPath, 'utf-8');
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// File doesn't exist
|
|
201
|
+
}
|
|
202
|
+
// Extract README excerpt
|
|
203
|
+
const readmeExcerpt = await agentsMdService.extractReadmeExcerpt();
|
|
204
|
+
// Collect package descriptions for monorepo
|
|
205
|
+
let packageDescriptions;
|
|
206
|
+
if (analysis.monorepo?.packages && analysis.monorepo.packages.length > 0) {
|
|
207
|
+
packageDescriptions = await this.collectPackageDescriptions(repoRoot, analysis.monorepo.packages);
|
|
208
|
+
}
|
|
209
|
+
// Generate root content
|
|
210
|
+
const result = await agentsMdService.generateContent({
|
|
211
|
+
analysis,
|
|
212
|
+
readmeExcerpt,
|
|
213
|
+
repoPath: repoRoot,
|
|
214
|
+
existingContent,
|
|
215
|
+
packageDescriptions,
|
|
216
|
+
});
|
|
217
|
+
if (!existingContent) {
|
|
218
|
+
await writeFile(agentsMdPath, result.content, 'utf-8');
|
|
219
|
+
this.context.logger.info('Created AGENTS.md');
|
|
220
|
+
}
|
|
221
|
+
else if (!result.contentChanged) {
|
|
222
|
+
this.context.logger.info('AGENTS.md is up to date');
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
await writeFile(agentsMdPath, result.content, 'utf-8');
|
|
226
|
+
this.context.logger.info('Updated AGENTS.md');
|
|
227
|
+
}
|
|
228
|
+
// Generate per-package AGENTS.md for monorepo
|
|
229
|
+
if (analysis.monorepo?.packages && analysis.monorepo.packages.length > 0) {
|
|
230
|
+
await this.generatePerPackageAgentsMd(repoRoot, analysis, agentsMdService);
|
|
231
|
+
}
|
|
232
|
+
if (!existingContent)
|
|
233
|
+
return 'created';
|
|
234
|
+
if (!result.contentChanged)
|
|
235
|
+
return 'up-to-date';
|
|
236
|
+
return 'updated';
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Read package.json from each workspace directory to collect names and descriptions
|
|
240
|
+
*/
|
|
241
|
+
async collectPackageDescriptions(repoRoot, packages) {
|
|
242
|
+
const descriptions = [];
|
|
243
|
+
for (const pkgPath of packages) {
|
|
244
|
+
const fullPath = join(repoRoot, pkgPath);
|
|
245
|
+
const pkgJsonPath = join(fullPath, 'package.json');
|
|
246
|
+
try {
|
|
247
|
+
const raw = await readFile(pkgJsonPath, 'utf-8');
|
|
248
|
+
const pkgJson = JSON.parse(raw);
|
|
249
|
+
descriptions.push({
|
|
250
|
+
name: pkgJson.name,
|
|
251
|
+
path: pkgPath,
|
|
252
|
+
description: pkgJson.description,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
descriptions.push({ path: pkgPath });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return descriptions;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Generate AGENTS.md for each workspace package in a monorepo
|
|
263
|
+
*/
|
|
264
|
+
async generatePerPackageAgentsMd(repoRoot, analysis, agentsMdService) {
|
|
265
|
+
const packages = analysis.monorepo?.packages ?? [];
|
|
266
|
+
for (const pkgPath of packages) {
|
|
267
|
+
const fullPkgPath = join(repoRoot, pkgPath);
|
|
268
|
+
const pkgAgentsMdPath = join(fullPkgPath, 'AGENTS.md');
|
|
269
|
+
let existingPkgContent;
|
|
270
|
+
try {
|
|
271
|
+
existingPkgContent = await readFile(pkgAgentsMdPath, 'utf-8');
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
// File doesn't exist
|
|
275
|
+
}
|
|
276
|
+
const pkgResult = await agentsMdService.generatePackageContent(fullPkgPath, analysis, existingPkgContent);
|
|
277
|
+
if (!existingPkgContent || pkgResult.contentChanged) {
|
|
278
|
+
await writeFile(pkgAgentsMdPath, pkgResult.content, 'utf-8');
|
|
279
|
+
this.context.logger.debug(`Wrote AGENTS.md for ${pkgPath}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
169
283
|
/**
|
|
170
284
|
* Register repository in dashboard for visibility
|
|
171
285
|
* Called after successful analysis regardless of invocation method
|
|
@@ -506,6 +620,9 @@ export class AnalyseService {
|
|
|
506
620
|
lines.push(` Frameworks: ${result.analysis.frameworks.join(', ')}`);
|
|
507
621
|
lines.push(` Build Tools: ${result.analysis.buildTools.join(', ')}`);
|
|
508
622
|
lines.push(` Testing Tools: ${result.analysis.testingTools.join(', ')}`);
|
|
623
|
+
if (result.analysis.packageManager) {
|
|
624
|
+
lines.push(` Package Manager: ${result.analysis.packageManager}`);
|
|
625
|
+
}
|
|
509
626
|
if (result.analysis.documentationPattern) {
|
|
510
627
|
lines.push(`\n📚 Documentation: ${result.analysis.documentationPattern}`);
|
|
511
628
|
if (result.analysis.documentationLocations) {
|
|
@@ -578,8 +695,64 @@ export class AnalyseService {
|
|
|
578
695
|
lines.push(' None');
|
|
579
696
|
}
|
|
580
697
|
}
|
|
698
|
+
// Rich metadata sections (shown in all cases when data exists)
|
|
699
|
+
this.appendRichMetadataOutput(lines, result.analysis);
|
|
700
|
+
// AGENTS.md status (shown in all cases)
|
|
701
|
+
if (result.agentsMdStatus) {
|
|
702
|
+
const statusLabel = result.agentsMdStatus === 'created' ? 'Created'
|
|
703
|
+
: result.agentsMdStatus === 'updated' ? 'Updated'
|
|
704
|
+
: 'Up to date';
|
|
705
|
+
lines.push(`\nAGENTS.md: ${statusLabel}`);
|
|
706
|
+
}
|
|
581
707
|
return lines.join('\n');
|
|
582
708
|
}
|
|
709
|
+
/**
|
|
710
|
+
* Append rich metadata sections to output lines
|
|
711
|
+
*/
|
|
712
|
+
appendRichMetadataOutput(lines, analysis) {
|
|
713
|
+
// Package manager (shown when not already in tech stack section)
|
|
714
|
+
if (analysis.packageManager) {
|
|
715
|
+
lines.push(`\n Package Manager: ${analysis.packageManager}`);
|
|
716
|
+
}
|
|
717
|
+
// Commands section
|
|
718
|
+
const scripts = analysis.scripts;
|
|
719
|
+
if (scripts && (scripts.test?.command || scripts.build?.command || scripts.dev?.command || scripts.lint?.command || scripts.e2e?.command)) {
|
|
720
|
+
lines.push('\n📋 Commands:');
|
|
721
|
+
if (scripts.test?.command)
|
|
722
|
+
lines.push(` Test: ${scripts.test.command}`);
|
|
723
|
+
if (scripts.build?.command)
|
|
724
|
+
lines.push(` Build: ${scripts.build.command}`);
|
|
725
|
+
if (scripts.dev?.command)
|
|
726
|
+
lines.push(` Dev: ${scripts.dev.command}`);
|
|
727
|
+
if (scripts.lint?.command)
|
|
728
|
+
lines.push(` Lint: ${scripts.lint.command}`);
|
|
729
|
+
if (scripts.e2e?.command)
|
|
730
|
+
lines.push(` E2E: ${scripts.e2e.command}`);
|
|
731
|
+
}
|
|
732
|
+
// Linting section
|
|
733
|
+
if (analysis.linting) {
|
|
734
|
+
lines.push('\n🔍 Linting:');
|
|
735
|
+
if (analysis.linting.tool) {
|
|
736
|
+
lines.push(` Tool: ${analysis.linting.tool}`);
|
|
737
|
+
}
|
|
738
|
+
if (analysis.linting.plugins && analysis.linting.plugins.length > 0) {
|
|
739
|
+
lines.push(` Plugins: ${analysis.linting.plugins.join(', ')}`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
// Monorepo section
|
|
743
|
+
if (analysis.monorepo) {
|
|
744
|
+
lines.push('\n📦 Monorepo:');
|
|
745
|
+
if (analysis.monorepo.tool) {
|
|
746
|
+
lines.push(` Tool: ${analysis.monorepo.tool}`);
|
|
747
|
+
}
|
|
748
|
+
if (analysis.monorepo.packages && analysis.monorepo.packages.length > 0) {
|
|
749
|
+
lines.push(' Packages:');
|
|
750
|
+
for (const pkg of analysis.monorepo.packages) {
|
|
751
|
+
lines.push(` - ${pkg}`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
583
756
|
/**
|
|
584
757
|
* Compare two tech stacks and return differences
|
|
585
758
|
* Handles null previous analysis (first run)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"repo-service.d.ts","sourceRoot":"","sources":["../../src/services/repo-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAIlE,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,qBAAa,WAAW;IAMV,OAAO,CAAC,OAAO;IAL3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAA2B;IACnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAyB;IAE/D,OAAO,CAAC,aAAa,CAAgB;gBAEjB,OAAO,EAAE,cAAc;IAS3C;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACG,4BAA4B,CAChC,eAAe,GAAE,MAAoB,GACpC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmCzB;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAOvD;;OAEG;IACH,cAAc,IAAI,OAAO;IAUzB;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAcjD;;OAEG;IACH,kBAAkB,IAAI,MAAM;IAa5B;;;;;;;;OAQG;IACG,qBAAqB,CAAC,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,IAAI,CAAC;IA6FtF;;;OAGG;IACH,OAAO,CAAC,wBAAwB;
|
|
1
|
+
{"version":3,"file":"repo-service.d.ts","sourceRoot":"","sources":["../../src/services/repo-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAIlE,MAAM,WAAW,4BAA4B;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;GAIG;AACH,qBAAa,WAAW;IAMV,OAAO,CAAC,OAAO;IAL3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAA2B;IACnE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAyB;IAE/D,OAAO,CAAC,aAAa,CAAgB;gBAEjB,OAAO,EAAE,cAAc;IAS3C;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACG,4BAA4B,CAChC,eAAe,GAAE,MAAoB,GACpC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAmCzB;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAOvD;;OAEG;IACH,cAAc,IAAI,OAAO;IAUzB;;;OAGG;IACG,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;IAcjD;;OAEG;IACH,kBAAkB,IAAI,MAAM;IAa5B;;;;;;;;OAQG;IACG,qBAAqB,CAAC,OAAO,GAAE,4BAAiC,GAAG,OAAO,CAAC,IAAI,CAAC;IA6FtF;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IA0BhC;;OAEG;IACH,OAAO,CAAC,0BAA0B;IA6GlC;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAoChC;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAwErC;;OAEG;IACH,OAAO,CAAC,gCAAgC;CAyCzC"}
|
|
@@ -229,7 +229,8 @@ export class RepoService {
|
|
|
229
229
|
formatRepoContextSection(enableAgenticCoding) {
|
|
230
230
|
let content = `## Repository Context
|
|
231
231
|
|
|
232
|
-
Before starting work, read
|
|
232
|
+
Before starting work, read \`AGENTS.md\` for comprehensive project context (tech stack, commands, structure).
|
|
233
|
+
Also read \`.tiny-brain/analysis.json\` for detailed detection data and test patterns.
|
|
233
234
|
|
|
234
235
|
`;
|
|
235
236
|
if (enableAgenticCoding) {
|
|
@@ -292,6 +293,7 @@ Description of changes...
|
|
|
292
293
|
| \`fix:\` | Bug fixes | Yes |
|
|
293
294
|
| \`refactor:\` | Code improvement | Yes |
|
|
294
295
|
| \`chore:\` | Maintenance (untracked) | No |
|
|
296
|
+
| \`untracked:\` | Work not related to any PRD or Fix | No |
|
|
295
297
|
|
|
296
298
|
**WARNING:** The commit-msg hook will reject commits missing required headers.
|
|
297
299
|
|
|
@@ -500,8 +500,8 @@ export class AsTool {
|
|
|
500
500
|
if (libraryAuth?.token) {
|
|
501
501
|
formatted += ` 🔑 Connected to Tiny Brain Remote\n`;
|
|
502
502
|
}
|
|
503
|
-
if (libraryAuth?.
|
|
504
|
-
formatted += `
|
|
503
|
+
if (libraryAuth?.hasClaudeCli) {
|
|
504
|
+
formatted += ` 🤖 Claude CLI authenticated\n`;
|
|
505
505
|
}
|
|
506
506
|
// Only show pre-flight and agent-first workflow if agents are installed
|
|
507
507
|
if (hasAgents) {
|
|
@@ -19,5 +19,10 @@ 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 handleImplementPlan;
|
|
26
|
+
private static handleMergeResults;
|
|
22
27
|
}
|
|
23
28
|
//# 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;
|
|
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;AA0DrE;;GAEG;AACH,qBAAa,WAAW;IACtB,MAAM,CAAC,iBAAiB,IAAI,OAAO;WAiLtB,OAAO,CAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC;mBAgDD,UAAU;mBAqCV,aAAa;mBA2Bb,aAAa;mBA2Eb,aAAa;mBAsEb,UAAU;mBA0CV,iBAAiB;mBAgEjB,qBAAqB;mBA6BrB,kBAAkB;mBAgDlB,iBAAiB;mBAmDjB,mBAAmB;IAiDxC,OAAO,CAAC,MAAM,CAAC,kBAAkB;CAoBlC"}
|
|
@@ -8,12 +8,16 @@ 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([
|
|
16
|
+
operation: z.enum([
|
|
17
|
+
'save', 'history', 'details', 'compare', 'plan', 'plan-details',
|
|
18
|
+
'detect-analysers', 'run-analysers', 'merge-results', 'assemble-run',
|
|
19
|
+
'implement-plan',
|
|
20
|
+
]),
|
|
17
21
|
// For 'save' operation
|
|
18
22
|
score: z.number().min(0).max(100).optional(),
|
|
19
23
|
grade: z.enum(['A', 'B', 'C', 'D', 'F']).optional(),
|
|
@@ -38,6 +42,9 @@ const QualityArgsSchema = z.object({
|
|
|
38
42
|
targetGrade: z.string().optional(),
|
|
39
43
|
// For 'plan-details' operation
|
|
40
44
|
planId: z.string().optional(),
|
|
45
|
+
// For 'merge-results' operation
|
|
46
|
+
analyzerIssues: z.array(QualityIssueSchema).optional(),
|
|
47
|
+
llmIssues: z.array(QualityIssueSchema).optional(),
|
|
41
48
|
});
|
|
42
49
|
/**
|
|
43
50
|
* MCP Tool for quality analysis persistence
|
|
@@ -57,6 +64,11 @@ Persists and retrieves quality analysis results. Analysis is performed by agents
|
|
|
57
64
|
• compare: Compare two runs to see new, resolved, and persistent issues
|
|
58
65
|
• plan: Generate a Quality Improvement Plan from a saved run
|
|
59
66
|
• plan-details: Retrieve a saved Quality Improvement Plan by ID
|
|
67
|
+
• detect-analysers: Scan repo for configured static analyzers
|
|
68
|
+
• run-analysers: Execute detected analyzers and return normalized issues
|
|
69
|
+
• merge-results: Merge analyzer + LLM issues with deduplication
|
|
70
|
+
• assemble-run: Read all intermediate files, merge, score, and save final report
|
|
71
|
+
• implement-plan: Generate fix documents from a saved Quality Improvement Plan
|
|
60
72
|
|
|
61
73
|
💡 WORKFLOW:
|
|
62
74
|
1. Quality-coordinator agent performs analysis
|
|
@@ -69,7 +81,11 @@ Persists and retrieves quality analysis results. Analysis is performed by agents
|
|
|
69
81
|
properties: {
|
|
70
82
|
operation: {
|
|
71
83
|
type: 'string',
|
|
72
|
-
enum: [
|
|
84
|
+
enum: [
|
|
85
|
+
'save', 'history', 'details', 'compare', 'plan', 'plan-details',
|
|
86
|
+
'detect-analysers', 'run-analysers', 'merge-results', 'assemble-run',
|
|
87
|
+
'implement-plan',
|
|
88
|
+
],
|
|
73
89
|
description: 'The quality operation to perform',
|
|
74
90
|
},
|
|
75
91
|
// For 'save' operation
|
|
@@ -151,7 +167,7 @@ Persists and retrieves quality analysis results. Analysis is performed by agents
|
|
|
151
167
|
// For 'details' operation
|
|
152
168
|
runId: {
|
|
153
169
|
type: 'string',
|
|
154
|
-
description: 'Run ID
|
|
170
|
+
description: 'Run ID (required for details, assemble-run; optional for run-analysers to reuse Phase 1 directory)',
|
|
155
171
|
},
|
|
156
172
|
// For 'compare' operation
|
|
157
173
|
baseRunId: {
|
|
@@ -176,6 +192,35 @@ Persists and retrieves quality analysis results. Analysis is performed by agents
|
|
|
176
192
|
type: 'string',
|
|
177
193
|
description: 'Plan ID to retrieve details for (required for plan-details)',
|
|
178
194
|
},
|
|
195
|
+
// For 'merge-results' operation
|
|
196
|
+
analyzerIssues: {
|
|
197
|
+
type: 'array',
|
|
198
|
+
description: 'Array of analyzer issues (required for merge-results)',
|
|
199
|
+
items: {
|
|
200
|
+
type: 'object',
|
|
201
|
+
properties: {
|
|
202
|
+
category: { type: 'string' },
|
|
203
|
+
severity: { type: 'string' },
|
|
204
|
+
file: { type: 'string' },
|
|
205
|
+
message: { type: 'string' },
|
|
206
|
+
},
|
|
207
|
+
required: ['category', 'severity', 'file', 'message'],
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
llmIssues: {
|
|
211
|
+
type: 'array',
|
|
212
|
+
description: 'Array of LLM investigation issues (required for merge-results)',
|
|
213
|
+
items: {
|
|
214
|
+
type: 'object',
|
|
215
|
+
properties: {
|
|
216
|
+
category: { type: 'string' },
|
|
217
|
+
severity: { type: 'string' },
|
|
218
|
+
file: { type: 'string' },
|
|
219
|
+
message: { type: 'string' },
|
|
220
|
+
},
|
|
221
|
+
required: ['category', 'severity', 'file', 'message'],
|
|
222
|
+
},
|
|
223
|
+
},
|
|
179
224
|
},
|
|
180
225
|
required: ['operation'],
|
|
181
226
|
},
|
|
@@ -202,6 +247,16 @@ Persists and retrieves quality analysis results. Analysis is performed by agents
|
|
|
202
247
|
return await QualityTool.handlePlan(validatedArgs, service);
|
|
203
248
|
case 'plan-details':
|
|
204
249
|
return await QualityTool.handlePlanDetails(validatedArgs, context.repositoryRoot);
|
|
250
|
+
case 'detect-analysers':
|
|
251
|
+
return await QualityTool.handleDetectAnalyzers(context.repositoryRoot);
|
|
252
|
+
case 'run-analysers':
|
|
253
|
+
return await QualityTool.handleRunAnalyzers(validatedArgs, context.repositoryRoot);
|
|
254
|
+
case 'merge-results':
|
|
255
|
+
return QualityTool.handleMergeResults(validatedArgs);
|
|
256
|
+
case 'assemble-run':
|
|
257
|
+
return await QualityTool.handleAssembleRun(validatedArgs, context.repositoryRoot);
|
|
258
|
+
case 'implement-plan':
|
|
259
|
+
return await QualityTool.handleImplementPlan(validatedArgs, service);
|
|
205
260
|
default:
|
|
206
261
|
return createErrorResult(`Unknown operation: ${validatedArgs.operation}`);
|
|
207
262
|
}
|
|
@@ -458,4 +513,150 @@ Persists and retrieves quality analysis results. Analysis is performed by agents
|
|
|
458
513
|
return createErrorResult(`Improvement plan not found: ${args.planId}`);
|
|
459
514
|
}
|
|
460
515
|
}
|
|
516
|
+
static async handleDetectAnalyzers(repositoryRoot) {
|
|
517
|
+
const service = new AnalyzerDetectionService(repositoryRoot);
|
|
518
|
+
const analyzers = await service.detectAnalyzers();
|
|
519
|
+
if (analyzers.length === 0) {
|
|
520
|
+
return createSuccessResult('No static analyzers detected in this repository.\n\nSupported analyzers: ESLint, TypeScript, npm audit, RuboCop, ruff.');
|
|
521
|
+
}
|
|
522
|
+
const lines = [
|
|
523
|
+
`🔍 Detected ${analyzers.length} analyzer(s):`,
|
|
524
|
+
'',
|
|
525
|
+
];
|
|
526
|
+
for (const analyzer of analyzers) {
|
|
527
|
+
lines.push(`- **${analyzer.name}** (${analyzer.analyzerId})`);
|
|
528
|
+
lines.push(` Configs: ${analyzer.configPaths.join(', ')}`);
|
|
529
|
+
lines.push(` Categories: ${analyzer.categories.join(', ')}`);
|
|
530
|
+
}
|
|
531
|
+
lines.push('');
|
|
532
|
+
lines.push(JSON.stringify(analyzers, null, 2));
|
|
533
|
+
return createSuccessResult(lines.join('\n'));
|
|
534
|
+
}
|
|
535
|
+
static async handleRunAnalyzers(args, repositoryRoot) {
|
|
536
|
+
const runId = args.runId ?? generateRunId();
|
|
537
|
+
const runDir = path.join(repositoryRoot, 'docs', 'quality', 'runs', runIdToPath(runId));
|
|
538
|
+
const outputPath = path.join(runDir, 'analysis.json');
|
|
539
|
+
const outputDir = path.join(runDir, 'analysers');
|
|
540
|
+
const detectionService = new AnalyzerDetectionService(repositoryRoot);
|
|
541
|
+
const analyzers = await detectionService.detectAnalyzers();
|
|
542
|
+
const emptyResults = {
|
|
543
|
+
issues: [],
|
|
544
|
+
executions: [],
|
|
545
|
+
totalDurationMs: 0,
|
|
546
|
+
summary: { total: 0, succeeded: 0, failed: 0, timedOut: 0, skipped: 0 },
|
|
547
|
+
};
|
|
548
|
+
if (analyzers.length === 0) {
|
|
549
|
+
await fs.mkdir(runDir, { recursive: true });
|
|
550
|
+
await fs.writeFile(outputPath, JSON.stringify(emptyResults, null, 2), 'utf-8');
|
|
551
|
+
return createSuccessResult(`🔧 No analyzers detected. Empty results written to file.\n Run ID: ${runId}\n Output: ${outputPath}`);
|
|
552
|
+
}
|
|
553
|
+
const executorService = new AnalyzerExecutorService(repositoryRoot);
|
|
554
|
+
const results = await executorService.executeAnalyzers(analyzers, outputDir);
|
|
555
|
+
// Write merged/normalized result to the auto-generated run directory
|
|
556
|
+
await fs.mkdir(runDir, { recursive: true });
|
|
557
|
+
await fs.writeFile(outputPath, JSON.stringify(results, null, 2), 'utf-8');
|
|
558
|
+
const lines = [
|
|
559
|
+
`🔧 Executed ${results.summary.total} analyzer(s):`,
|
|
560
|
+
` Succeeded: ${results.summary.succeeded}`,
|
|
561
|
+
` Failed: ${results.summary.failed}`,
|
|
562
|
+
` Total issues: ${results.issues.length}`,
|
|
563
|
+
` Duration: ${results.totalDurationMs}ms`,
|
|
564
|
+
` Run ID: ${runId}`,
|
|
565
|
+
` Output: ${outputPath}`,
|
|
566
|
+
` Raw files: ${outputDir}/`,
|
|
567
|
+
];
|
|
568
|
+
return createSuccessResult(lines.join('\n'));
|
|
569
|
+
}
|
|
570
|
+
static async handleAssembleRun(args, repositoryRoot) {
|
|
571
|
+
if (!args.runId) {
|
|
572
|
+
return createErrorResult('runId is required for assemble-run operation (YYYY-MM-DD date prefix)');
|
|
573
|
+
}
|
|
574
|
+
const assemblyService = new AssemblyService(repositoryRoot);
|
|
575
|
+
const result = await assemblyService.assembleRun(args.runId, args.baseRunId);
|
|
576
|
+
const lines = [
|
|
577
|
+
'✅ Quality run assembled successfully!',
|
|
578
|
+
'',
|
|
579
|
+
`📊 Run ID: ${result.runId}`,
|
|
580
|
+
`📈 Score: ${result.score}/100 (Grade: ${result.grade})`,
|
|
581
|
+
`🔍 Issues: ${result.issueCount}`,
|
|
582
|
+
`📁 File: ${result.filePath}`,
|
|
583
|
+
];
|
|
584
|
+
if (result.planId) {
|
|
585
|
+
lines.push(`📋 Plan: ${result.planId}`);
|
|
586
|
+
lines.push(`📁 Plan File: ${result.planFilePath}`);
|
|
587
|
+
}
|
|
588
|
+
if (result.incremental) {
|
|
589
|
+
lines.push('');
|
|
590
|
+
lines.push('### Incremental Analysis');
|
|
591
|
+
lines.push(` Base run: ${result.baseRunId}`);
|
|
592
|
+
lines.push(` Files analyzed: ${result.filesAnalyzed}`);
|
|
593
|
+
lines.push(` Files carried forward: ${result.filesCarriedForward}`);
|
|
594
|
+
}
|
|
595
|
+
lines.push('');
|
|
596
|
+
lines.push('### Source Breakdown');
|
|
597
|
+
lines.push(` Analyzer: ${result.sourceBreakdown.analyzer}`);
|
|
598
|
+
lines.push(` Specialist: ${result.sourceBreakdown.llm}`);
|
|
599
|
+
lines.push(` Total: ${result.sourceBreakdown.total}`);
|
|
600
|
+
const categoryEntries = Object.entries(result.categoryBreakdown);
|
|
601
|
+
if (categoryEntries.length > 0) {
|
|
602
|
+
lines.push('');
|
|
603
|
+
lines.push('### Category Breakdown');
|
|
604
|
+
for (const [category, count] of categoryEntries) {
|
|
605
|
+
lines.push(` ${category}: ${count}`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return createSuccessResult(lines.join('\n'));
|
|
609
|
+
}
|
|
610
|
+
static async handleImplementPlan(args, service) {
|
|
611
|
+
if (!args.planId) {
|
|
612
|
+
return createErrorResult('planId is required for implement-plan operation');
|
|
613
|
+
}
|
|
614
|
+
const plan = await service.loadImprovementPlan(args.planId);
|
|
615
|
+
if (!plan) {
|
|
616
|
+
return createErrorResult(`Improvement plan not found: ${args.planId}`);
|
|
617
|
+
}
|
|
618
|
+
const run = await service.getRunDetails(plan.sourceRunId);
|
|
619
|
+
if (!run) {
|
|
620
|
+
return createErrorResult(`Source quality run not found: ${plan.sourceRunId}`);
|
|
621
|
+
}
|
|
622
|
+
const fixIds = await service.generateFixesFromPlan(plan, run.issues);
|
|
623
|
+
if (fixIds.length === 0) {
|
|
624
|
+
return createSuccessResult('No initiatives found in plan. No fix documents created.');
|
|
625
|
+
}
|
|
626
|
+
const lines = [
|
|
627
|
+
`Created ${fixIds.length} fix document(s) from Quality Improvement Plan ${args.planId}`,
|
|
628
|
+
'',
|
|
629
|
+
'## Fixes Created',
|
|
630
|
+
'',
|
|
631
|
+
'| Fix ID | File |',
|
|
632
|
+
'|--------|------|',
|
|
633
|
+
];
|
|
634
|
+
for (const fixId of fixIds) {
|
|
635
|
+
lines.push(`| ${fixId} | .tiny-brain/fixes/${fixId}.md |`);
|
|
636
|
+
}
|
|
637
|
+
lines.push('');
|
|
638
|
+
lines.push('## Next Steps');
|
|
639
|
+
lines.push('');
|
|
640
|
+
for (const fixId of fixIds) {
|
|
641
|
+
lines.push(`1. Run \`npx tiny-brain sync-file .tiny-brain/fixes/${fixId}.md\``);
|
|
642
|
+
}
|
|
643
|
+
lines.push('2. Start working on fixes using the /fix workflow');
|
|
644
|
+
lines.push('3. Track progress in the dashboard');
|
|
645
|
+
return createSuccessResult(lines.join('\n'));
|
|
646
|
+
}
|
|
647
|
+
static handleMergeResults(args) {
|
|
648
|
+
const analyzerIssues = args.analyzerIssues ?? [];
|
|
649
|
+
const llmIssues = args.llmIssues ?? [];
|
|
650
|
+
const result = mergeResults(analyzerIssues, llmIssues);
|
|
651
|
+
const lines = [
|
|
652
|
+
`🔀 Merged results:`,
|
|
653
|
+
` Analyzer issues: ${result.sourceBreakdown.analyzer}`,
|
|
654
|
+
` LLM issues: ${result.sourceBreakdown.llm}`,
|
|
655
|
+
` Total (deduplicated): ${result.sourceBreakdown.total}`,
|
|
656
|
+
` Duplicates removed: ${result.duplicatesRemoved}`,
|
|
657
|
+
'',
|
|
658
|
+
JSON.stringify(result, null, 2),
|
|
659
|
+
];
|
|
660
|
+
return createSuccessResult(lines.join('\n'));
|
|
661
|
+
}
|
|
461
662
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magic-ingredients/tiny-brain-local",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
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.
|
|
34
|
+
"@magic-ingredients/tiny-brain-core": "^0.20.0",
|
|
35
35
|
"@magic-ingredients/tiny-brain-dashboard": "file:../tiny-brain-dashboard",
|
|
36
36
|
"@modelcontextprotocol/sdk": "^1.0.6",
|
|
37
37
|
"chalk": "^5.3.0",
|