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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5ba903d323be3e277a84ea8320af5b84a93456ad44b5d5aca0eb05e4c4ba29ae
4
- data.tar.gz: 9a7125925e3a3fbf85b382b5ea278e3b39390c618dc9a571c1657acef86ec82d
3
+ metadata.gz: 1a6b455ea4e8d08419665c731414589a39173932f0bdf70788b76cfbcc7c0380
4
+ data.tar.gz: 4552007bea3fbb27e4940f09542f76e557bdac18f06391dfcb7db6914adc9017
5
5
  SHA512:
6
- metadata.gz: 7c5703863b0d31aa5318c124d690eb0dccb93839935e07155a6bd6265267741183c38475761d597b15bfa8f18ea3b7a515b7613c7d31a9327eb0e6162a186d09
7
- data.tar.gz: d5b4072ee13a2e40d07f93c25c147298d63e8e782bfb75a66a03b10b90c4c8b844da396271fa0ed00f695be42067c009934ef3b4f8f5a01ad86d8272807abaf6
6
+ metadata.gz: 37efe8cb9847156d5d0e93ee981c108fddab0940eab62fe60bb609e6fc68f799c14fd84f08214263ec4e98d05b617b5be7cd8d035d8a9352d7c639b4737947bb
7
+ data.tar.gz: c26f29b7059e1997f62b1a9b25c628c6ff25832309e8766ba2d076ebdf8333ca7ac3206145ae4e05f623c84aa6771c316970b5377e0b48de2980dff3e5c077df
@@ -80,6 +80,7 @@ module Agentf
80
80
 
81
81
  {
82
82
  "success" => true,
83
+ "error" => nil,
83
84
  "analysis" => {
84
85
  "error_type" => analysis.error_type,
85
86
  "possible_causes" => analysis.possible_causes,
@@ -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
@@ -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-lesson" },
27
- "store_feature_intent" => { cli: "agentf memory add-feature-intent \"<title>\" \"<description>\" --acceptance=\"<criteria>\"", tool: "agentf-memory-add-lesson" }
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).each do |root|
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/memory/agentf-redis-schema.md"),
128
- render_opencode_memory_schema
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.json"),
132
- render_opencode_json
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
- return { "path" => path, "status" => "planned" } if @dry_run
208
+ if @dry_run
209
+ return { "path" => path, "status" => "planned" }
210
+ end
155
211
 
156
212
  FileUtils.mkdir_p(File.dirname(path))
157
- File.write(path, payload)
158
- { "path" => path, "status" => "written" }
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
- import { execFile } from "node:child_process";
358
- import { promisify } from "node:util";
359
- import path from "node:path";
360
- import { type Plugin, tool } from "@opencode-ai/plugin/tool";
361
- import fs from "node:fs";
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
- tools: {
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": ["./opencode/plugins/agentf-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
@@ -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"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Agentf
4
- VERSION = "0.4.4"
4
+ VERSION = "0.4.6"
5
5
  end
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
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-09 00:00:00.000000000 Z
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.0.3.1
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: []