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.
- checksums.yaml +4 -4
- data/.cursor/skills/ruby-code-review-levels/SKILL.md +115 -0
- data/.cursor/skills/self-improvement-sandbox-safety/SKILL.md +65 -0
- data/.env.example +25 -0
- data/CHANGELOG.md +40 -0
- data/README.md +135 -4
- data/docs/ARCHITECTURE.md +42 -0
- data/docs/PERFORMANCE.md +22 -0
- data/docs/SESSIONS.md +48 -0
- data/docs/TOOLS.md +53 -0
- data/docs/TOOL_RUNTIME.md +154 -0
- data/docs/superpowers/plans/2026-03-26-production-ready-ollama-agent.md +2454 -0
- data/docs/superpowers/specs/2026-03-26-production-ready-ollama-agent-design.md +400 -0
- data/lib/ollama_agent/agent/agent_config.rb +53 -0
- data/lib/ollama_agent/agent/client_wiring.rb +76 -0
- data/lib/ollama_agent/agent/prompt_wiring.rb +55 -0
- data/lib/ollama_agent/agent/session_wiring.rb +53 -0
- data/lib/ollama_agent/agent.rb +148 -73
- data/lib/ollama_agent/agent_prompt.rb +31 -1
- data/lib/ollama_agent/chat_stream_carry.rb +88 -0
- data/lib/ollama_agent/chat_stream_thinking_format.rb +29 -0
- data/lib/ollama_agent/cli.rb +394 -4
- data/lib/ollama_agent/console.rb +177 -5
- data/lib/ollama_agent/context/manager.rb +100 -0
- data/lib/ollama_agent/context/token_counter.rb +33 -0
- data/lib/ollama_agent/diff_path_validator.rb +32 -10
- data/lib/ollama_agent/env_config.rb +44 -0
- data/lib/ollama_agent/external_agents/TODO-plan.md +1948 -0
- data/lib/ollama_agent/external_agents/argv_interp.rb +21 -0
- data/lib/ollama_agent/external_agents/default_agents.yml +60 -0
- data/lib/ollama_agent/external_agents/delegate_logger.rb +31 -0
- data/lib/ollama_agent/external_agents/delegate_timeout_status.rb +12 -0
- data/lib/ollama_agent/external_agents/env_helpers.rb +38 -0
- data/lib/ollama_agent/external_agents/path_validator.rb +32 -0
- data/lib/ollama_agent/external_agents/probe.rb +122 -0
- data/lib/ollama_agent/external_agents/registry.rb +50 -0
- data/lib/ollama_agent/external_agents/runner.rb +118 -0
- data/lib/ollama_agent/external_agents.rb +9 -0
- data/lib/ollama_agent/global_dotenv.rb +39 -0
- data/lib/ollama_agent/model_env.rb +26 -0
- data/lib/ollama_agent/ollama_chat_thinking_stream.rb +41 -0
- data/lib/ollama_agent/ollama_connection.rb +6 -1
- data/lib/ollama_agent/patch_risk.rb +81 -0
- data/lib/ollama_agent/patch_support.rb +27 -1
- data/lib/ollama_agent/path_sandbox.rb +62 -0
- data/lib/ollama_agent/prompt_skills/clean_ruby.md +131 -0
- data/lib/ollama_agent/prompt_skills/code_review.md +112 -0
- data/lib/ollama_agent/prompt_skills/design_patterns.md +56 -0
- data/lib/ollama_agent/prompt_skills/manifest.yml +25 -0
- data/lib/ollama_agent/prompt_skills/ollama_agent_patterns.md +132 -0
- data/lib/ollama_agent/prompt_skills/rails_best_practices.md +41 -0
- data/lib/ollama_agent/prompt_skills/rails_style.md +138 -0
- data/lib/ollama_agent/prompt_skills/rspec.md +280 -0
- data/lib/ollama_agent/prompt_skills/rubocop.md +7 -0
- data/lib/ollama_agent/prompt_skills/ruby_style.md +121 -0
- data/lib/ollama_agent/prompt_skills/solid.md +270 -0
- data/lib/ollama_agent/prompt_skills/solid_ruby.md +223 -0
- data/lib/ollama_agent/prompt_skills.rb +169 -0
- data/lib/ollama_agent/repo_list.rb +4 -1
- data/lib/ollama_agent/resilience/audit_logger.rb +79 -0
- data/lib/ollama_agent/resilience/retry_middleware.rb +45 -0
- data/lib/ollama_agent/resilience/retry_policy.rb +51 -0
- data/lib/ollama_agent/ruby_index_tool_support.rb +17 -6
- data/lib/ollama_agent/runner.rb +123 -0
- data/lib/ollama_agent/sandboxed_tools/delegate_external.rb +62 -0
- data/lib/ollama_agent/sandboxed_tools/file_read_write.rb +100 -0
- data/lib/ollama_agent/sandboxed_tools/search_text.rb +60 -0
- data/lib/ollama_agent/sandboxed_tools.rb +55 -116
- data/lib/ollama_agent/search_backend.rb +93 -0
- data/lib/ollama_agent/self_improvement/analyzer.rb +34 -0
- data/lib/ollama_agent/self_improvement/improver.rb +340 -0
- data/lib/ollama_agent/self_improvement/modes.rb +25 -0
- data/lib/ollama_agent/self_improvement/ruby_mastery_context.rb +66 -0
- data/lib/ollama_agent/self_improvement.rb +5 -0
- data/lib/ollama_agent/session/session.rb +8 -0
- data/lib/ollama_agent/session/store.rb +68 -0
- data/lib/ollama_agent/streaming/console_streamer.rb +29 -0
- data/lib/ollama_agent/streaming/hooks.rb +39 -0
- data/lib/ollama_agent/tool_arguments.rb +13 -1
- data/lib/ollama_agent/tool_content_parser.rb +1 -1
- data/lib/ollama_agent/tool_runtime/executor.rb +34 -0
- data/lib/ollama_agent/tool_runtime/json_extractor.rb +62 -0
- data/lib/ollama_agent/tool_runtime/loop.rb +72 -0
- data/lib/ollama_agent/tool_runtime/memory.rb +32 -0
- data/lib/ollama_agent/tool_runtime/ollama_json_planner.rb +98 -0
- data/lib/ollama_agent/tool_runtime/plan_extractor.rb +12 -0
- data/lib/ollama_agent/tool_runtime/registry.rb +60 -0
- data/lib/ollama_agent/tool_runtime/tool.rb +24 -0
- data/lib/ollama_agent/tool_runtime.rb +24 -0
- data/lib/ollama_agent/tools/registry.rb +55 -0
- data/lib/ollama_agent/tools_schema.rb +74 -1
- data/lib/ollama_agent/user_prompt.rb +35 -0
- data/lib/ollama_agent/version.rb +1 -1
- data/lib/ollama_agent.rb +25 -0
- data/reproduce_429.rb +40 -0
- data/sig/ollama_agent.rbs +111 -1
- metadata +78 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c3518f124795a2efb13b9e88abb2b7c90f935c918da3c7d30458ff2fff245f1b
|
|
4
|
+
data.tar.gz: 101d5e4bdbfbd5e6196e9744c94f1803c1ca32fea746b275cda8ad30783788d1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1aad321bf07bb4ddea9daffeca46724e842408ac3f5d96997d77c3f81b30f10a3b1d28a4f8260bc14cde249cb63069e3fcc5aa1aecc982c0852a979733a2b1b3
|
|
7
|
+
data.tar.gz: 5d67531648baf16bf30b63e9d3726f14243d19d939f56875fb245254ad9538eded9ea4c1fe3e38cf1ded9d93b77f392f1ac4145ae0e351b654a531802696f230
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ruby-code-review-levels
|
|
3
|
+
description: >
|
|
4
|
+
Deterministic 5-level Ruby/Rails PR review checklist for production risk and architectural depth.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Skill: Ruby Code Review Levels (Production-Oriented)
|
|
8
|
+
|
|
9
|
+
Use this skill when the user asks for a Ruby (Rails API system) code review, refactor guidance, or PR feedback. The goal is to catch failure modes before they ship—not just fix style.
|
|
10
|
+
|
|
11
|
+
## How to use
|
|
12
|
+
|
|
13
|
+
For each review, walk levels in order and produce findings grouped by level. Apply these gates:
|
|
14
|
+
|
|
15
|
+
- Reject immediately if Level 2 fails (logic/edge-case correctness).
|
|
16
|
+
- Reject immediately if Level 4 violates system invariants (idempotency, determinism, state correctness).
|
|
17
|
+
- Reject immediately if Level 5 lacks required safety controls (observability + external/API safety + graceful failure).
|
|
18
|
+
- Otherwise, accept with Level 1/3 improvements if they are non-blocking.
|
|
19
|
+
|
|
20
|
+
## Level 1 — Syntax & Style Review (Mechanical)
|
|
21
|
+
|
|
22
|
+
Objective: enforce Ruby idioms, readability, and consistency.
|
|
23
|
+
|
|
24
|
+
Checks:
|
|
25
|
+
- Project RuboCop compliance using the repo’s configuration (not “default” relaxed rules).
|
|
26
|
+
- Naming clarity: no generic `data`, `obj`, `thing`, `tmp`; purpose-named identifiers.
|
|
27
|
+
- Ruby idioms: prefer `&&`/`||` appropriately, avoid `present?`/Rails-only helpers in hot paths, avoid awkward defensive `nil` checks when Ruby truthiness is sufficient.
|
|
28
|
+
- No dead code / no commented-out blocks.
|
|
29
|
+
- Method size: aim for small methods (roughly <= 10–15 LOC); split larger logic into intention-revealing private methods.
|
|
30
|
+
|
|
31
|
+
W5H (required for non-trivial changes):
|
|
32
|
+
- What: what does this code do?
|
|
33
|
+
- Why: why is this approach needed here?
|
|
34
|
+
- Where: where does this belong (controller/model/service/pattern)?
|
|
35
|
+
- When: when does it run (sync vs async, request vs background)?
|
|
36
|
+
- How it fails: what are the failure modes and what happens next?
|
|
37
|
+
|
|
38
|
+
## Level 2 — Correctness & Edge Case Review (Logic)
|
|
39
|
+
|
|
40
|
+
Objective: ensure correct behavior for realistic inputs and boundary conditions.
|
|
41
|
+
|
|
42
|
+
Mandatory checks:
|
|
43
|
+
- Nil handling is explicit and intentional (no accidental `nil` propagation).
|
|
44
|
+
- Boundaries: empty collections, zero values, missing keys, unexpected formats.
|
|
45
|
+
- Determinism: same inputs should produce the same outputs (especially for decision logic).
|
|
46
|
+
- Numeric safety: for floats/rounding, ensure the behavior is stable and documented.
|
|
47
|
+
- Time/zone correctness: use the correct time source (avoid mixing app time and system time incorrectly).
|
|
48
|
+
- Idempotency guards (where relevant): avoid double-processing, double-exits, duplicate updates.
|
|
49
|
+
- Validate invariants before doing work (e.g., existence checks, “state is allowed” checks).
|
|
50
|
+
|
|
51
|
+
Questions:
|
|
52
|
+
- What if upstream data is stale/incomplete?
|
|
53
|
+
- What if state already changed between decision and action?
|
|
54
|
+
- Are there hidden assumptions about ordering or concurrency?
|
|
55
|
+
|
|
56
|
+
## Level 3 — Design & Abstraction Review (Structure)
|
|
57
|
+
|
|
58
|
+
Objective: ensure boundaries are real and abstractions are earned.
|
|
59
|
+
|
|
60
|
+
Rules:
|
|
61
|
+
- SRP: each unit has one reason to change.
|
|
62
|
+
- Don’t add useless service objects that just proxy model calls.
|
|
63
|
+
- Put domain logic with models (or dedicated POROs) and keep controllers thin.
|
|
64
|
+
- Extract shared logic across strategies instead of duplicating it with small variations.
|
|
65
|
+
- Avoid “fake polymorphism”: only abstract when it reduces duplication or clarifies invariants.
|
|
66
|
+
|
|
67
|
+
Review heuristics:
|
|
68
|
+
- Is the abstraction describing a real concept (e.g., `Position`, `Policy`, `Validator`)?
|
|
69
|
+
- Where should this logic live: model, service (orchestrator), or PORO (calculation/logic)?
|
|
70
|
+
- Are naming and ownership clear enough that a future developer can extend it safely?
|
|
71
|
+
|
|
72
|
+
## Level 4 — System & Architectural Review (Integrity)
|
|
73
|
+
|
|
74
|
+
Objective: ensure correctness under load, failures, and concurrency.
|
|
75
|
+
|
|
76
|
+
Focus:
|
|
77
|
+
- Deterministic flows: event-driven or async sequences should be explicit.
|
|
78
|
+
- Idempotency at system boundaries: prevent duplicate side effects (DB writes, external calls).
|
|
79
|
+
- State management: single source of truth; avoid mixing “cache truth” and “DB truth” without reconciliation.
|
|
80
|
+
- Invariants:
|
|
81
|
+
- Never place/trigger the same action twice for the same logical entity.
|
|
82
|
+
- Never transition state in an invalid order.
|
|
83
|
+
- Always re-check critical preconditions close to execution if the world can change.
|
|
84
|
+
|
|
85
|
+
Failure modes:
|
|
86
|
+
- What happens if external dependencies fail or time out?
|
|
87
|
+
- Are retries safe (idempotent) and bounded (no infinite loops)?
|
|
88
|
+
- Are partial failures handled with clear compensation or persistence rules?
|
|
89
|
+
|
|
90
|
+
## Level 5 — Production Readiness Review (Safety)
|
|
91
|
+
|
|
92
|
+
Objective: make the change safe to ship.
|
|
93
|
+
|
|
94
|
+
Required checks:
|
|
95
|
+
- Observability: structured logs (or consistent log lines), including correlation/request IDs when available.
|
|
96
|
+
- External/API safety: timeouts, bounded retries with backoff, and verification of post-conditions.
|
|
97
|
+
- Graceful degradation: clear error propagation; avoid silent failures.
|
|
98
|
+
- Performance sanity: avoid accidental N+1, and keep work bounded (no unbounded loops).
|
|
99
|
+
- Testing coverage for the critical behavior:
|
|
100
|
+
- at least one spec for the new behavior
|
|
101
|
+
- edge cases for nil/boundaries
|
|
102
|
+
- idempotency/duplicate prevention when side effects exist
|
|
103
|
+
|
|
104
|
+
Exit criteria:
|
|
105
|
+
- Level 2 + Level 4 must be green.
|
|
106
|
+
- Level 5 requires explicit safety and observability for any side-effecting changes.
|
|
107
|
+
|
|
108
|
+
## Output format (what to write back to the user)
|
|
109
|
+
|
|
110
|
+
For each finding, include:
|
|
111
|
+
- Level (1–5)
|
|
112
|
+
- File and symbol/method name (where possible)
|
|
113
|
+
- Why it fails or risks failure (one concise sentence)
|
|
114
|
+
- Concrete fix suggestion (smallest change that addresses it)
|
|
115
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: self-improvement-sandbox-safety
|
|
3
|
+
description: >
|
|
4
|
+
Guardrails for `ollama_agent improve --mode automated` sandbox safety: minimal diffs, build-file restoration, patch validation, and safe merge rules.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Skill: Self-Improvement Sandbox Safety
|
|
8
|
+
|
|
9
|
+
Use this skill when the agent is asked to run or design improvements using the gem’s self-improvement flow, especially:
|
|
10
|
+
|
|
11
|
+
- `ollama_agent self_review --mode automated`
|
|
12
|
+
- `ollama_agent improve --mode automated --apply`
|
|
13
|
+
|
|
14
|
+
This skill is about preventing “test succeeded in sandbox but tree broke” failures and preventing the model from taking risky actions in the live repo.
|
|
15
|
+
|
|
16
|
+
## Non-negotiable invariants
|
|
17
|
+
|
|
18
|
+
### 1) Keep build-critical files intact (or restore them)
|
|
19
|
+
- Do not delete or corrupt `Gemfile`, `Gemfile.lock`, `*.gemspec`, `exe/`, or `.ruby-version` in the sandbox.
|
|
20
|
+
- Assume the model may still break these during `edit_file`.
|
|
21
|
+
- The system should restore build essentials before running tests. Still, avoid “creative” patching of those files.
|
|
22
|
+
|
|
23
|
+
### 2) Run tests inside the sandbox
|
|
24
|
+
- Always run `bundle exec rspec` with the working directory set to the sandbox root.
|
|
25
|
+
- Ensure bundler points at the sandbox `Gemfile` (via `BUNDLE_GEMFILE`).
|
|
26
|
+
|
|
27
|
+
### 3) Require valid unified diffs
|
|
28
|
+
When producing an `edit_file` patch:
|
|
29
|
+
- Include `--- a/<path>` and `+++ b/<path>` headers.
|
|
30
|
+
- Use the correct ordering: `---` then `+++` then `@@ -x,y +x,y @@` hunk headers.
|
|
31
|
+
- Ensure the hunk `@@` line counts match the changed block exactly.
|
|
32
|
+
- Avoid legacy context-diff hunks like `--- N,M ----`.
|
|
33
|
+
- Prefer minimal, local hunks: one logical change per hunk.
|
|
34
|
+
|
|
35
|
+
### 4) Avoid merging ignored test artifacts
|
|
36
|
+
With `--apply`:
|
|
37
|
+
- Merge only actual source changes.
|
|
38
|
+
- Never merge files that should be treated as artifacts, caches, or status trackers created during test runs (e.g. `.rspec_status`).
|
|
39
|
+
- If the sandbox contains ignored test artifacts, skip them during merge.
|
|
40
|
+
|
|
41
|
+
## Minimal-diff strategy (to avoid fragile mega-patches)
|
|
42
|
+
|
|
43
|
+
Required behavior for `--mode automated`:
|
|
44
|
+
- Keep each `edit_file` patch small.
|
|
45
|
+
- Do not replace whole methods unless the patch is tiny and context is exact.
|
|
46
|
+
- Do not replace multi-hundred-line hunks.
|
|
47
|
+
- Prefer a sequence:
|
|
48
|
+
- add a helper (small)
|
|
49
|
+
- update one call site (small)
|
|
50
|
+
- add/adjust one focused spec (small)
|
|
51
|
+
|
|
52
|
+
## “Patch checklist” before edit_file
|
|
53
|
+
|
|
54
|
+
Before generating a patch, verify:
|
|
55
|
+
- The target path in the patch (`+++ b/...`) matches the file you’re editing.
|
|
56
|
+
- The patch contains at least one `@@ ... @@` hunk header.
|
|
57
|
+
- The diff hunk order is correct (first `+++ ...` must appear before the first `@@`).
|
|
58
|
+
- The patch contains the exact surrounding context lines expected by the dry-run validator.
|
|
59
|
+
|
|
60
|
+
## Prompt addendum snippet (to include in FIX_PROMPT)
|
|
61
|
+
|
|
62
|
+
If you need to extend an existing FIX_PROMPT, add something like:
|
|
63
|
+
|
|
64
|
+
Minimal diffs only: fewest lines per edit_file, exact @@ counts—no whole-method or mega-hunks. Never delete build-critical files (Gemfile, Gemfile.lock, *.gemspec, exe/) and rely on restore before tests. With --apply, never merge ignored test artifacts (e.g. .rspec_status).
|
|
65
|
+
|
data/.env.example
CHANGED
|
@@ -25,3 +25,28 @@ OLLAMA_AGENT_MODEL=gpt-oss:120b-cloud
|
|
|
25
25
|
OLLAMA_AGENT_TIMEOUT=120
|
|
26
26
|
# Parse tool JSON from assistant output (set to "1" to enable)
|
|
27
27
|
OLLAMA_AGENT_PARSE_TOOL_JSON=0
|
|
28
|
+
# Bundled Markdown prompt skills (default: on). Set to "0" to disable.
|
|
29
|
+
OLLAMA_AGENT_SKILLS=1
|
|
30
|
+
# Comma-separated manifest ids to load (omit = all bundled). Example: ruby_style,rubocop
|
|
31
|
+
# OLLAMA_AGENT_SKILLS_INCLUDE=
|
|
32
|
+
# Comma-separated ids to omit from bundled skills
|
|
33
|
+
# OLLAMA_AGENT_SKILLS_EXCLUDE=
|
|
34
|
+
# Extra SKILL-style .md files or directories (colon-separated on Unix)
|
|
35
|
+
# OLLAMA_AGENT_SKILL_PATHS=
|
|
36
|
+
# Extra paths from env (default: on). Set to "0" to disable.
|
|
37
|
+
OLLAMA_AGENT_EXTERNAL_SKILLS=1
|
|
38
|
+
# Set to "1" to add orchestrator tools (list_external_agents, delegate_to_agent) to `ask`
|
|
39
|
+
OLLAMA_AGENT_ORCHESTRATOR=0
|
|
40
|
+
# Optional alias for orchestrator mode on ask: set to "orchestrator"
|
|
41
|
+
# OLLAMA_AGENT_MODE=
|
|
42
|
+
# Optional YAML path overriding ~/.config/ollama_agent/agents.yml for external CLI definitions
|
|
43
|
+
# OLLAMA_AGENT_EXTERNAL_AGENTS_CONFIG=
|
|
44
|
+
# Max bytes of stdout+stderr returned from delegate_to_agent (default 100000)
|
|
45
|
+
# OLLAMA_AGENT_DELEGATE_MAX_OUTPUT_BYTES=
|
|
46
|
+
# Emit structured delegation audit logs to stderr (also enabled by OLLAMA_AGENT_DEBUG=1)
|
|
47
|
+
# OLLAMA_AGENT_DELEGATE_LOG=0
|
|
48
|
+
# Per-tool binary overrides (examples; see default_agents.yml)
|
|
49
|
+
# OLLAMA_AGENT_CLAUDE_CLI_PATH=
|
|
50
|
+
# OLLAMA_AGENT_GEMINI_CLI_PATH=
|
|
51
|
+
# OLLAMA_AGENT_CODEX_CLI_PATH=
|
|
52
|
+
# OLLAMA_AGENT_CURSOR_CLI_PATH=
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.3.0] - 2026-04-06
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `ToolRuntime` — JSON plan loop for custom tools (`OllamaJsonPlanner`, registry, executor); see `docs/TOOL_RUNTIME.md`
|
|
7
|
+
- Optional **ruby_mastery** context for `self_review` / `improve` (`OLLAMA_AGENT_RUBY_MASTERY`, `--no-ruby-mastery`)
|
|
8
|
+
- `OllamaAgent::ModelEnv` — shared model name resolution from environment
|
|
9
|
+
- `OllamaAgent::GlobalDotenv` — load repo-root `.env` after `ollama_client` so CLI picks up `OLLAMA_AGENT_*` without extra exports
|
|
10
|
+
- Self-improvement automated mode: `--verify` (`syntax`, `rubocop`, `rspec`), `OLLAMA_AGENT_IMPROVE_VERIFY`, `--stream`, and a success message when `--apply` was not used
|
|
11
|
+
- External agents / argv expansion and related orchestration refinements
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- `SearchBackend` finds `rg` / `grep` by scanning `PATH` (avoids relying on a `command` executable on trimmed `PATH`)
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- `SelfImprovement::Improver#run` accepts `max_tokens` and `context_summarize` from the CLI (Ruby 3 keyword compatibility)
|
|
18
|
+
|
|
19
|
+
## [0.2.0] - 2026-03-26
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- `write_file` tool — create or overwrite files (complements `edit_file` for surgical diffs)
|
|
23
|
+
- `OllamaAgent::Tools.register` — extensible tool registry for library consumers
|
|
24
|
+
- `Streaming::Hooks` — event bus (`on_token`, `on_tool_call`, `on_tool_result`, `on_complete`, `on_error`, `on_retry`)
|
|
25
|
+
- `--stream` / `OLLAMA_AGENT_STREAM=1` — live streaming token output
|
|
26
|
+
- `Resilience::RetryMiddleware` — exponential backoff on timeout/503/429 (default 3 retries)
|
|
27
|
+
- `Resilience::AuditLogger` — NDJSON audit log under `.ollama_agent/logs/` (`--audit` / `OLLAMA_AGENT_AUDIT=1`)
|
|
28
|
+
- `Context::Manager` — sliding-window token trim before each chat call (`OLLAMA_AGENT_MAX_TOKENS`)
|
|
29
|
+
- `Session::Store` — crash-safe NDJSON session persistence (`--session`, `--resume`)
|
|
30
|
+
- `ollama_agent sessions` — list saved sessions
|
|
31
|
+
- `OllamaAgent::Runner` — stable public library facade with SemVer contract from 0.2.0
|
|
32
|
+
- `docs/ARCHITECTURE.md`, `docs/TOOLS.md`, `docs/SESSIONS.md`
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
- `READ_ONLY_TOOLS` now excludes both `edit_file` and `write_file`
|
|
36
|
+
- `Agent` now exposes `#hooks` (`Streaming::Hooks`) and `#session_id`
|
|
37
|
+
|
|
38
|
+
### New environment variables
|
|
39
|
+
- `OLLAMA_AGENT_STREAM`, `OLLAMA_AGENT_MAX_TOKENS`
|
|
40
|
+
- `OLLAMA_AGENT_MAX_RETRIES`, `OLLAMA_AGENT_RETRY_BASE_DELAY`
|
|
41
|
+
- `OLLAMA_AGENT_AUDIT`, `OLLAMA_AGENT_AUDIT_LOG_PATH`
|
|
42
|
+
|
|
3
43
|
## [0.1.0] - 2026-03-21
|
|
4
44
|
|
|
5
45
|
- Initial release
|
data/README.md
CHANGED
|
@@ -11,13 +11,25 @@ Ruby gem that runs a **CLI coding agent** against a local [Ollama](https://ollam
|
|
|
11
11
|
- Tool `search_code` – search code with ripgrep or grep.
|
|
12
12
|
- Tool `edit_file` – apply unified diffs safely.
|
|
13
13
|
- CLI built with Thor, entry point `exe/ollama_agent`.
|
|
14
|
+
- **`self_review`** – self-review / improvement with a **`--mode`**:
|
|
15
|
+
- **`analysis`** (default, alias `1`) — read-only tools; report only; no writes.
|
|
16
|
+
- **`interactive`** (alias `2`, `fix`) — full tools on `--root`; you confirm each patch (like `ask`); optional `-y` / `--semi`.
|
|
17
|
+
- **`automated`** (alias `3`, `sandbox`) — temp copy, agent edits, **`bundle exec rspec`** in the sandbox, optional **`--apply`** to merge into your checkout.
|
|
18
|
+
- **`improve`** — same as **`self_review --mode automated`** (you can pass **`--mode automated`** explicitly; other modes belong on **`self_review`**).
|
|
19
|
+
- **`orchestrate`** / **`OLLAMA_AGENT_ORCHESTRATOR=1`** — optional **orchestrator** tools to probe and delegate to other local CLI agents (see [Orchestrator](#orchestrator-external-cli-agents)); **`agents`** lists availability.
|
|
20
|
+
- **Ruby API** — embed **`Runner`**, **`Agent`**, custom tools, hooks, sessions, and (optionally) **`ToolRuntime`**; see [Library usage (Ruby)](#library-usage-ruby).
|
|
14
21
|
|
|
15
22
|
## Requirements
|
|
16
23
|
|
|
17
|
-
- Ruby ≥ 3.2
|
|
24
|
+
- Ruby ≥ 3.2 (enforced in the gemspec as `required_ruby_version`)
|
|
18
25
|
- **Local:** Ollama running and a capable tool-calling model, **or**
|
|
19
26
|
- **Ollama Cloud:** API key and a cloud-capable model name (see below)
|
|
20
27
|
|
|
28
|
+
### Prerequisites (external tools)
|
|
29
|
+
|
|
30
|
+
- **`patch`** — required for `edit_file` (GNU `patch` on `PATH`). On Windows, use Git Bash, WSL, GnuWin32, or another environment that provides `patch`.
|
|
31
|
+
- **`rg` (ripgrep) or `grep`** — text mode for `search_code` needs at least one of these on `PATH` (ripgrep is preferred when present).
|
|
32
|
+
|
|
21
33
|
## Installation
|
|
22
34
|
|
|
23
35
|
From RubyGems (when published) or from this repository:
|
|
@@ -40,6 +52,9 @@ Apply proposed patches without interactive confirmation:
|
|
|
40
52
|
|
|
41
53
|
```bash
|
|
42
54
|
bundle exec ruby exe/ollama_agent ask -y "Your task"
|
|
55
|
+
|
|
56
|
+
# Review / audit only (no patches, writes, or delegation)—same as a report-style self_review
|
|
57
|
+
bundle exec ruby exe/ollama_agent ask --read-only "Summarize risks in this repo"
|
|
43
58
|
```
|
|
44
59
|
|
|
45
60
|
Long-running models (slow local inference):
|
|
@@ -54,6 +69,27 @@ Interactive REPL:
|
|
|
54
69
|
bundle exec ruby exe/ollama_agent ask --interactive
|
|
55
70
|
```
|
|
56
71
|
|
|
72
|
+
Self-review modes (default project root is the **current working directory** unless you set `--root` or `OLLAMA_AGENT_ROOT`):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Mode 1 — analysis only (default)
|
|
76
|
+
bundle exec ruby exe/ollama_agent self_review
|
|
77
|
+
bundle exec ruby exe/ollama_agent self_review --mode analysis
|
|
78
|
+
|
|
79
|
+
# Mode 2 — optional fixes in the working tree (confirm each patch, or -y / --semi)
|
|
80
|
+
bundle exec ruby exe/ollama_agent self_review --mode interactive
|
|
81
|
+
|
|
82
|
+
# Mode 3 — sandbox + tests + optional merge back (same as `improve`)
|
|
83
|
+
# Without --apply, edits stay in a temp dir only; pass --apply to copy changed files into your checkout.
|
|
84
|
+
bundle exec ruby exe/ollama_agent self_review --mode automated
|
|
85
|
+
bundle exec ruby exe/ollama_agent self_review --mode automated --apply
|
|
86
|
+
bundle exec ruby exe/ollama_agent improve --apply
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**`ruby_mastery` (optional):** When the [`ruby_mastery`](https://github.com/shubhamtaywade82/ruby_mastery) gem is installed (this repo lists it in the `Gemfile` for development), **`self_review`** (all modes) and **`improve`** prepend a **markdown static-analysis** section to the user prompt. Add the same gem to your app’s `Gemfile` if you want that behavior outside this checkout. Disable with **`--no-ruby-mastery`** or **`OLLAMA_AGENT_RUBY_MASTERY=0`**. Limit size with **`OLLAMA_AGENT_RUBY_MASTERY_MAX_CHARS`** (default `60000`).
|
|
90
|
+
|
|
91
|
+
For mode 3, `-y` skips all patch prompts; `--no-semi` prompts for every patch when not using `-y`.
|
|
92
|
+
|
|
57
93
|
With a **thinking-capable** model, enable reasoning output:
|
|
58
94
|
|
|
59
95
|
```bash
|
|
@@ -64,7 +100,9 @@ bundle exec ruby exe/ollama_agent ask -i --think true
|
|
|
64
100
|
|
|
65
101
|
The CLI uses **ANSI colors** on a TTY (banner, prompt, patch prompts). **Assistant replies** are rendered as **Markdown** (headings, lists, bold, code fences) via `tty-markdown` when stdout is a TTY and **`NO_COLOR`** is unset. Disable Markdown rendering with **`OLLAMA_AGENT_MARKDOWN=0`**. Disable all colors with **`NO_COLOR`** or **`OLLAMA_AGENT_COLOR=0`**.
|
|
66
102
|
|
|
67
|
-
When **thinking** is enabled, internal reasoning is shown
|
|
103
|
+
When **thinking** is enabled, internal reasoning is shown under a **Thinking** label; the user-facing reply is labeled **Assistant** in green when the model returns both fields. By default (**`OLLAMA_AGENT_THINKING_STYLE=compact`**, Cursor-like), one **Thinking** header is printed per `ask` run and every later reasoning chunk in that run is appended with **blank lines only** (no repeated banner, no rule lines)—including after turns where the model printed tool JSON or other non-empty `content`. Set **`OLLAMA_AGENT_THINKING_STYLE=framed`** for the legacy boxed style (banner + long rulers on every assistant message). Thinking body text is **plain dim** by default. Set **`OLLAMA_AGENT_THINKING_MARKDOWN=1`** to render thinking through Markdown too (muted colors).
|
|
104
|
+
|
|
105
|
+
With **`--stream`** / **`OLLAMA_AGENT_STREAM=1`**, reasoning streams in **dim** text under a single **Thinking** line, then **`Assistant`** and the reply stream in normal styling—closer to Cursor than printing everything as one token stream. (This uses a small hook on ollama-client’s chat stream; `hooks[:on_thinking]` is also emitted for custom subscribers.)
|
|
68
106
|
|
|
69
107
|
### Ollama Cloud
|
|
70
108
|
|
|
@@ -87,23 +125,116 @@ bundle exec ruby exe/ollama_agent ask "Your task"
|
|
|
87
125
|
| `OLLAMA_BASE_URL` | Ollama API base URL (default from ollama-client: `http://localhost:11434`; use `https://ollama.com` for cloud) |
|
|
88
126
|
| `OLLAMA_API_KEY` | API key for Ollama Cloud (`https://ollama.com`); optional for local HTTP |
|
|
89
127
|
| `OLLAMA_AGENT_MODEL` | Model name (overrides default from ollama-client) |
|
|
90
|
-
| `OLLAMA_AGENT_ROOT` | Project root (
|
|
128
|
+
| `OLLAMA_AGENT_ROOT` | Project root for tools (`list_files`, `read_file`, etc.). Defaults to **current working directory** when unset (CLI never falls back to the gem install path). |
|
|
91
129
|
| `OLLAMA_AGENT_DEBUG` | Set to `1` to print validation diagnostics on stderr |
|
|
130
|
+
| `OLLAMA_AGENT_STRICT_ENV` | Set to `1` so invalid numeric env values (e.g. `OLLAMA_AGENT_MAX_TURNS`) raise `ConfigurationError` instead of falling back to defaults |
|
|
92
131
|
| `OLLAMA_AGENT_MAX_TURNS` | Max chat rounds with tool calls (default: 64) |
|
|
93
132
|
| `OLLAMA_AGENT_TIMEOUT` | HTTP read/open timeout in seconds for Ollama requests (default **120**; use `ask --timeout` / `-t` to override per run) |
|
|
94
133
|
| `OLLAMA_AGENT_PARSE_TOOL_JSON` | Set to `1` to run tools parsed from JSON lines in assistant text (fallback when the model does not emit native tool calls) |
|
|
95
134
|
| `NO_COLOR` | Set (any value) to disable ANSI colors (see [no-color.org](https://no-color.org/)) |
|
|
96
135
|
| `OLLAMA_AGENT_COLOR` | Set to `0` to disable colors even on a TTY |
|
|
97
136
|
| `OLLAMA_AGENT_MARKDOWN` | Set to `0` to disable Markdown formatting of assistant replies (plain text only) |
|
|
98
|
-
| `
|
|
137
|
+
| `OLLAMA_AGENT_THINKING_STYLE` | `compact` (default) = one **Thinking** label per run, blank lines between later reasoning chunks; `framed` = repeat full banner/rulers each message |
|
|
138
|
+
| `OLLAMA_AGENT_THINKING_MARKDOWN` | Set to `1` to render **thinking** text with Markdown (muted); default is plain dim text |
|
|
99
139
|
| `OLLAMA_AGENT_THINK` | Model **thinking** mode for compatible models: `true` / `false`, or `high` / `medium` / `low` (see ollama-client `think:`). Empty = omit (server default). |
|
|
140
|
+
| `OLLAMA_AGENT_PATCH_RISK_MAX_DIFF_LINES` | Max changed-line count before a diff is treated as "large" for semi-auto patch risk (default **80**) |
|
|
100
141
|
| `OLLAMA_AGENT_INDEX_REBUILD` | Set to `1` to drop the cached Prism Ruby index before the next symbol search in this process |
|
|
101
142
|
| `OLLAMA_AGENT_RUBY_INDEX_MAX_FILES` | Max `.rb` files to parse per index build (default **5000**) |
|
|
102
143
|
| `OLLAMA_AGENT_RUBY_INDEX_MAX_FILE_BYTES` | Skip Ruby files larger than this many bytes (default **512000**) |
|
|
103
144
|
| `OLLAMA_AGENT_RUBY_INDEX_MAX_LINES` | Max result lines for `search_code` class/module/method modes (default **200**) |
|
|
104
145
|
| `OLLAMA_AGENT_RUBY_INDEX_MAX_CHARS` | Max characters of index output per search (default **60000**) |
|
|
105
146
|
| `OLLAMA_AGENT_MAX_READ_FILE_BYTES` | Max bytes for a **full** `read_file` (no line range); larger files return an error (default **2097152**, 2 MiB). Line-range reads stream and are not limited by this cap. |
|
|
147
|
+
| `OLLAMA_AGENT_RG_PATH` | Absolute path to `rg` for `search_code` text mode (optional; otherwise first `rg` on `PATH`) |
|
|
148
|
+
| `OLLAMA_AGENT_GREP_PATH` | Absolute path to `grep` fallback (optional; otherwise first `grep` on `PATH`) |
|
|
106
149
|
| `OLLAMA_AGENT_INDEX_REBUILD` | The Prism index is rebuilt when this env value **changes** (e.g. unset → `1`); it is **not** rebuilt on every tool call while it stays `1`. |
|
|
150
|
+
| `OLLAMA_AGENT_SKILLS` | `1`/`on`/`0`/`off` — include **bundled** prompt skills (default **on**). Same as `--no-skills` on the CLI when off. |
|
|
151
|
+
| `OLLAMA_AGENT_SKILLS_INCLUDE` | Comma-separated **manifest ids** to load (omit = all bundled). Example: `ruby_style,rubocop,code_review`. |
|
|
152
|
+
| `OLLAMA_AGENT_SKILLS_EXCLUDE` | Comma-separated ids to skip from the bundled set. |
|
|
153
|
+
| `OLLAMA_AGENT_SKILL_PATHS` | Extra `.md` files or directories, **colon-separated** (Unix `PATH` style). Directory entries load all `*.md` in sorted order. Merged with `--skill-paths`. |
|
|
154
|
+
| `OLLAMA_AGENT_EXTERNAL_SKILLS` | `1`/`0` — include content from `OLLAMA_AGENT_SKILL_PATHS` (default **on**). Set `0` to use bundled-only without unsetting paths. |
|
|
155
|
+
|
|
156
|
+
### Prompt skills (bundled + optional paths)
|
|
157
|
+
|
|
158
|
+
The system prompt is the **base agent instructions** (`AgentPrompt`) plus optional **Markdown** sections. Bundled files live under `lib/ollama_agent/prompt_skills/` and are listed in `manifest.yml`. Each file may use Cursor-style YAML frontmatter (`---` … `---`); the loader strips frontmatter before sending text to the model.
|
|
159
|
+
|
|
160
|
+
**Manifest ids** (in load order): `clean_ruby`, `ruby_style`, `rubocop`, `solid`, `solid_ruby`, `design_patterns`, `rspec`, `rails_style`, `rails_best_practices`, `code_review`, `ollama_agent_patterns`.
|
|
161
|
+
|
|
162
|
+
Bundled bodies were copied from Cursor `SKILL.md` files under `~/.cursor/skills/` (and `ollama_agent_patterns` from this repo’s `.cursor/skills/ollama-agent-patterns`). Re-copy when you update those skills upstream.
|
|
163
|
+
|
|
164
|
+
Many full skills can be **large**; use `OLLAMA_AGENT_SKILLS_INCLUDE` to trim for small-context models.
|
|
165
|
+
|
|
166
|
+
CLI flags (also available on `ask`, `self_review`, `improve`): `--no-skills`, `--skill-paths 'path1:path2/dir'`.
|
|
167
|
+
|
|
168
|
+
To run **`self_review` / `ask` against the installed gem’s source** (e.g. to hack on `ollama_agent` itself), pass an explicit root, for example `--root "$(bundle show ollama_agent)"` or a path to a git clone.
|
|
169
|
+
|
|
170
|
+
### Orchestrator (external CLI agents)
|
|
171
|
+
|
|
172
|
+
Use the **`orchestrate`** command (or **`OLLAMA_AGENT_ORCHESTRATOR=1`** with **`ask`**) to expose tools **`list_external_agents`** and **`delegate_to_agent`**. The Ollama model should gather context with **`read_file` / `search_code`**, list installed CLIs, then delegate a **short** task + context to an external agent (Claude Code, Gemini CLI, Codex, Cursor CLI, etc.). Definitions live in `lib/ollama_agent/external_agents/default_agents.yml`; override or extend via **`~/.config/ollama_agent/agents.yml`** or **`OLLAMA_AGENT_EXTERNAL_AGENTS_CONFIG`**.
|
|
173
|
+
|
|
174
|
+
- **`ollama_agent agents`** — print a table of configured agents and whether each binary is on `PATH`.
|
|
175
|
+
- **`ollama_agent doctor`** — alias for `agents`.
|
|
176
|
+
- **`delegate_to_agent`** runs a **fixed argv** (no shell) with **`cwd`** = project root; output is capped (**`OLLAMA_AGENT_DELEGATE_MAX_OUTPUT_BYTES`**, default 100k). Confirm each run unless **`-y`**.
|
|
177
|
+
- Delegation audit logs: set **`OLLAMA_AGENT_DELEGATE_LOG=1`** (or `OLLAMA_AGENT_DEBUG=1`) to emit a structured stderr line with agent id, argv, env keys (names only), exit code, and duration.
|
|
178
|
+
- Adjust **`argv` / `version_argv`** in YAML to match your real CLI (vendor flags differ). If a tool has no stable non-interactive mode, do not expose it in the registry.
|
|
179
|
+
- Tool contract version: **`OllamaAgent::ORCHESTRATOR_TOOLS_SCHEMA_VERSION`**.
|
|
180
|
+
|
|
181
|
+
### Library usage (Ruby)
|
|
182
|
+
|
|
183
|
+
Most of this README is **CLI-first** (commands and environment variables above). The same capabilities exist as **Ruby APIs**—the [Features](#features) list (file tools, `self_review` / `improve`, orchestrator, skills, etc.) is implemented under `lib/ollama_agent/`. For a **layer diagram** (agent → tools → hooks → session), see [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
|
|
184
|
+
|
|
185
|
+
**Coding agent — `Runner` (facade)** — Stable entry for apps: `OllamaAgent::Runner.build(root:, model:, stream:, session_id:, resume:, read_only:, orchestrator:, skills_enabled:, skill_paths:, audit:, max_tokens:, context_summarize:, stdin:, stdout:, ...)` then `#run(query)`. Optional **`stdin`** / **`stdout`** (default TTY) feed patch/write/delegate confirmations—use `StringIO` in tests or automation to avoid blocking on `$stdin.gets`. Exposes `#hooks` (`Streaming::Hooks`) for `:on_token`, `:on_thinking` (streamed reasoning when `stream: true` and the model supports it), `:on_tool_call`, `:on_tool_result`, `:on_complete`. Full keyword list: [`lib/ollama_agent/runner.rb`](lib/ollama_agent/runner.rb).
|
|
186
|
+
|
|
187
|
+
**Coding agent — `Agent` (direct)** — `OllamaAgent::Agent.new(client:, root:, ...)` when you inject an `Ollama::Client` (or test double), tweak options the CLI does not expose, or skip `Runner`.
|
|
188
|
+
|
|
189
|
+
**Custom tools (coding agent)** — `OllamaAgent::Tools.register("tool_name", schema: { ... }) { |args, root:, read_only:| ... }` merges extra function definitions into the chat tool list; handlers run in the same sandbox as built-in tools.
|
|
190
|
+
|
|
191
|
+
**Resilience and observability** — Default client path uses `Resilience::RetryMiddleware`. Structured step logging: enable **`audit: true`** on `Runner.build` or **`OLLAMA_AGENT_AUDIT=1`** (see Environment table). Context trimming: **`max_tokens`** / **`context_summarize`** on `Runner.build`.
|
|
192
|
+
|
|
193
|
+
**Sessions** — Pass **`session_id`** and optional **`resume: true`** on `Runner.build` to persist messages under `.ollama_agent/sessions/` (`Session::Store`).
|
|
194
|
+
|
|
195
|
+
**Self-improvement (sandbox)** — CLI commands **`improve`** / **`self_review --mode automated`** wrap `OllamaAgent::SelfImprovement` (sandbox copy, tests, optional merge). Use the CLI for the full flow; the module is available for advanced integration.
|
|
196
|
+
|
|
197
|
+
**`ToolRuntime` (alternate loop, optional)** — Not used by the CLI. For **non–file-edit** agents (e.g. another gem that defines its own tools), a small **JSON plan** loop: the model returns one object per step `{"tool":"name","args":{...}}`, `ToolRuntime::Registry` resolves it, `Executor` runs your `Tool` subclasses, `Memory` holds short-term history. Use a **swappable planner** (anything implementing `next_step(context:, memory:, registry:)`) such as `OllamaJsonPlanner` (`Ollama::Client#chat` + JSON extraction). **Step-by-step guide:** [docs/TOOL_RUNTIME.md](docs/TOOL_RUNTIME.md).
|
|
198
|
+
|
|
199
|
+
- **Termination:** a tool may return `{ "status" => "done" }` to stop. Unknown tool names → `OllamaAgent::ToolRuntime::InvalidPlanError`; too many steps → `MaxStepsExceeded`. **`Loop#run`** returns the **last tool result** (same value as the final `Executor#execute` return).
|
|
200
|
+
- **Runnable examples:** `spec/ollama_agent/tool_runtime/`.
|
|
201
|
+
|
|
202
|
+
**Model and server:** `OllamaJsonPlanner` uses the same default as the coding agent: `OLLAMA_AGENT_MODEL` if set, otherwise `Ollama::Config.new.model` (from ollama-client). The model must exist on whatever host you use. **Use the same client setup as the CLI:** `OllamaAgent::OllamaConnection.apply_env_to_config` copies `OLLAMA_BASE_URL` and `OLLAMA_API_KEY` into `Ollama::Config`. If you only run `Ollama::Client.new(config: Ollama::Config.new)` in `irb`, you stay on **localhost** while `OLLAMA_AGENT_MODEL` may still name a **cloud** model from the README cloud example → **404**. Either apply `apply_env_to_config` (below) or unset the cloud model / pass `model: "llama3.2"`.
|
|
203
|
+
|
|
204
|
+
```ruby
|
|
205
|
+
require "ollama_agent"
|
|
206
|
+
require "ollama_client"
|
|
207
|
+
|
|
208
|
+
class EchoTool < OllamaAgent::ToolRuntime::Tool
|
|
209
|
+
def name = "echo"
|
|
210
|
+
|
|
211
|
+
def description = "Echo args"
|
|
212
|
+
|
|
213
|
+
def schema = { "type" => "object", "properties" => { "msg" => { "type" => "string" } } }
|
|
214
|
+
|
|
215
|
+
def call(args)
|
|
216
|
+
return { "status" => "done", "echo" => args["msg"] } if args["msg"] == "bye"
|
|
217
|
+
|
|
218
|
+
{ "status" => "ok", "echo" => args["msg"] }
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
registry = OllamaAgent::ToolRuntime::Registry.new([EchoTool.new])
|
|
223
|
+
memory = OllamaAgent::ToolRuntime::Memory.new
|
|
224
|
+
config = Ollama::Config.new
|
|
225
|
+
OllamaAgent::OllamaConnection.apply_env_to_config(config)
|
|
226
|
+
client = Ollama::Client.new(config: config)
|
|
227
|
+
planner = OllamaAgent::ToolRuntime::OllamaJsonPlanner.new(client: client)
|
|
228
|
+
|
|
229
|
+
last = OllamaAgent::ToolRuntime::Loop.new(
|
|
230
|
+
planner: planner,
|
|
231
|
+
registry: registry,
|
|
232
|
+
executor: OllamaAgent::ToolRuntime::Executor.new,
|
|
233
|
+
memory: memory,
|
|
234
|
+
max_steps: 10
|
|
235
|
+
).run(context: "Say hello then echo bye to finish.")
|
|
236
|
+
# last => e.g. { "status" => "done", "echo" => "bye" }
|
|
237
|
+
```
|
|
107
238
|
|
|
108
239
|
## Troubleshooting
|
|
109
240
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
ollama_agent is a layered gem. Each layer is independently opt-in.
|
|
4
|
+
|
|
5
|
+
## Data Flow
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
CLI / Runner.run(query)
|
|
9
|
+
→ Session::Store.resume (if --resume)
|
|
10
|
+
→ Agent#run
|
|
11
|
+
→ Context::Manager.trim(messages)
|
|
12
|
+
→ OllamaConnection + Resilience::RetryMiddleware
|
|
13
|
+
→ Ollama::Client#chat
|
|
14
|
+
→ Streaming::Hooks.emit(:on_token, ...)
|
|
15
|
+
→ Tools::Registry / SandboxedTools.execute_tool(name, args)
|
|
16
|
+
→ Resilience::AuditLogger (via hooks)
|
|
17
|
+
→ Session::Store.save (after each turn)
|
|
18
|
+
→ Streaming::Hooks.emit(:on_complete, ...)
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Layers
|
|
22
|
+
|
|
23
|
+
| Layer | Files | Opt-in via |
|
|
24
|
+
|-------|-------|-----------|
|
|
25
|
+
| Core agent | `agent.rb`, `agent/*.rb`, `sandboxed_tools.rb`, `sandboxed_tools/*.rb` | Always on |
|
|
26
|
+
| Path sandbox | `path_sandbox.rb` | Always on for file/search/list tools |
|
|
27
|
+
| Env helpers | `env_config.rb` | Used by `Agent` and `SandboxedTools` for numeric ENV parsing |
|
|
28
|
+
| User prompts | `user_prompt.rb` | Injectable stdin/stdout (default TTY); `Runner.build(stdin:, stdout:)` |
|
|
29
|
+
| Tool Registry | `tools/registry.rb` | `OllamaAgent::Tools.register(...)` |
|
|
30
|
+
| Streaming | `streaming/hooks.rb`, `streaming/console_streamer.rb` | `--stream` / `OLLAMA_AGENT_STREAM=1` |
|
|
31
|
+
| Resilience | `resilience/retry_middleware.rb`, `resilience/audit_logger.rb` | On by default (retries); `--audit` for logging |
|
|
32
|
+
| Context Manager | `context/manager.rb` | `--max-tokens N` / `OLLAMA_AGENT_MAX_TOKENS` |
|
|
33
|
+
| Session | `session/store.rb` | `--session NAME` |
|
|
34
|
+
| Runner API | `runner.rb` | `require "ollama_agent"; OllamaAgent::Runner.build(...)` |
|
|
35
|
+
|
|
36
|
+
## Path sandbox (symlinks)
|
|
37
|
+
|
|
38
|
+
Tool paths are checked with `PathSandbox.allowed?`: after expanding relative to the project root, `File.realpath` must stay under `File.realpath(project_root)`. A symlink **inside** the repo that points **outside** is rejected, so the model cannot follow `link → /etc` style escapes. Paths that do not yet exist are allowed only when every existing parent directory resolves under the real root (see `nonexistent_path_allowed_under_root?` in `path_sandbox.rb`).
|
|
39
|
+
|
|
40
|
+
## ToolRuntime (parallel path)
|
|
41
|
+
|
|
42
|
+
The coding agent flow above is **not** the only entry point. `OllamaAgent::ToolRuntime` implements a separate **JSON plan → tool → memory** loop for custom `Tool` classes and injectable planners. It is **not** used by `exe/ollama_agent`. See [TOOL_RUNTIME.md](TOOL_RUNTIME.md).
|
data/docs/PERFORMANCE.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Performance notes
|
|
2
|
+
|
|
3
|
+
This document records **known costs** and **when to optimize**. No changes here are mandatory for typical CLI use.
|
|
4
|
+
|
|
5
|
+
## Text search (`search_code`, text mode)
|
|
6
|
+
|
|
7
|
+
Each call spawns **`rg`** or **`grep`** as a subprocess. For very chatty agents or huge trees, that overhead can dominate. Before changing behavior:
|
|
8
|
+
|
|
9
|
+
1. Measure wall time for your workload (local disk vs network FS matters).
|
|
10
|
+
2. Consider narrowing `directory`, or using Ruby index modes (`mode: method`, etc.) which avoid ripgrep for symbol queries.
|
|
11
|
+
|
|
12
|
+
## Patch application (`edit_file`)
|
|
13
|
+
|
|
14
|
+
The flow runs **`patch --dry-run`** before apply when validation passes, then **`patch`** again on success—two processes per confirmed edit. Caching or reusing dry-run output would save one spawn but adds complexity; only pursue if profiling shows it matters.
|
|
15
|
+
|
|
16
|
+
## Full-file reads
|
|
17
|
+
|
|
18
|
+
`read_file` without line range loads the whole file (subject to `OLLAMA_AGENT_MAX_READ_FILE_BYTES`). Prefer `start_line` / `end_line` for large logs.
|
|
19
|
+
|
|
20
|
+
## Context trimming (`Context::Manager#trim`)
|
|
21
|
+
|
|
22
|
+
Each trim pass keeps a parallel array of per-message token estimates so the sliding-window loop does not re-scan message bodies on every `over_budget?` check. If you change trimming strategy, profile long sessions with a tight `max_tokens` budget before adding further caching.
|
data/docs/SESSIONS.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Session Persistence
|
|
2
|
+
|
|
3
|
+
Sessions save conversation history to `.ollama_agent/sessions/` under the project root.
|
|
4
|
+
|
|
5
|
+
## CLI usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Start a named session
|
|
9
|
+
ollama_agent ask --session my-refactor "Refactor the CLI module"
|
|
10
|
+
|
|
11
|
+
# Resume it later (picks up exactly where it left off)
|
|
12
|
+
ollama_agent ask --session my-refactor --resume "Now update the specs too"
|
|
13
|
+
|
|
14
|
+
# Resume in interactive REPL
|
|
15
|
+
ollama_agent ask -i --session my-refactor --resume
|
|
16
|
+
|
|
17
|
+
# Resume most recent session (no name needed)
|
|
18
|
+
ollama_agent ask --resume
|
|
19
|
+
|
|
20
|
+
# List all sessions for the current project
|
|
21
|
+
ollama_agent sessions
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Library API
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
runner = OllamaAgent::Runner.build(
|
|
28
|
+
root: "/my/project",
|
|
29
|
+
session_id: "my-refactor",
|
|
30
|
+
resume: true
|
|
31
|
+
)
|
|
32
|
+
runner.run("Continue — now also add integration tests")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## File format
|
|
36
|
+
|
|
37
|
+
Sessions are NDJSON files — one JSON object per line, human-readable and `jq`-able:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
.ollama_agent/sessions/my-refactor.ndjson
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# View the last 5 messages
|
|
45
|
+
tail -5 .ollama_agent/sessions/my-refactor.ndjson | jq .
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Messages are appended after every agent turn — if the agent crashes mid-session, all completed turns are preserved.
|
data/docs/TOOLS.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Custom Tool Registration
|
|
2
|
+
|
|
3
|
+
Register a custom tool before calling `Runner.build`. The tool is automatically injected into the model's tool list.
|
|
4
|
+
|
|
5
|
+
```ruby
|
|
6
|
+
require "ollama_agent"
|
|
7
|
+
|
|
8
|
+
OllamaAgent::Tools.register(
|
|
9
|
+
:run_tests,
|
|
10
|
+
schema: {
|
|
11
|
+
description: "Run the RSpec test suite and return the output",
|
|
12
|
+
properties: {
|
|
13
|
+
suite: { type: "string", description: "Path to spec file or directory (default: spec/)" }
|
|
14
|
+
},
|
|
15
|
+
required: []
|
|
16
|
+
}
|
|
17
|
+
) do |args, root:, read_only:|
|
|
18
|
+
return "run_tests is disabled in read-only mode." if read_only
|
|
19
|
+
|
|
20
|
+
suite = args["suite"] || "spec/"
|
|
21
|
+
require "open3"
|
|
22
|
+
output, = Open3.capture2("bundle", "exec", "rspec", suite, chdir: root)
|
|
23
|
+
output
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
runner = OllamaAgent::Runner.build(root: "/my/project")
|
|
27
|
+
runner.run("Fix the failing tests, then run them to confirm they pass")
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Handler signature
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
OllamaAgent::Tools.register(:tool_name, schema: { ... }) do |args, root:, read_only:|
|
|
34
|
+
# args — Hash of tool arguments from the model
|
|
35
|
+
# root — String absolute path to the project root
|
|
36
|
+
# read_only — Boolean; return an error string if true and the tool writes files
|
|
37
|
+
"return value as String"
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Schema format
|
|
42
|
+
|
|
43
|
+
The `schema:` hash is the `function` body (without `name` — that comes from the first argument):
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
schema: {
|
|
47
|
+
description: "What this tool does",
|
|
48
|
+
properties: {
|
|
49
|
+
param_name: { type: "string", description: "what it is" }
|
|
50
|
+
},
|
|
51
|
+
required: ["param_name"]
|
|
52
|
+
}
|
|
53
|
+
```
|