research-git 0.0.2__tar.gz → 0.0.4__tar.gz

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 (91) hide show
  1. {research_git-0.0.2/src/research_git.egg-info → research_git-0.0.4}/PKG-INFO +21 -12
  2. {research_git-0.0.2 → research_git-0.0.4}/README.md +20 -11
  3. {research_git-0.0.2 → research_git-0.0.4}/pyproject.toml +1 -1
  4. {research_git-0.0.2 → research_git-0.0.4/src/research_git.egg-info}/PKG-INFO +21 -12
  5. research_git-0.0.4/src/rgit/__init__.py +1 -0
  6. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/_plugin/.claude-plugin/plugin.json +1 -1
  7. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/_plugin/skills/rgit-capture/SKILL.md +4 -1
  8. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/agent_guidance.py +14 -6
  9. research_git-0.0.4/src/rgit/astmap.py +142 -0
  10. research_git-0.0.4/src/rgit/cli.py +954 -0
  11. research_git-0.0.4/src/rgit/curation.py +101 -0
  12. research_git-0.0.4/src/rgit/gitutil.py +646 -0
  13. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/hooks.py +17 -4
  14. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/installer.py +29 -2
  15. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/provenance.py +21 -5
  16. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/runner.py +37 -11
  17. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/segmenter.py +58 -10
  18. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/store/db.py +4 -3
  19. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/store/models.py +1 -0
  20. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/store/store.py +13 -6
  21. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/toggles.py +8 -4
  22. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/watch.py +2 -2
  23. {research_git-0.0.2 → research_git-0.0.4}/tests/test_agent_guidance.py +18 -1
  24. research_git-0.0.4/tests/test_astmap.py +193 -0
  25. research_git-0.0.4/tests/test_cli.py +1247 -0
  26. {research_git-0.0.2 → research_git-0.0.4}/tests/test_compare.py +18 -0
  27. {research_git-0.0.2 → research_git-0.0.4}/tests/test_curation.py +82 -0
  28. {research_git-0.0.2 → research_git-0.0.4}/tests/test_e2e.py +41 -0
  29. research_git-0.0.4/tests/test_gitutil.py +599 -0
  30. {research_git-0.0.2 → research_git-0.0.4}/tests/test_hooks.py +40 -0
  31. {research_git-0.0.2 → research_git-0.0.4}/tests/test_installer.py +47 -0
  32. {research_git-0.0.2 → research_git-0.0.4}/tests/test_runner.py +28 -4
  33. research_git-0.0.4/tests/test_segmenter.py +256 -0
  34. {research_git-0.0.2 → research_git-0.0.4}/tests/test_store.py +51 -0
  35. {research_git-0.0.2 → research_git-0.0.4}/tests/test_toggles.py +44 -0
  36. research_git-0.0.4/tests/test_watch.py +76 -0
  37. research_git-0.0.2/src/rgit/__init__.py +0 -1
  38. research_git-0.0.2/src/rgit/astmap.py +0 -106
  39. research_git-0.0.2/src/rgit/cli.py +0 -417
  40. research_git-0.0.2/src/rgit/curation.py +0 -48
  41. research_git-0.0.2/src/rgit/gitutil.py +0 -112
  42. research_git-0.0.2/tests/test_astmap.py +0 -57
  43. research_git-0.0.2/tests/test_cli.py +0 -415
  44. research_git-0.0.2/tests/test_gitutil.py +0 -96
  45. research_git-0.0.2/tests/test_segmenter.py +0 -71
  46. research_git-0.0.2/tests/test_watch.py +0 -37
  47. {research_git-0.0.2 → research_git-0.0.4}/LICENSE +0 -0
  48. {research_git-0.0.2 → research_git-0.0.4}/setup.cfg +0 -0
  49. {research_git-0.0.2 → research_git-0.0.4}/src/research_git.egg-info/SOURCES.txt +0 -0
  50. {research_git-0.0.2 → research_git-0.0.4}/src/research_git.egg-info/dependency_links.txt +0 -0
  51. {research_git-0.0.2 → research_git-0.0.4}/src/research_git.egg-info/entry_points.txt +0 -0
  52. {research_git-0.0.2 → research_git-0.0.4}/src/research_git.egg-info/requires.txt +0 -0
  53. {research_git-0.0.2 → research_git-0.0.4}/src/research_git.egg-info/top_level.txt +0 -0
  54. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/_plugin/.claude-plugin/marketplace.json +0 -0
  55. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/_plugin/agents/capsule-regenerator.md +0 -0
  56. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/_plugin/agents/capsule-segmenter.md +0 -0
  57. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/_plugin/agents/edge-judge.md +0 -0
  58. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/_plugin/skills/rgit-recall/SKILL.md +0 -0
  59. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/ablation.py +0 -0
  60. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/agent_platforms.py +0 -0
  61. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/compare.py +0 -0
  62. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/compose.py +0 -0
  63. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/edges.py +0 -0
  64. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/graphview.py +0 -0
  65. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/mcp_server.py +0 -0
  66. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/metricdir.py +0 -0
  67. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/metrics.py +0 -0
  68. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/ranking.py +0 -0
  69. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/recall.py +0 -0
  70. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/store/__init__.py +0 -0
  71. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/store/ids.py +0 -0
  72. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/store/objects.py +0 -0
  73. {research_git-0.0.2 → research_git-0.0.4}/src/rgit/tables.py +0 -0
  74. {research_git-0.0.2 → research_git-0.0.4}/tests/test_ablation.py +0 -0
  75. {research_git-0.0.2 → research_git-0.0.4}/tests/test_active_edges.py +0 -0
  76. {research_git-0.0.2 → research_git-0.0.4}/tests/test_compose.py +0 -0
  77. {research_git-0.0.2 → research_git-0.0.4}/tests/test_db.py +0 -0
  78. {research_git-0.0.2 → research_git-0.0.4}/tests/test_edges.py +0 -0
  79. {research_git-0.0.2 → research_git-0.0.4}/tests/test_graphview.py +0 -0
  80. {research_git-0.0.2 → research_git-0.0.4}/tests/test_guidance_coupling.py +0 -0
  81. {research_git-0.0.2 → research_git-0.0.4}/tests/test_mcp_server.py +0 -0
  82. {research_git-0.0.2 → research_git-0.0.4}/tests/test_metricdir.py +0 -0
  83. {research_git-0.0.2 → research_git-0.0.4}/tests/test_metricdir_store.py +0 -0
  84. {research_git-0.0.2 → research_git-0.0.4}/tests/test_metrics.py +0 -0
  85. {research_git-0.0.2 → research_git-0.0.4}/tests/test_models.py +0 -0
  86. {research_git-0.0.2 → research_git-0.0.4}/tests/test_objects.py +0 -0
  87. {research_git-0.0.2 → research_git-0.0.4}/tests/test_provenance.py +0 -0
  88. {research_git-0.0.2 → research_git-0.0.4}/tests/test_ranking.py +0 -0
  89. {research_git-0.0.2 → research_git-0.0.4}/tests/test_recall.py +0 -0
  90. {research_git-0.0.2 → research_git-0.0.4}/tests/test_review_fixes.py +0 -0
  91. {research_git-0.0.2 → research_git-0.0.4}/tests/test_tables.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: research-git
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: A memory system that captures code ideas as semantic capsules you can regenerate onto today's codebase
5
5
  Author: Stepzero Lab
6
6
  License-Expression: MIT
@@ -43,15 +43,13 @@ Dynamic: license-file
43
43
  <strong>Git recovers history. It can't recover an entangled idea onto today's code.</strong>
44
44
  </p>
45
45
 
46
- ---
47
-
48
- **You're deep into building an agent. You've tried twenty things — a restructured system prompt, splitting one overloaded tool into three, a re-ranking step before retrieval, a scratchpad for intermediate reasoning, two different few-shot sets — half of them commented in and out, all tangled together in one working tree. Now you want *one* of those ideas back. Not the stale commit it lived in. The idea itself, re-applied to the agent you have today.**
49
-
50
- Git can't do that. Its unit is a *tree snapshot*, not an *idea*. `git checkout` drags back everything from that moment and throws away all the infrastructure you've built since. You can't pull one feature forward without rolling back the rest.
46
+ <p align="center">
47
+ <img src="assets/hero.png" alt="Capture a code idea as a clean semantic unit — regenerate it onto today's codebase." width="800" />
48
+ </p>
51
49
 
52
- research-git makes the **idea** the unit. It captures each change as a self-contained **Feature Capsule**, stores it in a graph, and — when you want it back — *regenerates* it onto your current code instead of pasting a stale patch. The capsule is a specification of intent; the code is always rebuilt against today's reality.
50
+ research-git captures experiments as Feature Capsules, then regenerates the one you need onto your current agent, using your existing Coding Plan subscription, no pay-per-use API.
53
51
 
54
- The intelligent steps (segmenting a diff into capsules, regenerating one onto changed code) run as **subagents on your existing Claude subscription** there is **no pay-per-use API** anywhere.
52
+ > **Think of it as Git for agentic coding experiments: not just recovering old code, but bringing old ideas back into today’s code.**
55
53
 
56
54
  ---
57
55
 
@@ -104,9 +102,9 @@ Five steps: install → init → run → capture → recall.
104
102
  pip install research-git # or, from a clone: pip install -e .
105
103
 
106
104
  # wire the plugin (agents + skills) and the MCP server into your client
107
- rgit install claude-code # Claude Code (via the official `claude` CLI)
108
- rgit install codex # Codex / Gemini / opencode: symlinks the skills into ~/.agents/skills/
109
- rgit install --list # all platforms; add --dry-run to preview, --uninstall to remove
105
+ rgit install # auto-detects every agent client on this machine and wires them all
106
+ rgit install claude-code # or pick one explicitly (claude-code / codex / gemini / opencode / generic)
107
+ rgit install --list # list platforms; --uninstall to remove
110
108
  ```
111
109
 
112
110
  `codex`, `gemini`, and `opencode` share the `~/.agents/skills/` convention — the installer symlinks each skill there and prints the one-line MCP server entry to drop into that client's config. It also writes a managed research-git guidance block into the client's global guidance file when the platform has one (`~/.codex/AGENTS.md`, `~/.claude/CLAUDE.md`, or `~/.gemini/GEMINI.md`). On an interactive terminal you're asked how proactive capture should be — `default`, `manual-only`, or `none`; pass `--guidance <mode>` to choose non-interactively. Start a new agent session after install so the guidance is loaded. Prefer the manual route on Claude Code? `/plugin marketplace add StepzeroLab/research-git` then `/plugin install research-git@research-git`.
@@ -118,6 +116,14 @@ cd your-project
118
116
  rgit init # creates .rgit/ (the store) at the git root
119
117
  ```
120
118
 
119
+ **Optional — capture on every commit.** `rgit install <platform>` wires the agent side only; it deliberately does **not** touch your git hooks. If you also want every `git commit` to stage its own diff as a pending proposal automatically, opt in with:
120
+
121
+ ```bash
122
+ rgit install-hooks # adds a post-commit hook (never clobbers an existing one)
123
+ ```
124
+
125
+ Good fit: solo research repos where you want nothing to slip through, even when you forget to capture. Skip it if the repo already has its own post-commit hook (the installer refuses to touch foreign hooks, so nothing breaks — it just won't install), if your team prefers deliberate manual capture, or in CI/shared clones where commit-time side effects are unwelcome. Without hooks you lose nothing: bare `rgit capture` takes the last commit when the tree is clean, `rgit capture A..B` a whole span, and `rgit review` is the gate either way — hooks only stage proposals, they never approve anything. Remove with `rgit install-hooks --uninstall`.
126
+
121
127
  ### 3. Run a variation and capture the idea
122
128
 
123
129
  Launch your work through `rgit run` — it executes your command, freezes a reproducible artifact, records the run + any metrics, and stages what changed:
@@ -134,6 +140,8 @@ rgit review # list proposals
134
140
  rgit review --approve <proposal_id> --name rerank-retrieval
135
141
  ```
136
142
 
143
+ Committed before capturing? Just run `rgit capture` — on a clean tree it captures the last commit (and says which one); `rgit capture main..HEAD` takes a whole span. (With the optional post-commit hook installed, every commit stages itself automatically.)
144
+
137
145
  ### 4. Bring an idea back onto today's code
138
146
 
139
147
  Weeks later, after the agent has moved on:
@@ -198,7 +206,8 @@ The five-step loop above is the core. These show up as your store grows — run
198
206
  | Command | What it does |
199
207
  |---------|--------------|
200
208
  | `rgit watch` | free, deterministic background capture — stages raw material as you edit, so fleeting in-between states aren't lost |
201
- | `rgit install-hooks` | stage on every commit via a post-commit hook (won't touch an existing hook) |
209
+ | `rgit capture [REV \| A..B]` | bare: auto-picks the working tree or, when clean, the last commit; pass a commit or an A..B range for precise control |
210
+ | `rgit install-hooks` | opt-in: stage every commit's diff via a post-commit hook (not installed by `rgit install`; won't touch an existing hook) — see step 2 above |
202
211
  | `rgit run --from <capsule>` | run a recalled variant and link the new run as a `variant_of` the original |
203
212
  | `rgit compare <query>` | which variant won: ranked table, Δ vs baseline, ★ winner |
204
213
  | `rgit provenance <run_id>` | per-feature clean (capsule) vs agent-adapted (frozen) diff for a run |
@@ -17,15 +17,13 @@
17
17
  <strong>Git recovers history. It can't recover an entangled idea onto today's code.</strong>
18
18
  </p>
19
19
 
20
- ---
21
-
22
- **You're deep into building an agent. You've tried twenty things — a restructured system prompt, splitting one overloaded tool into three, a re-ranking step before retrieval, a scratchpad for intermediate reasoning, two different few-shot sets — half of them commented in and out, all tangled together in one working tree. Now you want *one* of those ideas back. Not the stale commit it lived in. The idea itself, re-applied to the agent you have today.**
23
-
24
- Git can't do that. Its unit is a *tree snapshot*, not an *idea*. `git checkout` drags back everything from that moment and throws away all the infrastructure you've built since. You can't pull one feature forward without rolling back the rest.
20
+ <p align="center">
21
+ <img src="assets/hero.png" alt="Capture a code idea as a clean semantic unit — regenerate it onto today's codebase." width="800" />
22
+ </p>
25
23
 
26
- research-git makes the **idea** the unit. It captures each change as a self-contained **Feature Capsule**, stores it in a graph, and — when you want it back — *regenerates* it onto your current code instead of pasting a stale patch. The capsule is a specification of intent; the code is always rebuilt against today's reality.
24
+ research-git captures experiments as Feature Capsules, then regenerates the one you need onto your current agent, using your existing Coding Plan subscription, no pay-per-use API.
27
25
 
28
- The intelligent steps (segmenting a diff into capsules, regenerating one onto changed code) run as **subagents on your existing Claude subscription** there is **no pay-per-use API** anywhere.
26
+ > **Think of it as Git for agentic coding experiments: not just recovering old code, but bringing old ideas back into today’s code.**
29
27
 
30
28
  ---
31
29
 
@@ -78,9 +76,9 @@ Five steps: install → init → run → capture → recall.
78
76
  pip install research-git # or, from a clone: pip install -e .
79
77
 
80
78
  # wire the plugin (agents + skills) and the MCP server into your client
81
- rgit install claude-code # Claude Code (via the official `claude` CLI)
82
- rgit install codex # Codex / Gemini / opencode: symlinks the skills into ~/.agents/skills/
83
- rgit install --list # all platforms; add --dry-run to preview, --uninstall to remove
79
+ rgit install # auto-detects every agent client on this machine and wires them all
80
+ rgit install claude-code # or pick one explicitly (claude-code / codex / gemini / opencode / generic)
81
+ rgit install --list # list platforms; --uninstall to remove
84
82
  ```
85
83
 
86
84
  `codex`, `gemini`, and `opencode` share the `~/.agents/skills/` convention — the installer symlinks each skill there and prints the one-line MCP server entry to drop into that client's config. It also writes a managed research-git guidance block into the client's global guidance file when the platform has one (`~/.codex/AGENTS.md`, `~/.claude/CLAUDE.md`, or `~/.gemini/GEMINI.md`). On an interactive terminal you're asked how proactive capture should be — `default`, `manual-only`, or `none`; pass `--guidance <mode>` to choose non-interactively. Start a new agent session after install so the guidance is loaded. Prefer the manual route on Claude Code? `/plugin marketplace add StepzeroLab/research-git` then `/plugin install research-git@research-git`.
@@ -92,6 +90,14 @@ cd your-project
92
90
  rgit init # creates .rgit/ (the store) at the git root
93
91
  ```
94
92
 
93
+ **Optional — capture on every commit.** `rgit install <platform>` wires the agent side only; it deliberately does **not** touch your git hooks. If you also want every `git commit` to stage its own diff as a pending proposal automatically, opt in with:
94
+
95
+ ```bash
96
+ rgit install-hooks # adds a post-commit hook (never clobbers an existing one)
97
+ ```
98
+
99
+ Good fit: solo research repos where you want nothing to slip through, even when you forget to capture. Skip it if the repo already has its own post-commit hook (the installer refuses to touch foreign hooks, so nothing breaks — it just won't install), if your team prefers deliberate manual capture, or in CI/shared clones where commit-time side effects are unwelcome. Without hooks you lose nothing: bare `rgit capture` takes the last commit when the tree is clean, `rgit capture A..B` a whole span, and `rgit review` is the gate either way — hooks only stage proposals, they never approve anything. Remove with `rgit install-hooks --uninstall`.
100
+
95
101
  ### 3. Run a variation and capture the idea
96
102
 
97
103
  Launch your work through `rgit run` — it executes your command, freezes a reproducible artifact, records the run + any metrics, and stages what changed:
@@ -108,6 +114,8 @@ rgit review # list proposals
108
114
  rgit review --approve <proposal_id> --name rerank-retrieval
109
115
  ```
110
116
 
117
+ Committed before capturing? Just run `rgit capture` — on a clean tree it captures the last commit (and says which one); `rgit capture main..HEAD` takes a whole span. (With the optional post-commit hook installed, every commit stages itself automatically.)
118
+
111
119
  ### 4. Bring an idea back onto today's code
112
120
 
113
121
  Weeks later, after the agent has moved on:
@@ -172,7 +180,8 @@ The five-step loop above is the core. These show up as your store grows — run
172
180
  | Command | What it does |
173
181
  |---------|--------------|
174
182
  | `rgit watch` | free, deterministic background capture — stages raw material as you edit, so fleeting in-between states aren't lost |
175
- | `rgit install-hooks` | stage on every commit via a post-commit hook (won't touch an existing hook) |
183
+ | `rgit capture [REV \| A..B]` | bare: auto-picks the working tree or, when clean, the last commit; pass a commit or an A..B range for precise control |
184
+ | `rgit install-hooks` | opt-in: stage every commit's diff via a post-commit hook (not installed by `rgit install`; won't touch an existing hook) — see step 2 above |
176
185
  | `rgit run --from <capsule>` | run a recalled variant and link the new run as a `variant_of` the original |
177
186
  | `rgit compare <query>` | which variant won: ranked table, Δ vs baseline, ★ winner |
178
187
  | `rgit provenance <run_id>` | per-feature clean (capsule) vs agent-adapted (frozen) diff for a run |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "research-git"
3
- version = "0.0.2"
3
+ version = "0.0.4"
4
4
  description = "A memory system that captures code ideas as semantic capsules you can regenerate onto today's codebase"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: research-git
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: A memory system that captures code ideas as semantic capsules you can regenerate onto today's codebase
5
5
  Author: Stepzero Lab
6
6
  License-Expression: MIT
@@ -43,15 +43,13 @@ Dynamic: license-file
43
43
  <strong>Git recovers history. It can't recover an entangled idea onto today's code.</strong>
44
44
  </p>
45
45
 
46
- ---
47
-
48
- **You're deep into building an agent. You've tried twenty things — a restructured system prompt, splitting one overloaded tool into three, a re-ranking step before retrieval, a scratchpad for intermediate reasoning, two different few-shot sets — half of them commented in and out, all tangled together in one working tree. Now you want *one* of those ideas back. Not the stale commit it lived in. The idea itself, re-applied to the agent you have today.**
49
-
50
- Git can't do that. Its unit is a *tree snapshot*, not an *idea*. `git checkout` drags back everything from that moment and throws away all the infrastructure you've built since. You can't pull one feature forward without rolling back the rest.
46
+ <p align="center">
47
+ <img src="assets/hero.png" alt="Capture a code idea as a clean semantic unit — regenerate it onto today's codebase." width="800" />
48
+ </p>
51
49
 
52
- research-git makes the **idea** the unit. It captures each change as a self-contained **Feature Capsule**, stores it in a graph, and — when you want it back — *regenerates* it onto your current code instead of pasting a stale patch. The capsule is a specification of intent; the code is always rebuilt against today's reality.
50
+ research-git captures experiments as Feature Capsules, then regenerates the one you need onto your current agent, using your existing Coding Plan subscription, no pay-per-use API.
53
51
 
54
- The intelligent steps (segmenting a diff into capsules, regenerating one onto changed code) run as **subagents on your existing Claude subscription** there is **no pay-per-use API** anywhere.
52
+ > **Think of it as Git for agentic coding experiments: not just recovering old code, but bringing old ideas back into today’s code.**
55
53
 
56
54
  ---
57
55
 
@@ -104,9 +102,9 @@ Five steps: install → init → run → capture → recall.
104
102
  pip install research-git # or, from a clone: pip install -e .
105
103
 
106
104
  # wire the plugin (agents + skills) and the MCP server into your client
107
- rgit install claude-code # Claude Code (via the official `claude` CLI)
108
- rgit install codex # Codex / Gemini / opencode: symlinks the skills into ~/.agents/skills/
109
- rgit install --list # all platforms; add --dry-run to preview, --uninstall to remove
105
+ rgit install # auto-detects every agent client on this machine and wires them all
106
+ rgit install claude-code # or pick one explicitly (claude-code / codex / gemini / opencode / generic)
107
+ rgit install --list # list platforms; --uninstall to remove
110
108
  ```
111
109
 
112
110
  `codex`, `gemini`, and `opencode` share the `~/.agents/skills/` convention — the installer symlinks each skill there and prints the one-line MCP server entry to drop into that client's config. It also writes a managed research-git guidance block into the client's global guidance file when the platform has one (`~/.codex/AGENTS.md`, `~/.claude/CLAUDE.md`, or `~/.gemini/GEMINI.md`). On an interactive terminal you're asked how proactive capture should be — `default`, `manual-only`, or `none`; pass `--guidance <mode>` to choose non-interactively. Start a new agent session after install so the guidance is loaded. Prefer the manual route on Claude Code? `/plugin marketplace add StepzeroLab/research-git` then `/plugin install research-git@research-git`.
@@ -118,6 +116,14 @@ cd your-project
118
116
  rgit init # creates .rgit/ (the store) at the git root
119
117
  ```
120
118
 
119
+ **Optional — capture on every commit.** `rgit install <platform>` wires the agent side only; it deliberately does **not** touch your git hooks. If you also want every `git commit` to stage its own diff as a pending proposal automatically, opt in with:
120
+
121
+ ```bash
122
+ rgit install-hooks # adds a post-commit hook (never clobbers an existing one)
123
+ ```
124
+
125
+ Good fit: solo research repos where you want nothing to slip through, even when you forget to capture. Skip it if the repo already has its own post-commit hook (the installer refuses to touch foreign hooks, so nothing breaks — it just won't install), if your team prefers deliberate manual capture, or in CI/shared clones where commit-time side effects are unwelcome. Without hooks you lose nothing: bare `rgit capture` takes the last commit when the tree is clean, `rgit capture A..B` a whole span, and `rgit review` is the gate either way — hooks only stage proposals, they never approve anything. Remove with `rgit install-hooks --uninstall`.
126
+
121
127
  ### 3. Run a variation and capture the idea
122
128
 
123
129
  Launch your work through `rgit run` — it executes your command, freezes a reproducible artifact, records the run + any metrics, and stages what changed:
@@ -134,6 +140,8 @@ rgit review # list proposals
134
140
  rgit review --approve <proposal_id> --name rerank-retrieval
135
141
  ```
136
142
 
143
+ Committed before capturing? Just run `rgit capture` — on a clean tree it captures the last commit (and says which one); `rgit capture main..HEAD` takes a whole span. (With the optional post-commit hook installed, every commit stages itself automatically.)
144
+
137
145
  ### 4. Bring an idea back onto today's code
138
146
 
139
147
  Weeks later, after the agent has moved on:
@@ -198,7 +206,8 @@ The five-step loop above is the core. These show up as your store grows — run
198
206
  | Command | What it does |
199
207
  |---------|--------------|
200
208
  | `rgit watch` | free, deterministic background capture — stages raw material as you edit, so fleeting in-between states aren't lost |
201
- | `rgit install-hooks` | stage on every commit via a post-commit hook (won't touch an existing hook) |
209
+ | `rgit capture [REV \| A..B]` | bare: auto-picks the working tree or, when clean, the last commit; pass a commit or an A..B range for precise control |
210
+ | `rgit install-hooks` | opt-in: stage every commit's diff via a post-commit hook (not installed by `rgit install`; won't touch an existing hook) — see step 2 above |
202
211
  | `rgit run --from <capsule>` | run a recalled variant and link the new run as a `variant_of` the original |
203
212
  | `rgit compare <query>` | which variant won: ranked table, Δ vs baseline, ★ winner |
204
213
  | `rgit provenance <run_id>` | per-feature clean (capsule) vs agent-adapted (frozen) diff for a run |
@@ -0,0 +1 @@
1
+ __version__ = "0.0.3"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "research-git",
3
3
  "description": "A memory system for the code you're exploring: capture each idea as a semantic Feature Capsule, recall it, and regenerate it onto today's codebase. Segmentation/regeneration run on natively-dispatched subagents (your subscription) — no pay-per-use API. MCP serves the graph read-only for sharing.",
4
- "version": "0.0.2",
4
+ "version": "0.0.3",
5
5
  "author": { "name": "Stepzero Lab" },
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -26,9 +26,12 @@ Every `agents/<name>.md` reference below (`agents/capsule-segmenter.md`, `agents
26
26
  If the user just made changes and there is no open proposal yet, create one:
27
27
 
28
28
  ```
29
- rgit capture --trigger manual
29
+ rgit capture # picks for you: uncommitted work, or the last commit when the tree is clean
30
+ rgit capture main..HEAD # a specific span of commits (any A..B range)
30
31
  ```
31
32
 
33
+ The bare form auto-picks its source, so it works the same before or after a `git commit`; repeated captures of the same diff dedup into the existing proposal. If the repo has the post-commit hook installed (`rgit install-hooks`), each commit is captured automatically; don't capture the same commit twice.
34
+
32
35
  This runs the libcst symbol mapping + the free heuristic, producing one or more open proposals with a raw diff and a crude candidate. Proposals also appear automatically from `rgit run`, the post-commit hook, and the `rgit watch` daemon.
33
36
 
34
37
  ### 2. Read the pending captures
@@ -45,14 +45,22 @@ def render_global_block(mode: str = "default") -> str:
45
45
  "- Do not write repo preferences for one-off session instructions.\n"
46
46
  "\n"
47
47
  "Use:\n"
48
- "- After meaningful code/research changes, consider "
49
- "`rgit capture --trigger manual` and the `rgit-capture` skill.\n"
48
+ "- After meaningful code/research changes, run `rgit capture` — it "
49
+ "captures uncommitted work, or the last commit when the tree is "
50
+ "clean, so committing first is fine. Then use the `rgit-capture` "
51
+ "skill to segment.\n"
52
+ "- For a specific span of commits: `rgit capture main..HEAD` (any "
53
+ "A..B range works).\n"
54
+ "- If a post-commit hook is installed (`rgit install-hooks`), commits "
55
+ "are captured automatically; do not capture the same commit again.\n"
56
+ "- Skip mechanical formatting, dependency churn, generated files, or "
57
+ "changes with no reusable research/code idea.\n"
50
58
  "- For recall/resurrection requests, use the `rgit-recall` skill.\n"
51
59
  "- If `.rgit/` is missing in a git repo: when operating autonomously "
52
- "(no human to ask), bootstrap the store with `rgit capture --init "
53
- "--trigger manual` (store only — never install hooks unless asked); in "
54
- "an interactive session, tell the user to run `rgit init` rather than "
55
- "initializing silently.\n"
60
+ "(no human to ask), bootstrap the store with `rgit capture --init` "
61
+ "(store only — never install hooks unless asked); in an interactive "
62
+ "session, tell the user to run `rgit init` rather than initializing "
63
+ "silently.\n"
56
64
  "- In final feedback, mention any capsules created, approved, applied, "
57
65
  "or skipped, plus important graph relations.\n"
58
66
  f"{END}\n"
@@ -0,0 +1,142 @@
1
+ from __future__ import annotations
2
+ import re
3
+ from pathlib import Path
4
+ from typing import Callable, Optional
5
+
6
+ import libcst as cst
7
+ from libcst.metadata import MetadataWrapper, PositionProvider
8
+
9
+ from .gitutil import parse_git_diff_header, read_worktree_python
10
+
11
+ _HUNK = re.compile(r"^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@", re.M)
12
+
13
+
14
+ def _changed_line_ranges(diff: str) -> dict[str, list[tuple[int, int]]]:
15
+ """file -> list of (start, end) ranges of *actually changed* new-side lines.
16
+
17
+ Only added lines — plus the new-side anchor of a deletion — count; unified-diff
18
+ context lines are walked to advance the new-side line counter but never recorded.
19
+ Using the whole hunk span (header length) would treat untouched neighbouring
20
+ symbols that merely appear as context as changed (issue #10).
21
+ """
22
+ result: dict[str, list[tuple[int, int]]] = {}
23
+ current: Optional[str] = None
24
+ in_hunk = False
25
+ new_line = 0
26
+ hunk_start = 0
27
+ for line in diff.splitlines():
28
+ matched, path = parse_git_diff_header(line, "+++")
29
+ if matched:
30
+ current = path
31
+ in_hunk = False
32
+ if current is not None:
33
+ result.setdefault(current, [])
34
+ continue
35
+ h = _HUNK.match(line)
36
+ if h:
37
+ new_line = hunk_start = int(h.group(1))
38
+ in_hunk = current is not None
39
+ continue
40
+ if not in_hunk:
41
+ continue
42
+ if not line: # empty context line
43
+ new_line += 1
44
+ continue
45
+ tag = line[0]
46
+ if tag == "+": # added line -> genuinely changed
47
+ result[current].append((new_line, new_line))
48
+ new_line += 1
49
+ elif tag == "-": # deletion -> anchor to the surviving line
50
+ anchor = new_line - 1 if new_line > hunk_start else new_line
51
+ result[current].append((anchor, anchor))
52
+ elif tag == " ": # context -> advance, do not record
53
+ new_line += 1
54
+ elif tag == "\\": # ""
55
+ continue
56
+ else: # non-body line ends the hunk (e.g. next `diff --git`)
57
+ in_hunk = False
58
+ return result
59
+
60
+
61
+ class _SymbolFinder(cst.CSTVisitor):
62
+ METADATA_DEPENDENCIES = (PositionProvider,)
63
+
64
+ def __init__(self, ranges: list[tuple[int, int]]):
65
+ self.ranges = ranges
66
+ self.found: set[str] = set()
67
+
68
+ def _overlaps(self, node) -> bool:
69
+ pos = self.get_metadata(PositionProvider, node)
70
+ for s, e in self.ranges:
71
+ if pos.start.line <= e and pos.end.line >= s:
72
+ return True
73
+ return False
74
+
75
+ def visit_FunctionDef(self, node: cst.FunctionDef) -> None:
76
+ if self._overlaps(node):
77
+ self.found.add(node.name.value)
78
+
79
+ def visit_ClassDef(self, node: cst.ClassDef) -> None:
80
+ if self._overlaps(node):
81
+ self.found.add(node.name.value)
82
+
83
+
84
+ def changed_symbols(diff: str, repo: Path,
85
+ read_source: Optional[Callable[[str], Optional[str]]] = None,
86
+ ) -> list[dict]:
87
+ """[{file, symbol}] for each top-level def/class overlapping a diff hunk.
88
+
89
+ `read_source(file)` supplies the new-side source text (None skips the
90
+ file); the default reads the working tree. Committed-diff capture passes a
91
+ reader pinned to the captured commit, so symbol mapping cannot drift when
92
+ the worktree has moved past — or never matched — the diff being segmented
93
+ (e.g. a partially staged commit).
94
+ """
95
+ if read_source is None:
96
+ read_source = lambda file: read_worktree_python(repo, file) # noqa: E731
97
+ out: list[dict] = []
98
+ for file, ranges in _changed_line_ranges(diff).items():
99
+ if not ranges:
100
+ continue
101
+ text = read_source(file)
102
+ if text is None:
103
+ continue
104
+ try:
105
+ wrapper = MetadataWrapper(cst.parse_module(text))
106
+ except (cst.ParserSyntaxError, UnicodeDecodeError):
107
+ continue
108
+ finder = _SymbolFinder(ranges)
109
+ wrapper.visit(finder)
110
+ for sym in sorted(finder.found):
111
+ out.append({"file": file, "symbol": sym})
112
+ return out
113
+
114
+
115
+ def read_symbol_source(repo: Path, file: str, symbol: str) -> Optional[str]:
116
+ """Current source text of a top-level def/class, or None if absent."""
117
+ text = read_worktree_python(repo, file)
118
+ if text is None:
119
+ return None
120
+ try:
121
+ module = cst.parse_module(text)
122
+ except (cst.ParserSyntaxError, UnicodeDecodeError):
123
+ return None
124
+ for stmt in module.body:
125
+ if isinstance(stmt, (cst.FunctionDef, cst.ClassDef)) and stmt.name.value == symbol:
126
+ return module.code_for_node(stmt)
127
+ return None
128
+
129
+
130
+ def symbol_at_line(repo: Path, file: str, line: int) -> Optional[str]:
131
+ """Name of the top-level def/class enclosing `line` (1-based), or None."""
132
+ text = read_worktree_python(repo, file)
133
+ if text is None:
134
+ return None
135
+ try:
136
+ wrapper = MetadataWrapper(cst.parse_module(text))
137
+ except (cst.ParserSyntaxError, UnicodeDecodeError):
138
+ return None
139
+ finder = _SymbolFinder([(line, line)])
140
+ wrapper.visit(finder)
141
+ found = sorted(finder.found)
142
+ return found[0] if found else None