Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 164 additions & 0 deletions src/adapters/deepagents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
import { resolve, join } from 'node:path';
import { loadAgentManifest, loadFileIfExists } from '../utils/loader.js';
import { loadAllSkills, getAllowedTools } from '../utils/skill-loader.js';

/**
* Export a gitagent directory to a Deep Agents (deepagents) Python agent.
*
* Generates a ready-to-run Python file using the `deepagents` SDK
* (`pip install deepagents`). Deep Agents is built on top of LangChain
* and the LangGraph runtime, providing built-in task planning, filesystem
* context management, subagent-spawning, and long-term memory.
*
* Compliance constraints from agent.yaml are injected as system_prompt rules.
* Skills become tool stubs that can be swapped for real implementations.
*/
export function exportToDeepAgents(dir: string): string {
const agentDir = resolve(dir);
const manifest = loadAgentManifest(agentDir);

const systemPrompt = buildSystemPrompt(agentDir, manifest);
const tools = buildToolDefinitions(agentDir);
const hasSubAgents = manifest.agents && Object.keys(manifest.agents).length > 0;
const modelName = resolveModelName(manifest.model?.preferred);

const lines: string[] = [];

lines.push('"""');
lines.push(`Deep Agents agent for ${manifest.name} v${manifest.version}`);
lines.push('Generated by gitagent export --format deepagents');
lines.push('Install: pip install deepagents');
lines.push('"""');
lines.push('');
lines.push('from deepagents import create_deep_agent');
lines.push('from langchain_core.tools import tool');
lines.push('');

if (tools.length > 0) {
lines.push('# --- Tools (from gitagent skills) ---');
for (const t of tools) {
const funcName = t.name.replace(/[^a-zA-Z0-9]/g, '_');
lines.push('');
lines.push('@tool');
lines.push(`def ${funcName}(input: str) -> str:`);
lines.push(` """${t.description}"""`);
lines.push(` # TODO: implement tool logic`);
lines.push(` raise NotImplementedError("${funcName} not yet implemented")`);
}
lines.push('');
lines.push(`tools = [${tools.map(t => t.name.replace(/[^a-zA-Z0-9]/g, '_')).join(', ')}]`);
} else {
lines.push('tools = []');
}

lines.push('');
lines.push('SYSTEM_PROMPT = """' + systemPrompt.replace(/"""/g, '\\"\\"\\"') + '"""');
lines.push('');

lines.push('# --- Agent ---');
lines.push(`agent = create_deep_agent(`);
lines.push(` tools=tools,`);
lines.push(` system_prompt=SYSTEM_PROMPT,`);
if (modelName) {
lines.push(` model="${modelName}",`);
}
lines.push(`)`);

if (hasSubAgents) {
lines.push('');
lines.push('# --- Sub-Agents ---');
lines.push('# Sub-agents defined in agent.yaml can be modelled as additional');
lines.push('# Deep Agents instances passed as tools or orchestrated via a supervisor.');
lines.push('# See: https://docs.langchain.com/oss/python/deepagents/overview');
for (const [name] of Object.entries(manifest.agents ?? {})) {
lines.push(`# Sub-agent: ${name}`);
}
}

lines.push('');
lines.push(`if __name__ == "__main__":`);
lines.push(` print("Agent: ${manifest.name} v${manifest.version}")`);
lines.push(` while True:`);
lines.push(` user_input = input("You: ").strip()`);
lines.push(` if not user_input or user_input.lower() in ("exit", "quit"):`);
lines.push(` break`);
lines.push(` result = agent.invoke(`);
lines.push(` {"messages": [{"role": "user", "content": user_input}]}`);
lines.push(` )`);
lines.push(` messages = result.get("messages", [])`);
lines.push(` last_ai = next(`);
lines.push(` (m for m in reversed(messages) if getattr(m, "type", None) == "ai"),`);
lines.push(` None,`);
lines.push(` )`);
lines.push(` if last_ai:`);
lines.push(` print(f"Agent: {last_ai.content}")`);

return lines.join('\n');
}

function buildSystemPrompt(
agentDir: string,
manifest: ReturnType<typeof loadAgentManifest>,
): string {
const parts: string[] = [];

const soul = loadFileIfExists(join(agentDir, 'SOUL.md'));
if (soul) parts.push(soul);

const rules = loadFileIfExists(join(agentDir, 'RULES.md'));
if (rules) parts.push(`## Rules\n${rules}`);

const skillsDir = join(agentDir, 'skills');
const skills = loadAllSkills(skillsDir);
for (const skill of skills) {
const allowedTools = getAllowedTools(skill.frontmatter);
const toolsNote =
allowedTools.length > 0 ? `\nAllowed tools: ${allowedTools.join(', ')}` : '';
parts.push(
`## Skill: ${skill.frontmatter.name}\n${skill.frontmatter.description}${toolsNote}\n\n${skill.instructions}`,
);
}

if (manifest.compliance) {
const c = manifest.compliance;
const constraints: string[] = ['## Compliance Constraints'];
if (c.communications?.fair_balanced)
constraints.push('- All outputs must be fair and balanced (FINRA 2210)');
if (c.communications?.no_misleading)
constraints.push('- Never make misleading or promissory statements');
if (c.data_governance?.pii_handling === 'redact')
constraints.push('- Redact all PII from outputs');
if (c.supervision?.human_in_the_loop === 'always')
constraints.push('- All decisions require human approval');
if (manifest.compliance.segregation_of_duties) {
const sod = manifest.compliance.segregation_of_duties;
if (sod.conflicts) {
constraints.push('- Segregation of duties conflicts:');
for (const [a, b] of sod.conflicts) {
constraints.push(` - "${a}" and "${b}" may not be held by the same agent`);
}
}
}
if (constraints.length > 1) parts.push(constraints.join('\n'));
}

return parts.join('\n\n');
}

interface ToolDef {
name: string;
description: string;
}

function buildToolDefinitions(agentDir: string): ToolDef[] {
const skills = loadAllSkills(join(agentDir, 'skills'));
return skills.map(s => ({
name: s.frontmatter.name,
description: s.frontmatter.description ?? s.frontmatter.name,
}));
}

function resolveModelName(model?: string): string | undefined {
if (!model) return undefined;
return model;
}
1 change: 1 addition & 0 deletions src/adapters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ export { exportToCrewAI } from './crewai.js';
export { exportToOpenClawString, exportToOpenClaw } from './openclaw.js';
export { exportToNanobotString, exportToNanobot } from './nanobot.js';
export { exportToCopilotString, exportToCopilot } from './copilot.js';
export { exportToDeepAgents } from './deepagents.js';
8 changes: 6 additions & 2 deletions src/commands/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from '../adapters/index.js';
import { exportToLyzrString } from '../adapters/lyzr.js';
import { exportToGitHubString } from '../adapters/github.js';
import { exportToDeepAgents } from '../adapters/deepagents.js';

interface ExportOptions {
format: string;
Expand All @@ -21,7 +22,7 @@ interface ExportOptions {

export const exportCommand = new Command('export')
.description('Export agent to other formats')
.requiredOption('-f, --format <format>', 'Export format (system-prompt, claude-code, openai, crewai, openclaw, nanobot, lyzr, github, copilot)')
.requiredOption('-f, --format <format>', 'Export format (system-prompt, claude-code, openai, crewai, openclaw, nanobot, lyzr, github, copilot, deepagents)')
.option('-d, --dir <dir>', 'Agent directory', '.')
.option('-o, --output <output>', 'Output file path')
.action(async (options: ExportOptions) => {
Expand Down Expand Up @@ -61,9 +62,12 @@ export const exportCommand = new Command('export')
case 'copilot':
result = exportToCopilotString(dir);
break;
case 'deepagents':
result = exportToDeepAgents(dir);
break;
default:
error(`Unknown format: ${options.format}`);
info('Supported formats: system-prompt, claude-code, openai, crewai, openclaw, nanobot, lyzr, github, copilot');
info('Supported formats: system-prompt, claude-code, openai, crewai, openclaw, nanobot, lyzr, github, copilot, deepagents');
process.exit(1);
}

Expand Down