Code Agent Module
Agent workflow module for the Loopstack automation framework.
AI-powered code exploration for Loopstack workflows. Provides a tool that launches a sub-agent workflow to search, read, and synthesize findings from a remote workspace using glob, grep, and read tools from @loopstack/remote-client.
When to Use
- You need a workflow to answer questions about a codebase before acting — locating functions, summarizing features, understanding architecture
- You want to delegate exploration as a single tool call from a parent workflow or agent
- You need a self-contained agent that iteratively searches and reads files on a remote workspace and returns a synthesized answer
- Use
@loopstack/remote-clientdirectly if you only need individual file operations without the agent loop
Installation
npm install @loopstack/code-agentRegister the module in your application:
import { Module } from '@nestjs/common';import { CodeAgentModule } from '@loopstack/code-agent';
@Module({ imports: [CodeAgentModule],})export class AppModule {}Your app must also configure:
LlmProviderModulewith a registered LLM provider —CodeAgentModuleimportsAgentModulefrom@loopstack/agent, which needs it.RemoteClientModulefrom@loopstack/remote-client(bare orforRoot) — it supplies theglob,grepandreadtools theexplore_tasksub-agent uses. Without it the app fails at boot with an error namingRemoteClientModule.
To override the LLM provider or model for this module specifically:
@Module({ imports: [CodeAgentModule.forFeature({ llm: { provider: 'claude', model: 'claude-sonnet-4-20250514' } })],})export class AppModule {}Quick Start
The most common pattern is launching the code agent from a parent workflow using AgentWorkflow directly:
import { Module } from '@nestjs/common';import { z } from 'zod';import { AgentWorkflow } from '@loopstack/agent';import { CodeAgentModule } from '@loopstack/code-agent';import { BaseWorkflow, MessageDocument, Transition, type TransitionInput, Workflow } from '@loopstack/common';
const ExploreResponseSchema = z.object({ response: z.string() });
@Workflow({ title: 'Code Agent Example' })export class MyWorkflow extends BaseWorkflow { constructor(private readonly agentWorkflow: AgentWorkflow) { super(); }
@Transition({ to: 'exploring' }) async start(state: Record<string, unknown>) { await this.agentWorkflow.run( { system: 'You are a codebase exploration agent. Search and read source code to answer the question thoroughly.', tools: ['glob', 'grep', 'read'], userMessage: 'Find the entry-point module and list its top-level providers.', }, { callback: { transition: 'onExploreComplete' }, show: 'inline', label: 'Exploring codebase...' }, ); }
@Transition({ from: 'exploring', to: 'end', wait: true, schema: ExploreResponseSchema }) async onExploreComplete(state: Record<string, unknown>, input: TransitionInput<{ response: string }>) { await this.documentStore.save(MessageDocument, { role: 'assistant', text: input.data.response, }); }}
@Module({ imports: [CodeAgentModule], providers: [MyWorkflow], exports: [MyWorkflow, CodeAgentModule],})export class MyModule {}How It Works
State Machine
start ──► exploring (wait) ──► end │ ▲ └── callback ───────┘- The parent workflow calls
agentWorkflow.run()with a system prompt, tool list, and user message - The
AgentWorkflowsub-workflow starts its own agent loop: LLM generates text, calls tools (glob,grep,read), loops until it produces a final answer - The orchestrator renders the sub-workflow inline in the parent’s UI based on the
showoption ('inline'by default) - When the agent finishes, it calls back to the parent workflow’s wait transition with
{ data: { response } } - The parent receives the synthesized answer and can display it or act on it
ExploreTask Tool
The module also provides ExploreTask, a tool wrapper around AgentWorkflow that can be used by other agents:
Parent Agent ──► explore_task tool call ──► AgentWorkflow sub-agent │ glob/grep/read loop │ synthesized answer │ ◄── complete() callbackExploreTask launches the sub-agent and returns { workflowId } with pending status. The orchestrator renders the sub-agent inline in the parent’s view. When the sub-agent completes, ExploreTask.complete() returns the response text.
Args Reference
AgentWorkflow.run()
| Arg | Type | Required | Description |
|---|---|---|---|
system |
string |
Yes | System prompt for the agent |
tools |
string[] |
Yes | Tool names to make available (e.g. ['glob', 'grep', 'read']) |
userMessage |
string |
Yes | The question or instruction for the agent |
context |
string |
No | Additional context to include in the conversation |
Second argument (options):
| Arg | Type | Required | Description |
|---|---|---|---|
callback.transition |
string |
No | Transition name to call when the agent finishes |
Returns: QueueResult with { workflowId: string }
Callback Envelope
The parent’s wait transition receives a TransitionInput<TData>. Pass only the data shape as the schema — the framework wraps it:
const ExploreResponseSchema = z.object({ response: z.string() });// Receiver: input: TransitionInput<{ response: string }>| Field | Type | Description |
|---|---|---|
input.workflowId |
string |
ID of the completed sub-workflow |
input.status |
enum | 'completed' / 'failed' / 'canceled' |
input.hasError |
boolean |
Whether the sub-agent terminated in failure |
input.errorMessage |
string | null |
Error description when hasError |
input.data.response |
string |
The agent’s synthesized text answer (validated by schema) |
Tools Reference
explore_task
| Field | Value |
|---|---|
| Name | explore_task |
| Description | Launch a sub-agent to explore and analyze the codebase. The agent uses glob, grep, and read tools to search for files and code patterns, then returns a synthesized summary. |
Args (Zod schema):
| Arg | Type | Required | Description |
|---|---|---|---|
instructions |
string |
Yes | Detailed instructions for what to explore in the codebase |
Returns: ToolEnvelope<ExploreTaskResult>
- On launch:
{ data: { workflowId }, pending: { workflowId } } - On complete:
{ data: string }— the agent’s synthesized response text
Configuration
CodeAgentModule.forFeature()
Override the LLM provider or model for the code agent specifically:
CodeAgentModule.forFeature({ llm: { provider: 'claude', // LLM provider name model: 'claude-sonnet-4-20250514', // Model identifier },});| Option | Type | Required | Description |
|---|---|---|---|
llm.provider |
string |
No | LLM provider name |
llm.model |
string |
No | Model identifier |
Public API
- Module:
CodeAgentModule - Tool:
ExploreTask - Schema:
ExploreTaskResultSchema - Type:
ExploreTaskResult
Dependencies
| Package | Role |
|---|---|
@loopstack/agent |
AgentWorkflow — generic LLM agent loop with tool calling |
@loopstack/common |
BaseTool, BaseWorkflow, decorators, document types |
@loopstack/core |
Workflow engine, scheduling, state management |
@loopstack/remote-client |
glob, grep, read tools executed on the remote workspace |
@nestjs/common |
NestJS dependency injection |
zod |
Schema validation |
Related
code-agent-example-workflow— full working example showingAgentWorkflowwith glob/grep/read- Agent Workflows — how the agent loop, tool resolution, and callbacks work
@loopstack/agent— the genericAgentWorkflowthat powers the code agent@loopstack/remote-client— theglob,grep, andreadtools used by the agent
About
Author: Jakob Klippel
License: MIT