agent_c 2.9

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.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +10 -0
  3. data/.ruby-version +1 -0
  4. data/CLAUDE.md +21 -0
  5. data/README.md +360 -0
  6. data/Rakefile +16 -0
  7. data/TODO.md +105 -0
  8. data/agent_c.gemspec +38 -0
  9. data/docs/batch.md +503 -0
  10. data/docs/chat-methods.md +156 -0
  11. data/docs/cost-reporting.md +86 -0
  12. data/docs/pipeline-tips-and-tricks.md +453 -0
  13. data/docs/session-configuration.md +274 -0
  14. data/docs/testing.md +747 -0
  15. data/docs/tools.md +103 -0
  16. data/docs/versioned-store.md +840 -0
  17. data/lib/agent_c/agent/chat.rb +211 -0
  18. data/lib/agent_c/agent/chat_response.rb +38 -0
  19. data/lib/agent_c/agent/chats/anthropic_bedrock.rb +48 -0
  20. data/lib/agent_c/batch.rb +102 -0
  21. data/lib/agent_c/configs/repo.rb +90 -0
  22. data/lib/agent_c/context.rb +56 -0
  23. data/lib/agent_c/costs/data.rb +39 -0
  24. data/lib/agent_c/costs/report.rb +219 -0
  25. data/lib/agent_c/db/store.rb +162 -0
  26. data/lib/agent_c/errors.rb +19 -0
  27. data/lib/agent_c/pipeline.rb +152 -0
  28. data/lib/agent_c/pipelines/agent.rb +219 -0
  29. data/lib/agent_c/processor.rb +98 -0
  30. data/lib/agent_c/prompts.yml +53 -0
  31. data/lib/agent_c/schema.rb +71 -0
  32. data/lib/agent_c/session.rb +206 -0
  33. data/lib/agent_c/store.rb +72 -0
  34. data/lib/agent_c/test_helpers.rb +173 -0
  35. data/lib/agent_c/tools/dir_glob.rb +46 -0
  36. data/lib/agent_c/tools/edit_file.rb +114 -0
  37. data/lib/agent_c/tools/file_metadata.rb +43 -0
  38. data/lib/agent_c/tools/git_status.rb +30 -0
  39. data/lib/agent_c/tools/grep.rb +119 -0
  40. data/lib/agent_c/tools/paths.rb +36 -0
  41. data/lib/agent_c/tools/read_file.rb +94 -0
  42. data/lib/agent_c/tools/run_rails_test.rb +87 -0
  43. data/lib/agent_c/tools.rb +61 -0
  44. data/lib/agent_c/utils/git.rb +87 -0
  45. data/lib/agent_c/utils/shell.rb +58 -0
  46. data/lib/agent_c/version.rb +5 -0
  47. data/lib/agent_c.rb +32 -0
  48. data/lib/versioned_store/base.rb +314 -0
  49. data/lib/versioned_store/config.rb +26 -0
  50. data/lib/versioned_store/stores/schema.rb +127 -0
  51. data/lib/versioned_store/version.rb +5 -0
  52. data/lib/versioned_store.rb +5 -0
  53. data/template/Gemfile +9 -0
  54. data/template/Gemfile.lock +152 -0
  55. data/template/README.md +61 -0
  56. data/template/Rakefile +50 -0
  57. data/template/bin/rake +27 -0
  58. data/template/lib/autoload.rb +10 -0
  59. data/template/lib/config.rb +59 -0
  60. data/template/lib/pipeline.rb +19 -0
  61. data/template/lib/prompts.yml +57 -0
  62. data/template/lib/store.rb +17 -0
  63. data/template/test/pipeline_test.rb +221 -0
  64. data/template/test/test_helper.rb +18 -0
  65. metadata +194 -0
@@ -0,0 +1,221 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "test_helper"
4
+
5
+ class PipelineTest < Minitest::Test
6
+ include TestHelpers
7
+
8
+ def setup
9
+ @store = Store.new(
10
+ logger: Logger.new(nil),
11
+ dir: File.join(
12
+ Dir.mktmpdir,
13
+ "template_test"
14
+ ),
15
+ )
16
+
17
+ @workspace = @store.workspace.create!(
18
+ dir: Dir.mktmpdir,
19
+ env: {}
20
+ )
21
+
22
+ # Load I18n translations from prompts.yml
23
+ I18n.load_path << File.expand_path("../lib/prompts.yml", __dir__)
24
+ I18n.backend.load_translations
25
+ end
26
+
27
+ def test_pipeline_end_to_end
28
+ summary = @store.summary.create!(language: "Spanish")
29
+ task = @store.task.create!(
30
+ record: summary,
31
+ workspace: @workspace
32
+ )
33
+
34
+ dummy_chat = AgentC::TestHelpers::DummyChat.new(responses: {
35
+ /Find a random ruby file/ =>
36
+ '{"status": "success", "input_path": "lib/pipeline.rb"}',
37
+ /language: Spanish/ =>
38
+ '{"status": "success", "summary_body": "Este archivo define un pipeline que resume archivos de Ruby"}',
39
+ /---BEGIN-SUMMARY---/ =>
40
+ '{"status": "success", "summary_path": "resumen_spanish.md"}'
41
+ })
42
+
43
+ # Setup dummy git to avoid actual git operations
44
+ dummy_git = AgentC::TestHelpers::DummyGit.new(@workspace.dir)
45
+ dummy_git.simulate_file_created!
46
+
47
+ session = test_session(
48
+ workspace_dir: @workspace.dir,
49
+ chat_provider: ->(**params) { dummy_chat }
50
+ )
51
+
52
+ Pipeline.call(task:, session:, git: ->(_dir) { dummy_git })
53
+
54
+ summary.reload
55
+ assert_equal "lib/pipeline.rb", summary.input_path
56
+ assert_equal "Este archivo define un pipeline que resume archivos de Ruby", summary.summary_body
57
+ assert_equal "resumen_spanish.md", summary.summary_path
58
+ assert task.reload.done?
59
+ assert_equal ["pick_a_random_file", "summarize_the_file", "write_summary_to_disk", "finalize"],
60
+ task.completed_steps
61
+ end
62
+
63
+ def test_pipeline_with_different_languages
64
+ summary = @store.summary.create!(language: "French")
65
+ task = @store.task.create!(
66
+ record: summary,
67
+ workspace: @workspace
68
+ )
69
+
70
+ dummy_chat = AgentC::TestHelpers::DummyChat.new(responses: {
71
+ /Find a random ruby file/ =>
72
+ '{"status": "success", "input_path": "lib/store.rb"}',
73
+ /language: French/ =>
74
+ '{"status": "success", "summary_body": "Ce fichier définit le schéma de données pour les résumés"}',
75
+ /---BEGIN-SUMMARY---/ =>
76
+ '{"status": "success", "summary_path": "resume_french.md"}'
77
+ })
78
+
79
+ dummy_git = AgentC::TestHelpers::DummyGit.new(@workspace.dir)
80
+ dummy_git.simulate_file_created!
81
+
82
+ session = test_session(
83
+ workspace_dir: @workspace.dir,
84
+ chat_provider: ->(**params) { dummy_chat }
85
+ )
86
+
87
+ Pipeline.call(task:, session:, git: ->(_dir) { dummy_git })
88
+
89
+ summary.reload
90
+ assert_equal "lib/store.rb", summary.input_path
91
+ assert_equal "Ce fichier définit le schéma de données pour les résumés", summary.summary_body
92
+ assert_equal "resume_french.md", summary.summary_path
93
+ assert task.reload.done?
94
+ end
95
+
96
+ def test_pipeline_finalize_commits_when_file_created
97
+ summary = @store.summary.create!(
98
+ language: "English",
99
+ input_path: "lib/pipeline.rb",
100
+ summary_body: "This file defines a pipeline",
101
+ summary_path: "summary.md"
102
+ )
103
+ task = @store.task.create!(
104
+ record: summary,
105
+ workspace: @workspace
106
+ )
107
+
108
+ # Mark all agent steps as completed
109
+ task.update!(completed_steps: ["pick_a_random_file", "summarize_the_file", "write_summary_to_disk"])
110
+
111
+ dummy_git = AgentC::TestHelpers::DummyGit.new(@workspace.dir)
112
+ dummy_git.simulate_file_created!
113
+
114
+ session = test_session(workspace_dir: @workspace.dir)
115
+
116
+ Pipeline.call(task:, session:, git: ->(_dir) { dummy_git })
117
+
118
+ assert task.reload.done?
119
+ assert_equal 1, dummy_git.invocations.count
120
+ commit = dummy_git.invocations.first
121
+ assert_equal :commit_all, commit[:method]
122
+ assert_match(/claude: added file: summary\.md/, commit.dig(:args, 0))
123
+ end
124
+
125
+ def test_pipeline_finalize_fails_when_no_file_created
126
+ summary = @store.summary.create!(
127
+ language: "English",
128
+ input_path: "lib/pipeline.rb",
129
+ summary_body: "This file defines a pipeline",
130
+ summary_path: "summary.md"
131
+ )
132
+ task = @store.task.create!(
133
+ record: summary,
134
+ workspace: @workspace
135
+ )
136
+
137
+ # Mark all agent steps as completed
138
+ task.update!(completed_steps: ["pick_a_random_file", "summarize_the_file", "write_summary_to_disk"])
139
+
140
+ dummy_git = AgentC::TestHelpers::DummyGit.new(@workspace.dir)
141
+ # Don't simulate file creation - no changes
142
+
143
+ session = test_session(workspace_dir: @workspace.dir)
144
+
145
+ Pipeline.call(task:, session:, git: ->(_dir) { dummy_git })
146
+
147
+ assert task.reload.failed?
148
+ assert_match(/didn't create a file/, task.error_message)
149
+ end
150
+
151
+ def test_pipeline_handles_agent_step_failure
152
+ summary = @store.summary.create!(language: "English")
153
+ task = @store.task.create!(
154
+ record: summary,
155
+ workspace: @workspace
156
+ )
157
+
158
+ dummy_chat = AgentC::TestHelpers::DummyChat.new(responses: {
159
+ /Find a random ruby file/ =>
160
+ '{"status": "error", "message": "No suitable files found in repository"}'
161
+ })
162
+
163
+ dummy_git = AgentC::TestHelpers::DummyGit.new(@workspace.dir)
164
+
165
+ session = test_session(
166
+ workspace_dir: @workspace.dir,
167
+ chat_provider: ->(**params) { dummy_chat }
168
+ )
169
+
170
+ Pipeline.call(task:, session:, git: ->(_dir) { dummy_git })
171
+
172
+ assert task.reload.failed?
173
+ assert_match(/No suitable files found/, task.error_message)
174
+ assert_nil summary.reload.input_path
175
+ assert_equal [], task.completed_steps
176
+ end
177
+
178
+ def test_pipeline_resumes_from_completed_steps
179
+ summary = @store.summary.create!(
180
+ language: "Japanese",
181
+ input_path: "lib/config.rb",
182
+ summary_body: "このファイルは設定を定義します"
183
+ )
184
+ task = @store.task.create!(
185
+ record: summary,
186
+ workspace: @workspace
187
+ )
188
+
189
+ # Mark first two steps as completed
190
+ task.update!(completed_steps: ["pick_a_random_file", "summarize_the_file"])
191
+
192
+ dummy_chat = AgentC::TestHelpers::DummyChat.new(responses: {
193
+ /---BEGIN-SUMMARY---/ =>
194
+ '{"status": "success", "summary_path": "config_summary_ja.md"}'
195
+ })
196
+
197
+ dummy_git = AgentC::TestHelpers::DummyGit.new(@workspace.dir)
198
+ dummy_git.simulate_file_created!
199
+
200
+ session = test_session(
201
+ workspace_dir: @workspace.dir,
202
+ chat_provider: ->(**params) { dummy_chat }
203
+ )
204
+
205
+ Pipeline.call(task:, session:, git: ->(_dir) { dummy_git })
206
+
207
+ summary.reload
208
+ # First two steps' data should still be there
209
+ assert_equal "lib/config.rb", summary.input_path
210
+ assert_equal "このファイルは設定を定義します", summary.summary_body
211
+ # Only the last step's data should be updated
212
+ assert_equal "config_summary_ja.md", summary.summary_path
213
+ assert task.reload.done?
214
+ assert_equal ["pick_a_random_file", "summarize_the_file", "write_summary_to_disk", "finalize"],
215
+ task.completed_steps
216
+ end
217
+
218
+ def dummy_chat_factory(responses)
219
+ ->(**_kwargs) { AgentC::TestHelpers::DummyChat.new(responses: responses) }
220
+ end
221
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/setup"
4
+ require "minitest/autorun"
5
+ require "fileutils"
6
+
7
+ require_relative "../lib/autoload"
8
+
9
+ # Require the base gems first
10
+ require "agent_c/test_helpers"
11
+
12
+ module TestHelpers
13
+ include AgentC::TestHelpers
14
+
15
+ def dummy_chat_factory(responses)
16
+ ->(**_kwargs) { AgentC::TestHelpers::DummyChat.new(responses: responses) }
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,194 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: agent_c
3
+ version: !ruby/object:Gem::Version
4
+ version: '2.9'
5
+ platform: ruby
6
+ authors:
7
+ - Pete Kinnecom
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-02-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: zeitwerk
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.7'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: activerecord
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '8.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '8.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.9'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.9'
55
+ - !ruby/object:Gem::Dependency
56
+ name: async
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.35'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.35'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ruby_llm
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.9'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.9'
83
+ - !ruby/object:Gem::Dependency
84
+ name: json-schema
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '6.1'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '6.1'
97
+ description:
98
+ email:
99
+ - git@k7u7.com
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - ".rubocop.yml"
105
+ - ".ruby-version"
106
+ - CLAUDE.md
107
+ - README.md
108
+ - Rakefile
109
+ - TODO.md
110
+ - agent_c.gemspec
111
+ - docs/batch.md
112
+ - docs/chat-methods.md
113
+ - docs/cost-reporting.md
114
+ - docs/pipeline-tips-and-tricks.md
115
+ - docs/session-configuration.md
116
+ - docs/testing.md
117
+ - docs/tools.md
118
+ - docs/versioned-store.md
119
+ - lib/agent_c.rb
120
+ - lib/agent_c/agent/chat.rb
121
+ - lib/agent_c/agent/chat_response.rb
122
+ - lib/agent_c/agent/chats/anthropic_bedrock.rb
123
+ - lib/agent_c/batch.rb
124
+ - lib/agent_c/configs/repo.rb
125
+ - lib/agent_c/context.rb
126
+ - lib/agent_c/costs/data.rb
127
+ - lib/agent_c/costs/report.rb
128
+ - lib/agent_c/db/store.rb
129
+ - lib/agent_c/errors.rb
130
+ - lib/agent_c/pipeline.rb
131
+ - lib/agent_c/pipelines/agent.rb
132
+ - lib/agent_c/processor.rb
133
+ - lib/agent_c/prompts.yml
134
+ - lib/agent_c/schema.rb
135
+ - lib/agent_c/session.rb
136
+ - lib/agent_c/store.rb
137
+ - lib/agent_c/test_helpers.rb
138
+ - lib/agent_c/tools.rb
139
+ - lib/agent_c/tools/dir_glob.rb
140
+ - lib/agent_c/tools/edit_file.rb
141
+ - lib/agent_c/tools/file_metadata.rb
142
+ - lib/agent_c/tools/git_status.rb
143
+ - lib/agent_c/tools/grep.rb
144
+ - lib/agent_c/tools/paths.rb
145
+ - lib/agent_c/tools/read_file.rb
146
+ - lib/agent_c/tools/run_rails_test.rb
147
+ - lib/agent_c/utils/git.rb
148
+ - lib/agent_c/utils/shell.rb
149
+ - lib/agent_c/version.rb
150
+ - lib/versioned_store.rb
151
+ - lib/versioned_store/base.rb
152
+ - lib/versioned_store/config.rb
153
+ - lib/versioned_store/stores/schema.rb
154
+ - lib/versioned_store/version.rb
155
+ - template/Gemfile
156
+ - template/Gemfile.lock
157
+ - template/README.md
158
+ - template/Rakefile
159
+ - template/bin/rake
160
+ - template/lib/autoload.rb
161
+ - template/lib/config.rb
162
+ - template/lib/pipeline.rb
163
+ - template/lib/prompts.yml
164
+ - template/lib/store.rb
165
+ - template/test/pipeline_test.rb
166
+ - template/test/test_helper.rb
167
+ homepage: https://github.com/petekinnecom/agent_c
168
+ licenses:
169
+ - WTFPL
170
+ metadata:
171
+ allowed_push_host: https://rubygems.org
172
+ homepage_uri: https://github.com/petekinnecom/agent_c
173
+ source_code_uri: https://github.com/petekinnecom/agent_c
174
+ changelog_uri: https://github.com/petekinnecom/agent_c
175
+ post_install_message:
176
+ rdoc_options: []
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: 3.0.0
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ requirements: []
190
+ rubygems_version: 3.4.19
191
+ signing_key:
192
+ specification_version: 4
193
+ summary: Batch processing for pipelines of steps for AI. AgentC, get it?
194
+ test_files: []