agentf 0.4.7 → 0.6.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.
- checksums.yaml +4 -4
- data/lib/agentf/agents/architect.rb +7 -3
- data/lib/agentf/agents/base.rb +31 -3
- data/lib/agentf/agents/debugger.rb +30 -8
- data/lib/agentf/agents/designer.rb +20 -8
- data/lib/agentf/agents/documenter.rb +8 -2
- data/lib/agentf/agents/explorer.rb +29 -11
- data/lib/agentf/agents/reviewer.rb +12 -7
- data/lib/agentf/agents/security.rb +27 -15
- data/lib/agentf/agents/specialist.rb +34 -18
- data/lib/agentf/agents/tester.rb +48 -8
- data/lib/agentf/cli/agent.rb +95 -0
- data/lib/agentf/cli/eval.rb +203 -0
- data/lib/agentf/cli/install.rb +7 -0
- data/lib/agentf/cli/memory.rb +138 -90
- data/lib/agentf/cli/router.rb +16 -4
- data/lib/agentf/cli/update.rb +9 -2
- data/lib/agentf/commands/memory_reviewer.rb +22 -48
- data/lib/agentf/commands/metrics.rb +18 -25
- data/lib/agentf/commands/registry.rb +28 -0
- data/lib/agentf/context_builder.rb +4 -14
- data/lib/agentf/embedding_provider.rb +35 -0
- data/lib/agentf/evals/report.rb +134 -0
- data/lib/agentf/evals/runner.rb +771 -0
- data/lib/agentf/evals/scenario.rb +211 -0
- data/lib/agentf/installer.rb +498 -365
- data/lib/agentf/mcp/server.rb +294 -114
- data/lib/agentf/memory.rb +354 -214
- data/lib/agentf/service/providers.rb +10 -62
- data/lib/agentf/version.rb +1 -1
- data/lib/agentf/workflow_engine.rb +205 -77
- data/lib/agentf.rb +10 -3
- metadata +9 -3
- data/lib/agentf/packs.rb +0 -74
data/lib/agentf/installer.rb
CHANGED
|
@@ -9,12 +9,9 @@ module Agentf
|
|
|
9
9
|
class Installer
|
|
10
10
|
READ_ACTIONS = {
|
|
11
11
|
"get_recent_memories" => { cli: "agentf memory recent -n 10", tool: "agentf-memory-recent" },
|
|
12
|
-
"
|
|
12
|
+
"get_episodes" => { cli: "agentf memory episodes -n 10", tool: "agentf-memory-episodes" },
|
|
13
13
|
"get_lessons" => { cli: "agentf memory lessons -n 10", tool: "agentf-memory-recent" },
|
|
14
|
-
"get_successes" => { cli: "agentf memory successes -n 10", tool: "agentf-memory-recent" },
|
|
15
14
|
"get_intents" => { cli: "agentf memory intents", tool: "agentf-memory-recent" },
|
|
16
|
-
"get_all_tags" => { cli: "agentf memory tags", tool: "agentf-memory-recent" },
|
|
17
|
-
"get_by_tag" => { cli: "agentf memory by-tag <tag> -n 10", tool: "agentf-memory-search" },
|
|
18
15
|
"get_by_type" => { cli: "agentf memory by-type <type> -n 10", tool: "agentf-memory-search" },
|
|
19
16
|
"get_by_agent" => { cli: "agentf memory by-agent <agent> -n 10", tool: "agentf-memory-search" },
|
|
20
17
|
"search" => { cli: "agentf memory search \"<query>\" -n 10", tool: "agentf-memory-search" },
|
|
@@ -22,10 +19,10 @@ module Agentf
|
|
|
22
19
|
}.freeze
|
|
23
20
|
|
|
24
21
|
WRITE_ACTIONS = {
|
|
25
|
-
"store_lesson" => { cli: "agentf memory add-lesson \"<title>\" \"<description>\" --agent=<AGENT>
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"store_business_intent" => { cli: "agentf memory add-business-intent \"<title>\" \"<description>\"
|
|
22
|
+
"store_lesson" => { cli: "agentf memory add-lesson \"<title>\" \"<description>\" --agent=<AGENT>", tool: "agentf-memory-add-lesson" },
|
|
23
|
+
"store_episode" => { cli: "agentf memory add-episode \"<title>\" \"<description>\" --outcome=positive --agent=<AGENT>", tool: "agentf-memory-episodes" },
|
|
24
|
+
"store_playbook" => { cli: "agentf memory add-playbook \"<title>\" \"<description>\" --steps=\"<step1>;<step2>\"", tool: "agentf-memory-add-playbook" },
|
|
25
|
+
"store_business_intent" => { cli: "agentf memory add-business-intent \"<title>\" \"<description>\"", tool: "agentf-memory-add-business-intent" },
|
|
29
26
|
"store_feature_intent" => { cli: "agentf memory add-feature-intent \"<title>\" \"<description>\" --acceptance=\"<criteria>\"", tool: "agentf-memory-add-feature-intent" }
|
|
30
27
|
}.freeze
|
|
31
28
|
|
|
@@ -46,12 +43,13 @@ module Agentf
|
|
|
46
43
|
}
|
|
47
44
|
}.freeze
|
|
48
45
|
|
|
49
|
-
def initialize(global_root: Dir.home, local_root: Dir.pwd, dry_run: false, verbose: false, install_deps: true)
|
|
46
|
+
def initialize(global_root: Dir.home, local_root: Dir.pwd, dry_run: false, verbose: false, install_deps: true, opencode_runtime: "mcp")
|
|
50
47
|
@global_root = global_root
|
|
51
48
|
@local_root = local_root
|
|
52
49
|
@dry_run = dry_run
|
|
53
50
|
@verbose = verbose
|
|
54
51
|
@install_deps = install_deps
|
|
52
|
+
@opencode_runtime = opencode_runtime.to_s
|
|
55
53
|
end
|
|
56
54
|
|
|
57
55
|
def install(
|
|
@@ -83,10 +81,11 @@ module Agentf
|
|
|
83
81
|
writes.concat(write_agents(root: root, layout: layout, provider: provider, only_agents: only_agents))
|
|
84
82
|
writes.concat(write_commands(root: root, layout: layout, provider: provider, only_commands: only_commands))
|
|
85
83
|
writes.concat(write_opencode_helpers(root: root)) if provider.to_s == "opencode"
|
|
84
|
+
writes.concat(write_copilot_helpers(root: root)) if provider.to_s == "copilot"
|
|
86
85
|
end
|
|
87
86
|
|
|
88
87
|
# Optionally install dependencies for opencode helper package.json
|
|
89
|
-
if provider.to_s == "opencode" && @install_deps
|
|
88
|
+
if provider.to_s == "opencode" && @install_deps && opencode_plugin_runtime?
|
|
90
89
|
roots.each do |root|
|
|
91
90
|
package_json_path = File.join(root, ".opencode/package.json")
|
|
92
91
|
if @dry_run
|
|
@@ -171,23 +170,39 @@ module Agentf
|
|
|
171
170
|
File.join(root, ".opencode/agents/agentf-orchestrator.md"),
|
|
172
171
|
render_workflow_engine_manifest
|
|
173
172
|
)
|
|
174
|
-
writes << write_manifest(
|
|
175
|
-
File.join(root, ".opencode/plugins/agentf-plugin.ts"),
|
|
176
|
-
render_opencode_plugin
|
|
177
|
-
)
|
|
178
|
-
writes << write_manifest(
|
|
179
|
-
File.join(root, ".opencode/tsconfig.json"),
|
|
180
|
-
render_opencode_tsconfig
|
|
181
|
-
)
|
|
182
|
-
writes << write_package_json(root)
|
|
183
173
|
writes << write_manifest(
|
|
184
174
|
File.join(root, ".opencode/memory/agentf-redis-schema.md"),
|
|
185
175
|
render_opencode_memory_schema
|
|
186
176
|
)
|
|
187
177
|
writes << write_opencode_json(root)
|
|
178
|
+
if opencode_plugin_runtime?
|
|
179
|
+
writes << write_manifest(
|
|
180
|
+
File.join(root, ".opencode/plugins/opencode-plugin.d.ts"),
|
|
181
|
+
render_opencode_plugin
|
|
182
|
+
)
|
|
183
|
+
writes << write_manifest(
|
|
184
|
+
File.join(root, ".opencode/plugins/agentf-plugin.ts"),
|
|
185
|
+
render_agentf_plugin
|
|
186
|
+
)
|
|
187
|
+
writes << write_manifest(
|
|
188
|
+
File.join(root, ".opencode/tsconfig.json"),
|
|
189
|
+
render_opencode_tsconfig
|
|
190
|
+
)
|
|
191
|
+
writes << write_package_json(root)
|
|
192
|
+
end
|
|
188
193
|
writes
|
|
189
194
|
end
|
|
190
195
|
|
|
196
|
+
def write_copilot_helpers(root:)
|
|
197
|
+
return [] unless root == @local_root
|
|
198
|
+
|
|
199
|
+
[write_copilot_mcp_json(root)]
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def opencode_plugin_runtime?
|
|
203
|
+
@opencode_runtime == "plugin"
|
|
204
|
+
end
|
|
205
|
+
|
|
191
206
|
def discover_agents
|
|
192
207
|
Agentf::Agents.constants
|
|
193
208
|
.map { |const| Agentf::Agents.const_get(const) }
|
|
@@ -223,46 +238,42 @@ module Agentf
|
|
|
223
238
|
end
|
|
224
239
|
|
|
225
240
|
def render_agent_manifest(klass, provider:)
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
"memory" => klass.memory_concepts,
|
|
231
|
-
"policy" => klass.policy_boundaries
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
<<~MARKDOWN
|
|
235
|
-
#{meta.to_yaml}---
|
|
236
|
-
#{klass.prompt}
|
|
241
|
+
# Emit a minimal, stable manifest that acts as a pointer to the runtime
|
|
242
|
+
# tool implemented by the plugin/CLI. Keep filename and `name` stable so
|
|
243
|
+
# upgrades remain compatible with existing installs.
|
|
244
|
+
tool_name = agent_identifier(klass)
|
|
237
245
|
|
|
238
|
-
|
|
239
|
-
|
|
246
|
+
# Build a short policy summary (guard nils and limit length)
|
|
247
|
+
pb = klass.respond_to?(:policy_boundaries) ? klass.policy_boundaries || {} : {}
|
|
248
|
+
always = Array(pb["always"]).join("; ")
|
|
249
|
+
ask_first = Array(pb["ask_first"]).join("; ")
|
|
250
|
+
never = Array(pb["never"]).join("; ")
|
|
240
251
|
|
|
241
|
-
|
|
242
|
-
|
|
252
|
+
parts = []
|
|
253
|
+
parts << "Always: #{always}" unless always.to_s.strip.empty?
|
|
254
|
+
parts << "Ask first: #{ask_first}" unless ask_first.to_s.strip.empty?
|
|
255
|
+
parts << "Never: #{never}" unless never.to_s.strip.empty?
|
|
256
|
+
policy_summary = parts.join(" | ")
|
|
243
257
|
|
|
244
|
-
|
|
245
|
-
#{Array(klass.deliverables).map { |item| "- #{item}" }.join("\n")}
|
|
258
|
+
description = klass.respond_to?(:description) ? klass.description.to_s.strip : ""
|
|
246
259
|
|
|
247
|
-
|
|
248
|
-
|
|
260
|
+
<<~MARKDOWN
|
|
261
|
+
---
|
|
262
|
+
name: #{tool_name}
|
|
263
|
+
description: #{description}
|
|
264
|
+
---
|
|
265
|
+
This manifest is a thin pointer. All runtime logic lives in the `#{tool_name}` tool.
|
|
249
266
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
- Policy: #{klass.memory_concepts["policy"]}
|
|
267
|
+
IMPORTANT: Use the `#{tool_name}` tool for any filesystem, codebase, or memory actions.
|
|
268
|
+
The manifest contains only routing and a small policy summary — the tool is the
|
|
269
|
+
authoritative implementation.
|
|
254
270
|
|
|
255
|
-
|
|
256
|
-
|
|
271
|
+
If the tool returns `confirmation_required: true`, ask the user whether to continue.
|
|
272
|
+
If they approve, rerun the same tool with `confirmedWrite=confirmed`. If they decline,
|
|
273
|
+
do not retry the write.
|
|
257
274
|
|
|
258
|
-
|
|
259
|
-
- Always: #{Array(klass.policy_boundaries["always"]).join("; ")}
|
|
260
|
-
- Ask first: #{Array(klass.policy_boundaries["ask_first"]).join("; ")}
|
|
261
|
-
- Never: #{Array(klass.policy_boundaries["never"]).join("; ")}
|
|
262
|
-
- Required inputs: #{Array(klass.policy_boundaries["required_inputs"]).join(", ")}
|
|
263
|
-
- Required outputs: #{Array(klass.policy_boundaries["required_outputs"]).join(", ")}
|
|
275
|
+
Policy Summary: #{policy_summary}
|
|
264
276
|
|
|
265
|
-
#{copilot_mcp_agent_section(provider: provider)}
|
|
266
277
|
MARKDOWN
|
|
267
278
|
end
|
|
268
279
|
|
|
@@ -319,20 +330,19 @@ module Agentf
|
|
|
319
330
|
end
|
|
320
331
|
|
|
321
332
|
def render_command_manifest(manifest, provider:)
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
"name" => command_identifier(manifest.fetch("name")),
|
|
325
|
-
"description" => manifest.fetch("description"),
|
|
326
|
-
"commands" => commands
|
|
327
|
-
}
|
|
333
|
+
cmd_name = command_identifier(manifest.fetch("name"))
|
|
334
|
+
desc = manifest.fetch("description", "").to_s.strip
|
|
328
335
|
|
|
329
336
|
<<~MARKDOWN
|
|
330
|
-
|
|
331
|
-
#
|
|
337
|
+
---
|
|
338
|
+
name: #{cmd_name}
|
|
339
|
+
description: #{desc}
|
|
340
|
+
---
|
|
341
|
+
This is a thin command manifest that routes execution to the `#{cmd_name}` tool.
|
|
332
342
|
|
|
333
|
-
|
|
343
|
+
IMPORTANT: Do not embed runtime logic here. Invoke the `#{cmd_name}` tool to perform
|
|
344
|
+
any codebase or memory operations.
|
|
334
345
|
|
|
335
|
-
#{copilot_mcp_command_section(manifest: manifest, provider: provider)}
|
|
336
346
|
MARKDOWN
|
|
337
347
|
end
|
|
338
348
|
|
|
@@ -345,8 +355,8 @@ module Agentf
|
|
|
345
355
|
Copilot should call the local `agentf` MCP server tools for runtime actions.
|
|
346
356
|
|
|
347
357
|
- Code discovery tools: `agentf-code-glob`, `agentf-code-grep`, `agentf-code-tree`, `agentf-code-related-files`
|
|
348
|
-
- Memory read tools: `agentf-memory-recent`, `agentf-memory-search`
|
|
349
|
-
- Memory write tools (if enabled): `agentf-memory-add-lesson`, `agentf-memory-add-
|
|
358
|
+
- Memory read tools: `agentf-memory-recent`, `agentf-memory-search`, `agentf-memory-episodes`
|
|
359
|
+
- Memory write tools (if enabled): `agentf-memory-add-lesson`, `agentf-memory-add-playbook`
|
|
350
360
|
|
|
351
361
|
MCP server is started via `agentf mcp-server` and runs locally over stdio.
|
|
352
362
|
MARKDOWN
|
|
@@ -359,8 +369,8 @@ module Agentf
|
|
|
359
369
|
recommended_tools = case command_name
|
|
360
370
|
when "explorer"
|
|
361
371
|
"`agentf-code-glob`, `agentf-code-grep`, `agentf-code-tree`, `agentf-code-related-files`"
|
|
362
|
-
|
|
363
|
-
|
|
372
|
+
when "memory"
|
|
373
|
+
"`agentf-memory-recent`, `agentf-memory-search`, `agentf-memory-episodes`, `agentf-memory-add-lesson`, `agentf-memory-add-playbook`"
|
|
364
374
|
else
|
|
365
375
|
"`agentf-code-glob`, `agentf-code-grep`, `agentf-memory-recent`, `agentf-memory-search`"
|
|
366
376
|
end
|
|
@@ -395,7 +405,7 @@ module Agentf
|
|
|
395
405
|
1. Build plan from provider adapter (`Agentf::Service::Providers::OpenCode` or `Agentf::Service::Providers::Copilot`)
|
|
396
406
|
2. Enrich each agent step with brain context from Redis memory
|
|
397
407
|
3. Persist feature intent at workflow start
|
|
398
|
-
4. Persist lessons
|
|
408
|
+
4. Persist lessons and negative episodes from each agent execution
|
|
399
409
|
5. Return full workflow state for manual review and future autonomous control
|
|
400
410
|
6. Enforce workflow contract stages (`spec`, `plan`, `execute`, `review`, `finalize`) when enabled
|
|
401
411
|
|
|
@@ -417,13 +427,34 @@ module Agentf
|
|
|
417
427
|
end
|
|
418
428
|
|
|
419
429
|
def render_opencode_plugin
|
|
430
|
+
<<~'TYPESCRIPT'
|
|
431
|
+
declare module "@opencode-ai/plugin" {
|
|
432
|
+
export type Plugin = (input: any) => Promise<any>;
|
|
433
|
+
|
|
434
|
+
// Minimal `tool` factory type used by our plugin. Keep very loose to avoid
|
|
435
|
+
// coupling to the full SDK types in this repo.
|
|
436
|
+
export function tool(def: any): any;
|
|
437
|
+
|
|
438
|
+
export const schema: any;
|
|
439
|
+
|
|
440
|
+
export default {} as { tool: typeof tool; schema: any };
|
|
441
|
+
}
|
|
442
|
+
TYPESCRIPT
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
def render_agentf_plugin
|
|
420
446
|
<<~'TYPESCRIPT'
|
|
421
447
|
// tools:
|
|
422
448
|
import { execFile } from "child_process";
|
|
423
449
|
import { promisify } from "util";
|
|
424
|
-
import path from "path";
|
|
425
|
-
|
|
426
|
-
|
|
450
|
+
import * as path from "path";
|
|
451
|
+
// Avoid importing host SDK types directly to reduce coupling during local
|
|
452
|
+
// type-checks. Use a runtime require and loose `any` types here.
|
|
453
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
454
|
+
const _opencode_plugin: any = require("@opencode-ai/plugin");
|
|
455
|
+
const tool = _opencode_plugin.tool;
|
|
456
|
+
type Plugin = any;
|
|
457
|
+
import * as fs from "fs";
|
|
427
458
|
|
|
428
459
|
const execFileAsync = promisify(execFile);
|
|
429
460
|
|
|
@@ -553,317 +584,373 @@ module Agentf
|
|
|
553
584
|
});
|
|
554
585
|
|
|
555
586
|
const text = stdout.toString().trim();
|
|
556
|
-
|
|
587
|
+
if (!text) return {};
|
|
588
|
+
|
|
589
|
+
try {
|
|
590
|
+
return JSON.parse(text);
|
|
591
|
+
} catch (err) {
|
|
592
|
+
// If the CLI returned non-JSON, return a structured error object so callers
|
|
593
|
+
// can surface useful debugging info instead of crashing.
|
|
594
|
+
return { ok: false, _parse_error: String(err), _raw: text };
|
|
595
|
+
}
|
|
557
596
|
}
|
|
558
597
|
|
|
559
|
-
|
|
560
|
-
|
|
598
|
+
// Lightweight frontmatter parser: extract YAML between leading `---` blocks
|
|
599
|
+
function parseFrontmatter(content: string): Record<string, string> {
|
|
600
|
+
const res: Record<string, string> = {};
|
|
601
|
+
const fmStart = content.indexOf("---");
|
|
602
|
+
if (fmStart === -1) return res;
|
|
603
|
+
const rest = content.slice(fmStart + 3);
|
|
604
|
+
const fmEndIdx = rest.indexOf("---");
|
|
605
|
+
if (fmEndIdx === -1) return res;
|
|
606
|
+
const block = rest.slice(0, fmEndIdx).trim();
|
|
607
|
+
const lines = block.split(/\r?\n/);
|
|
608
|
+
for (const line of lines) {
|
|
609
|
+
const m = line.match(/^\s*([A-Za-z0-9_\-]+)\s*:\s*(.+)\s*$/);
|
|
610
|
+
if (!m) continue;
|
|
611
|
+
let key = m[1];
|
|
612
|
+
let value = m[2];
|
|
613
|
+
// strip surrounding quotes
|
|
614
|
+
if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
|
|
615
|
+
value = value.slice(1, -1);
|
|
616
|
+
}
|
|
617
|
+
res[key] = value;
|
|
618
|
+
}
|
|
619
|
+
return res;
|
|
620
|
+
}
|
|
561
621
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
622
|
+
export const agentfPlugin = async (input: any) => {
|
|
623
|
+
const workspaceDir = input?.directory || process.env.PWD || process.cwd();
|
|
624
|
+
await ensureAgentfPreflight(workspaceDir);
|
|
625
|
+
|
|
626
|
+
// Build static tools map
|
|
627
|
+
const staticTools: Record<string, ReturnType<typeof tool>> = {
|
|
628
|
+
"agentf-code-glob": tool({
|
|
629
|
+
description: "Find files using project glob patterns via Agentf code CLI.",
|
|
630
|
+
args: {
|
|
631
|
+
pattern: tool.schema.string().describe("Glob pattern, example: lib/**/*.rb"),
|
|
632
|
+
types: tool.schema.array(tool.schema.string()).optional().describe("Optional file extensions"),
|
|
633
|
+
},
|
|
634
|
+
async execute(_args: any, context: any) {
|
|
635
|
+
const commandArgs = [];
|
|
636
|
+
if (_args.types?.length) {
|
|
637
|
+
commandArgs.push(`--types=${_args.types.join(",")}`);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return runAgentfCli(context.directory, "code", "glob", [_args.pattern, ...commandArgs]);
|
|
641
|
+
},
|
|
642
|
+
}),
|
|
643
|
+
"agentf-code-grep": tool({
|
|
644
|
+
description: "Search file contents via Agentf code CLI.",
|
|
645
|
+
args: {
|
|
646
|
+
pattern: tool.schema.string().describe("Regex/text to search"),
|
|
647
|
+
filePattern: tool.schema.string().optional().describe("Optional include pattern"),
|
|
648
|
+
context: tool.schema.number().int().min(0).max(20).optional().describe("Context lines"),
|
|
649
|
+
},
|
|
650
|
+
async execute(_args: any, context: any) {
|
|
651
|
+
const commandArgs = [];
|
|
652
|
+
if (_args.filePattern) commandArgs.push(`--file-pattern=${_args.filePattern}`);
|
|
653
|
+
if (Number.isInteger(_args.context)) commandArgs.push(`--context=${_args.context}`);
|
|
654
|
+
|
|
655
|
+
return runAgentfCli(context.directory, "code", "grep", [_args.pattern, ...commandArgs]);
|
|
656
|
+
},
|
|
657
|
+
}),
|
|
658
|
+
"agentf-code-tree": tool({
|
|
659
|
+
description: "Get directory tree data via Agentf code CLI.",
|
|
660
|
+
args: {
|
|
661
|
+
depth: tool.schema.number().int().min(1).max(10).optional().describe("Max traversal depth"),
|
|
662
|
+
},
|
|
663
|
+
async execute(_args: any, context: any) {
|
|
664
|
+
const depth = _args.depth ?? 3;
|
|
665
|
+
return runAgentfCli(context.directory, "code", "tree", [`--depth=${depth}`]);
|
|
666
|
+
},
|
|
667
|
+
}),
|
|
668
|
+
"agentf-code-related-files": tool({
|
|
669
|
+
description: "Find import and related file hints for a target file.",
|
|
670
|
+
args: {
|
|
671
|
+
targetFile: tool.schema.string().describe("Workspace-relative file path"),
|
|
672
|
+
},
|
|
673
|
+
async execute(_args: any, context: any) {
|
|
674
|
+
return runAgentfCli(context.directory, "code", "related", [_args.targetFile]);
|
|
675
|
+
},
|
|
676
|
+
}),
|
|
677
|
+
"agentf-memory-recent": tool({
|
|
678
|
+
description: "Get recent Agentf memories from Redis.",
|
|
679
|
+
args: {
|
|
680
|
+
limit: tool.schema.number().int().min(1).max(100).optional().describe("How many memories to return"),
|
|
681
|
+
},
|
|
682
|
+
async execute(_args: any, context: any) {
|
|
683
|
+
const limit = _args.limit ?? 10;
|
|
684
|
+
return runAgentfCli(context.directory, "memory", "recent", ["-n", String(limit)]);
|
|
685
|
+
},
|
|
686
|
+
}),
|
|
687
|
+
"agentf-memory-search": tool({
|
|
688
|
+
description: "Search Agentf memories by keyword.",
|
|
689
|
+
args: {
|
|
690
|
+
query: tool.schema.string().describe("Search query"),
|
|
691
|
+
limit: tool.schema.number().int().min(1).max(100).optional().describe("How many results to return"),
|
|
692
|
+
},
|
|
693
|
+
async execute(_args: any, context: any) {
|
|
694
|
+
const limit = _args.limit ?? 10;
|
|
695
|
+
return runAgentfCli(context.directory, "memory", "search", [_args.query, "-n", String(limit)]);
|
|
696
|
+
},
|
|
697
|
+
}),
|
|
698
|
+
"agentf-memory-by-agent": tool({
|
|
699
|
+
description: "Get Agentf memories by agent.",
|
|
700
|
+
args: {
|
|
701
|
+
agent: tool.schema.string().describe("Agent name"),
|
|
702
|
+
limit: tool.schema.number().int().min(1).max(100).optional().describe("How many results to return"),
|
|
703
|
+
},
|
|
704
|
+
async execute(_args: any, context: any) {
|
|
705
|
+
const limit = _args.limit ?? 10;
|
|
706
|
+
return runAgentfCli(context.directory, "memory", "by-agent", [_args.agent, "-n", String(limit)]);
|
|
707
|
+
},
|
|
708
|
+
}),
|
|
709
|
+
"agentf-memory-by-type": tool({
|
|
710
|
+
description: "Get Agentf memories by type.",
|
|
711
|
+
args: {
|
|
712
|
+
type: tool.schema.string().describe("Memory type (episode|lesson|playbook|business_intent|feature_intent|incident)"),
|
|
713
|
+
limit: tool.schema.number().int().min(1).max(100).optional().describe("How many results to return"),
|
|
714
|
+
},
|
|
715
|
+
async execute(_args: any, context: any) {
|
|
716
|
+
const limit = _args.limit ?? 10;
|
|
717
|
+
return runAgentfCli(context.directory, "memory", "by-type", [_args.type, "-n", String(limit)]);
|
|
718
|
+
},
|
|
719
|
+
}),
|
|
720
|
+
"agentf-memory-episodes": tool({
|
|
721
|
+
description: "List episode memories.",
|
|
722
|
+
args: {
|
|
723
|
+
outcome: tool.schema.string().optional().describe("Optional outcome filter (positive|negative|neutral)"),
|
|
724
|
+
limit: tool.schema.number().int().min(1).max(100).optional(),
|
|
725
|
+
},
|
|
726
|
+
async execute(_args: any, context: any) {
|
|
727
|
+
const limit = _args.limit ?? 10;
|
|
728
|
+
const commandArgs = ["-n", String(limit)];
|
|
729
|
+
if (_args.outcome) commandArgs.push(`--outcome=${_args.outcome}`);
|
|
730
|
+
return runAgentfCli(context.directory, "memory", "episodes", commandArgs);
|
|
731
|
+
},
|
|
732
|
+
}),
|
|
733
|
+
"agentf-memory-lessons": tool({
|
|
734
|
+
description: "List lesson memories.",
|
|
735
|
+
args: { limit: tool.schema.number().int().min(1).max(100).optional() },
|
|
736
|
+
async execute(_args: any, context: any) {
|
|
737
|
+
const limit = _args.limit ?? 10;
|
|
738
|
+
return runAgentfCli(context.directory, "memory", "lessons", ["-n", String(limit)]);
|
|
739
|
+
},
|
|
740
|
+
}),
|
|
741
|
+
"agentf-memory-intents": tool({
|
|
742
|
+
description: "List intents (business, feature or both).",
|
|
743
|
+
args: { kind: tool.schema.string().optional(), limit: tool.schema.number().int().min(1).max(100).optional() },
|
|
744
|
+
async execute(_args: any, context: any) {
|
|
745
|
+
const limit = _args.limit ?? 10;
|
|
746
|
+
const kind = _args.kind ? String(_args.kind) : "";
|
|
747
|
+
const cmdArgs = kind ? [kind, "-n", String(limit)] : ["-n", String(limit)];
|
|
748
|
+
return runAgentfCli(context.directory, "memory", "intents", cmdArgs);
|
|
749
|
+
},
|
|
750
|
+
}),
|
|
751
|
+
"agentf-memory-business-intents": tool({
|
|
752
|
+
description: "List business intents.",
|
|
753
|
+
args: { limit: tool.schema.number().int().min(1).max(100).optional() },
|
|
754
|
+
async execute(_args: any, context: any) {
|
|
755
|
+
const limit = _args.limit ?? 10;
|
|
756
|
+
return runAgentfCli(context.directory, "memory", "business-intents", ["-n", String(limit)]);
|
|
757
|
+
},
|
|
758
|
+
}),
|
|
759
|
+
"agentf-memory-feature-intents": tool({
|
|
760
|
+
description: "List feature intents.",
|
|
761
|
+
args: { limit: tool.schema.number().int().min(1).max(100).optional() },
|
|
762
|
+
async execute(_args: any, context: any) {
|
|
763
|
+
const limit = _args.limit ?? 10;
|
|
764
|
+
return runAgentfCli(context.directory, "memory", "feature-intents", ["-n", String(limit)]);
|
|
765
|
+
},
|
|
766
|
+
}),
|
|
767
|
+
"agentf-memory-add-business-intent": tool({
|
|
768
|
+
description: "Store a business intent in Redis.",
|
|
769
|
+
args: {
|
|
770
|
+
title: tool.schema.string(),
|
|
771
|
+
description: tool.schema.string(),
|
|
772
|
+
constraints: tool.schema.array(tool.schema.string()).optional(),
|
|
773
|
+
priority: tool.schema.number().int().optional(),
|
|
774
|
+
},
|
|
775
|
+
async execute(_args: any, context: any) {
|
|
776
|
+
const commandArgs = [_args.title, _args.description];
|
|
777
|
+
if (_args.constraints?.length) commandArgs.push(`--constraints=${_args.constraints.join(";")}`);
|
|
778
|
+
if (Number.isInteger(_args.priority)) commandArgs.push(`--priority=${String(_args.priority)}`);
|
|
779
|
+
return runAgentfCli(context.directory, "memory", "add-business-intent", commandArgs);
|
|
780
|
+
},
|
|
781
|
+
}),
|
|
782
|
+
"agentf-memory-add-feature-intent": tool({
|
|
783
|
+
description: "Store a feature intent in Redis.",
|
|
784
|
+
args: {
|
|
785
|
+
title: tool.schema.string(),
|
|
786
|
+
description: tool.schema.string(),
|
|
787
|
+
acceptance: tool.schema.array(tool.schema.string()).optional(),
|
|
788
|
+
non_goals: tool.schema.array(tool.schema.string()).optional(),
|
|
789
|
+
related_task_id: tool.schema.string().optional(),
|
|
790
|
+
},
|
|
791
|
+
async execute(_args: any, context: any) {
|
|
792
|
+
const commandArgs = [_args.title, _args.description];
|
|
793
|
+
if (_args.acceptance?.length) commandArgs.push(`--acceptance=${_args.acceptance.join(";")}`);
|
|
794
|
+
if (_args.non_goals?.length) commandArgs.push(`--non-goals=${_args.non_goals.join(";")}`);
|
|
795
|
+
if (_args.related_task_id) commandArgs.push(`--task=${_args.related_task_id}`);
|
|
796
|
+
return runAgentfCli(context.directory, "memory", "add-feature-intent", commandArgs);
|
|
797
|
+
},
|
|
798
|
+
}),
|
|
799
|
+
"agentf-memory-neighbors": tool({
|
|
800
|
+
description: "Get neighboring memory nodes by edge traversal.",
|
|
801
|
+
args: {
|
|
802
|
+
node_id: tool.schema.string(),
|
|
803
|
+
relation: tool.schema.string().optional(),
|
|
804
|
+
depth: tool.schema.number().int().optional(),
|
|
805
|
+
limit: tool.schema.number().int().optional(),
|
|
806
|
+
},
|
|
807
|
+
async execute(_args: any, context: any) {
|
|
808
|
+
const commandArgs = [_args.node_id];
|
|
809
|
+
if (_args.relation) commandArgs.push(`--relation=${_args.relation}`);
|
|
810
|
+
if (Number.isInteger(_args.depth)) commandArgs.push(`--depth=${String(_args.depth)}`);
|
|
811
|
+
if (Number.isInteger(_args.limit)) commandArgs.push(`-n`, String(_args.limit));
|
|
812
|
+
return runAgentfCli(context.directory, "memory", "neighbors", commandArgs);
|
|
813
|
+
},
|
|
814
|
+
}),
|
|
815
|
+
"agentf-memory-subgraph": tool({
|
|
816
|
+
description: "Build a subgraph from seed ids.",
|
|
817
|
+
args: {
|
|
818
|
+
seed_ids: tool.schema.array(tool.schema.string()),
|
|
819
|
+
relation_filters: tool.schema.array(tool.schema.string()).optional(),
|
|
820
|
+
depth: tool.schema.number().int().optional(),
|
|
821
|
+
limit: tool.schema.number().int().optional(),
|
|
822
|
+
},
|
|
823
|
+
async execute(_args: any, context: any) {
|
|
824
|
+
const seeds = (_args.seed_ids || []).join(",");
|
|
825
|
+
const commandArgs = [seeds];
|
|
826
|
+
if (_args.relation_filters?.length) commandArgs.push(`--relation=${_args.relation_filters.join(",")}`);
|
|
827
|
+
if (Number.isInteger(_args.depth)) commandArgs.push(`--depth=${String(_args.depth)}`);
|
|
828
|
+
if (Number.isInteger(_args.limit)) commandArgs.push(`-n`, String(_args.limit));
|
|
829
|
+
return runAgentfCli(context.directory, "memory", "subgraph", commandArgs);
|
|
830
|
+
},
|
|
831
|
+
}),
|
|
832
|
+
"agentf-memory-add-lesson": tool({
|
|
833
|
+
description: "Store a lesson memory in Redis.",
|
|
834
|
+
args: {
|
|
835
|
+
title: tool.schema.string(),
|
|
836
|
+
description: tool.schema.string(),
|
|
837
|
+
agent: tool.schema.string().optional(),
|
|
838
|
+
context: tool.schema.string().optional(),
|
|
839
|
+
},
|
|
840
|
+
async execute(_args: any, context: any) {
|
|
841
|
+
const commandArgs = [_args.title, _args.description];
|
|
842
|
+
if (_args.agent) commandArgs.push(`--agent=${_args.agent}`);
|
|
843
|
+
if (_args.context) commandArgs.push(`--context=${_args.context}`);
|
|
844
|
+
|
|
845
|
+
return runAgentfCli(context.directory, "memory", "add-lesson", commandArgs);
|
|
846
|
+
},
|
|
847
|
+
}),
|
|
848
|
+
"agentf-memory-add-playbook": tool({
|
|
849
|
+
description: "Store a playbook memory in Redis.",
|
|
850
|
+
args: {
|
|
851
|
+
title: tool.schema.string(),
|
|
852
|
+
description: tool.schema.string(),
|
|
853
|
+
agent: tool.schema.string().optional(),
|
|
854
|
+
steps: tool.schema.array(tool.schema.string()).optional(),
|
|
855
|
+
feature_area: tool.schema.string().optional(),
|
|
856
|
+
},
|
|
857
|
+
async execute(_args: any, context: any) {
|
|
858
|
+
const commandArgs = [_args.title, _args.description];
|
|
859
|
+
if (_args.agent) commandArgs.push(`--agent=${_args.agent}`);
|
|
860
|
+
if (_args.steps?.length) commandArgs.push(`--steps=${_args.steps.join(";")}`);
|
|
861
|
+
if (_args.feature_area) commandArgs.push(`--feature-area=${_args.feature_area}`);
|
|
862
|
+
|
|
863
|
+
return runAgentfCli(context.directory, "memory", "add-playbook", commandArgs);
|
|
864
|
+
},
|
|
865
|
+
}),
|
|
866
|
+
};
|
|
575
867
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
}),
|
|
579
|
-
"agentf-code-grep": tool({
|
|
580
|
-
description: "Search file contents via Agentf code CLI.",
|
|
581
|
-
args: {
|
|
582
|
-
pattern: tool.schema.string().describe("Regex/text to search"),
|
|
583
|
-
filePattern: tool.schema.string().optional().describe("Optional include pattern"),
|
|
584
|
-
context: tool.schema.number().int().min(0).max(20).optional().describe("Context lines"),
|
|
585
|
-
},
|
|
586
|
-
async execute(args, context) {
|
|
587
|
-
const commandArgs = [];
|
|
588
|
-
if (args.filePattern) commandArgs.push(`--file-pattern=${args.filePattern}`);
|
|
589
|
-
if (Number.isInteger(args.context)) commandArgs.push(`--context=${args.context}`);
|
|
868
|
+
const agentTools: Record<string, ReturnType<typeof tool>> = {};
|
|
869
|
+
const absDir = path.join(process.cwd(), ".opencode/agents");
|
|
590
870
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
async execute(args, context) {
|
|
600
|
-
const depth = args.depth ?? 3;
|
|
601
|
-
return runAgentfCli(context.directory, "code", "tree", [`--depth=${depth}`]);
|
|
602
|
-
},
|
|
603
|
-
}),
|
|
604
|
-
"agentf-code-related-files": tool({
|
|
605
|
-
description: "Find import and related file hints for a target file.",
|
|
606
|
-
args: {
|
|
607
|
-
targetFile: tool.schema.string().describe("Workspace-relative file path"),
|
|
608
|
-
},
|
|
609
|
-
async execute(args, context) {
|
|
610
|
-
return runAgentfCli(context.directory, "code", "related", [args.targetFile]);
|
|
611
|
-
},
|
|
612
|
-
}),
|
|
613
|
-
"agentf-memory-recent": tool({
|
|
614
|
-
description: "Get recent Agentf memories from Redis.",
|
|
615
|
-
args: {
|
|
616
|
-
limit: tool.schema.number().int().min(1).max(100).optional().describe("How many memories to return"),
|
|
617
|
-
},
|
|
618
|
-
async execute(args, context) {
|
|
619
|
-
const limit = args.limit ?? 10;
|
|
620
|
-
return runAgentfCli(context.directory, "memory", "recent", ["-n", String(limit)]);
|
|
621
|
-
},
|
|
622
|
-
}),
|
|
623
|
-
"agentf-memory-search": tool({
|
|
624
|
-
description: "Search Agentf memories by keyword.",
|
|
625
|
-
args: {
|
|
626
|
-
query: tool.schema.string().describe("Search query"),
|
|
627
|
-
limit: tool.schema.number().int().min(1).max(100).optional().describe("How many results to return"),
|
|
628
|
-
},
|
|
629
|
-
async execute(args, context) {
|
|
630
|
-
const limit = args.limit ?? 10;
|
|
631
|
-
return runAgentfCli(context.directory, "memory", "search", [args.query, "-n", String(limit)]);
|
|
632
|
-
},
|
|
633
|
-
}),
|
|
634
|
-
"agentf-memory-by-tag": tool({
|
|
635
|
-
description: "Get Agentf memories by tag.",
|
|
636
|
-
args: {
|
|
637
|
-
tag: tool.schema.string().describe("Tag to filter by"),
|
|
638
|
-
limit: tool.schema.number().int().min(1).max(100).optional().describe("How many results to return"),
|
|
639
|
-
},
|
|
640
|
-
async execute(args, context) {
|
|
641
|
-
const limit = args.limit ?? 10;
|
|
642
|
-
return runAgentfCli(context.directory, "memory", "by-tag", [args.tag, "-n", String(limit)]);
|
|
643
|
-
},
|
|
644
|
-
}),
|
|
645
|
-
"agentf-memory-by-agent": tool({
|
|
646
|
-
description: "Get Agentf memories by agent.",
|
|
647
|
-
args: {
|
|
648
|
-
agent: tool.schema.string().describe("Agent name"),
|
|
649
|
-
limit: tool.schema.number().int().min(1).max(100).optional().describe("How many results to return"),
|
|
650
|
-
},
|
|
651
|
-
async execute(args, context) {
|
|
652
|
-
const limit = args.limit ?? 10;
|
|
653
|
-
return runAgentfCli(context.directory, "memory", "by-agent", [args.agent, "-n", String(limit)]);
|
|
654
|
-
},
|
|
655
|
-
}),
|
|
656
|
-
"agentf-memory-by-type": tool({
|
|
657
|
-
description: "Get Agentf memories by type.",
|
|
658
|
-
args: {
|
|
659
|
-
type: tool.schema.string().describe("Memory type (pitfall|lesson|success|business_intent|feature_intent)"),
|
|
660
|
-
limit: tool.schema.number().int().min(1).max(100).optional().describe("How many results to return"),
|
|
661
|
-
},
|
|
662
|
-
async execute(args, context) {
|
|
663
|
-
const limit = args.limit ?? 10;
|
|
664
|
-
return runAgentfCli(context.directory, "memory", "by-type", [args.type, "-n", String(limit)]);
|
|
665
|
-
},
|
|
666
|
-
}),
|
|
667
|
-
"agentf-memory-tags": tool({
|
|
668
|
-
description: "List all unique memory tags.",
|
|
669
|
-
args: {},
|
|
670
|
-
async execute(_args, context) {
|
|
671
|
-
return runAgentfCli(context.directory, "memory", "tags", []);
|
|
672
|
-
},
|
|
673
|
-
}),
|
|
674
|
-
"agentf-memory-pitfalls": tool({
|
|
675
|
-
description: "List pitfall memories.",
|
|
676
|
-
args: { limit: tool.schema.number().int().min(1).max(100).optional() },
|
|
677
|
-
async execute(args, context) {
|
|
678
|
-
const limit = args.limit ?? 10;
|
|
679
|
-
return runAgentfCli(context.directory, "memory", "pitfalls", ["-n", String(limit)]);
|
|
680
|
-
},
|
|
681
|
-
}),
|
|
682
|
-
"agentf-memory-lessons": tool({
|
|
683
|
-
description: "List lesson memories.",
|
|
684
|
-
args: { limit: tool.schema.number().int().min(1).max(100).optional() },
|
|
685
|
-
async execute(args, context) {
|
|
686
|
-
const limit = args.limit ?? 10;
|
|
687
|
-
return runAgentfCli(context.directory, "memory", "lessons", ["-n", String(limit)]);
|
|
688
|
-
},
|
|
689
|
-
}),
|
|
690
|
-
"agentf-memory-successes": tool({
|
|
691
|
-
description: "List success memories.",
|
|
692
|
-
args: { limit: tool.schema.number().int().min(1).max(100).optional() },
|
|
693
|
-
async execute(args, context) {
|
|
694
|
-
const limit = args.limit ?? 10;
|
|
695
|
-
return runAgentfCli(context.directory, "memory", "successes", ["-n", String(limit)]);
|
|
696
|
-
},
|
|
697
|
-
}),
|
|
698
|
-
"agentf-memory-intents": tool({
|
|
699
|
-
description: "List intents (business, feature or both).",
|
|
700
|
-
args: { kind: tool.schema.string().optional(), limit: tool.schema.number().int().min(1).max(100).optional() },
|
|
701
|
-
async execute(args, context) {
|
|
702
|
-
const limit = args.limit ?? 10;
|
|
703
|
-
const kind = args.kind ? String(args.kind) : "";
|
|
704
|
-
const cmdArgs = kind ? [kind, "-n", String(limit)] : ["-n", String(limit)];
|
|
705
|
-
return runAgentfCli(context.directory, "memory", "intents", cmdArgs);
|
|
706
|
-
},
|
|
707
|
-
}),
|
|
708
|
-
"agentf-memory-business-intents": tool({
|
|
709
|
-
description: "List business intents.",
|
|
710
|
-
args: { limit: tool.schema.number().int().min(1).max(100).optional() },
|
|
711
|
-
async execute(args, context) {
|
|
712
|
-
const limit = args.limit ?? 10;
|
|
713
|
-
return runAgentfCli(context.directory, "memory", "business-intents", ["-n", String(limit)]);
|
|
714
|
-
},
|
|
715
|
-
}),
|
|
716
|
-
"agentf-memory-feature-intents": tool({
|
|
717
|
-
description: "List feature intents.",
|
|
718
|
-
args: { limit: tool.schema.number().int().min(1).max(100).optional() },
|
|
719
|
-
async execute(args, context) {
|
|
720
|
-
const limit = args.limit ?? 10;
|
|
721
|
-
return runAgentfCli(context.directory, "memory", "feature-intents", ["-n", String(limit)]);
|
|
722
|
-
},
|
|
723
|
-
}),
|
|
724
|
-
"agentf-memory-add-business-intent": tool({
|
|
725
|
-
description: "Store a business intent in Redis.",
|
|
726
|
-
args: {
|
|
727
|
-
title: tool.schema.string(),
|
|
728
|
-
description: tool.schema.string(),
|
|
729
|
-
tags: tool.schema.array(tool.schema.string()).optional(),
|
|
730
|
-
constraints: tool.schema.array(tool.schema.string()).optional(),
|
|
731
|
-
priority: tool.schema.number().int().optional(),
|
|
732
|
-
},
|
|
733
|
-
async execute(args, context) {
|
|
734
|
-
const commandArgs = [args.title, args.description];
|
|
735
|
-
if (args.tags?.length) commandArgs.push(`--tags=${args.tags.join(",")}`);
|
|
736
|
-
if (args.constraints?.length) commandArgs.push(`--constraints=${args.constraints.join(";")}`);
|
|
737
|
-
if (Number.isInteger(args.priority)) commandArgs.push(`--priority=${String(args.priority)}`);
|
|
738
|
-
return runAgentfCli(context.directory, "memory", "add-business-intent", commandArgs);
|
|
739
|
-
},
|
|
740
|
-
}),
|
|
741
|
-
"agentf-memory-add-feature-intent": tool({
|
|
742
|
-
description: "Store a feature intent in Redis.",
|
|
743
|
-
args: {
|
|
744
|
-
title: tool.schema.string(),
|
|
745
|
-
description: tool.schema.string(),
|
|
746
|
-
tags: tool.schema.array(tool.schema.string()).optional(),
|
|
747
|
-
acceptance: tool.schema.array(tool.schema.string()).optional(),
|
|
748
|
-
non_goals: tool.schema.array(tool.schema.string()).optional(),
|
|
749
|
-
related_task_id: tool.schema.string().optional(),
|
|
750
|
-
},
|
|
751
|
-
async execute(args, context) {
|
|
752
|
-
const commandArgs = [args.title, args.description];
|
|
753
|
-
if (args.tags?.length) commandArgs.push(`--tags=${args.tags.join(",")}`);
|
|
754
|
-
if (args.acceptance?.length) commandArgs.push(`--acceptance=${args.acceptance.join(";")}`);
|
|
755
|
-
if (args.non_goals?.length) commandArgs.push(`--non-goals=${args.non_goals.join(";")}`);
|
|
756
|
-
if (args.related_task_id) commandArgs.push(`--task=${args.related_task_id}`);
|
|
757
|
-
return runAgentfCli(context.directory, "memory", "add-feature-intent", commandArgs);
|
|
758
|
-
},
|
|
759
|
-
}),
|
|
760
|
-
"agentf-memory-neighbors": tool({
|
|
761
|
-
description: "Get neighboring memory nodes by edge traversal.",
|
|
762
|
-
args: {
|
|
763
|
-
node_id: tool.schema.string(),
|
|
764
|
-
relation: tool.schema.string().optional(),
|
|
765
|
-
depth: tool.schema.number().int().optional(),
|
|
766
|
-
limit: tool.schema.number().int().optional(),
|
|
767
|
-
},
|
|
768
|
-
async execute(args, context) {
|
|
769
|
-
const commandArgs = [args.node_id];
|
|
770
|
-
if (args.relation) commandArgs.push(`--relation=${args.relation}`);
|
|
771
|
-
if (Number.isInteger(args.depth)) commandArgs.push(`--depth=${String(args.depth)}`);
|
|
772
|
-
if (Number.isInteger(args.limit)) commandArgs.push(`-n`, String(args.limit));
|
|
773
|
-
return runAgentfCli(context.directory, "memory", "neighbors", commandArgs);
|
|
774
|
-
},
|
|
775
|
-
}),
|
|
776
|
-
"agentf-memory-subgraph": tool({
|
|
777
|
-
description: "Build a subgraph from seed ids.",
|
|
778
|
-
args: {
|
|
779
|
-
seed_ids: tool.schema.array(tool.schema.string()),
|
|
780
|
-
relation_filters: tool.schema.array(tool.schema.string()).optional(),
|
|
781
|
-
depth: tool.schema.number().int().optional(),
|
|
782
|
-
limit: tool.schema.number().int().optional(),
|
|
783
|
-
},
|
|
784
|
-
async execute(args, context) {
|
|
785
|
-
const seeds = (args.seed_ids || []).join(",");
|
|
786
|
-
const commandArgs = [seeds];
|
|
787
|
-
if (args.relation_filters?.length) commandArgs.push(`--relation=${args.relation_filters.join(",")}`);
|
|
788
|
-
if (Number.isInteger(args.depth)) commandArgs.push(`--depth=${String(args.depth)}`);
|
|
789
|
-
if (Number.isInteger(args.limit)) commandArgs.push(`-n`, String(args.limit));
|
|
790
|
-
return runAgentfCli(context.directory, "memory", "subgraph", commandArgs);
|
|
791
|
-
},
|
|
792
|
-
}),
|
|
793
|
-
"agentf-memory-add-lesson": tool({
|
|
794
|
-
description: "Store a lesson memory in Redis.",
|
|
795
|
-
args: {
|
|
796
|
-
title: tool.schema.string(),
|
|
797
|
-
description: tool.schema.string(),
|
|
798
|
-
agent: tool.schema.string().optional(),
|
|
799
|
-
tags: tool.schema.array(tool.schema.string()).optional(),
|
|
800
|
-
context: tool.schema.string().optional(),
|
|
801
|
-
},
|
|
802
|
-
async execute(args, context) {
|
|
803
|
-
const commandArgs = [args.title, args.description];
|
|
804
|
-
if (args.agent) commandArgs.push(`--agent=${args.agent}`);
|
|
805
|
-
if (args.tags?.length) commandArgs.push(`--tags=${args.tags.join(",")}`);
|
|
806
|
-
if (args.context) commandArgs.push(`--context=${args.context}`);
|
|
871
|
+
// Guard: agents directory may not exist in minimal workspaces (eg. tests).
|
|
872
|
+
if (fs.existsSync(absDir)) {
|
|
873
|
+
for (const file of fs.readdirSync(absDir)) {
|
|
874
|
+
const full = path.join(absDir, file);
|
|
875
|
+
if (!fs.statSync(full).isFile()) continue;
|
|
876
|
+
const content = fs.readFileSync(full, "utf8");
|
|
877
|
+
const fm = parseFrontmatter(content);
|
|
878
|
+
const toolName = fm["name"] || path.basename(file, path.extname(file));
|
|
807
879
|
|
|
808
|
-
|
|
809
|
-
},
|
|
810
|
-
}),
|
|
811
|
-
"agentf-memory-add-success": tool({
|
|
812
|
-
description: "Store a success memory in Redis.",
|
|
813
|
-
args: {
|
|
814
|
-
title: tool.schema.string(),
|
|
815
|
-
description: tool.schema.string(),
|
|
816
|
-
agent: tool.schema.string().optional(),
|
|
817
|
-
tags: tool.schema.array(tool.schema.string()).optional(),
|
|
818
|
-
context: tool.schema.string().optional(),
|
|
819
|
-
},
|
|
820
|
-
async execute(args, context) {
|
|
821
|
-
const commandArgs = [args.title, args.description];
|
|
822
|
-
if (args.agent) commandArgs.push(`--agent=${args.agent}`);
|
|
823
|
-
if (args.tags?.length) commandArgs.push(`--tags=${args.tags.join(",")}`);
|
|
824
|
-
if (args.context) commandArgs.push(`--context=${args.context}`);
|
|
880
|
+
if ((staticTools as any)[toolName]) continue;
|
|
825
881
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
description: "Store a pitfall memory in Redis.",
|
|
882
|
+
const agentName = toolName.replace(/^agentf-/, "");
|
|
883
|
+
|
|
884
|
+
agentTools[toolName] = tool({
|
|
885
|
+
description: `Invoke agent ${agentName} via the agentf CLI. If the result includes confirmation_required=true, ask the user before retrying with confirmedWrite=confirmed.`,
|
|
831
886
|
args: {
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
agent: tool.schema.string().optional(),
|
|
835
|
-
tags: tool.schema.array(tool.schema.string()).optional(),
|
|
836
|
-
context: tool.schema.string().optional(),
|
|
887
|
+
input: tool.schema.string().optional().describe("Optional input prompt or payload"),
|
|
888
|
+
confirmedWrite: tool.schema.string().optional().describe("Continuation token for confirmed writes"),
|
|
837
889
|
},
|
|
838
|
-
async execute(
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
890
|
+
async execute(_args: any, context: any) {
|
|
891
|
+
const cmdArgs: string[] = [];
|
|
892
|
+
// Ensure complex payloads are passed as a single JSON argument so the
|
|
893
|
+
// Ruby CLI can parse structured tasks. Accept strings as-is but
|
|
894
|
+
// stringify objects to avoid `[object Object]` being sent.
|
|
895
|
+
if (_args.input !== undefined) {
|
|
896
|
+
if (typeof _args.input === "object") {
|
|
897
|
+
cmdArgs.push(JSON.stringify(_args.input));
|
|
898
|
+
} else {
|
|
899
|
+
cmdArgs.push(String(_args.input));
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
if (_args.confirmedWrite) cmdArgs.push(`--confirmed-write=${_args.confirmedWrite}`);
|
|
903
|
+
return runAgentfCli(context.directory, "agent", agentName, cmdArgs);
|
|
845
904
|
},
|
|
846
|
-
})
|
|
847
|
-
}
|
|
848
|
-
}
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
const tools = { ...staticTools, ...agentTools };
|
|
910
|
+
|
|
911
|
+
// The plugin host expects a `tool` map (singular key) in the returned hooks.
|
|
912
|
+
return { tool: tools };
|
|
849
913
|
};
|
|
850
914
|
|
|
851
915
|
export default agentfPlugin;
|
|
852
916
|
TYPESCRIPT
|
|
853
917
|
end
|
|
854
918
|
|
|
855
|
-
def render_opencode_json
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
919
|
+
def render_opencode_json(root)
|
|
920
|
+
JSON.pretty_generate(opencode_json_config(root))
|
|
921
|
+
end
|
|
922
|
+
|
|
923
|
+
def render_copilot_mcp_json(root)
|
|
924
|
+
JSON.pretty_generate(copilot_mcp_config(root))
|
|
925
|
+
end
|
|
926
|
+
|
|
927
|
+
def opencode_json_config(root)
|
|
928
|
+
base = {
|
|
929
|
+
"$schema" => "https://opencode.ai/config.json"
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
if opencode_plugin_runtime?
|
|
933
|
+
base["plugin"] = ["./.opencode/plugins/agentf-plugin"]
|
|
934
|
+
else
|
|
935
|
+
base["mcp"] = {
|
|
936
|
+
"agentf" => {
|
|
937
|
+
"type" => "local",
|
|
938
|
+
"enabled" => true,
|
|
939
|
+
"command" => opencode_mcp_command(root)
|
|
940
|
+
}
|
|
860
941
|
}
|
|
861
|
-
|
|
942
|
+
end
|
|
943
|
+
|
|
944
|
+
base
|
|
945
|
+
end
|
|
946
|
+
|
|
947
|
+
def opencode_mcp_command(root)
|
|
948
|
+
[File.join(root, "bin", "agentf"), "mcp-server"]
|
|
862
949
|
end
|
|
863
950
|
|
|
864
951
|
def write_opencode_json(root)
|
|
865
952
|
path = File.join(root, "opencode.json")
|
|
866
|
-
new_content = JSON.parse(render_opencode_json)
|
|
953
|
+
new_content = JSON.parse(render_opencode_json(root))
|
|
867
954
|
|
|
868
955
|
return write_manifest(path, JSON.pretty_generate(new_content)) unless File.exist?(path)
|
|
869
956
|
|
|
@@ -875,8 +962,53 @@ module Agentf
|
|
|
875
962
|
end
|
|
876
963
|
|
|
877
964
|
merged = existing.dup
|
|
878
|
-
|
|
879
|
-
|
|
965
|
+
|
|
966
|
+
if new_content["plugin"]
|
|
967
|
+
merged_plugins = Array(existing["plugin"]) + Array(new_content["plugin"])
|
|
968
|
+
merged["plugin"] = merged_plugins.uniq
|
|
969
|
+
elsif existing["plugin"]
|
|
970
|
+
filtered_plugins = Array(existing["plugin"]).reject { |entry| entry == "./.opencode/plugins/agentf-plugin" }
|
|
971
|
+
if filtered_plugins.empty?
|
|
972
|
+
merged.delete("plugin")
|
|
973
|
+
else
|
|
974
|
+
merged["plugin"] = filtered_plugins
|
|
975
|
+
end
|
|
976
|
+
end
|
|
977
|
+
|
|
978
|
+
if new_content["mcp"]
|
|
979
|
+
merged["mcp"] = (existing["mcp"] || {}).merge(new_content["mcp"])
|
|
980
|
+
end
|
|
981
|
+
|
|
982
|
+
write_manifest(path, JSON.pretty_generate(merged))
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
def copilot_mcp_config(root)
|
|
986
|
+
{
|
|
987
|
+
"servers" => {
|
|
988
|
+
"agentf" => {
|
|
989
|
+
"type" => "stdio",
|
|
990
|
+
"command" => "agentf",
|
|
991
|
+
"args" => ["mcp-server"]
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
end
|
|
996
|
+
|
|
997
|
+
def write_copilot_mcp_json(root)
|
|
998
|
+
path = File.join(root, ".vscode", "mcp.json")
|
|
999
|
+
new_content = JSON.parse(render_copilot_mcp_json(root))
|
|
1000
|
+
|
|
1001
|
+
return write_manifest(path, JSON.pretty_generate(new_content)) unless File.exist?(path)
|
|
1002
|
+
|
|
1003
|
+
begin
|
|
1004
|
+
existing = JSON.parse(File.read(path))
|
|
1005
|
+
rescue StandardError => e
|
|
1006
|
+
warn "Failed to parse existing #{path}: #{e.message}"
|
|
1007
|
+
return write_manifest(path, JSON.pretty_generate(new_content))
|
|
1008
|
+
end
|
|
1009
|
+
|
|
1010
|
+
merged = existing.dup
|
|
1011
|
+
merged["servers"] = (existing["servers"] || {}).merge(new_content.fetch("servers"))
|
|
880
1012
|
|
|
881
1013
|
write_manifest(path, JSON.pretty_generate(merged))
|
|
882
1014
|
end
|
|
@@ -955,19 +1087,20 @@ module Agentf
|
|
|
955
1087
|
- `agent`: string
|
|
956
1088
|
|
|
957
1089
|
### 2. Episodic Memory (`episodic:*`)
|
|
958
|
-
Used for
|
|
1090
|
+
Used for episode, lesson, playbook, and intent records.
|
|
959
1091
|
|
|
960
1092
|
**Search index**: `episodic:logs`
|
|
961
1093
|
|
|
962
1094
|
**Schema fields**:
|
|
963
1095
|
- `$.id`
|
|
964
1096
|
- `$.type`
|
|
1097
|
+
- `$.outcome`
|
|
965
1098
|
- `$.title`
|
|
966
1099
|
- `$.description`
|
|
967
1100
|
- `$.project`
|
|
968
1101
|
- `$.context`
|
|
969
1102
|
- `$.code_snippet`
|
|
970
|
-
- `$.
|
|
1103
|
+
- `$.embedding`
|
|
971
1104
|
- `$.created_at`
|
|
972
1105
|
- `$.agent`
|
|
973
1106
|
- `$.related_task_id`
|
|
@@ -978,9 +1111,9 @@ module Agentf
|
|
|
978
1111
|
|
|
979
1112
|
- Read recent: `agentf memory recent -n 10`
|
|
980
1113
|
- Search: `agentf memory search "query" -n 10`
|
|
1114
|
+
- List episodes: `agentf memory episodes -n 10 --outcome=negative`
|
|
981
1115
|
- Add lesson: `agentf memory add-lesson "<title>" "<description>" --agent=<AGENT>`
|
|
982
|
-
- Add
|
|
983
|
-
- Add pitfall: `agentf memory add-pitfall "<title>" "<description>" --agent=<AGENT>`
|
|
1116
|
+
- Add playbook: `agentf memory add-playbook "<title>" "<description>" --steps="<step1>;<step2>"`
|
|
984
1117
|
MARKDOWN
|
|
985
1118
|
end
|
|
986
1119
|
end
|