Skip to content

Commit d22ed32

Browse files
committed
fix: update export commands in documentation and examples to use 'opengap' instead of 'gapman'; add TODOs for tool implementations
1 parent b1986ea commit d22ed32

8 files changed

Lines changed: 56 additions & 10 deletions

File tree

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ If your framework has a config file format, add an exporter in `commands/export.
136136
If your example directory commits an `expected_output.py` (or similar) snapshot, regenerate it whenever you change the adapter so it does not drift:
137137

138138
```bash
139-
gapman export --dir examples/<name> --format <name> --output examples/<name>/expected_output.py
139+
opengap export --dir examples/<name> --format <name> --output examples/<name>/expected_output.py
140140
```
141141

142142
### 4. Document what's lossy

examples/deepagents/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ A research agent with a fact-checker sub-agent, demonstrating the DeepAgents ada
66
- `SOUL.md` — agent identity, embedded in the generated `system_prompt`
77
- `skills/web-research/`, `skills/summarize/` — passed via a `skills=` path resolved next to the generated module (`Path(__file__).parent / "skills"`), so discovery works from any working directory; DeepAgents loads each `SKILL.md` natively
88
- `tools/web-search.yaml` — emitted as a `@tool` function bound into `tools=[...]`
9-
- `agents/fact-checker/` — emitted as a `SubAgent` dict in `subagents=[...]`
9+
- `agents/fact-checker/` — emitted as a `SubAgent` dict in `subagents=[...]`. By default a sub-agent inherits the full parent `TOOLS` list; add a `tools:` list to the sub-agent's own `agent.yaml` to narrow it to a subset.
1010
- `expected_output.py` — the Python module the adapter produces
1111

1212
DeepAgents is a higher-level harness on top of LangGraph — there is no graph
@@ -17,7 +17,7 @@ adapter instead.
1717
## Regenerate
1818

1919
```bash
20-
gapman export --dir examples/deepagents --format deepagents --output examples/deepagents/expected_output.py
20+
opengap export --dir examples/deepagents --format deepagents --output examples/deepagents/expected_output.py
2121
```
2222

2323
## Run the generated agent
@@ -30,7 +30,7 @@ python examples/deepagents/expected_output.py
3030
Or let gitagent generate and run it for you (pass an initial message with `-p`, read from `GITAGENT_PROMPT`):
3131

3232
```bash
33-
gapman run --dir examples/deepagents --adapter deepagents -p "Who won the 2022 World Cup?"
33+
opengap run --dir examples/deepagents --adapter deepagents -p "Who won the 2022 World Cup?"
3434
```
3535

3636
The generated file leaves tool implementations as `NotImplementedError` stubs — replace them with your own logic before invoking.

examples/deepagents/expected_output.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def _run_pre_tool_use_hooks(tool_name: str) -> None:
3939
def web_search(query: str) -> str:
4040
"""Search the public web for a query"""
4141
_run_pre_tool_use_hooks("web-search")
42+
# TODO: replace this stub with a real implementation of "web-search"
4243
raise NotImplementedError("Implement tool: web-search")
4344

4445
TOOLS = [web_search]
@@ -63,7 +64,8 @@ def web_search(query: str) -> str:
6364
You are a pedantic fact-checker. Treat every claim as unverified until you
6465
locate a primary source. If a claim cannot be substantiated, say so plainly.
6566
""",
66-
"tools": TOOLS,
67+
"tools": TOOLS, # inherits the full parent toolset (default) — add `tools: [...]` to
68+
# this sub-agent's agent.yaml (agents/fact-checker/agent.yaml) to narrow it
6769
}
6870

6971
SUBAGENTS = [fact_checker_subagent]

examples/langgraph/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ A two-step research agent that demonstrates the LangGraph adapter:
1313
## Regenerate
1414

1515
```bash
16-
gapman export --dir examples/langgraph --format langgraph --output examples/langgraph/expected_output.py
16+
opengap export --dir examples/langgraph --format langgraph --output examples/langgraph/expected_output.py
1717
```
1818

1919
## Run the generated graph
@@ -26,7 +26,7 @@ python examples/langgraph/expected_output.py
2626
Or let gitagent generate and run it for you (pass an initial message with `-p`, read from `GITAGENT_PROMPT`):
2727

2828
```bash
29-
gapman run --dir examples/langgraph --adapter langgraph -p "Summarize the latest on RISC-V adoption"
29+
opengap run --dir examples/langgraph --adapter langgraph -p "Summarize the latest on RISC-V adoption"
3030
```
3131

3232
The generated file leaves tool implementations as `NotImplementedError` stubs — replace them with your own logic before invoking.

examples/langgraph/expected_output.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class AgentState(TypedDict):
5353
@tool
5454
def web_search(query: str) -> str:
5555
"""Search the public web for a query"""
56+
# TODO: replace this stub with a real implementation of "web-search"
5657
raise NotImplementedError("Implement tool: web-search")
5758

5859
TOOLS = [web_search]

src/adapters/deepagents.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,30 @@ describe('exportToDeepAgents', () => {
191191
assert.match(code, /subagents=SUBAGENTS,/);
192192
});
193193

194+
test('sub-agent with its own `tools:` list gets a narrowed tools array, not the full parent TOOLS', () => {
195+
const dir = makeAgentDir({
196+
tools: [{ name: 'web-search' }, { name: 'send-email' }],
197+
subAgents: [{ name: 'fact-checker', description: 'Verifies claims' }],
198+
});
199+
writeFileSync(
200+
join(dir, 'agents', 'fact-checker', 'agent.yaml'),
201+
`spec_version: '0.1.0'\nname: fact-checker\nversion: '0.1.0'\ndescription: 'Verifies claims'\ntools:\n - web-search\n`,
202+
'utf-8',
203+
);
204+
const { code } = exportToDeepAgents(dir);
205+
assert.match(code, /"tools": \[web_search\],/);
206+
assert.doesNotMatch(code, /"tools": TOOLS,/);
207+
});
208+
209+
test('sub-agent without a `tools:` list inherits the full parent TOOLS', () => {
210+
const dir = makeAgentDir({
211+
tools: [{ name: 'web-search' }],
212+
subAgents: [{ name: 'fact-checker', description: 'Verifies claims' }],
213+
});
214+
const { code } = exportToDeepAgents(dir);
215+
assert.match(code, /"tools": TOOLS,/);
216+
});
217+
194218
test('pre_tool_use hooks are invoked from inside each generated tool function', () => {
195219
const dir = makeAgentDir({
196220
tools: [{ name: 'noop', description: 'A no-op' }],

src/adapters/deepagents.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ interface SubAgentDef {
3838
description: string;
3939
systemPrompt: string;
4040
hasSkills: boolean;
41+
/** Tool names to expose to this sub-agent, or null to inherit the full parent TOOLS list. */
42+
toolNames: string[] | null;
4143
}
4244

4345
export function exportToDeepAgents(dir: string): DeepAgentsExport {
@@ -48,7 +50,7 @@ export function exportToDeepAgents(dir: string): DeepAgentsExport {
4850
const skills = loadAllSkills(join(agentDir, 'skills'));
4951
const tools = collectTools(agentDir);
5052
const preToolUseScripts = collectPreToolUseHooks(agentDir);
51-
const subAgents = collectSubAgents(agentDir);
53+
const subAgents = collectSubAgents(agentDir, tools);
5254

5355
const code = renderPython({
5456
manifest,
@@ -137,10 +139,11 @@ function collectPreToolUseHooks(agentDir: string): string[] {
137139
}
138140
}
139141

140-
function collectSubAgents(agentDir: string): SubAgentDef[] {
142+
function collectSubAgents(agentDir: string, tools: ToolDef[]): SubAgentDef[] {
141143
const agentsDir = join(agentDir, 'agents');
142144
if (!existsSync(agentsDir)) return [];
143145

146+
const knownToolNames = new Set(tools.map(t => t.name));
144147
const out: SubAgentDef[] = [];
145148
const entries = readdirSync(agentsDir, { withFileTypes: true });
146149
for (const entry of entries) {
@@ -150,11 +153,16 @@ function collectSubAgents(agentDir: string): SubAgentDef[] {
150153

151154
try {
152155
const subManifest = loadAgentManifest(subDir);
156+
// A sub-agent's agent.yaml may declare its own `tools:` list to narrow
157+
// which of the parent's tools it receives. Unknown names are dropped;
158+
// an empty/absent list means "inherit the full parent TOOLS" (default).
159+
const declared = (subManifest.tools ?? []).filter(name => knownToolNames.has(name));
153160
out.push({
154161
name: subManifest.name,
155162
description: subManifest.description,
156163
systemPrompt: buildSystemPrompt(subDir, subManifest),
157164
hasSkills: existsSync(join(subDir, 'skills')),
165+
toolNames: declared.length > 0 ? declared : null,
158166
});
159167
} catch {
160168
// skip sub-agents that fail to load
@@ -199,6 +207,9 @@ function renderPython(ctx: RenderContext): string {
199207
lines.push('# Agent metadata');
200208
lines.push(`AGENT_NAME = ${pyStr(manifest.name)}`);
201209
lines.push(`AGENT_VERSION = ${pyStr(manifest.version)}`);
210+
// create_deep_agent() resolves a string MODEL via langchain's init_chat_model,
211+
// which natively understands "provider:model" strings (e.g. "anthropic:claude-sonnet-4-5").
212+
// No prefix stripping is needed here — passing it straight through is correct.
202213
lines.push(`MODEL = ${pyStr(model)}`);
203214
lines.push('');
204215

@@ -236,6 +247,7 @@ function renderPython(ctx: RenderContext): string {
236247
lines.push(`def ${fnName}(${sig}) -> str:`);
237248
lines.push(` ${pyTripleStr(t.description || `Tool: ${t.name}`)}`);
238249
lines.push(` _run_pre_tool_use_hooks(${pyStr(t.name)})`);
250+
lines.push(` # TODO: replace this stub with a real implementation of "${t.name}"`);
239251
lines.push(` raise NotImplementedError("Implement tool: ${t.name}")`);
240252
lines.push('');
241253
}
@@ -270,7 +282,13 @@ function renderPython(ctx: RenderContext): string {
270282
lines.push(` "name": ${pyStr(sub.name)},`);
271283
lines.push(` "description": ${pyStr(sub.description)},`);
272284
lines.push(` "system_prompt": ${pyTripleStr(sub.systemPrompt)},`);
273-
lines.push(' "tools": TOOLS,');
285+
if (sub.toolNames) {
286+
const list = sub.toolNames.map(pyIdent).join(', ');
287+
lines.push(` "tools": [${list}], # narrowed via agents/${sub.name}/agent.yaml's \`tools:\` list`);
288+
} else {
289+
lines.push(' "tools": TOOLS, # inherits the full parent toolset (default) — add `tools: [...]` to');
290+
lines.push(` # this sub-agent's agent.yaml (agents/${sub.name}/agent.yaml) to narrow it`);
291+
}
274292
if (sub.hasSkills) {
275293
lines.push(` "skills": [str(Path(__file__).resolve().parent / "agents" / ${pyStr(sub.name)} / "skills")],`);
276294
}

src/adapters/langgraph.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ function renderPython(ctx: RenderContext): string {
305305
lines.push('@tool');
306306
lines.push(`def ${fnName}(${sig}) -> str:`);
307307
lines.push(` ${pyTripleStr(t.description || `Tool: ${t.name}`)}`);
308+
lines.push(` # TODO: replace this stub with a real implementation of "${t.name}"`);
308309
lines.push(` raise NotImplementedError("Implement tool: ${t.name}")`);
309310
lines.push('');
310311
}

0 commit comments

Comments
 (0)