agentf 0.3.0 → 0.4.1

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: dc86ebb30ccffc8b338dd6e728947f87886c302046e81c01e10d739aac78095f
4
- data.tar.gz: dda17a3643259253dac2b299e8b45c4f016f4e01f381e6b81956f54633eebcd3
3
+ metadata.gz: 289cd869806a950c6bb25a92b56cc05d5f90481f3bbe61af65d97af23768d321
4
+ data.tar.gz: 8f16969b26684677f01d01821c541d14a338b924a310ad9cdd83435e7b7896b3
5
5
  SHA512:
6
- metadata.gz: ff8dd589ee31f74de1a35b0fc8122292b7b78747eb3d45338cc4f9fdcff4f8956f69ad0935aa0cc9b3d7bc3395b607b98d5143e1563c786f83eae44902153bb3
7
- data.tar.gz: 4334dabaf36eabfdfbfeac1b7ff4bda60f1f2b17361df549ef641a85479c2b80b82806a5c18ffacfcba1ee0f7d618c13d55e0ffef8d7e66d0164aade58298a86
6
+ metadata.gz: 230df28afb68ed450b76c16185dc14227342cfc3048ed5ab25183a99aebb57c79833180d6185c3d35d33e89dac9d9a514b8ee6ebbeef8ecf64d3822f34db0b0f
7
+ data.tar.gz: 9347250f29a7402d17ed0784c16387e268461785c02aa1d3f94024bdab6a89f2022a065169b1055f6b168d4a0472a193000e88f5c428ea8ce4757ed2843ff066
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Agentf
4
+ module AgentRoles
5
+ ORCHESTRATOR = "ORCHESTRATOR"
6
+ PLANNER = "PLANNER"
7
+ ENGINEER = "ENGINEER"
8
+ REVIEWER = "REVIEWER"
9
+ KNOWLEDGE_MANAGER = "KNOWLEDGE_MANAGER"
10
+ RESEARCHER = "RESEARCHER"
11
+ QA_TESTER = "QA_TESTER"
12
+ INCIDENT_RESPONDER = "INCIDENT_RESPONDER"
13
+ UI_ENGINEER = "UI_ENGINEER"
14
+ SECURITY_REVIEWER = "SECURITY_REVIEWER"
15
+
16
+ ALL = [
17
+ ORCHESTRATOR,
18
+ PLANNER,
19
+ ENGINEER,
20
+ REVIEWER,
21
+ KNOWLEDGE_MANAGER,
22
+ RESEARCHER,
23
+ QA_TESTER,
24
+ INCIDENT_RESPONDER,
25
+ UI_ENGINEER,
26
+ SECURITY_REVIEWER
27
+ ].freeze
28
+ end
29
+ end
@@ -22,6 +22,22 @@ module Agentf
22
22
  COMMANDS
23
23
  end
24
24
 
25
+ def self.typed_name
26
+ Agentf::AgentRoles::PLANNER
27
+ end
28
+
29
+ def self.when_to_use
30
+ "Use for planning, decomposition, and constraints mapping before implementation."
31
+ end
32
+
33
+ def self.deliverables
34
+ ["Execution plan", "Decomposed subtasks", "Risk and pitfall notes"]
35
+ end
36
+
37
+ def self.working_style
38
+ "Strategic and constraint-aware with explicit decomposition."
39
+ end
40
+
25
41
  def self.memory_concepts
26
42
  MEMORY_CONCEPTS
27
43
  end
@@ -14,6 +14,18 @@ module Agentf
14
14
  "Agent for #{typed_name.downcase}"
15
15
  end
16
16
 
17
+ def self.when_to_use
18
+ "Use when the workflow needs #{typed_name.downcase.tr('_', ' ')} expertise."
19
+ end
20
+
21
+ def self.deliverables
22
+ []
23
+ end
24
+
25
+ def self.working_style
26
+ "Structured, evidence-based, and outcome-oriented."
27
+ end
28
+
17
29
  def self.commands
18
30
  []
19
31
  end
@@ -23,6 +23,22 @@ module Agentf
23
23
  COMMANDS
24
24
  end
25
25
 
26
+ def self.typed_name
27
+ Agentf::AgentRoles::INCIDENT_RESPONDER
28
+ end
29
+
30
+ def self.when_to_use
31
+ "Use for incident triage, root-cause analysis, and remediation paths."
32
+ end
33
+
34
+ def self.deliverables
35
+ ["Root-cause analysis", "Fix guidance", "Incident lesson record"]
36
+ end
37
+
38
+ def self.working_style
39
+ "Diagnostic, hypothesis-driven, and remediation-focused."
40
+ end
41
+
26
42
  def self.memory_concepts
27
43
  MEMORY_CONCEPTS
28
44
  end
@@ -23,6 +23,22 @@ module Agentf
23
23
  COMMANDS
24
24
  end
25
25
 
26
+ def self.typed_name
27
+ Agentf::AgentRoles::UI_ENGINEER
28
+ end
29
+
30
+ def self.when_to_use
31
+ "Use for transforming design specs into framework-ready UI components."
32
+ end
33
+
34
+ def self.deliverables
35
+ ["Component implementation", "Generated UI code", "Design-system alignment"]
36
+ end
37
+
38
+ def self.working_style
39
+ "Specification-driven with implementation-grade UI output."
40
+ end
41
+
26
42
  def self.memory_concepts
27
43
  MEMORY_CONCEPTS
28
44
  end
@@ -22,6 +22,22 @@ module Agentf
22
22
  COMMANDS
23
23
  end
24
24
 
25
+ def self.typed_name
26
+ Agentf::AgentRoles::KNOWLEDGE_MANAGER
27
+ end
28
+
29
+ def self.when_to_use
30
+ "Use for memory synthesis, knowledge rollups, and delivery-ready summaries."
31
+ end
32
+
33
+ def self.deliverables
34
+ ["Success summary", "Pitfall summary", "Knowledge digest"]
35
+ end
36
+
37
+ def self.working_style
38
+ "Concise synthesis with attention to sensitive data boundaries."
39
+ end
40
+
25
41
  def self.memory_concepts
26
42
  MEMORY_CONCEPTS
27
43
  end
@@ -23,6 +23,22 @@ module Agentf
23
23
  COMMANDS
24
24
  end
25
25
 
26
+ def self.typed_name
27
+ Agentf::AgentRoles::RESEARCHER
28
+ end
29
+
30
+ def self.when_to_use
31
+ "Use for codebase discovery, evidence gathering, and dependency tracing."
32
+ end
33
+
34
+ def self.deliverables
35
+ ["Relevant file list", "Search evidence", "Context breadcrumbs"]
36
+ end
37
+
38
+ def self.working_style
39
+ "Fast exploration with concrete references and traceable findings."
40
+ end
41
+
26
42
  def self.memory_concepts
27
43
  MEMORY_CONCEPTS
28
44
  end
@@ -22,6 +22,22 @@ module Agentf
22
22
  COMMANDS
23
23
  end
24
24
 
25
+ def self.typed_name
26
+ Agentf::AgentRoles::REVIEWER
27
+ end
28
+
29
+ def self.when_to_use
30
+ "Use for approval decisions, regression checks, and evidence-backed review."
31
+ end
32
+
33
+ def self.deliverables
34
+ ["Approval decision", "Issue list", "Pitfall-aligned feedback"]
35
+ end
36
+
37
+ def self.working_style
38
+ "Evidence-first with explicit approval criteria."
39
+ end
40
+
25
41
  def self.memory_concepts
26
42
  MEMORY_CONCEPTS
27
43
  end
@@ -23,6 +23,22 @@ module Agentf
23
23
  COMMANDS
24
24
  end
25
25
 
26
+ def self.typed_name
27
+ Agentf::AgentRoles::SECURITY_REVIEWER
28
+ end
29
+
30
+ def self.when_to_use
31
+ "Use for security gating, prompt-injection checks, and secret leak detection."
32
+ end
33
+
34
+ def self.deliverables
35
+ ["Security findings", "Best-practice checklist", "Pass/warn outcome"]
36
+ end
37
+
38
+ def self.working_style
39
+ "Risk-focused with redaction-safe reporting."
40
+ end
41
+
26
42
  def self.memory_concepts
27
43
  MEMORY_CONCEPTS
28
44
  end
@@ -22,6 +22,22 @@ module Agentf
22
22
  COMMANDS
23
23
  end
24
24
 
25
+ def self.typed_name
26
+ Agentf::AgentRoles::ENGINEER
27
+ end
28
+
29
+ def self.when_to_use
30
+ "Use for implementation, code edits, and deterministic execution outcomes."
31
+ end
32
+
33
+ def self.deliverables
34
+ ["Implemented code", "Execution status", "Success or pitfall memory"]
35
+ end
36
+
37
+ def self.working_style
38
+ "Execution-focused, deterministic, and evidence-driven."
39
+ end
40
+
25
41
  def self.memory_concepts
26
42
  MEMORY_CONCEPTS
27
43
  end
@@ -23,6 +23,22 @@ module Agentf
23
23
  COMMANDS
24
24
  end
25
25
 
26
+ def self.typed_name
27
+ Agentf::AgentRoles::QA_TESTER
28
+ end
29
+
30
+ def self.when_to_use
31
+ "Use for test generation, red/green validation, and execution verification."
32
+ end
33
+
34
+ def self.deliverables
35
+ ["Generated test artifacts", "Pass/fail evidence", "TDD phase signals"]
36
+ end
37
+
38
+ def self.working_style
39
+ "Quality-gate oriented with explicit pass/fail reporting."
40
+ end
41
+
26
42
  def self.memory_concepts
27
43
  MEMORY_CONCEPTS
28
44
  end
@@ -54,6 +54,12 @@ module Agentf
54
54
  list_tags
55
55
  when "search"
56
56
  search_memories(args)
57
+ when "delete"
58
+ delete_memories(args)
59
+ when "neighbors"
60
+ neighbors(args)
61
+ when "subgraph"
62
+ subgraph(args)
57
63
  when "summary", "stats"
58
64
  show_summary
59
65
  when "by-tag"
@@ -195,7 +201,7 @@ module Agentf
195
201
 
196
202
  tags = parse_list_option(args, "--tags=")
197
203
  context = parse_single_option(args, "--context=").to_s
198
- agent = parse_single_option(args, "--agent=") || "SPECIALIST"
204
+ agent = parse_single_option(args, "--agent=") || Agentf::AgentRoles::ENGINEER
199
205
  code_snippet = parse_single_option(args, "--code=").to_s
200
206
 
201
207
  intent_id = @memory.store_episode(
@@ -297,6 +303,93 @@ module Agentf
297
303
  output(result)
298
304
  end
299
305
 
306
+ def neighbors(args)
307
+ node_id = args.shift.to_s
308
+ if node_id.empty?
309
+ $stderr.puts "Error: neighbors requires a node id"
310
+ exit 1
311
+ end
312
+
313
+ relation = parse_single_option(args, "--relation=")
314
+ depth = parse_integer_option(args, "--depth=", default: 1)
315
+ limit = extract_limit(args)
316
+ result = @reviewer.neighbors(node_id, relation: relation, depth: depth, limit: limit)
317
+ output_graph(result)
318
+ end
319
+
320
+ def delete_memories(args)
321
+ mode = args.shift.to_s
322
+ case mode
323
+ when "id"
324
+ delete_by_id(args)
325
+ when "last"
326
+ delete_last(args)
327
+ when "all"
328
+ delete_all(args)
329
+ else
330
+ $stderr.puts "Error: delete requires one of: id|last|all"
331
+ exit 1
332
+ end
333
+ end
334
+
335
+ def delete_by_id(args)
336
+ id = args.shift.to_s
337
+ if id.empty?
338
+ $stderr.puts "Error: delete id requires a memory id"
339
+ exit 1
340
+ end
341
+
342
+ scope = parse_scope_option(args)
343
+ dry_run = parse_boolean_flag(args, "--dry-run")
344
+ result = @memory.delete_memory_by_id(id: id, scope: scope, dry_run: dry_run)
345
+ output_delete(result)
346
+ end
347
+
348
+ def delete_last(args)
349
+ limit = extract_limit(args)
350
+ if limit <= 0
351
+ $stderr.puts "Error: delete last requires -n with value > 0"
352
+ exit 1
353
+ end
354
+
355
+ scope = parse_scope_option(args)
356
+ type = parse_single_option(args, "--type=")
357
+ agent = parse_single_option(args, "--agent=")
358
+ dry_run = parse_boolean_flag(args, "--dry-run")
359
+ result = @memory.delete_recent(limit: limit, scope: scope, type: type, agent: agent, dry_run: dry_run)
360
+ output_delete(result)
361
+ end
362
+
363
+ def delete_all(args)
364
+ scope = parse_scope_option(args)
365
+ type = parse_single_option(args, "--type=")
366
+ agent = parse_single_option(args, "--agent=")
367
+ dry_run = parse_boolean_flag(args, "--dry-run")
368
+ confirmed = parse_boolean_flag(args, "--yes")
369
+
370
+ if !dry_run && !confirmed
371
+ $stderr.puts "Error: delete all requires --yes (or use --dry-run)"
372
+ exit 1
373
+ end
374
+
375
+ result = @memory.delete_all(scope: scope, type: type, agent: agent, dry_run: dry_run)
376
+ output_delete(result)
377
+ end
378
+
379
+ def subgraph(args)
380
+ seeds = args.shift.to_s.split(",").map(&:strip).reject(&:empty?)
381
+ if seeds.empty?
382
+ $stderr.puts "Error: subgraph requires comma-separated seed ids"
383
+ exit 1
384
+ end
385
+
386
+ relation_filters = parse_list_option(args, "--relation=")
387
+ depth = parse_integer_option(args, "--depth=", default: 2)
388
+ limit = extract_limit(args, default: 200)
389
+ result = @reviewer.subgraph(seed_ids: seeds, relation_filters: relation_filters, depth: depth, limit: limit)
390
+ output_graph(result)
391
+ end
392
+
300
393
  def merge_memory_results(*results, limit:)
301
394
  entries = results.flat_map { |result| result["memories"] || [] }
302
395
  sorted = entries.sort_by { |entry| -(entry["created_at_unix"] || 0) }
@@ -331,6 +424,65 @@ module Agentf
331
424
  end
332
425
  end
333
426
 
427
+ def output_delete(result)
428
+ if result["error"]
429
+ if @json_output
430
+ puts JSON.generate({ "error" => result["error"] })
431
+ else
432
+ $stderr.puts "Error: #{result['error']}"
433
+ end
434
+ exit 1
435
+ end
436
+
437
+ if @json_output
438
+ puts JSON.generate(result)
439
+ return
440
+ end
441
+
442
+ action = result["dry_run"] ? "Planned" : "Deleted"
443
+ puts "#{action} #{result['deleted_count']} keys (candidates: #{result['candidate_count']})"
444
+ puts "Mode: #{result['mode']} | Scope: #{result['scope']}"
445
+ filters = result["filters"] || {}
446
+ puts "Filters: type=#{filters['type'] || 'any'}, agent=#{filters['agent'] || 'any'}"
447
+ ids = Array(result["deleted_ids"])
448
+ puts "Memory ids: #{ids.join(', ')}" unless ids.empty?
449
+ end
450
+
451
+ def parse_scope_option(args)
452
+ scope = parse_single_option(args, "--scope=") || "project"
453
+ unless %w[project all].include?(scope)
454
+ $stderr.puts "Error: --scope must be project or all"
455
+ exit 1
456
+ end
457
+ scope
458
+ end
459
+
460
+ def parse_boolean_flag(args, flag)
461
+ !args.delete(flag).nil?
462
+ end
463
+
464
+ def output_graph(result)
465
+ if result["error"]
466
+ if @json_output
467
+ puts JSON.generate({ "error" => result["error"] })
468
+ else
469
+ $stderr.puts "Error: #{result['error']}"
470
+ end
471
+ exit 1
472
+ end
473
+
474
+ if @json_output
475
+ puts JSON.generate(result)
476
+ return
477
+ end
478
+
479
+ puts "Graph result: #{result['count']} edges"
480
+ puts "Nodes: #{Array(result['nodes']).length}"
481
+ Array(result["edges"]).each do |edge|
482
+ puts " - #{edge['source_id']} --#{edge['relation']}--> #{edge['target_id']}"
483
+ end
484
+ end
485
+
334
486
  def format_memory(mem)
335
487
  <<~OUTPUT
336
488
  [#{mem["type"]&.upcase}] #{mem["title"]}
@@ -366,6 +518,11 @@ module Agentf
366
518
  add-pitfall Store pitfall memory
367
519
  tags List all unique tags
368
520
  search <query> Search memories by keyword
521
+ delete id <memory_id> Delete one memory and related edges
522
+ delete last -n <count> Delete most recent memories
523
+ delete all Delete memories and graph/task keys
524
+ neighbors <id> Traverse graph edges from a memory id
525
+ subgraph <ids> Build graph from comma-separated seed ids
369
526
  summary, stats Show summary statistics
370
527
  by-tag <tag> Get memories with specific tag
371
528
  by-agent <agent> Get memories from specific agent
@@ -381,9 +538,13 @@ module Agentf
381
538
  agentf memory intents business -n 5
382
539
  agentf memory add-business-intent "Reliability" "Prioritize uptime" --tags=ops,platform --constraints="No downtime;No vendor lock-in"
383
540
  agentf memory add-feature-intent "Agent handoff" "Improve orchestrator continuity" --acceptance="Keeps context;Preserves task state"
384
- agentf memory add-lesson "Refactor strategy" "Extracted adapter seam" --agent=ARCHITECT --tags=architecture
385
- agentf memory add-success "Provider install works" "Installed copilot + opencode manifests" --agent=SPECIALIST
541
+ agentf memory add-lesson "Refactor strategy" "Extracted adapter seam" --agent=PLANNER --tags=architecture
542
+ agentf memory add-success "Provider install works" "Installed copilot + opencode manifests" --agent=ENGINEER
386
543
  agentf memory search "react"
544
+ agentf memory delete id episode_abcd
545
+ agentf memory delete last -n 10 --scope=project
546
+ agentf memory delete all --scope=all --yes
547
+ agentf memory neighbors episode_abcd --depth=2
387
548
  agentf memory by-tag "performance"
388
549
  agentf memory summary
389
550
  HELP
@@ -90,7 +90,7 @@ module Agentf
90
90
 
91
91
  Examples:
92
92
  agentf memory recent -n 5
93
- agentf memory add-lesson "Title" "Description" --agent=ARCHITECT
93
+ agentf memory add-lesson "Title" "Description" --agent=PLANNER
94
94
  agentf code glob "lib/**/*.rb"
95
95
  agentf code grep "def execute" --file-pattern=*.rb
96
96
  agentf install --provider opencode,copilot --scope local
@@ -138,11 +138,11 @@ module Agentf
138
138
 
139
139
  old_files = [
140
140
  File.join(opencode_dir, "tools", "agentf-tools.ts"),
141
- File.join(opencode_dir, "agents", "WORKFLOW_ENGINE.md"),
141
+ File.join(opencode_dir, "agents", "ORCHESTRATOR.md"),
142
142
  File.join(opencode_dir, "memory", "REDIS_SCHEMA.md")
143
143
  ]
144
144
 
145
- old_agent_names = %w[EXPLORER ARCHITECT DESIGNER DEBUGGER REVIEWER TESTER DOCUMENTER SECURITY SPECIALIST]
145
+ old_agent_names = %w[RESEARCHER PLANNER UI_ENGINEER INCIDENT_RESPONDER REVIEWER QA_TESTER KNOWLEDGE_MANAGER SECURITY_REVIEWER ENGINEER]
146
146
  old_files.concat(old_agent_names.map { |name| File.join(opencode_dir, "agents", "#{name}.md") })
147
147
 
148
148
  old_command_names = %w[explorer tester metrics security_scanner memory_reviewer designer debugger architecture]
@@ -23,7 +23,9 @@ module Agentf
23
23
  { "name" => "get_by_type", "type" => "function" },
24
24
  { "name" => "get_by_agent", "type" => "function" },
25
25
  { "name" => "search", "type" => "function" },
26
- { "name" => "get_summary", "type" => "function" }
26
+ { "name" => "get_summary", "type" => "function" },
27
+ { "name" => "neighbors", "type" => "function" },
28
+ { "name" => "subgraph", "type" => "function" }
27
29
  ]
28
30
  }
29
31
  end
@@ -150,6 +152,18 @@ module Agentf
150
152
  { "error" => e.message }
151
153
  end
152
154
 
155
+ def neighbors(node_id, relation: nil, depth: 1, limit: 50)
156
+ @memory.neighbors(node_id: node_id, relation: relation, depth: depth, limit: limit)
157
+ rescue => e
158
+ { "error" => e.message }
159
+ end
160
+
161
+ def subgraph(seed_ids:, depth: 2, relation_filters: nil, limit: 200)
162
+ @memory.subgraph(seed_ids: seed_ids, depth: depth, relation_filters: relation_filters, limit: limit)
163
+ rescue => e
164
+ { "error" => e.message }
165
+ end
166
+
153
167
  private
154
168
 
155
169
  def format_memories(memories)
@@ -169,6 +183,9 @@ module Agentf
169
183
  "code_snippet" => m["code_snippet"],
170
184
  "tags" => m["tags"],
171
185
  "agent" => m["agent"],
186
+ "metadata" => m["metadata"],
187
+ "entity_ids" => m["entity_ids"],
188
+ "relationships" => m["relationships"],
172
189
  "created_at" => format_time(m["created_at"]),
173
190
  "created_at_unix" => m["created_at"]
174
191
  }
@@ -35,7 +35,7 @@ module Agentf
35
35
  description: metric_description(metrics),
36
36
  context: metric_context(metrics),
37
37
  tags: metric_tags(metrics),
38
- agent: "WORKFLOW_ENGINE",
38
+ agent: Agentf::AgentRoles::ORCHESTRATOR,
39
39
  code_snippet: ""
40
40
  )
41
41
 
@@ -128,12 +128,12 @@ module Agentf
128
128
  end
129
129
 
130
130
  def reviewer_approved?(results)
131
- review = results.find { |entry| entry["agent"] == "REVIEWER" }
131
+ review = results.find { |entry| entry["agent"] == Agentf::AgentRoles::REVIEWER }
132
132
  review&.dig("result", "approved") == true
133
133
  end
134
134
 
135
135
  def security_issue_count(results)
136
- security_result = results.find { |entry| entry["agent"] == "SECURITY" }
136
+ security_result = results.find { |entry| entry["agent"] == Agentf::AgentRoles::SECURITY_REVIEWER }
137
137
  issues = security_result&.dig("result", "issues")
138
138
  Array(issues).length
139
139
  end
@@ -190,12 +190,11 @@ module Agentf
190
190
 
191
191
  memories
192
192
  .select { |m| Array(m["tags"]).include?(WORKFLOW_METRICS_TAG) }
193
- .filter_map do |m|
193
+ .map do |m|
194
194
  context = parse_context_json(m["context"])
195
- next if context.nil?
196
-
197
195
  context
198
196
  end
197
+ .compact
199
198
  end
200
199
 
201
200
  def parse_context_json(value)