ollama_agent 0.1.0 → 0.3.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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.cursor/skills/ruby-code-review-levels/SKILL.md +115 -0
  3. data/.cursor/skills/self-improvement-sandbox-safety/SKILL.md +65 -0
  4. data/.env.example +25 -0
  5. data/CHANGELOG.md +40 -0
  6. data/README.md +135 -4
  7. data/docs/ARCHITECTURE.md +42 -0
  8. data/docs/PERFORMANCE.md +22 -0
  9. data/docs/SESSIONS.md +48 -0
  10. data/docs/TOOLS.md +53 -0
  11. data/docs/TOOL_RUNTIME.md +154 -0
  12. data/docs/superpowers/plans/2026-03-26-production-ready-ollama-agent.md +2454 -0
  13. data/docs/superpowers/specs/2026-03-26-production-ready-ollama-agent-design.md +400 -0
  14. data/lib/ollama_agent/agent/agent_config.rb +53 -0
  15. data/lib/ollama_agent/agent/client_wiring.rb +76 -0
  16. data/lib/ollama_agent/agent/prompt_wiring.rb +55 -0
  17. data/lib/ollama_agent/agent/session_wiring.rb +53 -0
  18. data/lib/ollama_agent/agent.rb +148 -73
  19. data/lib/ollama_agent/agent_prompt.rb +31 -1
  20. data/lib/ollama_agent/chat_stream_carry.rb +88 -0
  21. data/lib/ollama_agent/chat_stream_thinking_format.rb +29 -0
  22. data/lib/ollama_agent/cli.rb +394 -4
  23. data/lib/ollama_agent/console.rb +177 -5
  24. data/lib/ollama_agent/context/manager.rb +100 -0
  25. data/lib/ollama_agent/context/token_counter.rb +33 -0
  26. data/lib/ollama_agent/diff_path_validator.rb +32 -10
  27. data/lib/ollama_agent/env_config.rb +44 -0
  28. data/lib/ollama_agent/external_agents/TODO-plan.md +1948 -0
  29. data/lib/ollama_agent/external_agents/argv_interp.rb +21 -0
  30. data/lib/ollama_agent/external_agents/default_agents.yml +60 -0
  31. data/lib/ollama_agent/external_agents/delegate_logger.rb +31 -0
  32. data/lib/ollama_agent/external_agents/delegate_timeout_status.rb +12 -0
  33. data/lib/ollama_agent/external_agents/env_helpers.rb +38 -0
  34. data/lib/ollama_agent/external_agents/path_validator.rb +32 -0
  35. data/lib/ollama_agent/external_agents/probe.rb +122 -0
  36. data/lib/ollama_agent/external_agents/registry.rb +50 -0
  37. data/lib/ollama_agent/external_agents/runner.rb +118 -0
  38. data/lib/ollama_agent/external_agents.rb +9 -0
  39. data/lib/ollama_agent/global_dotenv.rb +39 -0
  40. data/lib/ollama_agent/model_env.rb +26 -0
  41. data/lib/ollama_agent/ollama_chat_thinking_stream.rb +41 -0
  42. data/lib/ollama_agent/ollama_connection.rb +6 -1
  43. data/lib/ollama_agent/patch_risk.rb +81 -0
  44. data/lib/ollama_agent/patch_support.rb +27 -1
  45. data/lib/ollama_agent/path_sandbox.rb +62 -0
  46. data/lib/ollama_agent/prompt_skills/clean_ruby.md +131 -0
  47. data/lib/ollama_agent/prompt_skills/code_review.md +112 -0
  48. data/lib/ollama_agent/prompt_skills/design_patterns.md +56 -0
  49. data/lib/ollama_agent/prompt_skills/manifest.yml +25 -0
  50. data/lib/ollama_agent/prompt_skills/ollama_agent_patterns.md +132 -0
  51. data/lib/ollama_agent/prompt_skills/rails_best_practices.md +41 -0
  52. data/lib/ollama_agent/prompt_skills/rails_style.md +138 -0
  53. data/lib/ollama_agent/prompt_skills/rspec.md +280 -0
  54. data/lib/ollama_agent/prompt_skills/rubocop.md +7 -0
  55. data/lib/ollama_agent/prompt_skills/ruby_style.md +121 -0
  56. data/lib/ollama_agent/prompt_skills/solid.md +270 -0
  57. data/lib/ollama_agent/prompt_skills/solid_ruby.md +223 -0
  58. data/lib/ollama_agent/prompt_skills.rb +169 -0
  59. data/lib/ollama_agent/repo_list.rb +4 -1
  60. data/lib/ollama_agent/resilience/audit_logger.rb +79 -0
  61. data/lib/ollama_agent/resilience/retry_middleware.rb +45 -0
  62. data/lib/ollama_agent/resilience/retry_policy.rb +51 -0
  63. data/lib/ollama_agent/ruby_index_tool_support.rb +17 -6
  64. data/lib/ollama_agent/runner.rb +123 -0
  65. data/lib/ollama_agent/sandboxed_tools/delegate_external.rb +62 -0
  66. data/lib/ollama_agent/sandboxed_tools/file_read_write.rb +100 -0
  67. data/lib/ollama_agent/sandboxed_tools/search_text.rb +60 -0
  68. data/lib/ollama_agent/sandboxed_tools.rb +55 -116
  69. data/lib/ollama_agent/search_backend.rb +93 -0
  70. data/lib/ollama_agent/self_improvement/analyzer.rb +34 -0
  71. data/lib/ollama_agent/self_improvement/improver.rb +340 -0
  72. data/lib/ollama_agent/self_improvement/modes.rb +25 -0
  73. data/lib/ollama_agent/self_improvement/ruby_mastery_context.rb +66 -0
  74. data/lib/ollama_agent/self_improvement.rb +5 -0
  75. data/lib/ollama_agent/session/session.rb +8 -0
  76. data/lib/ollama_agent/session/store.rb +68 -0
  77. data/lib/ollama_agent/streaming/console_streamer.rb +29 -0
  78. data/lib/ollama_agent/streaming/hooks.rb +39 -0
  79. data/lib/ollama_agent/tool_arguments.rb +13 -1
  80. data/lib/ollama_agent/tool_content_parser.rb +1 -1
  81. data/lib/ollama_agent/tool_runtime/executor.rb +34 -0
  82. data/lib/ollama_agent/tool_runtime/json_extractor.rb +62 -0
  83. data/lib/ollama_agent/tool_runtime/loop.rb +72 -0
  84. data/lib/ollama_agent/tool_runtime/memory.rb +32 -0
  85. data/lib/ollama_agent/tool_runtime/ollama_json_planner.rb +98 -0
  86. data/lib/ollama_agent/tool_runtime/plan_extractor.rb +12 -0
  87. data/lib/ollama_agent/tool_runtime/registry.rb +60 -0
  88. data/lib/ollama_agent/tool_runtime/tool.rb +24 -0
  89. data/lib/ollama_agent/tool_runtime.rb +24 -0
  90. data/lib/ollama_agent/tools/registry.rb +55 -0
  91. data/lib/ollama_agent/tools_schema.rb +74 -1
  92. data/lib/ollama_agent/user_prompt.rb +35 -0
  93. data/lib/ollama_agent/version.rb +1 -1
  94. data/lib/ollama_agent.rb +25 -0
  95. data/reproduce_429.rb +40 -0
  96. data/sig/ollama_agent.rbs +111 -1
  97. metadata +78 -2
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OllamaAgent
4
+ # Classifies a proposed unified diff for semi-automatic patch approval (obvious vs risky).
5
+ module PatchRisk
6
+ FORBIDDEN_PATTERNS = [
7
+ /\beval\s*\(/,
8
+ /`rm\s+-rf/,
9
+ /system\s*\(\s*["']sudo/,
10
+ /\bFile\.delete\b/,
11
+ /\bKernel\.exec\b/
12
+ ].freeze
13
+
14
+ LARGE_DIFF_LINES = 80
15
+
16
+ module_function
17
+
18
+ def large_diff_line_limit
19
+ v = ENV.fetch("OLLAMA_AGENT_PATCH_RISK_MAX_DIFF_LINES", nil)
20
+ return LARGE_DIFF_LINES if v.nil? || v.to_s.strip.empty?
21
+
22
+ Integer(v)
23
+ rescue ArgumentError, TypeError
24
+ LARGE_DIFF_LINES
25
+ end
26
+
27
+ def forbidden?(diff)
28
+ FORBIDDEN_PATTERNS.any? { |pattern| diff.match?(pattern) }
29
+ end
30
+
31
+ # Returns :auto_approve (no prompt) or :require_confirmation (prompt when confirm_patches is on).
32
+ def assess(path, diff)
33
+ relative = path.to_s.tr("\\", "/")
34
+
35
+ return :require_confirmation if risky?(relative, diff)
36
+
37
+ return :auto_approve if obvious_path?(relative)
38
+ return :auto_approve if safe_spec_change?(relative, diff)
39
+
40
+ :require_confirmation
41
+ end
42
+
43
+ def risky?(relative, diff)
44
+ forbidden?(diff) ||
45
+ critical_path?(relative) ||
46
+ large_diff?(diff) ||
47
+ critical_lib_file?(relative)
48
+ end
49
+
50
+ def large_diff?(diff, limit: nil)
51
+ max = limit.nil? ? large_diff_line_limit : limit
52
+ diff.scan(/^[-+][^-+]/).size > max
53
+ end
54
+
55
+ def obvious_path?(relative)
56
+ relative.end_with?(".md") || relative.start_with?("docs/")
57
+ end
58
+
59
+ def safe_spec_change?(relative, diff)
60
+ relative.start_with?("spec/") && !large_diff?(diff, limit: 40)
61
+ end
62
+
63
+ def critical_lib_file?(relative)
64
+ return false unless relative.start_with?("lib/")
65
+
66
+ relative.include?("sandboxed_tools") ||
67
+ relative.include?("patch_support") ||
68
+ relative.end_with?("ollama_agent/agent.rb") ||
69
+ relative.end_with?("ollama_agent/tools_schema.rb")
70
+ end
71
+
72
+ def critical_path?(relative)
73
+ return true if relative.match?(/\A(Gemfile|Gemfile\.lock)\z/)
74
+ return true if relative.end_with?(".gemspec")
75
+ return true if relative == "lib/ollama_agent/version.rb"
76
+ return true if relative.start_with?("exe/")
77
+
78
+ false
79
+ end
80
+ end
81
+ end
@@ -8,6 +8,8 @@ module OllamaAgent
8
8
  private
9
9
 
10
10
  def patch_dry_run(diff)
11
+ return patch_missing_tool_message unless patch_available?
12
+
11
13
  output, status = Open3.capture2e(
12
14
  "patch", "-p1", "-f", "-d", @root, "--dry-run",
13
15
  stdin_data: diff
@@ -35,7 +37,8 @@ module OllamaAgent
35
37
  Re-read the file with read_file, then rebuild the diff using exact lines from that file (not placeholders).
36
38
  The @@ hunk line counts must match the hunk body the way git diff would emit them.
37
39
  MSG
38
- hint.empty? ? msg : "#{msg}\n#{hint}"
40
+ body = hint.empty? ? msg : "#{msg}\n#{hint}"
41
+ body.end_with?("\n") ? body : "#{body}\n"
39
42
  end
40
43
 
41
44
  def patch_stderr_hint(detail)
@@ -65,6 +68,8 @@ module OllamaAgent
65
68
  end
66
69
 
67
70
  def apply_patch(diff)
71
+ return patch_missing_tool_message unless patch_available?
72
+
68
73
  output, status = Open3.capture2e(
69
74
  "patch", "-p1", "-f", "-d", @root,
70
75
  stdin_data: diff
@@ -74,5 +79,26 @@ module OllamaAgent
74
79
 
75
80
  patch_failure_message(output, dry_run: false)
76
81
  end
82
+
83
+ def patch_available?
84
+ return @patch_available if defined?(@patch_available)
85
+
86
+ @patch_available = patch_binary_usable?
87
+ end
88
+
89
+ def patch_binary_usable?
90
+ _, status = Open3.capture2e("patch", "--version")
91
+ return true if status.success?
92
+
93
+ _, which_status = Open3.capture2e("which", "patch")
94
+ which_status.success?
95
+ end
96
+
97
+ def patch_missing_tool_message
98
+ <<~MSG.strip
99
+ Error: ollama_agent: the `patch` program was not found or is not usable on PATH. edit_file requires GNU patch.
100
+ Install it (e.g. apt install patch, brew install gpatch), or on Windows use Git Bash, WSL, or GnuWin32.
101
+ MSG
102
+ end
77
103
  end
78
104
  end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+
5
+ module OllamaAgent
6
+ # Resolves paths under a project root using File.realpath so symlinks cannot escape the sandbox.
7
+ module PathSandbox
8
+ module_function
9
+
10
+ # @param root_abs [String] File.expand_path of the project root (may be a symlink path)
11
+ # @param root_real [String] File.realpath(root_abs) when the root exists
12
+ # @param user_path [String] relative or absolute path from tool args
13
+ def allowed?(root_abs, root_real, user_path)
14
+ return false if user_path.nil? || user_path.to_s.strip.empty?
15
+
16
+ expanded = Pathname(user_path.to_s).expand_path(root_abs).cleanpath.to_s
17
+ return false unless lexically_under_root_abs?(expanded, root_abs)
18
+
19
+ candidate_under_root?(expanded, root_real, root_abs)
20
+ end
21
+
22
+ def lexically_under_root_abs?(expanded_abs, root_abs)
23
+ expanded_abs == root_abs || expanded_abs.start_with?(root_abs + File::SEPARATOR)
24
+ end
25
+
26
+ def candidate_under_root?(expanded_abs, root_real, root_abs)
27
+ path_real = File.realpath(expanded_abs)
28
+ under_root_real?(path_real, root_real)
29
+ rescue Errno::ENOENT
30
+ nonexistent_path_allowed_under_root?(expanded_abs, root_real, root_abs)
31
+ rescue Errno::ELOOP, Errno::EACCES
32
+ false
33
+ end
34
+
35
+ def under_root_real?(path_real, root_real)
36
+ path_real == root_real || path_real.start_with?(root_real + File::SEPARATOR)
37
+ end
38
+
39
+ # rubocop:disable Metrics/MethodLength -- parent walk for missing path segments
40
+ def nonexistent_path_allowed_under_root?(expanded_abs, root_real, root_abs)
41
+ parent = expanded_abs
42
+ loop do
43
+ next_parent = File.dirname(parent)
44
+ break if next_parent == parent
45
+
46
+ parent = next_parent
47
+ begin
48
+ pr = File.realpath(parent)
49
+ return false unless under_root_real?(pr, root_real)
50
+
51
+ return lexically_under_root_abs?(expanded_abs, root_abs)
52
+ rescue Errno::ENOENT
53
+ next
54
+ rescue Errno::ELOOP, Errno::EACCES
55
+ return false
56
+ end
57
+ end
58
+ false
59
+ end
60
+ # rubocop:enable Metrics/MethodLength
61
+ end
62
+ end
@@ -0,0 +1,131 @@
1
+ ---
2
+ name: clean-ruby-code
3
+ description: >
4
+ Enforces clean Ruby code principles during all Ruby/Rails code generation, review, and refactoring tasks.
5
+ Applies naming conventions, method design, boolean logic, class architecture, refactoring patterns,
6
+ and TDD practices derived from Clean Ruby methodology. Use this skill whenever writing Ruby code,
7
+ reviewing Ruby pull requests, refactoring Rails models/controllers/services, creating new Ruby classes
8
+ or modules, writing RSpec tests, or when the user asks for code review, code improvement, naming help,
9
+ or architecture guidance in Ruby. Also trigger when the user mentions "clean code", "refactor",
10
+ "code smell", "naming", "SRP", "TDD", or "code quality" in a Ruby context. This skill should activate
11
+ for ANY Ruby code generation task — even if the user doesn't explicitly ask for "clean" code — because
12
+ all generated Ruby should be clean by default.
13
+ ---
14
+
15
+ # Clean Ruby Code Agent
16
+
17
+ This skill enforces clean Ruby code principles across all code generation, review, and refactoring.
18
+ Every piece of Ruby code produced must satisfy three core qualities: **readable**, **extensible**, **simple**.
19
+
20
+ ## Core Philosophy
21
+
22
+ Code is read far more often than it is written. Every naming choice, method signature, and class boundary
23
+ should minimize the cognitive load on the next reader. When in doubt, choose the simpler solution —
24
+ complexity is the enemy of maintainability.
25
+
26
+ ## Decision Flow
27
+
28
+ When generating or reviewing Ruby code, apply these checks in order:
29
+
30
+ 1. **Naming** → Are all names descriptive, snake_case, verb-prefixed (methods), purpose-named (classes)?
31
+ 2. **Methods** → Fewer params? Guard clauses? Under 10 lines? No deep nesting?
32
+ 3. **Boolean Logic** → Extracted to named methods/variables? No double negatives? No raw `unless` with compound conditions?
33
+ 4. **Classes** → Simple `initialize`? SRP? Max 3 levels of inheritance? Composition where appropriate?
34
+ 5. **Refactoring** → Can any piece be simplified without losing functionality? Are there comments that should be methods?
35
+ 6. **Tests** → Does the code have clear, descriptive RSpec tests with context blocks and explicit expectations?
36
+
37
+ For detailed rules on each area, read the corresponding reference file:
38
+
39
+ - **Naming rules**: `references/naming.md`
40
+ - **Method design**: `references/methods.md`
41
+ - **Boolean logic**: `references/boolean-logic.md`
42
+ - **Class architecture**: `references/classes.md`
43
+ - **Refactoring patterns**: `references/refactoring.md`
44
+ - **TDD practices**: `references/tdd.md`
45
+
46
+ Read the relevant reference file(s) before generating or reviewing code in that area.
47
+
48
+ ## Enforcement Rules
49
+
50
+ These rules apply to ALL Ruby code this agent produces or reviews:
51
+
52
+ ### Naming (always enforced)
53
+
54
+ - Variables: snake_case, descriptive of the data, no Hungarian notation, no conjunctions ("and"/"or"), no numeric suffixes unless versioning, no crutch words (Manager, Data, Info, List)
55
+ - Methods: verb-prefixed, `?` suffix for boolean returns, `!` suffix for destructive mutations
56
+ - Classes: named for purpose (e.g., `UserSetup`) or role (e.g., `InActiveUserQuery`), never generic (`UserManager`)
57
+ - Modules: named for the concept they group (e.g., `Calculable`, `Loggable`)
58
+
59
+ ### Methods (always enforced)
60
+
61
+ - Maximum 3 parameters; use a config/params object beyond that
62
+ - Guard clauses instead of deep `if/else` nesting
63
+ - Target length: 5–10 lines; extract sub-operations into named private methods
64
+ - No unnecessary intermediate variables; leverage Ruby's implicit return
65
+ - Comments only when they explain *why*, never *what* — if a comment describes what code does, extract a method instead
66
+
67
+ ### Boolean Logic (always enforced)
68
+
69
+ - Complex conditions → extract to a named predicate method (`def ready_to_spawn?`)
70
+ - Never use `unless` with compound conditions (`unless a && b` is banned)
71
+ - Avoid double negatives (`!not_found` → `found?`)
72
+ - Use `&&` (short-circuit) not `&` (eager) unless eager evaluation is explicitly needed
73
+ - Leverage Ruby truthy/falsy — don't write `if x == true`, write `if x`
74
+ - Ternary only for single simple conditions; use `if/else` for compound logic
75
+
76
+ ### Classes (always enforced)
77
+
78
+ - `initialize` does assignment only — no external calls, no side effects
79
+ - Error-prone operations go in explicit setup methods called after instantiation
80
+ - Limit inheritance to 3 levels; prefer composition (`has-a`) over inheritance (`is-a`)
81
+ - Instance variables: use `attr_reader`/`attr_accessor` — avoid raw `@var` access in public methods
82
+ - Private methods below `private` keyword, ordered by call sequence
83
+ - If private methods are generic utilities (math, formatting), extract to a module
84
+
85
+ ### Refactoring (applied during review and generation)
86
+
87
+ - No change is too small — rename a variable, extract a method, remove a comment
88
+ - Replace comments with self-documenting method names
89
+ - Apply SRP: if a class/method does two things, split it
90
+ - Watch for shotgun surgery (one change requires edits in many places) — consolidate
91
+ - Replace conditional chains (`if/elsif/elsif`) with polymorphism or lookup tables
92
+
93
+ ### TDD (applied when generating tests)
94
+
95
+ - Write the test first, then the minimal implementation
96
+ - Test descriptions: `context '#method_name'` → `it 'returns X when Y'`
97
+ - Use `subject`, `let`, and `before` blocks — no repeated setup
98
+ - Separate expected and actual values for clarity
99
+ - Cover: happy path, nil inputs, edge cases, error conditions
100
+ - Tests are production code — apply the same naming and structure rules
101
+
102
+ ## Anti-Patterns to Flag
103
+
104
+ When reviewing code, flag these immediately:
105
+
106
+ | Anti-Pattern | Fix |
107
+ |---|---|
108
+ | Method > 15 lines | Extract private methods |
109
+ | > 3 params | Introduce parameter object |
110
+ | Nested `if` > 2 levels | Guard clauses or extract predicate |
111
+ | `unless` with `&&`/`||` | Convert to `if` with inverted logic |
112
+ | Comment describing *what* | Extract named method |
113
+ | God class (> 200 lines) | Split by responsibility |
114
+ | `initialize` with side effects | Move to explicit setup method |
115
+ | Generic name (Manager, Helper, Data) | Rename to purpose/role |
116
+ | Double negative (`!not_x`) | Invert the method name |
117
+ | Bare `rescue` | Rescue specific exceptions |
118
+
119
+ ## Output Format
120
+
121
+ When generating Ruby code, structure output as:
122
+
123
+ 1. The code itself — complete, runnable, following all rules above
124
+ 2. If refactoring existing code: brief annotation of what changed and why (1-2 lines per change)
125
+ 3. If the user asked for review: list violations as `[RULE] description → fix` format
126
+
127
+ When the code is part of a Rails app, also apply:
128
+ - Models: thin, no business logic beyond validations and scopes
129
+ - Controllers: thin, delegate to domain objects
130
+ - Services: only for orchestration across multiple domain objects or external systems
131
+ - Queries: extract complex `where` chains to query objects (e.g., `InActiveUserQuery`)
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: code-review
3
+ description: Structured code review for Ruby/Rails projects. Detects file type, scans the local codebase for project-specific patterns first, then applies global skills in priority order. Use when asked to review, audit, or check code quality.
4
+ disable-model-invocation: true
5
+ argument-hint: [file-or-directory]
6
+ allowed-tools: Read, Glob, Grep
7
+ ---
8
+
9
+ # Code Review — Orchestrator
10
+
11
+ Perform a structured code review of `$ARGUMENTS`. Follow this exact workflow.
12
+
13
+ ## Step 1 — Identify File Type
14
+
15
+ | File Pattern | Type | Primary Skills |
16
+ |---|---|---|
17
+ | `app/controllers/**` | Controller | rails-best-practices, solid-ruby |
18
+ | `app/models/**` | Model | rails-best-practices, solid-ruby, ruby-design-patterns |
19
+ | `app/services/**` | Service Object | solid-ruby, rails-best-practices |
20
+ | `app/workers/**`, `app/jobs/**` | Background Job | rails-best-practices, solid-ruby |
21
+ | `spec/**/*_spec.rb` | RSpec spec | rspec, solid-ruby |
22
+ | `db/migrate/**` | Migration | rails-best-practices |
23
+ | `config/routes.rb` | Routes | rails-best-practices |
24
+ | `app/views/**` | View/Partial | rails-best-practices, ruby-style |
25
+ | `lib/**` | Library | solid-ruby, ruby-style, ruby-design-patterns |
26
+
27
+ ## Step 2 — Scan Project for Existing Patterns (MANDATORY)
28
+
29
+ Before applying any rule, read 2–3 similar files in the same directory to extract project conventions.
30
+
31
+ **Find and extract:**
32
+ - Controllers: response format, base controller, auth pattern, error format, param naming
33
+ - Models: shared concerns/modules, validation style, enum convention, scope naming
34
+ - Services: calling convention (`.call` vs `.run`), return type, class naming, base class
35
+ - Specs: factory style, shared examples, helper includes, context naming
36
+
37
+ See [project-pattern-detection.md](project-pattern-detection.md) for full detection checklist.
38
+
39
+ ## Step 3 — Review in Priority Order
40
+
41
+ ### 🔴 CRITICAL — Always flag, regardless of project style
42
+ - SQL injection via string interpolation
43
+ - Missing Strong Parameters / mass assignment vulnerability
44
+ - `rescue Exception` (swallows signals)
45
+ - User input in file paths, shell commands, or redirects
46
+ - Hardcoded credentials or secrets
47
+ - `save` return value silently ignored
48
+ - Auth check missing or bypassable
49
+ - Sensitive data logged
50
+
51
+ ### 🟠 PERFORMANCE — Always flag
52
+ - N+1 queries (association in loop without preload)
53
+ - `.where` / query methods in AR instance methods (breaks preloading)
54
+ - `.count` where `.size` should be used
55
+ - `any?`/`empty?` then `.each` on same relation (two queries)
56
+ - `exists?` called multiple times (never memoized)
57
+ - `find_each` missing on large dataset iteration
58
+ - No timeout on external HTTP/Redis calls
59
+ - `after_save` for side effects instead of `after_commit`
60
+
61
+ ### 🟡 PROJECT CONSISTENCY — Most important correctness layer
62
+ Compare against patterns found in Step 2. Phrase as: "The rest of the codebase does X — this does Y."
63
+ - Different service calling convention
64
+ - Different response/error format in controller
65
+ - Different factory/spec helper pattern
66
+ - Missing shared concern/module that all similar models include
67
+ - Different enum/scope/validation naming
68
+
69
+ ### 🔵 BEST PRACTICES — Apply where no project pattern covers
70
+ Reference: rails-best-practices, solid-ruby, rspec, ruby-design-patterns
71
+
72
+ ### ⚪ STYLE — Only if clearly inconsistent
73
+ Reference: ruby-style, rails-style
74
+
75
+ ## Step 4 — Output Format
76
+
77
+ ```
78
+ ## Code Review: [filename]
79
+ **Type:** [file type]
80
+ **Project patterns detected:** [1-line summary]
81
+
82
+ ### 🔴 CRITICAL | [title]
83
+ **File:** path/to/file.rb:42
84
+ **Issue:** [what and why]
85
+ **Fix:**
86
+ # Before / After code
87
+ **Rule:** [source]
88
+
89
+ [repeat per finding, skip empty severity sections]
90
+
91
+ ---
92
+ ## Verdict: SHIP ✅ | NEEDS FIXES 🔧 | CRITICAL ISSUES 🚨
93
+
94
+ - 🔴 Critical: N
95
+ - 🟠 Performance: N
96
+ - 🟡 Consistency: N
97
+ - 🔵 Best Practice: N
98
+ - ⚪ Style: N
99
+
100
+ Must fix before merge: [list]
101
+ Can fix in follow-up: [list]
102
+ ```
103
+
104
+ After verdict, ask: "Want me to apply the fixes?"
105
+
106
+ ## Behavior Rules
107
+ - Cannot find similar files → say so, proceed with global skills only
108
+ - Do not flag style as critical
109
+ - Do not invent violations — only flag what is visible in the code
110
+ - Project patterns beat global skills when they conflict
111
+ - One finding per issue — no duplicates across severity levels
112
+ - For specs: also check that the spec tests what production code actually does
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: ruby-design-patterns
3
+ description: All 23 GoF design patterns with Ruby implementations from refactoring.guru. Use when designing or refactoring Ruby/Rails code and you need to apply or recognize a design pattern. Covers Creational (5), Structural (7), and Behavioral (11) patterns with Ruby code examples and Rails-specific applications.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Ruby Design Patterns — Complete Reference
8
+
9
+ Source: https://refactoring.guru/design-patterns/ruby
10
+
11
+ > Don't force patterns. Let them emerge from refactoring. Apply when you recognize the problem, not speculatively.
12
+
13
+ ## By Problem — Quick Lookup
14
+
15
+ | Problem | Pattern | Category |
16
+ |---|---|---|
17
+ | Creating families of compatible objects | Abstract Factory | Creational |
18
+ | Building complex objects step by step | Builder | Creational |
19
+ | Subclass decides which object to create | Factory Method | Creational |
20
+ | Clone objects without coupling to class | Prototype | Creational |
21
+ | One instance globally | Singleton | Creational |
22
+ | Incompatible interfaces must work together | Adapter | Structural |
23
+ | Split abstraction from implementation | Bridge | Structural |
24
+ | Tree structures, treat parts and wholes uniformly | Composite | Structural |
25
+ | Add behavior without subclassing | Decorator | Structural |
26
+ | Simplify a complex subsystem | Facade | Structural |
27
+ | Share common state across many objects | Flyweight | Structural |
28
+ | Control access to an object | Proxy | Structural |
29
+ | Pass request through a handler chain | Chain of Responsibility | Behavioral |
30
+ | Encapsulate a request as an object | Command | Behavioral |
31
+ | Traverse a collection without exposing it | Iterator | Behavioral |
32
+ | Decouple communicating objects | Mediator | Behavioral |
33
+ | Save and restore object state | Memento | Behavioral |
34
+ | Notify many objects about events | Observer | Behavioral |
35
+ | Alter behavior when state changes | State | Behavioral |
36
+ | Swap algorithms at runtime | Strategy | Behavioral |
37
+ | Define algorithm skeleton, defer steps | Template Method | Behavioral |
38
+ | Add operations without modifying classes | Visitor | Behavioral |
39
+
40
+ ## Ruby-Native Pattern Support
41
+
42
+ | Pattern | Ruby built-in |
43
+ |---|---|
44
+ | Iterator | `Enumerable`, `Enumerator` |
45
+ | Observer | `Observable` module (stdlib) |
46
+ | Singleton | `Singleton` module (stdlib) |
47
+ | Decorator | Modules + `prepend` / `extend` |
48
+ | Strategy | Blocks / Procs / lambdas |
49
+ | Template Method | Inheritance + hook methods |
50
+ | Command | Proc / Method objects |
51
+
52
+ ## Pattern Detail Files
53
+
54
+ - [Creational Patterns (1–5)](creational-patterns.md) — Abstract Factory, Builder, Factory Method, Prototype, Singleton
55
+ - [Structural Patterns (6–12)](structural-patterns.md) — Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy
56
+ - [Behavioral Patterns (13–22)](behavioral-patterns.md) — Chain of Responsibility, Command, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor
@@ -0,0 +1,25 @@
1
+ # Ordered bundled prompt skills (ids used by OLLAMA_AGENT_SKILLS_INCLUDE / EXCLUDE).
2
+ version: 1
3
+ skills:
4
+ - id: clean_ruby
5
+ file: clean_ruby.md
6
+ - id: ruby_style
7
+ file: ruby_style.md
8
+ - id: rubocop
9
+ file: rubocop.md
10
+ - id: solid
11
+ file: solid.md
12
+ - id: solid_ruby
13
+ file: solid_ruby.md
14
+ - id: design_patterns
15
+ file: design_patterns.md
16
+ - id: rspec
17
+ file: rspec.md
18
+ - id: rails_style
19
+ file: rails_style.md
20
+ - id: rails_best_practices
21
+ file: rails_best_practices.md
22
+ - id: code_review
23
+ file: code_review.md
24
+ - id: ollama_agent_patterns
25
+ file: ollama_agent_patterns.md
@@ -0,0 +1,132 @@
1
+ ---
2
+ name: ollama-agent-patterns
3
+ description: >-
4
+ Blueprint for building a CLI Ollama-based coding agent (Ruby gem) with design
5
+ patterns and judicious metaprogramming—Facade, Template Method, Factory/Registry,
6
+ Builder, Adapter, Proxy, Command, Observer, Strategy, State, and tool DSLs. Use
7
+ when working on ollama_agent, ollama-client integration, tools, LLM adapters,
8
+ prompts, streaming, patch application, or when the user asks for agent
9
+ architecture, extensibility, registries, or Ruby metaprogramming for agents.
10
+ ---
11
+
12
+ # Skill: Ollama Agent — Design Patterns & Metaprogramming
13
+
14
+ ## 1. Overview
15
+
16
+ Blueprint for a **CLI coding agent** (e.g. `ollama_agent`) that uses **ollama-client** to chat with tools: read/search files, apply small unified diffs from natural language. Patterns keep the design **extensible and maintainable** without front-loading complexity.
17
+
18
+ ## 2. Core components & patterns
19
+
20
+ | Component | Pattern(s) | Purpose |
21
+ |-----------|------------|---------|
22
+ | **Agent** | Facade, Template Method | Simple API (`run`); skeleton loop with overridable steps. |
23
+ | **Tools** | Factory Method, Command, Registry | Load/execute tools; optional logging/replay/undo. |
24
+ | **LLM client** | Adapter, (optional) Proxy | Swap backends; logging/metrics without forking core. |
25
+ | **Prompts** | Builder | Fluent construction of messages + tools + options. |
26
+ | **Streaming** | Observer | Multiple subscribers for tokens (UI, logs). |
27
+ | **Patch application** | Strategy | Swap `patch(1)` vs Ruby-native apply, etc. |
28
+ | **Conversation** | State | Phases (idle, tools, confirmation) without giant `if/else`. |
29
+
30
+ **Client lifetime:** **Singleton** is optional. Prefer **injectable** `Ollama::Client` (or adapter) for tests and per-run config (`timeout`, `base_url`, `api_key`).
31
+
32
+ ## 3. Principles
33
+
34
+ 1. **Start simple** — add Factory, State, Strategy when duplication or branching hurts; do not adopt every pattern up front.
35
+ 2. **Core loop stays explicit** — readable `run` / tool loop; collaborators (`Adapter`, `PromptBuilder`, `ToolCommand`) hide detail, not `send` spaghetti.
36
+ 3. **Metaprogramming at boundaries** — registration / DSLs at tool edges; avoid dynamic dispatch in error paths and hot loops unless measured.
37
+ 4. **Workspace rules** — validated tool schemas; loggable/replayable actions; **no hardcoded model names** (runtime config).
38
+
39
+ ## 4. Pattern map (where code lives)
40
+
41
+ | Area | Patterns | Role |
42
+ |------|-----------|------|
43
+ | **Agent** | Facade, Template Method | Single entry; fixed loop with overridable hooks. |
44
+ | **Tools** | Factory, Command, Registry | Instantiate tools; optional command objects for audit. |
45
+ | **LLM** | Adapter, Proxy | Backends; cross-cutting logging/rate limits. |
46
+ | **Prompts** | Builder | Messages + tools + options. |
47
+ | **Streaming** | Observer | Fan-out from client `hooks`. |
48
+ | **Patches** | Strategy | Pluggable apply path. |
49
+ | **Conversation** | State | Explicit phases when control flow grows. |
50
+
51
+ ### Target layout (illustrative)
52
+
53
+ A fuller **pattern-oriented** tree (with `bin/console`, `commands/`, `strategies/`, `states/`, etc.) and a **“this repo today”** note live in **reference.md** under *Recommended gem structure*. Migrate only when complexity justifies new directories.
54
+
55
+ ```
56
+ ollama_agent/
57
+ ├── exe/ollama_agent
58
+ ├── lib/ollama_agent.rb
59
+ └── lib/ollama_agent/
60
+ ├── agent.rb
61
+ ├── cli.rb
62
+ ├── prompt_builder.rb # Builder (optional)
63
+ ├── tool_registry.rb
64
+ ├── tools/base.rb
65
+ ├── tools/
66
+ ├── llm/base_adapter.rb
67
+ ├── llm/ollama_adapter.rb
68
+ ├── llm/logging_proxy.rb
69
+ ├── commands/tool_command.rb
70
+ ├── observers/
71
+ ├── strategies/
72
+ └── states/
73
+ ```
74
+
75
+ ## 5. Creational patterns (summary)
76
+
77
+ - **Factory + registry** — Replace a growing `case` with `ToolRegistry.get(name)`; auto-register via `inherited` **or** explicit `tool_name` + hash (clearer than magic naming).
78
+ - **Builder** — `PromptBuilder` when message construction branches; skip until you have real optional composition.
79
+ - **Singleton** — Avoid as default for HTTP clients; use when you truly need one process-wide resource and tests can still stub.
80
+
81
+ ## 6. Structural patterns (summary)
82
+
83
+ - **Adapter** — Common `chat(messages:, tools:, **options)` surface for Ollama vs future providers.
84
+ - **Proxy** — Wrap adapter for logging/metrics; keep thin.
85
+ - **Facade** — `Agent#run` — already the right shape.
86
+
87
+ ## 7. Behavioral patterns (summary)
88
+
89
+ - **Command** — Wrap tool invocations when you need queues, structured logs, or replay; **undo** only with a real story (VCS/snapshots).
90
+ - **Observer** — Fan-out streaming tokens; compose with ollama-client `hooks`.
91
+ - **Strategy** — `PatchStrategy` if you need non-`patch` apply paths.
92
+ - **State** — When confirmation + tool rounds + idle become tangled.
93
+ - **Template Method** — Base agent class only if you have **multiple** agent variants sharing one loop.
94
+
95
+ ## 8. Metaprogramming (judicious)
96
+
97
+ | Technique | Use when | Caution |
98
+ |-----------|----------|--------|
99
+ | **`inherited` + registry** | Many tools, stable naming | Keep in sync with **tool JSON schema**; consider explicit `tool_name`. |
100
+ | **Tool DSL** (`tool :name do …`) | Repetition dominates | Stack traces and IDE nav suffer; keep DSL thin. |
101
+ | **`send` for hooks** | Fixed event names | Prefer explicit methods for public API. |
102
+ | **`method_missing`** | Rare delegation | Not for core tool dispatch — use a Hash/registry. |
103
+ | **Plugin `extend`** | Third-party tools | Document load order and sandbox rules. |
104
+
105
+ ## 9. When to avoid heavy metaprogramming
106
+
107
+ - Main agent loop and **error handling** — explicit flow wins.
108
+ - **Performance-sensitive** inner loops — measure before dynamic dispatch.
109
+ - **Public APIs** — stable, documented entry points over hidden DSL magic.
110
+
111
+ ## 10. Summary
112
+
113
+ | Pattern | Benefit |
114
+ |---------|---------|
115
+ | Factory Method | Decouples tool creation from call sites. |
116
+ | Builder | Composes prompts/options without positional arg soup. |
117
+ | Singleton | Single shared resource (use sparingly for HTTP clients). |
118
+ | Adapter | Multiple LLM backends behind one shape. |
119
+ | Proxy | Cross-cutting concerns on the client. |
120
+ | Facade | Hides orchestration from CLI users. |
121
+ | Command | Tool calls as objects (log/replay/queue). |
122
+ | Observer | Decoupled streaming consumers. |
123
+ | Strategy | Swappable patch application. |
124
+ | State | Explicit conversation phases. |
125
+ | Template Method | Shared loop, varied steps. |
126
+ | Metaprogramming | Less boilerplate at **boundaries** only. |
127
+
128
+ **Start with straightforward code; refactor into patterns when pain appears.**
129
+
130
+ ## 11. Full code examples
131
+
132
+ Runnable snippets (registry, `PromptBuilder`, adapters, proxy, command, observer, strategy, state, template-method skeleton, DSL sketch) live in **[reference.md](reference.md)**. Prefer copying from there and adapting to the real `ollama-client` API and this repo’s `SandboxedTools` / `tools_schema` constraints.