agentf 0.4.4 → 0.4.6
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/debugger.rb +1 -0
- data/lib/agentf/cli/install.rb +10 -1
- data/lib/agentf/installer.rb +312 -18
- data/lib/agentf/mcp/server.rb +168 -0
- data/lib/agentf/version.rb +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1a6b455ea4e8d08419665c731414589a39173932f0bdf70788b76cfbcc7c0380
|
|
4
|
+
data.tar.gz: 4552007bea3fbb27e4940f09542f76e557bdac18f06391dfcb7db6914adc9017
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 37efe8cb9847156d5d0e93ee981c108fddab0940eab62fe60bb609e6fc68f799c14fd84f08214263ec4e98d05b617b5be7cd8d035d8a9352d7c639b4737947bb
|
|
7
|
+
data.tar.gz: c26f29b7059e1997f62b1a9b25c628c6ff25832309e8766ba2d076ebdf8333ca7ac3206145ae4e05f623c84aa6771c316970b5377e0b48de2980dff3e5c077df
|
data/lib/agentf/cli/install.rb
CHANGED
|
@@ -16,6 +16,7 @@ module Agentf
|
|
|
16
16
|
global_root: Dir.home,
|
|
17
17
|
local_root: Dir.pwd,
|
|
18
18
|
dry_run: false,
|
|
19
|
+
install_deps: true,
|
|
19
20
|
only_agents: nil,
|
|
20
21
|
only_commands: nil
|
|
21
22
|
}
|
|
@@ -32,7 +33,9 @@ module Agentf
|
|
|
32
33
|
installer = Agentf::Installer.new(
|
|
33
34
|
global_root: @options[:global_root],
|
|
34
35
|
local_root: @options[:local_root],
|
|
35
|
-
dry_run: @options[:dry_run]
|
|
36
|
+
dry_run: @options[:dry_run],
|
|
37
|
+
install_deps: @options[:install_deps],
|
|
38
|
+
verbose: @options.fetch(:verbose, false)
|
|
36
39
|
)
|
|
37
40
|
|
|
38
41
|
results = installer.install(
|
|
@@ -66,6 +69,9 @@ module Agentf
|
|
|
66
69
|
scope_val = parse_single_option(args, "--scope=") || parse_single_option(args, "-s=")
|
|
67
70
|
@options[:scope] = scope_val.downcase if scope_val
|
|
68
71
|
|
|
72
|
+
# Extract --install-deps flag
|
|
73
|
+
@options[:install_deps] = !args.delete("--install-deps").nil?
|
|
74
|
+
|
|
69
75
|
# Extract --global-root and --local-root
|
|
70
76
|
global_root = parse_single_option(args, "--global-root=")
|
|
71
77
|
@options[:global_root] = File.expand_path(global_root) if global_root
|
|
@@ -83,6 +89,9 @@ module Agentf
|
|
|
83
89
|
if command_val
|
|
84
90
|
@options[:only_commands] = command_val.split(",").map { |item| item.strip.downcase }.reject(&:empty?)
|
|
85
91
|
end
|
|
92
|
+
|
|
93
|
+
# Extract --verbose flag
|
|
94
|
+
@options[:verbose] = !args.delete("--verbose").nil?
|
|
86
95
|
end
|
|
87
96
|
|
|
88
97
|
def show_help
|
data/lib/agentf/installer.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
4
|
require "yaml"
|
|
5
|
+
require "open3"
|
|
6
|
+
require "json"
|
|
5
7
|
|
|
6
8
|
module Agentf
|
|
7
9
|
class Installer
|
|
@@ -23,8 +25,8 @@ module Agentf
|
|
|
23
25
|
"store_lesson" => { cli: "agentf memory add-lesson \"<title>\" \"<description>\" --agent=<AGENT> --tags=learning", tool: "agentf-memory-add-lesson" },
|
|
24
26
|
"store_success" => { cli: "agentf memory add-success \"<title>\" \"<description>\" --agent=<AGENT> --tags=success", tool: "agentf-memory-add-success" },
|
|
25
27
|
"store_pitfall" => { cli: "agentf memory add-pitfall \"<title>\" \"<description>\" --agent=<AGENT> --tags=pitfall", tool: "agentf-memory-add-pitfall" },
|
|
26
|
-
"store_business_intent" => { cli: "agentf memory add-business-intent \"<title>\" \"<description>\" --tags=strategy", tool: "agentf-memory-add-
|
|
27
|
-
"store_feature_intent" => { cli: "agentf memory add-feature-intent \"<title>\" \"<description>\" --acceptance=\"<criteria>\"", tool: "agentf-memory-add-
|
|
28
|
+
"store_business_intent" => { cli: "agentf memory add-business-intent \"<title>\" \"<description>\" --tags=strategy", tool: "agentf-memory-add-business-intent" },
|
|
29
|
+
"store_feature_intent" => { cli: "agentf memory add-feature-intent \"<title>\" \"<description>\" --acceptance=\"<criteria>\"", tool: "agentf-memory-add-feature-intent" }
|
|
28
30
|
}.freeze
|
|
29
31
|
|
|
30
32
|
PROVIDER_LAYOUTS = {
|
|
@@ -44,10 +46,12 @@ module Agentf
|
|
|
44
46
|
}
|
|
45
47
|
}.freeze
|
|
46
48
|
|
|
47
|
-
def initialize(global_root: Dir.home, local_root: Dir.pwd, dry_run: false)
|
|
49
|
+
def initialize(global_root: Dir.home, local_root: Dir.pwd, dry_run: false, verbose: false, install_deps: true)
|
|
48
50
|
@global_root = global_root
|
|
49
51
|
@local_root = local_root
|
|
50
52
|
@dry_run = dry_run
|
|
53
|
+
@verbose = verbose
|
|
54
|
+
@install_deps = install_deps
|
|
51
55
|
end
|
|
52
56
|
|
|
53
57
|
def install(
|
|
@@ -74,14 +78,62 @@ module Agentf
|
|
|
74
78
|
end
|
|
75
79
|
|
|
76
80
|
writes = []
|
|
77
|
-
roots_for(scope)
|
|
81
|
+
roots = roots_for(scope)
|
|
82
|
+
roots.each do |root|
|
|
78
83
|
writes.concat(write_agents(root: root, layout: layout, provider: provider, only_agents: only_agents))
|
|
79
84
|
writes.concat(write_commands(root: root, layout: layout, provider: provider, only_commands: only_commands))
|
|
80
85
|
writes.concat(write_opencode_helpers(root: root)) if provider.to_s == "opencode"
|
|
81
86
|
end
|
|
87
|
+
|
|
88
|
+
# Optionally install dependencies for opencode helper package.json
|
|
89
|
+
if provider.to_s == "opencode" && @install_deps
|
|
90
|
+
roots.each do |root|
|
|
91
|
+
package_json_path = File.join(root, ".opencode/package.json")
|
|
92
|
+
if @dry_run
|
|
93
|
+
# In dry-run, report that package.json would be written/installed
|
|
94
|
+
writes << write_manifest(package_json_path, render_opencode_package_json)
|
|
95
|
+
else
|
|
96
|
+
result = install_deps_in(root)
|
|
97
|
+
writes << result if result
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
82
102
|
writes
|
|
83
103
|
end
|
|
84
104
|
|
|
105
|
+
def install_deps_in(root)
|
|
106
|
+
pkg_dir = File.join(root, ".opencode")
|
|
107
|
+
pkg_json = File.join(pkg_dir, "package.json")
|
|
108
|
+
unless File.exist?(pkg_json)
|
|
109
|
+
warn "No .opencode/package.json at #{pkg_json}, skipping install" if @verbose
|
|
110
|
+
return { "path" => pkg_json, "status" => "skipped", "reason" => "missing package.json" }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
managers = [
|
|
114
|
+
{ cmd: ["bun", "install"], check: ["bun", "--version"] },
|
|
115
|
+
{ cmd: ["npm", "install"], check: ["npm", "--version"] },
|
|
116
|
+
{ cmd: ["yarn", "install"], check: ["yarn", "--version"] }
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
managers.each do |m|
|
|
120
|
+
stdout, stderr, status = Open3.capture3(*m[:check])
|
|
121
|
+
next unless status.success?
|
|
122
|
+
|
|
123
|
+
puts "Running #{m[:cmd].first} install in #{pkg_dir}" if @verbose
|
|
124
|
+
out, err, st = Open3.capture3(*m[:cmd], chdir: pkg_dir)
|
|
125
|
+
if st.success?
|
|
126
|
+
puts out if @verbose && !out.to_s.strip.empty?
|
|
127
|
+
return { "path" => pkg_dir, "status" => "installed", "manager" => m[:cmd].first }
|
|
128
|
+
else
|
|
129
|
+
warn "Install with #{m[:cmd].first} failed: #{err}" unless @verbose
|
|
130
|
+
return { "path" => pkg_dir, "status" => "error", "manager" => m[:cmd].first, "error" => err }
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
{ "path" => pkg_dir, "status" => "no_manager_found" }
|
|
135
|
+
end
|
|
136
|
+
|
|
85
137
|
def roots_for(scope)
|
|
86
138
|
case scope
|
|
87
139
|
when "global"
|
|
@@ -124,13 +176,15 @@ module Agentf
|
|
|
124
176
|
render_opencode_plugin
|
|
125
177
|
)
|
|
126
178
|
writes << write_manifest(
|
|
127
|
-
File.join(root, ".opencode/
|
|
128
|
-
|
|
179
|
+
File.join(root, ".opencode/tsconfig.json"),
|
|
180
|
+
render_opencode_tsconfig
|
|
129
181
|
)
|
|
182
|
+
writes << write_package_json(root)
|
|
130
183
|
writes << write_manifest(
|
|
131
|
-
File.join(root, "opencode.
|
|
132
|
-
|
|
184
|
+
File.join(root, ".opencode/memory/agentf-redis-schema.md"),
|
|
185
|
+
render_opencode_memory_schema
|
|
133
186
|
)
|
|
187
|
+
writes << write_opencode_json(root)
|
|
134
188
|
writes
|
|
135
189
|
end
|
|
136
190
|
|
|
@@ -151,11 +205,21 @@ module Agentf
|
|
|
151
205
|
end
|
|
152
206
|
|
|
153
207
|
def write_manifest(path, payload)
|
|
154
|
-
|
|
208
|
+
if @dry_run
|
|
209
|
+
return { "path" => path, "status" => "planned" }
|
|
210
|
+
end
|
|
155
211
|
|
|
156
212
|
FileUtils.mkdir_p(File.dirname(path))
|
|
157
|
-
|
|
158
|
-
|
|
213
|
+
begin
|
|
214
|
+
File.write(path, payload)
|
|
215
|
+
if @verbose
|
|
216
|
+
puts "WROTE: #{path}"
|
|
217
|
+
end
|
|
218
|
+
{ "path" => path, "status" => "written" }
|
|
219
|
+
rescue StandardError => e
|
|
220
|
+
warn "ERROR writing #{path}: #{e.message}"
|
|
221
|
+
{ "path" => path, "status" => "error", "error" => e.message }
|
|
222
|
+
end
|
|
159
223
|
end
|
|
160
224
|
|
|
161
225
|
def render_agent_manifest(klass, provider:)
|
|
@@ -354,11 +418,12 @@ module Agentf
|
|
|
354
418
|
|
|
355
419
|
def render_opencode_plugin
|
|
356
420
|
<<~'TYPESCRIPT'
|
|
357
|
-
|
|
358
|
-
import {
|
|
359
|
-
import
|
|
360
|
-
import
|
|
361
|
-
import
|
|
421
|
+
// tools:
|
|
422
|
+
import { execFile } from "child_process";
|
|
423
|
+
import { promisify } from "util";
|
|
424
|
+
import path from "path";
|
|
425
|
+
import { type Plugin, tool } from "@opencode-ai/plugin";
|
|
426
|
+
import fs from "fs";
|
|
362
427
|
|
|
363
428
|
const execFileAsync = promisify(execFile);
|
|
364
429
|
|
|
@@ -495,7 +560,7 @@ module Agentf
|
|
|
495
560
|
await ensureAgentfPreflight(process.env.PWD || process.cwd());
|
|
496
561
|
|
|
497
562
|
return {
|
|
498
|
-
|
|
563
|
+
tool: {
|
|
499
564
|
"agentf-code-glob": tool({
|
|
500
565
|
description: "Find files using project glob patterns via Agentf code CLI.",
|
|
501
566
|
args: {
|
|
@@ -566,6 +631,165 @@ module Agentf
|
|
|
566
631
|
return runAgentfCli(context.directory, "memory", "search", [args.query, "-n", String(limit)]);
|
|
567
632
|
},
|
|
568
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
|
+
}),
|
|
569
793
|
"agentf-memory-add-lesson": tool({
|
|
570
794
|
description: "Store a lesson memory in Redis.",
|
|
571
795
|
args: {
|
|
@@ -632,11 +856,81 @@ module Agentf
|
|
|
632
856
|
<<~JSON
|
|
633
857
|
{
|
|
634
858
|
"$schema": "https://opencode.ai/config.json",
|
|
635
|
-
"plugin": ["
|
|
859
|
+
"plugin": ["./.opencode/plugins/agentf-plugin"]
|
|
636
860
|
}
|
|
637
861
|
JSON
|
|
638
862
|
end
|
|
639
863
|
|
|
864
|
+
def write_opencode_json(root)
|
|
865
|
+
path = File.join(root, "opencode.json")
|
|
866
|
+
new_content = JSON.parse(render_opencode_json)
|
|
867
|
+
|
|
868
|
+
return write_manifest(path, JSON.pretty_generate(new_content)) unless File.exist?(path)
|
|
869
|
+
|
|
870
|
+
begin
|
|
871
|
+
existing = JSON.parse(File.read(path))
|
|
872
|
+
rescue StandardError => e
|
|
873
|
+
warn "Failed to parse existing opencode.json: #{e.message}"
|
|
874
|
+
return write_manifest(path, JSON.pretty_generate(new_content))
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
merged = existing.dup
|
|
878
|
+
merged_plugins = Array(existing["plugin"]) + Array(new_content["plugin"])
|
|
879
|
+
merged["plugin"] = merged_plugins.uniq
|
|
880
|
+
|
|
881
|
+
write_manifest(path, JSON.pretty_generate(merged))
|
|
882
|
+
end
|
|
883
|
+
|
|
884
|
+
def render_opencode_tsconfig
|
|
885
|
+
<<~JSON
|
|
886
|
+
{
|
|
887
|
+
"compilerOptions": {
|
|
888
|
+
"target": "ESNext",
|
|
889
|
+
"module": "ESNext",
|
|
890
|
+
"moduleResolution": "bundler",
|
|
891
|
+
"types": ["node"],
|
|
892
|
+
"strict": true,
|
|
893
|
+
"skipLibCheck": true,
|
|
894
|
+
"baseUrl": ".",
|
|
895
|
+
"paths": {
|
|
896
|
+
"@opencode-ai/plugin": ["./node_modules/@opencode-ai/plugin"]
|
|
897
|
+
}
|
|
898
|
+
},
|
|
899
|
+
"include": ["plugins/**/*.ts"]
|
|
900
|
+
}
|
|
901
|
+
JSON
|
|
902
|
+
end
|
|
903
|
+
|
|
904
|
+
def render_opencode_package_json
|
|
905
|
+
<<~JSON
|
|
906
|
+
{
|
|
907
|
+
"name": "agentf-opencode-helpers",
|
|
908
|
+
"private": true,
|
|
909
|
+
"dependencies": {
|
|
910
|
+
"@opencode-ai/plugin": "^1.2.24"
|
|
911
|
+
},
|
|
912
|
+
"devDependencies": {
|
|
913
|
+
"@types/node": "^25.4.0"
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
JSON
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
def write_package_json(root)
|
|
920
|
+
package_json_path = File.join(root, ".opencode/package.json")
|
|
921
|
+
new_content = render_opencode_package_json
|
|
922
|
+
return write_manifest(package_json_path, new_content) unless File.exist?(package_json_path)
|
|
923
|
+
|
|
924
|
+
existing = JSON.parse(File.read(package_json_path))
|
|
925
|
+
new_package_json = JSON.parse(new_content)
|
|
926
|
+
|
|
927
|
+
merged = existing.dup
|
|
928
|
+
merged["dependencies"] = (existing["dependencies"] || {}).merge(new_package_json["dependencies"] || {})
|
|
929
|
+
merged["devDependencies"] = (existing["devDependencies"] || {}).merge(new_package_json["devDependencies"] || {})
|
|
930
|
+
|
|
931
|
+
write_manifest(package_json_path, JSON.pretty_generate(merged))
|
|
932
|
+
end
|
|
933
|
+
|
|
640
934
|
def render_opencode_memory_schema
|
|
641
935
|
<<~MARKDOWN
|
|
642
936
|
# Redis Memory Schema
|
data/lib/agentf/mcp/server.rb
CHANGED
|
@@ -28,17 +28,31 @@ module Agentf
|
|
|
28
28
|
agentf-architecture-analyze-layers
|
|
29
29
|
agentf-memory-recent
|
|
30
30
|
agentf-memory-search
|
|
31
|
+
agentf-memory-by-tag
|
|
32
|
+
agentf-memory-by-agent
|
|
33
|
+
agentf-memory-by-type
|
|
34
|
+
agentf-memory-tags
|
|
35
|
+
agentf-memory-pitfalls
|
|
36
|
+
agentf-memory-lessons
|
|
37
|
+
agentf-memory-successes
|
|
38
|
+
agentf-memory-intents
|
|
39
|
+
agentf-memory-business-intents
|
|
40
|
+
agentf-memory-feature-intents
|
|
31
41
|
agentf-memory-neighbors
|
|
32
42
|
agentf-memory-subgraph
|
|
33
43
|
agentf-memory-add-lesson
|
|
34
44
|
agentf-memory-add-success
|
|
35
45
|
agentf-memory-add-pitfall
|
|
46
|
+
agentf-memory-add-business-intent
|
|
47
|
+
agentf-memory-add-feature-intent
|
|
36
48
|
].freeze
|
|
37
49
|
|
|
38
50
|
WRITE_TOOLS = Set.new(%w[
|
|
39
51
|
agentf-memory-add-lesson
|
|
40
52
|
agentf-memory-add-success
|
|
41
53
|
agentf-memory-add-pitfall
|
|
54
|
+
agentf-memory-add-business-intent
|
|
55
|
+
agentf-memory-add-feature-intent
|
|
42
56
|
]).freeze
|
|
43
57
|
|
|
44
58
|
attr_reader :server, :guardrails
|
|
@@ -214,6 +228,160 @@ module Agentf
|
|
|
214
228
|
end
|
|
215
229
|
end
|
|
216
230
|
|
|
231
|
+
s.tool("agentf-memory-by-tag") do
|
|
232
|
+
description "Get memories by tag."
|
|
233
|
+
argument :tag, String, required: true, description: "Tag to filter"
|
|
234
|
+
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
235
|
+
call do |args|
|
|
236
|
+
mcp_server.send(:guard!, "agentf-memory-by-tag", **args)
|
|
237
|
+
result = reviewer.get_by_tag(args[:tag], limit: args[:limit] || 10)
|
|
238
|
+
JSON.generate(result)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
s.tool("agentf-memory-by-agent") do
|
|
243
|
+
description "Get memories by agent."
|
|
244
|
+
argument :agent, String, required: true, description: "Agent name"
|
|
245
|
+
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
246
|
+
call do |args|
|
|
247
|
+
mcp_server.send(:guard!, "agentf-memory-by-agent", **args)
|
|
248
|
+
result = reviewer.get_by_agent(args[:agent], limit: args[:limit] || 10)
|
|
249
|
+
JSON.generate(result)
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
s.tool("agentf-memory-by-type") do
|
|
254
|
+
description "Get memories by type."
|
|
255
|
+
argument :type, String, required: true, description: "Memory type"
|
|
256
|
+
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
257
|
+
call do |args|
|
|
258
|
+
mcp_server.send(:guard!, "agentf-memory-by-type", **args)
|
|
259
|
+
result = reviewer.get_by_type(args[:type], limit: args[:limit] || 10)
|
|
260
|
+
JSON.generate(result)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
s.tool("agentf-memory-tags") do
|
|
265
|
+
description "List all unique memory tags."
|
|
266
|
+
call do |args|
|
|
267
|
+
mcp_server.send(:guard!, "agentf-memory-tags", **args)
|
|
268
|
+
result = reviewer.get_all_tags
|
|
269
|
+
JSON.generate(result)
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
s.tool("agentf-memory-pitfalls") do
|
|
274
|
+
description "List pitfall memories."
|
|
275
|
+
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
276
|
+
call do |args|
|
|
277
|
+
mcp_server.send(:guard!, "agentf-memory-pitfalls", **args)
|
|
278
|
+
result = reviewer.get_pitfalls(limit: args[:limit] || 10)
|
|
279
|
+
JSON.generate(result)
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
s.tool("agentf-memory-lessons") do
|
|
284
|
+
description "List lesson memories."
|
|
285
|
+
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
286
|
+
call do |args|
|
|
287
|
+
mcp_server.send(:guard!, "agentf-memory-lessons", **args)
|
|
288
|
+
result = reviewer.get_lessons(limit: args[:limit] || 10)
|
|
289
|
+
JSON.generate(result)
|
|
290
|
+
end
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
s.tool("agentf-memory-successes") do
|
|
294
|
+
description "List success memories."
|
|
295
|
+
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
296
|
+
call do |args|
|
|
297
|
+
mcp_server.send(:guard!, "agentf-memory-successes", **args)
|
|
298
|
+
result = reviewer.get_successes(limit: args[:limit] || 10)
|
|
299
|
+
JSON.generate(result)
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
s.tool("agentf-memory-intents") do
|
|
304
|
+
description "List intents (business|feature)."
|
|
305
|
+
argument :kind, String, required: false, description: "Optional: business|feature"
|
|
306
|
+
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
307
|
+
call do |args|
|
|
308
|
+
mcp_server.send(:guard!, "agentf-memory-intents", **args)
|
|
309
|
+
kind = args[:kind]
|
|
310
|
+
limit = args[:limit] || 10
|
|
311
|
+
result = case kind
|
|
312
|
+
when "business"
|
|
313
|
+
reviewer.get_business_intents(limit: limit)
|
|
314
|
+
when "feature"
|
|
315
|
+
reviewer.get_feature_intents(limit: limit)
|
|
316
|
+
else
|
|
317
|
+
reviewer.get_intents(limit: limit)
|
|
318
|
+
end
|
|
319
|
+
JSON.generate(result)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
s.tool("agentf-memory-business-intents") do
|
|
324
|
+
description "List business intents."
|
|
325
|
+
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
326
|
+
call do |args|
|
|
327
|
+
mcp_server.send(:guard!, "agentf-memory-business-intents", **args)
|
|
328
|
+
result = reviewer.get_business_intents(limit: args[:limit] || 10)
|
|
329
|
+
JSON.generate(result)
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
s.tool("agentf-memory-feature-intents") do
|
|
334
|
+
description "List feature intents."
|
|
335
|
+
argument :limit, Integer, required: false, description: "How many results to return (1-100)"
|
|
336
|
+
call do |args|
|
|
337
|
+
mcp_server.send(:guard!, "agentf-memory-feature-intents", **args)
|
|
338
|
+
result = reviewer.get_feature_intents(limit: args[:limit] || 10)
|
|
339
|
+
JSON.generate(result)
|
|
340
|
+
end
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
s.tool("agentf-memory-add-business-intent") do
|
|
344
|
+
description "Store a business intent in Redis."
|
|
345
|
+
argument :title, String, required: true, description: "Intent title"
|
|
346
|
+
argument :description, String, required: true, description: "Intent description"
|
|
347
|
+
argument :tags, Array, required: false, items: String, description: "Tags"
|
|
348
|
+
argument :constraints, Array, required: false, items: String, description: "Constraints"
|
|
349
|
+
argument :priority, Integer, required: false, description: "Priority"
|
|
350
|
+
call do |args|
|
|
351
|
+
mcp_server.send(:guard!, "agentf-memory-add-business-intent", **args)
|
|
352
|
+
id = memory.store_business_intent(
|
|
353
|
+
title: args[:title],
|
|
354
|
+
description: args[:description],
|
|
355
|
+
tags: args[:tags] || [],
|
|
356
|
+
constraints: args[:constraints] || [],
|
|
357
|
+
priority: args[:priority] || 1
|
|
358
|
+
)
|
|
359
|
+
JSON.generate(id: id, type: "business_intent", status: "stored")
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
s.tool("agentf-memory-add-feature-intent") do
|
|
364
|
+
description "Store a feature intent in Redis."
|
|
365
|
+
argument :title, String, required: true, description: "Intent title"
|
|
366
|
+
argument :description, String, required: true, description: "Intent description"
|
|
367
|
+
argument :tags, Array, required: false, items: String, description: "Tags"
|
|
368
|
+
argument :acceptance, Array, required: false, items: String, description: "Acceptance criteria"
|
|
369
|
+
argument :non_goals, Array, required: false, items: String, description: "Non-goals"
|
|
370
|
+
argument :related_task_id, String, required: false, description: "Related task id"
|
|
371
|
+
call do |args|
|
|
372
|
+
mcp_server.send(:guard!, "agentf-memory-add-feature-intent", **args)
|
|
373
|
+
id = memory.store_feature_intent(
|
|
374
|
+
title: args[:title],
|
|
375
|
+
description: args[:description],
|
|
376
|
+
tags: args[:tags] || [],
|
|
377
|
+
acceptance_criteria: args[:acceptance] || [],
|
|
378
|
+
non_goals: args[:non_goals] || [],
|
|
379
|
+
related_task_id: args[:related_task_id]
|
|
380
|
+
)
|
|
381
|
+
JSON.generate(id: id, type: "feature_intent", status: "stored")
|
|
382
|
+
end
|
|
383
|
+
end
|
|
384
|
+
|
|
217
385
|
s.tool("agentf-memory-neighbors") do
|
|
218
386
|
description "Get neighboring memory nodes by edge traversal."
|
|
219
387
|
argument :node_id, String, required: true, description: "Starting node id"
|
data/lib/agentf/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: agentf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Neal Deters
|
|
8
|
-
autorequire:
|
|
8
|
+
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-03-
|
|
11
|
+
date: 2026-03-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: redis
|
|
@@ -86,7 +86,7 @@ description: |2
|
|
|
86
86
|
Redis-backed semantic, episodic, and graph-style memory. It includes a
|
|
87
87
|
unified CLI, MCP server tools, and install/update workflows for generated
|
|
88
88
|
agent/command manifests.
|
|
89
|
-
email:
|
|
89
|
+
email:
|
|
90
90
|
executables:
|
|
91
91
|
- agentf
|
|
92
92
|
extensions: []
|
|
@@ -147,7 +147,7 @@ metadata:
|
|
|
147
147
|
homepage_uri: https://github.com/nealdeters/agentf
|
|
148
148
|
source_code_uri: https://github.com/nealdeters/agentf
|
|
149
149
|
rubygems_mfa_required: 'true'
|
|
150
|
-
post_install_message:
|
|
150
|
+
post_install_message:
|
|
151
151
|
rdoc_options: []
|
|
152
152
|
require_paths:
|
|
153
153
|
- lib
|
|
@@ -162,8 +162,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
162
162
|
- !ruby/object:Gem::Version
|
|
163
163
|
version: '0'
|
|
164
164
|
requirements: []
|
|
165
|
-
rubygems_version: 3.
|
|
166
|
-
signing_key:
|
|
165
|
+
rubygems_version: 3.5.22
|
|
166
|
+
signing_key:
|
|
167
167
|
specification_version: 4
|
|
168
168
|
summary: Ruby multi-agent workflow engine with Redis memory
|
|
169
169
|
test_files: []
|