@magic-ingredients/tiny-brain-local 0.16.0 → 0.17.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,106 @@
1
+ /**
2
+ * Tech Context Service
3
+ *
4
+ * Manages reading and writing of tech context files:
5
+ * - .tiny-brain/analysis.json - repo analysis data
6
+ * - .tiny-brain/tech/*.md - per-tech expertise files
7
+ * - .tiny-brain/tech/config.json - agent mode configuration
8
+ * - .claude/agents/tech-*.md - tech agent files (when enableAgentic=true)
9
+ */
10
+ import type { RepoAnalysisFile, TechExpertiseFrontmatter, TechExpertiseFile, TechConfig } from '@magic-ingredients/tiny-brain-core';
11
+ /** Input analysis data from repository analysis */
12
+ export interface AnalysisInput {
13
+ languages: string[];
14
+ frameworks: string[];
15
+ testingTools: string[];
16
+ buildTools: string[];
17
+ hasTests: boolean;
18
+ testFileCount: number;
19
+ testPatterns: string[];
20
+ isPolyglot?: boolean;
21
+ primaryLanguage?: string;
22
+ documentationPattern?: 'single-readme' | 'folder-readmes' | 'docs-folder' | 'mixed';
23
+ documentationLocations?: string[];
24
+ }
25
+ export declare class TechContextService {
26
+ private readonly tinyBrainDir;
27
+ private readonly techDir;
28
+ private readonly agentsDir;
29
+ constructor(repoPath: string);
30
+ /** Get the .tiny-brain directory path */
31
+ getTinyBrainDir(): string;
32
+ /** Get the .tiny-brain/tech directory path */
33
+ getTechDir(): string;
34
+ /** Get the .claude/agents directory path */
35
+ getAgentsDir(): string;
36
+ /** Ensure required directories exist */
37
+ ensureDirectories(): Promise<void>;
38
+ /**
39
+ * Write analysis data to .tiny-brain/analysis.json
40
+ */
41
+ writeAnalysis(analysis: AnalysisInput): Promise<void>;
42
+ /**
43
+ * Write a tech expertise file with YAML frontmatter
44
+ */
45
+ writeTechFile(name: string, frontmatter: TechExpertiseFrontmatter, content: string): Promise<void>;
46
+ /**
47
+ * Write raw markdown content to a tech file
48
+ * Used when receiving complete markdown from TBR API
49
+ */
50
+ writeTechFileRaw(name: string, content: string): Promise<void>;
51
+ /**
52
+ * Read analysis data from .tiny-brain/analysis.json
53
+ */
54
+ readAnalysis(): Promise<RepoAnalysisFile | null>;
55
+ /**
56
+ * Read all tech expertise files from .tiny-brain/tech/
57
+ */
58
+ readTechFiles(): Promise<TechExpertiseFile[]>;
59
+ /**
60
+ * Get tech files that match a given file path based on filePatterns
61
+ */
62
+ getTechForFile(filePath: string): Promise<TechExpertiseFile[]>;
63
+ /**
64
+ * Check if the tech stack has changed compared to previous analysis
65
+ */
66
+ hasStackChanged(analysis: AnalysisInput): Promise<boolean>;
67
+ /**
68
+ * Write config to .tiny-brain/tech/config.json
69
+ */
70
+ writeConfig(config: Omit<TechConfig, 'lastSynced'>): Promise<void>;
71
+ /**
72
+ * Read config from .tiny-brain/tech/config.json
73
+ */
74
+ readConfig(): Promise<TechConfig>;
75
+ /**
76
+ * Install tech agents to .claude/agents/
77
+ * Converts tech context files into proper Claude Code sub-agents with:
78
+ * - name: tech-{name} (matches subagent_type="tech-react")
79
+ * - description: for Claude Code auto-delegation
80
+ * - Sub-agent invocation context
81
+ */
82
+ installTechAgents(): Promise<void>;
83
+ /**
84
+ * Remove only tech-*.md files from .claude/agents/
85
+ */
86
+ removeTechAgents(): Promise<void>;
87
+ /**
88
+ * Sync agents based on enableAgentic preference
89
+ */
90
+ syncAgents(enableAgentic: boolean): Promise<void>;
91
+ /**
92
+ * Get versions of all local tech context files
93
+ * @returns Map of tech name → version
94
+ */
95
+ getLocalTechVersions(): Promise<Map<string, string>>;
96
+ /**
97
+ * Compare versions to determine if TBS version is newer
98
+ * @returns true if tbsVersion is newer than localVersion
99
+ */
100
+ shouldUpdateTech(localVersion: string, tbsVersion: string): boolean;
101
+ /**
102
+ * Parse YAML frontmatter from a markdown file
103
+ */
104
+ private parseFrontmatter;
105
+ }
106
+ //# sourceMappingURL=tech-context-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tech-context-service.d.ts","sourceRoot":"","sources":["../../src/services/tech-context-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EACV,gBAAgB,EAGhB,wBAAwB,EACxB,iBAAiB,EACjB,UAAU,EACX,MAAM,oCAAoC,CAAC;AAE5C,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,eAAe,GAAG,gBAAgB,GAAG,aAAa,GAAG,OAAO,CAAC;IACpF,sBAAsB,CAAC,EAAE,MAAM,EAAE,CAAC;CACnC;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;gBAEvB,QAAQ,EAAE,MAAM;IAM5B,yCAAyC;IACzC,eAAe,IAAI,MAAM;IAIzB,8CAA8C;IAC9C,UAAU,IAAI,MAAM;IAIpB,4CAA4C;IAC5C,YAAY,IAAI,MAAM;IAItB,wCAAwC;IAClC,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAKxC;;OAEG;IACG,aAAa,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAoC3D;;OAEG;IACG,aAAa,CACjB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,wBAAwB,EACrC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC;IAmBhB;;;OAGG;IACG,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpE;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAWtD;;OAEG;IACG,aAAa,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IA0BnD;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAwBpE;;OAEG;IACG,eAAe,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;IA4BhE;;OAEG;IACG,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAYxE;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC;IAWvC;;;;;;OAMG;IACG,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC;IAkCxC;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC;IAcvC;;OAEG;IACG,UAAU,CAAC,aAAa,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAUvD;;;OAGG;IACG,oBAAoB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAW1D;;;OAGG;IACH,gBAAgB,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO;IAsBnE;;OAEG;IACH,OAAO,CAAC,gBAAgB;CA+CzB"}
@@ -0,0 +1,365 @@
1
+ /**
2
+ * Tech Context Service
3
+ *
4
+ * Manages reading and writing of tech context files:
5
+ * - .tiny-brain/analysis.json - repo analysis data
6
+ * - .tiny-brain/tech/*.md - per-tech expertise files
7
+ * - .tiny-brain/tech/config.json - agent mode configuration
8
+ * - .claude/agents/tech-*.md - tech agent files (when enableAgentic=true)
9
+ */
10
+ import { promises as fs } from 'fs';
11
+ import path from 'path';
12
+ import crypto from 'crypto';
13
+ import minimatch from 'minimatch';
14
+ export class TechContextService {
15
+ tinyBrainDir;
16
+ techDir;
17
+ agentsDir;
18
+ constructor(repoPath) {
19
+ this.tinyBrainDir = path.join(repoPath, '.tiny-brain');
20
+ this.techDir = path.join(this.tinyBrainDir, 'tech');
21
+ this.agentsDir = path.join(repoPath, '.claude', 'agents');
22
+ }
23
+ /** Get the .tiny-brain directory path */
24
+ getTinyBrainDir() {
25
+ return this.tinyBrainDir;
26
+ }
27
+ /** Get the .tiny-brain/tech directory path */
28
+ getTechDir() {
29
+ return this.techDir;
30
+ }
31
+ /** Get the .claude/agents directory path */
32
+ getAgentsDir() {
33
+ return this.agentsDir;
34
+ }
35
+ /** Ensure required directories exist */
36
+ async ensureDirectories() {
37
+ await fs.mkdir(this.tinyBrainDir, { recursive: true });
38
+ await fs.mkdir(this.techDir, { recursive: true });
39
+ }
40
+ /**
41
+ * Write analysis data to .tiny-brain/analysis.json
42
+ */
43
+ async writeAnalysis(analysis) {
44
+ await this.ensureDirectories();
45
+ const stack = {
46
+ languages: analysis.languages,
47
+ frameworks: analysis.frameworks,
48
+ testing: analysis.testingTools,
49
+ build: analysis.buildTools,
50
+ };
51
+ const analysisData = {
52
+ hasTests: analysis.hasTests,
53
+ testFileCount: analysis.testFileCount,
54
+ testPatterns: analysis.testPatterns,
55
+ isPolyglot: analysis.isPolyglot ?? false,
56
+ primaryLanguage: analysis.primaryLanguage ?? analysis.languages[0] ?? 'unknown',
57
+ documentationPattern: analysis.documentationPattern,
58
+ documentationLocations: analysis.documentationLocations,
59
+ };
60
+ // Generate hash from stable analysis input
61
+ const hashInput = JSON.stringify({ stack, analysis: analysisData });
62
+ const analysisHash = crypto.createHash('sha256').update(hashInput).digest('hex');
63
+ const file = {
64
+ version: '1.0',
65
+ detectedAt: new Date().toISOString(),
66
+ analysisHash,
67
+ stack,
68
+ analysis: analysisData,
69
+ };
70
+ const filePath = path.join(this.tinyBrainDir, 'analysis.json');
71
+ await fs.writeFile(filePath, JSON.stringify(file, null, 2), 'utf-8');
72
+ }
73
+ /**
74
+ * Write a tech expertise file with YAML frontmatter
75
+ */
76
+ async writeTechFile(name, frontmatter, content) {
77
+ await this.ensureDirectories();
78
+ const yamlFrontmatter = [
79
+ '---',
80
+ `name: ${frontmatter.name}`,
81
+ `version: ${frontmatter.version}`,
82
+ `domain: ${frontmatter.domain}`,
83
+ `filePatterns:`,
84
+ ...frontmatter.filePatterns.map(p => ` - "${p}"`),
85
+ frontmatter.description ? `description: "${frontmatter.description}"` : null,
86
+ '---',
87
+ ].filter(Boolean).join('\n');
88
+ const fileContent = `${yamlFrontmatter}\n\n${content}`;
89
+ const filePath = path.join(this.techDir, `${name}.md`);
90
+ await fs.writeFile(filePath, fileContent, 'utf-8');
91
+ }
92
+ /**
93
+ * Write raw markdown content to a tech file
94
+ * Used when receiving complete markdown from TBR API
95
+ */
96
+ async writeTechFileRaw(name, content) {
97
+ await this.ensureDirectories();
98
+ const filePath = path.join(this.techDir, `${name}.md`);
99
+ await fs.writeFile(filePath, content, 'utf-8');
100
+ }
101
+ /**
102
+ * Read analysis data from .tiny-brain/analysis.json
103
+ */
104
+ async readAnalysis() {
105
+ const filePath = path.join(this.tinyBrainDir, 'analysis.json');
106
+ try {
107
+ const content = await fs.readFile(filePath, 'utf-8');
108
+ return JSON.parse(content);
109
+ }
110
+ catch {
111
+ return null;
112
+ }
113
+ }
114
+ /**
115
+ * Read all tech expertise files from .tiny-brain/tech/
116
+ */
117
+ async readTechFiles() {
118
+ try {
119
+ const files = await fs.readdir(this.techDir);
120
+ const techFiles = [];
121
+ for (const file of files) {
122
+ if (!file.endsWith('.md'))
123
+ continue;
124
+ const filePath = path.join(this.techDir, file);
125
+ const content = await fs.readFile(filePath, 'utf-8');
126
+ const parsed = this.parseFrontmatter(content);
127
+ if (parsed) {
128
+ techFiles.push({
129
+ ...parsed,
130
+ filePath,
131
+ });
132
+ }
133
+ }
134
+ return techFiles;
135
+ }
136
+ catch {
137
+ return [];
138
+ }
139
+ }
140
+ /**
141
+ * Get tech files that match a given file path based on filePatterns
142
+ */
143
+ async getTechForFile(filePath) {
144
+ const techFiles = await this.readTechFiles();
145
+ const matches = [];
146
+ const basename = path.basename(filePath);
147
+ for (const techFile of techFiles) {
148
+ for (const pattern of techFile.frontmatter.filePatterns) {
149
+ // Check multiple matching strategies:
150
+ // 1. Full path glob match
151
+ // 2. Basename match (for patterns like *.tsx)
152
+ // 3. Directory prefix match (for patterns like components/)
153
+ if (minimatch(filePath, pattern) ||
154
+ minimatch(basename, pattern) ||
155
+ minimatch(filePath, `**/${pattern}`) ||
156
+ filePath.includes(pattern.replace(/\/$/, ''))) {
157
+ matches.push(techFile);
158
+ break; // Don't add same file multiple times
159
+ }
160
+ }
161
+ }
162
+ return matches;
163
+ }
164
+ /**
165
+ * Check if the tech stack has changed compared to previous analysis
166
+ */
167
+ async hasStackChanged(analysis) {
168
+ const existing = await this.readAnalysis();
169
+ if (!existing)
170
+ return true;
171
+ // Generate hash for new analysis using same logic as writeAnalysis
172
+ const stack = {
173
+ languages: analysis.languages,
174
+ frameworks: analysis.frameworks,
175
+ testing: analysis.testingTools,
176
+ build: analysis.buildTools,
177
+ };
178
+ const analysisData = {
179
+ hasTests: analysis.hasTests,
180
+ testFileCount: analysis.testFileCount,
181
+ testPatterns: analysis.testPatterns,
182
+ isPolyglot: analysis.isPolyglot ?? false,
183
+ primaryLanguage: analysis.primaryLanguage ?? analysis.languages[0] ?? 'unknown',
184
+ documentationPattern: analysis.documentationPattern,
185
+ documentationLocations: analysis.documentationLocations,
186
+ };
187
+ const hashInput = JSON.stringify({ stack, analysis: analysisData });
188
+ const newHash = crypto.createHash('sha256').update(hashInput).digest('hex');
189
+ return newHash !== existing.analysisHash;
190
+ }
191
+ /**
192
+ * Write config to .tiny-brain/tech/config.json
193
+ */
194
+ async writeConfig(config) {
195
+ await this.ensureDirectories();
196
+ const fullConfig = {
197
+ ...config,
198
+ lastSynced: new Date().toISOString(),
199
+ };
200
+ const filePath = path.join(this.techDir, 'config.json');
201
+ await fs.writeFile(filePath, JSON.stringify(fullConfig, null, 2), 'utf-8');
202
+ }
203
+ /**
204
+ * Read config from .tiny-brain/tech/config.json
205
+ */
206
+ async readConfig() {
207
+ const filePath = path.join(this.techDir, 'config.json');
208
+ try {
209
+ const content = await fs.readFile(filePath, 'utf-8');
210
+ return JSON.parse(content);
211
+ }
212
+ catch {
213
+ return { useAgents: false };
214
+ }
215
+ }
216
+ /**
217
+ * Install tech agents to .claude/agents/
218
+ * Converts tech context files into proper Claude Code sub-agents with:
219
+ * - name: tech-{name} (matches subagent_type="tech-react")
220
+ * - description: for Claude Code auto-delegation
221
+ * - Sub-agent invocation context
222
+ */
223
+ async installTechAgents() {
224
+ await fs.mkdir(this.agentsDir, { recursive: true });
225
+ const techFiles = await this.readTechFiles();
226
+ for (const techFile of techFiles) {
227
+ const name = techFile.frontmatter.name;
228
+ const agentFileName = `tech-${name}.md`;
229
+ const agentPath = path.join(this.agentsDir, agentFileName);
230
+ // Build description from frontmatter or generate default
231
+ const description = techFile.frontmatter.description
232
+ || `${name} development specialist. Use for ${techFile.frontmatter.domain} tasks involving ${name}.`;
233
+ // Create agent file with proper Claude Code sub-agent format
234
+ const agentContent = `---
235
+ name: tech-${name}
236
+ description: ${description}
237
+ version: ${techFile.frontmatter.version}
238
+ domain: ${techFile.frontmatter.domain}
239
+ ---
240
+
241
+ # ${name} Sub-Agent
242
+
243
+ You are a specialized ${name} development agent invoked by the developer agent. Apply the expertise and patterns below to the task you've been given.
244
+
245
+ ## Tech Expertise
246
+
247
+ ${techFile.content}`;
248
+ await fs.writeFile(agentPath, agentContent, 'utf-8');
249
+ }
250
+ }
251
+ /**
252
+ * Remove only tech-*.md files from .claude/agents/
253
+ */
254
+ async removeTechAgents() {
255
+ try {
256
+ const files = await fs.readdir(this.agentsDir);
257
+ for (const file of files) {
258
+ if (file.startsWith('tech-') && file.endsWith('.md')) {
259
+ await fs.unlink(path.join(this.agentsDir, file));
260
+ }
261
+ }
262
+ }
263
+ catch {
264
+ // Directory may not exist, ignore
265
+ }
266
+ }
267
+ /**
268
+ * Sync agents based on enableAgentic preference
269
+ */
270
+ async syncAgents(enableAgentic) {
271
+ await this.writeConfig({ useAgents: enableAgentic });
272
+ if (enableAgentic) {
273
+ await this.installTechAgents();
274
+ }
275
+ else {
276
+ await this.removeTechAgents();
277
+ }
278
+ }
279
+ /**
280
+ * Get versions of all local tech context files
281
+ * @returns Map of tech name → version
282
+ */
283
+ async getLocalTechVersions() {
284
+ const techFiles = await this.readTechFiles();
285
+ const versions = new Map();
286
+ for (const file of techFiles) {
287
+ versions.set(file.frontmatter.name, file.frontmatter.version);
288
+ }
289
+ return versions;
290
+ }
291
+ /**
292
+ * Compare versions to determine if TBS version is newer
293
+ * @returns true if tbsVersion is newer than localVersion
294
+ */
295
+ shouldUpdateTech(localVersion, tbsVersion) {
296
+ const parseVersion = (v) => {
297
+ return v.split('.').map(s => parseInt(s, 10) || 0);
298
+ };
299
+ const local = parseVersion(localVersion);
300
+ const tbs = parseVersion(tbsVersion);
301
+ // Pad arrays to same length
302
+ const maxLen = Math.max(local.length, tbs.length);
303
+ while (local.length < maxLen)
304
+ local.push(0);
305
+ while (tbs.length < maxLen)
306
+ tbs.push(0);
307
+ // Compare each segment
308
+ for (let i = 0; i < maxLen; i++) {
309
+ if (tbs[i] > local[i])
310
+ return true;
311
+ if (tbs[i] < local[i])
312
+ return false;
313
+ }
314
+ return false; // Versions are equal
315
+ }
316
+ /**
317
+ * Parse YAML frontmatter from a markdown file
318
+ */
319
+ parseFrontmatter(content) {
320
+ const match = content.match(/^---\n([\s\S]*?)\n---\n\n?([\s\S]*)$/);
321
+ if (!match)
322
+ return null;
323
+ const yamlContent = match[1];
324
+ const markdownContent = match[2];
325
+ // Simple YAML parsing for our known structure
326
+ const frontmatter = {};
327
+ const lines = yamlContent.split('\n');
328
+ let currentKey = '';
329
+ let filePatterns = [];
330
+ for (const line of lines) {
331
+ const keyMatch = line.match(/^(\w+):\s*(.*)$/);
332
+ if (keyMatch) {
333
+ currentKey = keyMatch[1];
334
+ const value = keyMatch[2].replace(/^["']|["']$/g, '');
335
+ if (currentKey === 'filePatterns') {
336
+ filePatterns = [];
337
+ }
338
+ else if (currentKey === 'name') {
339
+ frontmatter.name = value;
340
+ }
341
+ else if (currentKey === 'version') {
342
+ frontmatter.version = value;
343
+ }
344
+ else if (currentKey === 'domain') {
345
+ frontmatter.domain = value;
346
+ }
347
+ else if (currentKey === 'description') {
348
+ frontmatter.description = value;
349
+ }
350
+ }
351
+ else if (currentKey === 'filePatterns' && line.trim().startsWith('-')) {
352
+ const pattern = line.trim().replace(/^-\s*/, '').replace(/^["']|["']$/g, '');
353
+ filePatterns.push(pattern);
354
+ }
355
+ }
356
+ frontmatter.filePatterns = filePatterns;
357
+ if (!frontmatter.name || !frontmatter.version || !frontmatter.domain) {
358
+ return null;
359
+ }
360
+ return {
361
+ frontmatter: frontmatter,
362
+ content: markdownContent,
363
+ };
364
+ }
365
+ }
@@ -2,10 +2,10 @@ import { type ToolArguments, type ToolResult } from './index.js';
2
2
  import type { RequestContext } from '../types/request-context.js';
3
3
  import { Tool as MCPTool } from '@modelcontextprotocol/sdk/types.js';
4
4
  /**
5
- * Analyse Tool - Analyze repository and manage agents
5
+ * Analyse Tool - Analyze repository and write tech context files
6
6
  *
7
- * This tool provides repository analysis and agent management functionality.
8
- * It analyzes the tech stack and installs/updates appropriate agents.
7
+ * This tool provides repository analysis and tech context management.
8
+ * It analyzes the tech stack and writes context files to .tiny-brain/.
9
9
  */
10
10
  export declare class AnalyseTool {
11
11
  static getToolDefinition(): MCPTool;
@@ -1 +1 @@
1
- {"version":3,"file":"analyse.tool.d.ts","sourceRoot":"","sources":["../../src/tools/analyse.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAGlE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAQrE;;;;;GAKG;AACH,qBAAa,WAAW;IACtB,MAAM,CAAC,iBAAiB,IAAI,OAAO;IAwBnC;;OAEG;mBACkB,sBAAsB;IA4B3C;;OAEG;WACU,OAAO,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;CAsGxF"}
1
+ {"version":3,"file":"analyse.tool.d.ts","sourceRoot":"","sources":["../../src/tools/analyse.tool.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AACpF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,OAAO,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAOrE;;;;;GAKG;AACH,qBAAa,WAAW;IACtB,MAAM,CAAC,iBAAiB,IAAI,OAAO;IAmBnC;;OAEG;mBACkB,sBAAsB;IA4B3C;;OAEG;WACU,OAAO,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC;CAoCxF"}
@@ -1,23 +1,21 @@
1
1
  import { z } from 'zod';
2
2
  import { createErrorResult } from './index.js';
3
3
  import { AnalyseService } from '../services/analyse-service.js';
4
- import { AgentManager } from '../services/agent-manager.js';
5
4
  import { ConfigService } from '@magic-ingredients/tiny-brain-core';
6
5
  const AnalyseArgsSchema = z.object({
7
6
  dryRun: z.boolean().optional().default(false),
8
- confirmInstall: z.boolean().optional().default(false),
9
7
  }).passthrough(); // Allow additional options to pass through
10
8
  /**
11
- * Analyse Tool - Analyze repository and manage agents
9
+ * Analyse Tool - Analyze repository and write tech context files
12
10
  *
13
- * This tool provides repository analysis and agent management functionality.
14
- * It analyzes the tech stack and installs/updates appropriate agents.
11
+ * This tool provides repository analysis and tech context management.
12
+ * It analyzes the tech stack and writes context files to .tiny-brain/.
15
13
  */
16
14
  export class AnalyseTool {
17
15
  static getToolDefinition() {
18
16
  return {
19
17
  name: 'analyse',
20
- description: "🔍 REPOSITORY ANALYSIS 🔍\n\n⚡ ANALYZE & UPDATE: Analyze repository tech stack and manage agents.\n\n✅ FEATURES:\n • Detect languages, frameworks, and tools\n • Prompt to install recommended agents\n • Check for agent updates\n • Show tech stack changes\n • Show installed skills with versions\n • Dry-run mode for preview\n\n💡 USAGE:\n • analyse - Analyze and prompt for installation\n • analyse --dry-run - Preview changes without prompting\n\n🤖 AGENTS:\n • Recommends agents based on tech stack\n • Updates agents to latest versions\n • Removes deprecated agents\n\n📜 SKILLS:\n • Shows installed skills (/plan, /feature, /fix, /adr)\n • Displays skill versions\n\n📊 OUTPUT:\n • Tech stack summary\n • Agent recommendations\n • Installed skills list\n • Installation results",
18
+ description: "🔍 REPOSITORY ANALYSIS 🔍\n\n⚡ ANALYZE & UPDATE: Analyze repository tech stack and write tech context files.\n\n✅ FEATURES:\n • Detect languages, frameworks, and tools\n • Write analysis to .tiny-brain/analysis.json\n • Write tech context files to .tiny-brain/tech/*.md\n • Auto-sync tech agents based on enableAgenticCoding config\n • Show Feature flags status (SDD, TDD, ADR, Agentic)\n • Dry-run mode for preview\n\n💡 USAGE:\n • analyse - Analyze repo and write tech context files\n • analyse --dry-run - Preview changes without writing\n\n📊 OUTPUT:\n • Feature flags status\n • Tech stack summary\n • Tech contexts detected\n • Directory initialization status",
21
19
  inputSchema: {
22
20
  type: 'object',
23
21
  properties: {
@@ -26,11 +24,6 @@ export class AnalyseTool {
26
24
  description: 'Preview changes without applying them',
27
25
  default: false,
28
26
  },
29
- confirmInstall: {
30
- type: 'boolean',
31
- description: 'Confirm installation of agents (used internally)',
32
- default: false,
33
- },
34
27
  },
35
28
  required: [],
36
29
  },
@@ -76,67 +69,11 @@ export class AnalyseTool {
76
69
  const service = new AnalyseService(context);
77
70
  const result = await service.performAnalysis(validatedArgs);
78
71
  // Format output for display
79
- let output = preAnalysisFeedback ? preAnalysisFeedback + service.formatAnalysisOutput(result) : service.formatAnalysisOutput(result);
80
- // Handle agent installation
81
- if (!validatedArgs.dryRun && result.recommendations) {
82
- const hasRecommendations = (result.recommendations.install?.length || 0) > 0 ||
83
- (result.recommendations.update?.length || 0) > 0 ||
84
- (result.recommendations.remove?.length || 0) > 0;
85
- if (hasRecommendations) {
86
- // Check if user has confirmed installation
87
- if (!validatedArgs.confirmInstall) {
88
- // Return JSON asking for confirmation (for MCP context)
89
- return {
90
- content: [{
91
- type: 'text',
92
- text: JSON.stringify({
93
- requiresConfirmation: true,
94
- action: 'installAgents',
95
- recommendations: {
96
- install: result.recommendations.install?.length || 0,
97
- update: result.recommendations.update?.length || 0,
98
- remove: result.recommendations.remove?.length || 0,
99
- },
100
- message: output + '\n\nWould you like to install these agents?',
101
- hint: 'To confirm installation, call the tool again with confirmInstall: true',
102
- }, null, 2),
103
- }],
104
- isError: false,
105
- };
106
- }
107
- // User has confirmed - proceed with installation
108
- const selection = {
109
- install: result.recommendations.install || [],
110
- update: result.recommendations.update || [],
111
- remove: result.recommendations.remove || []
112
- };
113
- // Apply the recommendations using AgentManager
114
- output += '\n📦 Installing agents...\n';
115
- const agentManager = new AgentManager(context);
116
- const executionResult = await agentManager.executeRecommendations(selection, result.analysis);
117
- // Report results
118
- if (executionResult.installed.length > 0) {
119
- output += `✅ Installed: ${executionResult.installed.join(', ')}\n`;
120
- }
121
- if (executionResult.updated.length > 0) {
122
- output += `✅ Updated: ${executionResult.updated.join(', ')}\n`;
123
- }
124
- if (executionResult.removed.length > 0) {
125
- output += `✅ Removed: ${executionResult.removed.join(', ')}\n`;
126
- }
127
- if (executionResult.skipped.length > 0) {
128
- output += `⏭️ Skipped (already up-to-date): ${executionResult.skipped.join(', ')}\n`;
129
- }
130
- if (executionResult.errors.length > 0) {
131
- output += '⚠️ Errors:\n';
132
- executionResult.errors.forEach(e => {
133
- output += ` - ${e.message}\n`;
134
- });
135
- }
136
- output += '\n✅ Agent management completed!';
137
- }
138
- }
139
- // Note: Repository registration now happens in AnalyseService.performAnalysis()
72
+ const output = preAnalysisFeedback
73
+ ? preAnalysisFeedback + service.formatAnalysisOutput(result)
74
+ : service.formatAnalysisOutput(result);
75
+ // Note: Tech context syncing is now handled automatically by AnalyseService
76
+ // based on enableAgenticCoding config - no user confirmation needed
140
77
  return {
141
78
  content: [{
142
79
  type: 'text',
@@ -1 +1 @@
1
- {"version":3,"file":"config.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/config/config.tool.ts"],"names":[],"mappings":"AACA,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;AAUrE,qBAAa,UAAU;IACrB,MAAM,CAAC,iBAAiB,IAAI,OAAO;WA4DtB,OAAO,CAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC;mBA6BD,UAAU;mBAwCV,SAAS;mBAoCT,iBAAiB;mBAgEjB,SAAS;mBA8CT,WAAW;CAqBjC"}
1
+ {"version":3,"file":"config.tool.d.ts","sourceRoot":"","sources":["../../../src/tools/config/config.tool.ts"],"names":[],"mappings":"AACA,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;AAWrE,qBAAa,UAAU;IACrB,MAAM,CAAC,iBAAiB,IAAI,OAAO;WA4DtB,OAAO,CAClB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,UAAU,CAAC;mBA6BD,UAAU;mBAwCV,SAAS;mBAoCT,iBAAiB;mBAgEjB,SAAS;mBAuDT,WAAW;CAqBjC"}
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { createSuccessResult, createErrorResult } from '../index.js';
3
3
  import { ConfigService } from '@magic-ingredients/tiny-brain-core';
4
+ import { TechContextService } from '../../services/tech-context-service.js';
4
5
  const ConfigArgsSchema = z.object({
5
6
  operation: z.enum(['list', 'get', 'show-sources', 'set', 'reset']),
6
7
  key: z.string().optional(),
@@ -79,7 +80,7 @@ export class ConfigTool {
79
80
  case 'show-sources':
80
81
  return await ConfigTool.handleShowSources(configService);
81
82
  case 'set':
82
- return await ConfigTool.handleSet(validatedArgs, configService);
83
+ return await ConfigTool.handleSet(validatedArgs, configService, context);
83
84
  case 'reset':
84
85
  return await ConfigTool.handleReset(configService);
85
86
  default:
@@ -213,7 +214,7 @@ export class ConfigTool {
213
214
  return createErrorResult(`Failed to show preference sources: ${error instanceof Error ? error.message : 'Unknown error'}`);
214
215
  }
215
216
  }
216
- static async handleSet(args, configService) {
217
+ static async handleSet(args, configService, context) {
217
218
  if (!args.key) {
218
219
  return createErrorResult('key is required for set operation');
219
220
  }
@@ -226,6 +227,13 @@ export class ConfigTool {
226
227
  if (!result.success) {
227
228
  return createErrorResult(result.error);
228
229
  }
230
+ // React to enableAgenticCoding changes for repo scope
231
+ // Tech files are repo-specific, so global scope doesn't trigger sync
232
+ if (args.key === 'enableAgenticCoding' && scope === 'repo' && context.repositoryRoot) {
233
+ const techContextService = new TechContextService(context.repositoryRoot);
234
+ const enableAgentic = args.value === 'true';
235
+ await techContextService.syncAgents(enableAgentic);
236
+ }
229
237
  const scopeLabel = scope === 'global' ? 'Global' : 'Repository';
230
238
  const displayValue = args.value === 'true' || args.value === 'false'
231
239
  ? args.value === 'true'
@@ -1 +1 @@
1
- {"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../src/tools/tool-registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,OAAO,KAAK,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qBAAa,YAAY;IACvB;;OAEG;IACH,MAAM,CAAC,QAAQ,IAAI,OAAO,EAAE;IAiC5B;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;CAkB9D;AAGD,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,8BAAqC,CAAC;AAChF,eAAO,MAAM,eAAe,gBAAyD,CAAC"}
1
+ {"version":3,"file":"tool-registry.d.ts","sourceRoot":"","sources":["../../src/tools/tool-registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH,OAAO,KAAK,EAAE,IAAI,IAAI,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qBAAa,YAAY;IACvB;;OAEG;IACH,MAAM,CAAC,QAAQ,IAAI,OAAO,EAAE;IAoC5B;;OAEG;IACH,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;CAmB9D;AAGD,eAAO,MAAM,aAAa,GAAI,MAAM,MAAM,8BAAqC,CAAC;AAChF,eAAO,MAAM,eAAe,gBAAyD,CAAC"}