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.
@@ -51,7 +51,8 @@ module Agentf
51
51
  end
52
52
 
53
53
  def pack_workflow_templates
54
- Agentf::Packs.fetch(@pack).fetch("workflow_templates")
54
+ # Workflow templates are now provided by the orchestrator profiles
55
+ Agentf::WorkflowEngine::PROFILES.fetch(@pack, Agentf::WorkflowEngine::PROFILES["generic"]).fetch("workflow_templates")
55
56
  end
56
57
 
57
58
  def execute_agent(agent_name:, task:, context:, agents:, commands:, logger: nil)
@@ -60,67 +61,14 @@ module Agentf
60
61
  agent = agents[agent_name]
61
62
  return { "error" => "Agent #{agent_name} not found" } unless agent
62
63
 
63
- result = case agent_name
64
- when Agentf::AgentRoles::PLANNER
65
- agent.plan_task(task)
66
- when Agentf::AgentRoles::RESEARCHER
67
- query = context["explore_query"] || "*.rb"
68
- files = commands.fetch("explorer").glob(query)
69
- response = agent.explore(query)
70
- response["files"] = files
71
- response
72
- when Agentf::AgentRoles::QA_TESTER
73
- source_file = context["source_file"] || "app/models/application_record.rb"
74
- tester_commands = commands.fetch("tester")
75
- tdd_phase = context["tdd_phase"] || "normal"
76
-
77
- if tdd_phase == "red"
78
- failure_signature = "expected-failure:#{File.basename(source_file)}:#{Time.now.to_i}"
79
- {
80
- "source_file" => source_file,
81
- "test_file" => source_file.sub(/\.rb$/, "_spec.rb"),
82
- "tdd_phase" => "red",
83
- "passed" => false,
84
- "failure_signature" => failure_signature,
85
- "stdout" => "Intentional TDD red failure captured"
86
- }
87
- else
88
- template = tester_commands.generate_unit_tests(source_file)
89
- response = agent.generate_tests(source_file)
90
- response["generated_code"] = template.test_code
91
- response["tdd_phase"] = tdd_phase
92
- response["failure_signature"] = context["tdd_failure_signature"]
93
- response
94
- end
95
- when Agentf::AgentRoles::INCIDENT_RESPONDER
96
- error = context["error"] || "No error provided"
97
- analysis = commands.fetch("debugger").parse_error(error)
98
- response = agent.diagnose(error, context: context["error_context"])
99
- response["analysis"] = {
100
- "error_type" => analysis.error_type,
101
- "root_cause" => analysis.possible_causes,
102
- "suggested_fix" => analysis.suggested_fix
103
- }
104
- response
105
- when Agentf::AgentRoles::UI_ENGINEER
106
- design_spec = context["design_spec"] || "Create a card component"
107
- spec = commands.fetch("designer").generate_component("GeneratedComponent", design_spec)
108
- response = agent.implement_design(design_spec)
109
- response["generated_code"] = spec.code
110
- response
111
- when Agentf::AgentRoles::ENGINEER
112
- subtask = context["current_subtask"] || { "description" => task }
113
- agent.execute(subtask)
114
- when Agentf::AgentRoles::SECURITY_REVIEWER
115
- agent.assess(task: task, context: context)
116
- when Agentf::AgentRoles::REVIEWER
117
- last_result = context["execution"] || {}
118
- agent.review(last_result)
119
- when Agentf::AgentRoles::KNOWLEDGE_MANAGER
120
- agent.sync_docs("project")
121
- else
122
- { "status" => "not_implemented" }
123
- end
64
+ # Provider no longer simulates TDD red-phase; delegate to Tester agent.
65
+
66
+ unless agent.respond_to?(:execute)
67
+ raise "Agent #{agent_name} does not implement execute"
68
+ end
69
+
70
+ # Delegate execution to the agent's unified entrypoint.
71
+ result = agent.execute(task: task, context: context || {}, agents: agents, commands: commands, logger: logger)
124
72
 
125
73
  logger&.call("→ #{agent_name} Complete")
126
74
  result
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Agentf
4
- VERSION = "0.4.7"
4
+ VERSION = "0.6.0"
5
5
  end
@@ -8,6 +8,54 @@ require_relative "agent_policy"
8
8
 
9
9
  module Agentf
10
10
  class WorkflowEngine
11
+ # Profiles previously lived in Agentf::Packs. They are now embedded in the
12
+ # orchestrator so there's a single source of truth for workflow templates
13
+ # and keyword-based inference used by both runtime orchestration and any
14
+ # installer/CLI functionality.
15
+ PROFILES = {
16
+ "generic" => {
17
+ "name" => "Generic",
18
+ "description" => "Default provider workflows without domain specialization.",
19
+ "keywords" => [],
20
+ "workflow_templates" => {}
21
+ },
22
+ "rails_standard" => {
23
+ "name" => "Rails Standard",
24
+ "description" => "Thin models/controllers with services, queries, presenters, and policy reviews.",
25
+ "keywords" => %w[rails activerecord rspec pundit viewcomponent hotwire turbo stimulus],
26
+ "workflow_templates" => {
27
+ "feature" => %w[PLANNER RESEARCHER ENGINEER QA_TESTER SECURITY_REVIEWER REVIEWER KNOWLEDGE_MANAGER],
28
+ "bugfix" => %w[PLANNER INCIDENT_RESPONDER ENGINEER QA_TESTER SECURITY_REVIEWER REVIEWER],
29
+ "refactor" => %w[PLANNER RESEARCHER ENGINEER QA_TESTER REVIEWER],
30
+ "quick_fix" => %w[ENGINEER QA_TESTER REVIEWER],
31
+ "exploration" => %w[RESEARCHER]
32
+ }
33
+ },
34
+ "rails_37signals" => {
35
+ "name" => "Rails 37signals",
36
+ "description" => "Resource-centric workflows favoring concerns, CRUD and model-rich patterns.",
37
+ "keywords" => %w[rails concern crud closure model minitest hotwire],
38
+ "workflow_templates" => {
39
+ "feature" => %w[PLANNER RESEARCHER ENGINEER QA_TESTER REVIEWER KNOWLEDGE_MANAGER],
40
+ "bugfix" => %w[PLANNER INCIDENT_RESPONDER ENGINEER QA_TESTER REVIEWER],
41
+ "refactor" => %w[PLANNER ENGINEER QA_TESTER REVIEWER],
42
+ "quick_fix" => %w[ENGINEER REVIEWER],
43
+ "exploration" => %w[RESEARCHER]
44
+ }
45
+ },
46
+ "rails_feature_spec" => {
47
+ "name" => "Rails Feature Spec",
48
+ "description" => "Feature-spec-first orchestration with planning and review emphasis.",
49
+ "keywords" => %w[rails feature specification acceptance criteria],
50
+ "workflow_templates" => {
51
+ "feature" => %w[PLANNER RESEARCHER UI_ENGINEER ENGINEER QA_TESTER REVIEWER KNOWLEDGE_MANAGER],
52
+ "bugfix" => %w[PLANNER INCIDENT_RESPONDER ENGINEER QA_TESTER REVIEWER],
53
+ "refactor" => %w[PLANNER RESEARCHER ENGINEER QA_TESTER REVIEWER],
54
+ "quick_fix" => %w[ENGINEER REVIEWER],
55
+ "exploration" => %w[RESEARCHER]
56
+ }
57
+ }
58
+ }.freeze
11
59
  PROVIDERS = {
12
60
  opencode: Agentf::Service::Providers::OpenCode,
13
61
  copilot: Agentf::Service::Providers::Copilot
@@ -20,7 +68,8 @@ module Agentf
20
68
  @base_path = base_path || Agentf.config.base_path
21
69
  @name = Agentf::AgentRoles::ORCHESTRATOR
22
70
  @provider_ref = provider
23
- @provider = build_provider(@provider_ref, pack: Agentf.config.default_pack)
71
+ # Initialize provider using the orchestrator's default profile ("generic").
72
+ @provider = build_provider(@provider_ref, pack: "generic")
24
73
 
25
74
  @explorer_commands = Commands::Explorer.new(base_path: @base_path)
26
75
  @tester_commands = Commands::Tester.new(base_path: @base_path)
@@ -51,7 +100,9 @@ module Agentf
51
100
  @workflow_state = {}
52
101
  end
53
102
 
54
- def execute(task, context: nil)
103
+ # Unified execute entrypoint for the workflow engine. Accepts keyword
104
+ # `task:` for consistency with agent `execute` contracts.
105
+ def execute(task:, context: nil)
55
106
  log "=" * 60
56
107
  log "EXECUTING #{provider.name} WORKFLOW"
57
108
  log "=" * 60
@@ -136,10 +187,22 @@ module Agentf
136
187
  requested = context["pack"].to_s.strip
137
188
  return requested.downcase unless requested.empty?
138
189
 
139
- default_pack = Agentf.config.default_pack.to_s.strip
140
- return default_pack.downcase unless default_pack.empty? || default_pack.casecmp("generic").zero?
190
+ # No config-based default profile is kept; rely on orchestrator inference.
191
+ infer_profile(context.merge("task" => task))
192
+ end
193
+
194
+ def infer_profile(context = {})
195
+ text = [context["task"], context["design_spec"], context["stack"], context["framework"]]
196
+ .compact.join(" ").downcase
197
+ return "generic" if text.empty?
198
+
199
+ return "rails_standard" if includes_any_keyword?(text, PROFILES["rails_standard"]["keywords"])
200
+
201
+ "generic"
202
+ end
141
203
 
142
- Agentf::Packs.infer(context.merge("task" => task))
204
+ def includes_any_keyword?(text, keywords)
205
+ keywords.any? { |keyword| text.include?(keyword) }
143
206
  end
144
207
 
145
208
  def log(message)
@@ -161,8 +224,12 @@ module Agentf
161
224
  enriched_context["tdd_failure_signature"] = @workflow_state.dig("tdd", "failure_signature")
162
225
  end
163
226
 
227
+ # For ENGINEER, provide the current TDD phase and the expected failing
228
+ # test signature so the engineer can attempt a repair. Do NOT change the
229
+ # orchestrator-wide TDD state here; phase transitions must be driven by
230
+ # QA TESTER results (to ensure tests actually pass/fail).
164
231
  if agent_name == Agentf::AgentRoles::ENGINEER
165
- enriched_context["tdd_phase"] = "green"
232
+ enriched_context["tdd_phase"] = @workflow_state.dig("tdd", "phase")
166
233
  enriched_context["expected_test_fix"] = @workflow_state.dig("tdd", "failure_signature")
167
234
  end
168
235
 
@@ -170,14 +237,23 @@ module Agentf
170
237
  enriched_context["execution"] = @workflow_state["results"].last&.fetch("result", {}) || {}
171
238
  end
172
239
 
173
- result = @provider.execute_agent(
174
- agent_name: agent_name,
175
- task: @workflow_state["task"],
176
- context: enriched_context,
177
- agents: @agents,
178
- commands: command_registry,
179
- logger: method(:log)
180
- )
240
+ begin
241
+ result = @provider.execute_agent(
242
+ agent_name: agent_name,
243
+ task: @workflow_state["task"],
244
+ context: enriched_context,
245
+ agents: @agents,
246
+ commands: command_registry,
247
+ logger: method(:log)
248
+ )
249
+ rescue Agentf::Memory::RedisMemory::ConfirmationRequired => e
250
+ # An agent attempted to persist memory but policy requires confirmation.
251
+ # Record the event and return a structured result that signals the
252
+ # orchestrator/UI to prompt the user. Do NOT set an "error" key so
253
+ # agent execution contract does not treat this as a failure.
254
+ handle_memory_confirmation(e, attempted: { action: "agent_persist", agent: agent_name })
255
+ return { "success" => false, "confirmation_required" => true, "confirmation_details" => e.details }
256
+ end
181
257
 
182
258
  policy_violations = @agent_policy.validate(
183
259
  agent_name: agent_name,
@@ -217,16 +293,15 @@ module Agentf
217
293
  def persist_feature_intent(task:, workflow_type:, context:)
218
294
  acceptance_criteria = Array(context["acceptance_criteria"])
219
295
  non_goals = Array(context["non_goals"])
220
- tags = [workflow_type, @provider.name.downcase]
221
-
222
296
  @memory.store_feature_intent(
223
297
  title: task,
224
298
  description: "Workflow intent captured by workflow engine",
225
299
  acceptance_criteria: acceptance_criteria,
226
300
  non_goals: non_goals,
227
- tags: tags,
228
301
  agent: @name
229
302
  )
303
+ rescue Agentf::Memory::RedisMemory::ConfirmationRequired => e
304
+ handle_memory_confirmation(e, attempted: { action: "store_feature_intent", title: task })
230
305
  rescue StandardError => e
231
306
  log "Intent capture skipped: #{e.message}"
232
307
  end
@@ -235,51 +310,69 @@ module Agentf
235
310
  return unless result.is_a?(Hash)
236
311
 
237
312
  if result["error"]
238
- @memory.store_pitfall(
239
- title: "#{agent_name} execution failure",
240
- description: result["error"],
241
- context: @workflow_state["task"],
242
- tags: [@workflow_state["workflow_type"], "workflow_error"],
243
- agent: agent_name,
244
- code_snippet: ""
245
- )
313
+ begin
314
+ @memory.store_episode(
315
+ type: "episode",
316
+ title: "#{agent_name} execution failure",
317
+ description: result["error"],
318
+ context: @workflow_state["task"],
319
+ agent: agent_name,
320
+ outcome: "negative",
321
+ code_snippet: ""
322
+ )
323
+ rescue Agentf::Memory::RedisMemory::ConfirmationRequired => e
324
+ handle_memory_confirmation(e, attempted: { action: "store_episode", agent: agent_name, error: result["error"], outcome: "negative" })
325
+ end
246
326
  return
247
327
  end
248
328
 
249
329
  if agent_name == Agentf::AgentRoles::QA_TESTER && result["tdd_phase"] == "red" && result["passed"] == false
250
- @memory.store_pitfall(
251
- title: "TDD red phase captured",
252
- description: result["failure_signature"] || "Intentional failing test captured",
253
- context: @workflow_state["task"],
254
- tags: [@workflow_state["workflow_type"], "tdd_red"],
255
- agent: agent_name,
256
- code_snippet: ""
257
- )
330
+ begin
331
+ @memory.store_episode(
332
+ type: "episode",
333
+ title: "TDD red phase captured",
334
+ description: result["failure_signature"] || "Intentional failing test captured",
335
+ context: @workflow_state["task"],
336
+ agent: agent_name,
337
+ outcome: "negative",
338
+ code_snippet: ""
339
+ )
340
+ rescue Agentf::Memory::RedisMemory::ConfirmationRequired => e
341
+ handle_memory_confirmation(e, attempted: { action: "store_episode", agent: agent_name, tdd: true, outcome: "negative" })
342
+ end
258
343
  return
259
344
  end
260
345
 
261
346
  if agent_name == Agentf::AgentRoles::QA_TESTER && result["tdd_phase"] == "green" && result["passed"] == true
262
- @memory.store_success(
263
- title: "TDD green phase passed",
264
- description: "Resolved failing test signature: #{result['failure_signature']}",
347
+ begin
348
+ @memory.store_episode(
349
+ type: "episode",
350
+ title: "TDD green phase passed",
351
+ description: "Resolved failing test signature: #{result['failure_signature']}",
352
+ context: @workflow_state["task"],
353
+ agent: agent_name,
354
+ outcome: "positive",
355
+ code_snippet: ""
356
+ )
357
+ rescue Agentf::Memory::RedisMemory::ConfirmationRequired => e
358
+ handle_memory_confirmation(e, attempted: { action: "store_episode", agent: agent_name, tdd: true, outcome: "positive" })
359
+ end
360
+ return
361
+ end
362
+
363
+ begin
364
+ @memory.store_lesson(
365
+ title: "#{agent_name} completed workflow step",
366
+ description: "Agent step completed for #{@workflow_state['workflow_type']} workflow",
265
367
  context: @workflow_state["task"],
266
- tags: [@workflow_state["workflow_type"], "tdd_green"],
267
368
  agent: agent_name,
268
369
  code_snippet: ""
269
370
  )
270
- return
371
+ rescue Agentf::Memory::RedisMemory::ConfirmationRequired => e
372
+ handle_memory_confirmation(e, attempted: { action: "store_lesson", agent: agent_name })
271
373
  end
272
-
273
- @memory.store_lesson(
274
- title: "#{agent_name} completed workflow step",
275
- description: "Agent step completed for #{@workflow_state['workflow_type']} workflow",
276
- context: @workflow_state["task"],
277
- tags: [@workflow_state["workflow_type"], "workflow_step"],
278
- agent: agent_name,
279
- code_snippet: ""
280
- )
281
374
  rescue StandardError => e
282
- log "Learning persistence skipped: #{e.message}"
375
+ log "Learning persistence skipped: #{e.class}: #{e.message}\n #{Array(e.backtrace).first(6).join("\n ")}"
283
376
  end
284
377
 
285
378
  def summarize_workflow
@@ -334,22 +427,27 @@ module Agentf
334
427
  @workflow_state["results"] << { "agent" => "QA_TESTER_TDD_RED", "result" => red_result }
335
428
  persist_agent_learning(agent_name: Agentf::AgentRoles::QA_TESTER, result: red_result)
336
429
  rescue StandardError => e
337
- log "TDD red phase skipped: #{e.message}"
430
+ log "TDD red phase skipped: #{e.class}: #{e.message}\n #{Array(e.backtrace).first(6).join("\n ")}"
338
431
  end
339
432
 
340
433
  def transition_tdd_phase(agent_name:, result:)
341
434
  tdd = @workflow_state["tdd"]
342
435
  return unless tdd["enabled"]
343
436
 
344
- if agent_name == Agentf::AgentRoles::ENGINEER
345
- tdd["phase"] = "green"
346
- elsif agent_name == Agentf::AgentRoles::QA_TESTER && tdd["phase"] == "green"
347
- tdd["green_executed"] = true
437
+ # Phase transitions should be decided by QA_TESTER outcomes. When the
438
+ # QA tester reports a green phase and passing tests, mark the workflow
439
+ # as green. We avoid changing phase when ENGINEER executes to prevent
440
+ # optimistic transitions.
441
+ if agent_name == Agentf::AgentRoles::QA_TESTER
442
+ if result["tdd_phase"] == "green" && result["passed"] == true
443
+ tdd["phase"] = "green"
444
+ tdd["green_executed"] = true
445
+ tdd["failure_signature"] ||= result["failure_signature"]
446
+ elsif result["tdd_phase"] == "green"
447
+ # Tester indicated green but didn't confirm passing — keep guarded.
448
+ tdd["failure_signature"] ||= result["failure_signature"]
449
+ end
348
450
  end
349
-
350
- return unless agent_name == Agentf::AgentRoles::QA_TESTER && result["tdd_phase"] == "green"
351
-
352
- tdd["failure_signature"] ||= result["failure_signature"]
353
451
  end
354
452
 
355
453
  def record_workflow_metrics
@@ -360,18 +458,21 @@ module Agentf
360
458
 
361
459
  log "Metrics capture skipped: #{result['error']}"
362
460
  rescue StandardError => e
363
- log "Metrics capture skipped: #{e.message}"
461
+ log "Metrics capture skipped: #{e.class}: #{e.message}\n #{Array(e.backtrace).first(6).join("\n ")}"
364
462
  end
365
463
 
366
464
  def perform_architecture_review
367
465
  result = @architecture_commands.review_layer_violations
368
- @memory.store_lesson(
369
- title: "Architecture review completed",
370
- description: "Layer violations: #{Array(result['violations']).length}",
371
- context: @workflow_state["task"],
372
- tags: [@workflow_state["workflow_type"], "architecture_review"],
373
- agent: @name
374
- )
466
+ begin
467
+ @memory.store_lesson(
468
+ title: "Architecture review completed",
469
+ description: "Layer violations: #{Array(result['violations']).length}",
470
+ context: @workflow_state["task"],
471
+ agent: @name
472
+ )
473
+ rescue Agentf::Memory::RedisMemory::ConfirmationRequired => e
474
+ handle_memory_confirmation(e, attempted: { action: "store_lesson", agent: @name, context: @workflow_state["task"] })
475
+ end
375
476
  result
376
477
  rescue StandardError => e
377
478
  { "error" => e.message, "violations" => [] }
@@ -398,12 +499,13 @@ module Agentf
398
499
  title: "Workflow contract #{evaluation['stage']} #{evaluation['ok'] ? 'passed' : 'violated'}",
399
500
  description: "mode=#{evaluation['mode']} blocked=#{evaluation['blocked']}",
400
501
  context: JSON.generate(evaluation),
401
- tags: ["workflow_contract", evaluation["stage"], evaluation["ok"] ? "pass" : "violation"],
402
502
  agent: @name,
403
503
  metadata: { "workflow_contract_event" => true }
404
504
  )
505
+ rescue Agentf::Memory::RedisMemory::ConfirmationRequired => e
506
+ handle_memory_confirmation(e, attempted: { action: "store_episode", title: "Workflow contract #{evaluation['stage']}", agent: @name })
405
507
  rescue StandardError => e
406
- log "Contract event persistence skipped: #{e.message}"
508
+ log "Contract event persistence skipped: #{e.class}: #{e.message}\n #{Array(e.backtrace).first(6).join("\n ")}"
407
509
  end
408
510
 
409
511
  def append_policy_violations(policy_violations)
@@ -412,18 +514,44 @@ module Agentf
412
514
  @workflow_state["policy_violations"] ||= []
413
515
  @workflow_state["policy_violations"].concat(policy_violations)
414
516
  policy_violations.each do |violation|
415
- @memory.store_episode(
416
- type: "pitfall",
417
- title: "Agent policy violation: #{violation['code']}",
418
- description: violation["message"],
419
- context: @workflow_state["task"],
420
- tags: ["agent_policy", violation["agent"].to_s.downcase],
421
- agent: @name,
422
- metadata: { "policy_violation" => true, "severity" => violation["severity"] }
423
- )
517
+ begin
518
+ @memory.store_episode(
519
+ type: "episode",
520
+ title: "Agent policy violation: #{violation['code']}",
521
+ description: violation["message"],
522
+ context: @workflow_state["task"],
523
+ agent: @name,
524
+ outcome: "negative",
525
+ metadata: { "policy_violation" => true, "severity" => violation["severity"] }
526
+ )
527
+ rescue Agentf::Memory::RedisMemory::ConfirmationRequired => e
528
+ handle_memory_confirmation(e, attempted: { action: "store_policy_violation", violation: violation, agent: @name })
529
+ end
424
530
  end
425
531
  rescue StandardError => e
426
- log "Policy violation persistence skipped: #{e.message}"
532
+ log "Policy violation persistence skipped: #{e.class}: #{e.message}\n #{Array(e.backtrace).first(6).join("\n ")}"
533
+ end
534
+
535
+ # Helper to format exceptions for concise logs. Exposed here so multiple
536
+ # rescue handlers can produce consistent output if desired in the future.
537
+ def format_exception(e)
538
+ "#{e.class}: #{e.message}\n #{Array(e.backtrace).first(6).join("\n ")}"
539
+ end
540
+
541
+ # Handle a memory confirmation exception by recording an event in the
542
+ # workflow_state and emitting a log. This allows the orchestrator or UI to
543
+ # surface a prompt to the user, and optionally retry the attempted action
544
+ # with explicit confirmation.
545
+ def handle_memory_confirmation(exception, attempted: {})
546
+ @workflow_state["memory_confirmation_required"] ||= []
547
+ entry = {
548
+ "timestamp" => Time.now.to_i,
549
+ "confirmation_required" => true,
550
+ "confirmation_details" => exception.details,
551
+ "attempted" => attempted
552
+ }
553
+ @workflow_state["memory_confirmation_required"] << entry
554
+ log "Memory confirmation required: #{exception.message} -- attempted=#{attempted.inspect}"
427
555
  end
428
556
  end
429
557
  end
data/lib/agentf.rb CHANGED
@@ -16,7 +16,7 @@ module Agentf
16
16
  attr_reader :redis_url
17
17
  attr_accessor :project_name, :base_path, :metrics_enabled, :workflow_contract_enabled,
18
18
  :workflow_contract_mode, :agent_contract_enabled, :agent_contract_mode,
19
- :default_pack, :gem_path
19
+ :gem_path
20
20
 
21
21
  def initialize
22
22
  @redis_url = normalize_redis_url(ENV.fetch("REDIS_URL", "redis://localhost:6379"))
@@ -37,7 +37,7 @@ module Agentf
37
37
  @agent_contract_mode = normalize_contract_mode(
38
38
  ENV.fetch("AGENTF_AGENT_CONTRACT_MODE", "enforcing")
39
39
  )
40
- @default_pack = ENV.fetch("AGENTF_DEFAULT_PACK", "generic").to_s.strip.downcase
40
+ # Default profile removed; orchestrator defaults to "generic" internally.
41
41
  @gem_path = ENV.fetch("AGENTF_GEM_PATH", nil)
42
42
  end
43
43
 
@@ -85,9 +85,16 @@ end
85
85
  require_relative "agentf/memory"
86
86
  require_relative "agentf/tools"
87
87
  require_relative "agentf/commands"
88
+ require_relative "agentf/commands/registry"
88
89
  require_relative "agentf/service/providers"
90
+ require_relative "agentf/embedding_provider"
89
91
  require_relative "agentf/context_builder"
90
- require_relative "agentf/packs"
92
+ require_relative "agentf/evals/scenario"
93
+ require_relative "agentf/evals/runner"
94
+ require_relative "agentf/evals/report"
95
+ # Profiles previously lived in lib/agentf/packs.rb; the profile data is now
96
+ # embedded in the orchestrator (WorkflowEngine::PROFILES). The old file was
97
+ # removed as part of simplifying the profile surface.
91
98
  require_relative "agentf/agent_policy"
92
99
  require_relative "agentf/agent_execution_contract"
93
100
  require_relative "agentf/workflow_contract"
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.7
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Neal Deters
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-03-11 00:00:00.000000000 Z
11
+ date: 2026-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -108,9 +108,11 @@ files:
108
108
  - lib/agentf/agents/security.rb
109
109
  - lib/agentf/agents/specialist.rb
110
110
  - lib/agentf/agents/tester.rb
111
+ - lib/agentf/cli/agent.rb
111
112
  - lib/agentf/cli/architecture.rb
112
113
  - lib/agentf/cli/arg_parser.rb
113
114
  - lib/agentf/cli/code.rb
115
+ - lib/agentf/cli/eval.rb
114
116
  - lib/agentf/cli/install.rb
115
117
  - lib/agentf/cli/memory.rb
116
118
  - lib/agentf/cli/metrics.rb
@@ -123,14 +125,18 @@ files:
123
125
  - lib/agentf/commands/explorer.rb
124
126
  - lib/agentf/commands/memory_reviewer.rb
125
127
  - lib/agentf/commands/metrics.rb
128
+ - lib/agentf/commands/registry.rb
126
129
  - lib/agentf/commands/security_scanner.rb
127
130
  - lib/agentf/commands/tester.rb
128
131
  - lib/agentf/context_builder.rb
132
+ - lib/agentf/embedding_provider.rb
133
+ - lib/agentf/evals/report.rb
134
+ - lib/agentf/evals/runner.rb
135
+ - lib/agentf/evals/scenario.rb
129
136
  - lib/agentf/installer.rb
130
137
  - lib/agentf/mcp/server.rb
131
138
  - lib/agentf/mcp/stub.rb
132
139
  - lib/agentf/memory.rb
133
- - lib/agentf/packs.rb
134
140
  - lib/agentf/service/providers.rb
135
141
  - lib/agentf/tools.rb
136
142
  - lib/agentf/tools/component_spec.rb
data/lib/agentf/packs.rb DELETED
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Agentf
4
- module Packs
5
- PROFILES = {
6
- "generic" => {
7
- "name" => "Generic",
8
- "description" => "Default provider workflows without domain specialization.",
9
- "keywords" => [],
10
- "workflow_templates" => {}
11
- },
12
- "rails_standard" => {
13
- "name" => "Rails Standard",
14
- "description" => "Thin models/controllers with services, queries, presenters, and policy reviews.",
15
- "keywords" => %w[rails activerecord rspec pundit viewcomponent hotwire turbo stimulus],
16
- "workflow_templates" => {
17
- "feature" => %w[PLANNER RESEARCHER ENGINEER QA_TESTER SECURITY_REVIEWER REVIEWER KNOWLEDGE_MANAGER],
18
- "bugfix" => %w[PLANNER INCIDENT_RESPONDER ENGINEER QA_TESTER SECURITY_REVIEWER REVIEWER],
19
- "refactor" => %w[PLANNER RESEARCHER ENGINEER QA_TESTER REVIEWER],
20
- "quick_fix" => %w[ENGINEER QA_TESTER REVIEWER],
21
- "exploration" => %w[RESEARCHER]
22
- }
23
- },
24
- "rails_37signals" => {
25
- "name" => "Rails 37signals",
26
- "description" => "Resource-centric workflows favoring concerns, CRUD and model-rich patterns.",
27
- "keywords" => %w[rails concern crud closure model minitest hotwire],
28
- "workflow_templates" => {
29
- "feature" => %w[PLANNER RESEARCHER ENGINEER QA_TESTER REVIEWER KNOWLEDGE_MANAGER],
30
- "bugfix" => %w[PLANNER INCIDENT_RESPONDER ENGINEER QA_TESTER REVIEWER],
31
- "refactor" => %w[PLANNER ENGINEER QA_TESTER REVIEWER],
32
- "quick_fix" => %w[ENGINEER REVIEWER],
33
- "exploration" => %w[RESEARCHER]
34
- }
35
- },
36
- "rails_feature_spec" => {
37
- "name" => "Rails Feature Spec",
38
- "description" => "Feature-spec-first orchestration with planning and review emphasis.",
39
- "keywords" => %w[rails feature specification acceptance criteria],
40
- "workflow_templates" => {
41
- "feature" => %w[PLANNER RESEARCHER UI_ENGINEER ENGINEER QA_TESTER REVIEWER KNOWLEDGE_MANAGER],
42
- "bugfix" => %w[PLANNER INCIDENT_RESPONDER ENGINEER QA_TESTER REVIEWER],
43
- "refactor" => %w[PLANNER RESEARCHER ENGINEER QA_TESTER REVIEWER],
44
- "quick_fix" => %w[ENGINEER REVIEWER],
45
- "exploration" => %w[RESEARCHER]
46
- }
47
- }
48
- }.freeze
49
-
50
- module_function
51
-
52
- def all
53
- PROFILES
54
- end
55
-
56
- def fetch(name)
57
- PROFILES[name.to_s.downcase] || PROFILES["generic"]
58
- end
59
-
60
- def infer(context = {})
61
- text = [context["task"], context["design_spec"], context["stack"], context["framework"]]
62
- .compact.join(" ").downcase
63
- return "generic" if text.empty?
64
-
65
- return "rails_standard" if includes_any_keyword?(text, PROFILES["rails_standard"]["keywords"])
66
-
67
- "generic"
68
- end
69
-
70
- def includes_any_keyword?(text, keywords)
71
- keywords.any? { |keyword| text.include?(keyword) }
72
- end
73
- end
74
- end