Skip to content

Commit 23cea0c

Browse files
committed
feat: enhance coding agents and workflows
- Added new workflows: researchSynthesisWorkflow, financialReportWorkflow, specGenerationWorkflow, repoIngestionWorkflow, and learningExtractionWorkflow to the Coding A2A Coordinator and Coding Team Network for improved orchestration of multi-topic tasks. - Updated the model used in Coding A2A Coordinator and Coding Team Network to googleAIFlashLite for better performance. - Integrated GitHub MCP client in codingAgents for enhanced tool capabilities. - Improved findReferencesTool to utilize PythonParser for more accurate reference finding in Python files. - Refactored multi-string edit tool for better readability and efficiency. - Added find_references function in semantic-utils for improved symbol reference tracking.
1 parent 0f31f20 commit 23cea0c

8 files changed

Lines changed: 207 additions & 70 deletions

File tree

src/mastra/a2a/codingA2ACoordinator.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@ import { Agent } from '@mastra/core/agent'
22
import { InternalSpans } from '@mastra/core/ai-tracing'
33
import { createAnswerRelevancyScorer, createToxicityScorer } from '@mastra/evals/scorers/llm'
44

5-
import { googleAI, googleAIFlashLite } from '../config/google'
5+
import { googleAIFlashLite } from '../config/google'
66
import { pgMemory } from '../config/pg-storage'
77
import { log } from '../config/logger'
88
import { taskCompletionScorer } from '../scorers/custom-scorers'
99

1010
import { codeArchitectAgent, codeReviewerAgent, testEngineerAgent, refactoringAgent } from '../agents/codingAgents'
11+
import { researchSynthesisWorkflow } from '../workflows/research-synthesis-workflow'
12+
import { financialReportWorkflow } from '../workflows/financial-report-workflow'
13+
import { specGenerationWorkflow } from '../workflows/spec-generation-workflow'
14+
import { repoIngestionWorkflow } from '../workflows/repo-ingestion-workflow'
15+
import { learningExtractionWorkflow } from '../workflows/learning-extraction-workflow'
1116

1217
log.info('Initializing Coding A2A Coordinator...')
1318

@@ -130,19 +135,26 @@ For each orchestration:
130135
- refactoringAgent: Generate improvement plan based on Phase 1
131136
- testEngineerAgent: Generate tests for proposed changes
132137
133-
CRITICAL: Prefer parallel execution for independent tasks. Only use sequential when results depend on previous agent outputs.`,
138+
CRITICAL: Prefer parallel execution for independent tasks. Only use sequential when results depend on previous agent outputs.
139+
140+
This coordinator also exposes higher-level workflows (researchSynthesisWorkflow, specGenerationWorkflow, repoIngestionWorkflow, learningExtractionWorkflow, financialReportWorkflow) that handle multi-topic research, spec generation, repo ingestion (RAG ingestion), learning extraction, and financial reports. When a user's request requires prolonged, structured work across multiple subtasks, prefer invoking these workflows and orchestrating agents around them.`,
134141
providerOptions: {
135142
google: {
136143
thinkingConfig: {
137-
thinkingLevel: 'high',
138144
includeThoughts: true,
139145
thinkingBudget: -1,
140-
}
146+
},
147+
mediaResolution: 'MEDIA_RESOLUTION_MEDIUM',
148+
responseModalities: ['TEXT', 'IMAGE'],
149+
maxOutputTokens: 64000,
150+
temperature: 0.2,
151+
topP: 0.95,
152+
topK: 40,
141153
}
142154
}
143155
}
144156
},
145-
model: googleAI,
157+
model: googleAIFlashLite,
146158
memory: pgMemory,
147159
options: { tracingPolicy: { internal: InternalSpans.AGENT } },
148160
agents: {
@@ -151,7 +163,13 @@ CRITICAL: Prefer parallel execution for independent tasks. Only use sequential w
151163
testEngineerAgent,
152164
refactoringAgent,
153165
},
154-
workflows: {},
166+
workflows: {
167+
researchSynthesisWorkflow,
168+
financialReportWorkflow,
169+
specGenerationWorkflow,
170+
repoIngestionWorkflow,
171+
learningExtractionWorkflow,
172+
},
155173
tools: {},
156174
maxRetries: 5,
157175
scorers: {
@@ -165,7 +183,7 @@ CRITICAL: Prefer parallel execution for independent tasks. Only use sequential w
165183
},
166184
taskCompletion: {
167185
scorer: taskCompletionScorer,
168-
sampling: { type: 'ratio', rate: 0.4 },
186+
sampling: { type: 'ratio', rate: 0.8 },
169187
},
170188
},
171189
})

src/mastra/agents/codingAgents.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
searchCode,
2626
getFileContent
2727
} from '../tools/github'
28+
import { githubMCP } from '../mcp/mcp-client'
2829

2930
export type UserTier = 'free' | 'pro' | 'enterprise'
3031
export type CodingRuntimeContext = {
@@ -84,13 +85,17 @@ Always consider maintainability, scalability, and testability in your recommenda
8485
google: {
8586
structuredOutput: true,
8687
thinkingConfig: {
87-
thinkingLevel: userTier === 'enterprise' ? 'high' : 'medium',
88+
thinkingLevel: userTier === 'enterprise' ? 'high' : 'low',
8889
includeThoughts: true,
8990
thinkingBudget: -1,
9091
},
91-
responseModalities: ['TEXT'],
92-
maxOutputTokens: 32000,
93-
temperature: 0.3,
92+
mediaResolution: 'MEDIA_RESOLUTION_LOW',
93+
responseModalities: ['TEXT', 'IMAGE'],
94+
maxOutputTokens: 64000,
95+
temperature: 0.2,
96+
topP: 0.95,
97+
topK: 40,
98+
9499
}
95100
}
96101
}
@@ -110,6 +115,7 @@ Always consider maintainability, scalability, and testability in your recommenda
110115
getRepositoryInfo,
111116
searchCode,
112117
getFileContent,
118+
...githubMCP.getTools(),
113119
},
114120
memory: pgMemory,
115121
options: { tracingPolicy: { internal: InternalSpans.AGENT } },
@@ -332,6 +338,7 @@ Always use Vitest syntax: describe, it, expect, vi.mock, vi.fn.`,
332338
testGeneratorTool,
333339
codeSearchTool,
334340
execaTool,
341+
...githubMCP.getTools(),
335342
},
336343
memory: pgMemory,
337344
options: { tracingPolicy: { internal: InternalSpans.AGENT } },

src/mastra/mcp/mcp-client.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const NEO4J_DATABASE = process.env.NEO4J_DATABASE ?? "neo4j"
99

1010
const Klavis = process.env.KLAVIS_INSTANCE_ID;
1111
const strata = process.env.KLAVIS_STRATA;
12-
12+
const github = process.env.GITHUB_API_KEY;
1313
export const mcpTools = new MCPClient({
1414
id: "mcp-client",
1515
servers: {
@@ -66,6 +66,30 @@ export const mcpTools = new MCPClient({
6666
},
6767
}
6868
});
69+
/*
70+
* MCPClient with GitHub MCP Server using HTTP transport & Authorization
71+
* https://github.com/microsoft/mcp/blob/main/docs/servers/github.md
72+
* Requires GitHub API key with appropriate permissions.
73+
* Set GITHUB_API_KEY env variable to use.
74+
* See https://github.com/settings/tokens for more information.
75+
*
76+
* Example:
77+
*
78+
* githubMCP
79+
*/
80+
export const githubMCP = new MCPClient({
81+
id: "mcp-client",
82+
servers: {
83+
github: {
84+
url: new URL("https://api.githubcopilot.com/mcp/"),
85+
requestInit: {
86+
headers: {
87+
Authorization: `Bearer ${github}`,
88+
},
89+
},
90+
},
91+
}
92+
});
6993

7094
// MCPClient with Ampersand MCP Server using stdio transport
7195
//export const mcp = new MCPClient({

src/mastra/networks/codingTeamNetwork.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
import { Agent } from '@mastra/core/agent'
22
import { InternalSpans } from '@mastra/core/ai-tracing'
33

4-
import { googleAI } from '../config/google'
4+
import { googleAI, googleAIFlashLite } from '../config/google'
55
import { pgMemory } from '../config/pg-storage'
66
import { log } from '../config/logger'
77

88
import { codeArchitectAgent, codeReviewerAgent, testEngineerAgent, refactoringAgent } from '../agents/codingAgents'
9+
import { researchSynthesisWorkflow } from '../workflows/research-synthesis-workflow'
10+
import { financialReportWorkflow } from '../workflows/financial-report-workflow'
11+
import { specGenerationWorkflow } from '../workflows/spec-generation-workflow'
12+
import { repoIngestionWorkflow } from '../workflows/repo-ingestion-workflow'
13+
import { learningExtractionWorkflow } from '../workflows/learning-extraction-workflow'
914

1015
log.info('Initializing Coding Team Network...')
1116

@@ -64,7 +69,11 @@ export const codingTeamNetwork = new Agent({
6469
- Architecture → Implementation: codeArchitectAgent → refactoringAgent
6570
- Review → Fix: codeReviewerAgent → refactoringAgent
6671
- Code → Test: refactoringAgent → testEngineerAgent
67-
- Full Cycle: codeArchitectAgent → refactoringAgent → codeReviewerAgent → testEngineerAgent
72+
- Full Cycle: codeArchitectAgent → refactoringAgent → codeReviewerAgent → testEngineerAgent
73+
74+
This network also exposes higher-level workflows for common orchestration patterns: researchSynthesisWorkflow (multi-topic research & synthesis), specGenerationWorkflow (design/spec creation), repoIngestionWorkflow (ingest repository content into RAG pipelines), learningExtractionWorkflow (extract learnings with human-in-the-loop), and financialReportWorkflow (financial reports). Prefer invoking these workflows when a single network call should trigger a longer-running, structured process.
75+
76+
Provider options: networks should not generally require top-level provider overrides; prefer configuring providerOptions at the agent level (inside an agent's instructions) or passing runtime orchestration constraints. Use network-level provider constraints only for shared execution limits or budgets when coordinating agents across a federated workflow.
6877
6978
## Examples
7079
@@ -88,8 +97,9 @@ export const codingTeamNetwork = new Agent({
8897
- Always explain which agent you're delegating to and why
8998
- For ambiguous requests, ask for clarification
9099
- Chain agents when the task requires multiple steps
91-
- Preserve context when passing between agents`,
92-
model: googleAI,
100+
- Preserve context when passing between agents
101+
`,
102+
model: googleAIFlashLite,
93103
memory: pgMemory,
94104
options: {
95105
tracingPolicy: { internal: InternalSpans.ALL },
@@ -101,7 +111,13 @@ export const codingTeamNetwork = new Agent({
101111
refactoringAgent,
102112
},
103113
tools: {},
104-
workflows: {},
114+
workflows: {
115+
researchSynthesisWorkflow,
116+
specGenerationWorkflow,
117+
repoIngestionWorkflow,
118+
learningExtractionWorkflow,
119+
financialReportWorkflow,
120+
},
105121
scorers: {},
106122
})
107123

src/mastra/tools/find-references.tool.ts

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -141,21 +141,17 @@ export const findReferencesTool = createTool({
141141
for (const pyFile of pythonFiles) {
142142
try {
143143
const content = await readFile(pyFile, 'utf-8');
144-
const lines = content.split('\n');
144+
const references = await PythonParser.findReferences(content, symbolName);
145145

146-
// Text-based search for Python (fallback)
147-
lines.forEach((lineContent, index) => {
148-
if (lineContent.includes(symbolName)) {
149-
const column = lineContent.indexOf(symbolName);
150-
allReferences.push({
151-
filePath: pyFile,
152-
line: index + 1,
153-
column: column + 1,
154-
text: lineContent.trim().substring(0, 100),
155-
isDefinition: /^(def|class)\s/.test(lineContent.trim())
156-
});
157-
}
158-
});
146+
for (const ref of references) {
147+
allReferences.push({
148+
filePath: pyFile,
149+
line: ref.line,
150+
column: ref.column + 1, // Convert 0-indexed column to 1-indexed
151+
text: ref.text.substring(0, 100),
152+
isDefinition: ref.isDefinition
153+
});
154+
}
159155
} catch (error) {
160156
log.warn(`Error parsing Python file ${pyFile}`, { error });
161157
}

src/mastra/tools/find-symbol.tool.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -197,16 +197,14 @@ function extractSymbolInfo(
197197
}
198198

199199
// Variables
200-
if (symbolType === 'all' || symbolType === 'variable') {
201-
if (Node.isVariableDeclaration(node)) {
202-
const name = node.getName();
203-
const initializer = node.getInitializer();
204-
if (name && name.includes(symbolName) &&
205-
!Node.isArrowFunction(initializer) &&
206-
!Node.isFunctionExpression(initializer)) {
207-
return { name, kind: 'variable' };
208-
}
209-
}
200+
if ((symbolType === 'all' || symbolType === 'variable') && Node.isVariableDeclaration(node)) {
201+
const name = node.getName();
202+
const initializer = node.getInitializer();
203+
if (name && name.includes(symbolName) &&
204+
!Node.isArrowFunction(initializer) &&
205+
!Node.isFunctionExpression(initializer)) {
206+
return { name, kind: 'variable' };
207+
}
210208
}
211209

212210
return null;

src/mastra/tools/multi-string-edit.tool.ts

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -126,32 +126,31 @@ Use for batch refactoring, multi-file updates, and coordinated code changes.`,
126126
let matchFound = false
127127

128128
if (edit.useRegex) {
129-
const flags = edit.replaceAll ? 'g' : ''
130-
const regex = new RegExp(edit.oldString, flags)
131-
if (regex.test(content)) {
132-
newContent = content.replace(regex, edit.newString)
133-
matchFound = true
134-
}
135-
} else {
136-
if (content.includes(edit.oldString)) {
137-
if (edit.replaceAll) {
138-
newContent = content.split(edit.oldString).join(edit.newString)
139-
matchFound = true
140-
} else {
141-
const occurrences = content.split(edit.oldString).length - 1
142-
if (occurrences > 1) {
143-
results.push({
144-
filePath,
145-
status: 'skipped',
146-
reason: `Multiple occurrences found (${occurrences}). Use replaceAll: true to replace all.`,
147-
})
148-
continue
149-
}
150-
newContent = content.replace(edit.oldString, edit.newString)
151-
matchFound = true
152-
}
153-
}
154-
}
129+
const flags = edit.replaceAll ? 'g' : ''
130+
const regex = new RegExp(edit.oldString, flags)
131+
if (regex.test(content)) {
132+
newContent = content.replace(regex, edit.newString)
133+
matchFound = true
134+
}
135+
}
136+
else if (content.includes(edit.oldString)) {
137+
if (edit.replaceAll) {
138+
newContent = content.split(edit.oldString).join(edit.newString)
139+
matchFound = true
140+
} else {
141+
const occurrences = content.split(edit.oldString).length - 1
142+
if (occurrences > 1) {
143+
results.push({
144+
filePath,
145+
status: 'skipped',
146+
reason: `Multiple occurrences found (${occurrences}). Use replaceAll: true to replace all.`,
147+
})
148+
continue
149+
}
150+
newContent = content.replace(edit.oldString, edit.newString)
151+
matchFound = true
152+
}
153+
}
155154

156155
if (!matchFound) {
157156
results.push({

0 commit comments

Comments
 (0)