rewindex 0.1.0__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 (80) hide show
  1. rewindex-0.1.0/LICENSE +20 -0
  2. rewindex-0.1.0/PKG-INFO +12 -0
  3. rewindex-0.1.0/README.md +433 -0
  4. rewindex-0.1.0/pyproject.toml +28 -0
  5. rewindex-0.1.0/rewindex/__init__.py +4 -0
  6. rewindex-0.1.0/rewindex/agent/__init__.py +2 -0
  7. rewindex-0.1.0/rewindex/agent/detector.py +40 -0
  8. rewindex-0.1.0/rewindex/agent/plugins/__init__.py +24 -0
  9. rewindex-0.1.0/rewindex/agent/plugins/aider.py +9 -0
  10. rewindex-0.1.0/rewindex/agent/plugins/base.py +18 -0
  11. rewindex-0.1.0/rewindex/agent/plugins/claude_code.py +9 -0
  12. rewindex-0.1.0/rewindex/agent/plugins/cursor.py +9 -0
  13. rewindex-0.1.0/rewindex/agent/plugins/nanobot.py +9 -0
  14. rewindex-0.1.0/rewindex/agent/registry.py +85 -0
  15. rewindex-0.1.0/rewindex/autostart/__init__.py +2 -0
  16. rewindex-0.1.0/rewindex/autostart/linux.py +61 -0
  17. rewindex-0.1.0/rewindex/autostart/macos.py +73 -0
  18. rewindex-0.1.0/rewindex/cli.py +67 -0
  19. rewindex-0.1.0/rewindex/commands/__init__.py +2 -0
  20. rewindex-0.1.0/rewindex/commands/cleanup.py +195 -0
  21. rewindex-0.1.0/rewindex/commands/diff.py +101 -0
  22. rewindex-0.1.0/rewindex/commands/init.py +146 -0
  23. rewindex-0.1.0/rewindex/commands/log.py +138 -0
  24. rewindex-0.1.0/rewindex/commands/note.py +58 -0
  25. rewindex-0.1.0/rewindex/commands/project.py +225 -0
  26. rewindex-0.1.0/rewindex/commands/rewind.py +254 -0
  27. rewindex-0.1.0/rewindex/commands/snap.py +57 -0
  28. rewindex-0.1.0/rewindex/commands/start.py +14 -0
  29. rewindex-0.1.0/rewindex/commands/status.py +100 -0
  30. rewindex-0.1.0/rewindex/commands/stop.py +12 -0
  31. rewindex-0.1.0/rewindex/commands/update.py +95 -0
  32. rewindex-0.1.0/rewindex/config/__init__.py +2 -0
  33. rewindex-0.1.0/rewindex/config/defaults.py +88 -0
  34. rewindex-0.1.0/rewindex/config/loader.py +69 -0
  35. rewindex-0.1.0/rewindex/config/validator.py +57 -0
  36. rewindex-0.1.0/rewindex/daemon/__init__.py +2 -0
  37. rewindex-0.1.0/rewindex/daemon/reconcile.py +97 -0
  38. rewindex-0.1.0/rewindex/daemon/runner.py +172 -0
  39. rewindex-0.1.0/rewindex/daemon/watcher.py +225 -0
  40. rewindex-0.1.0/rewindex/db/__init__.py +2 -0
  41. rewindex-0.1.0/rewindex/db/connection.py +53 -0
  42. rewindex-0.1.0/rewindex/db/queries.py +248 -0
  43. rewindex-0.1.0/rewindex/db/schema.py +41 -0
  44. rewindex-0.1.0/rewindex/session.py +80 -0
  45. rewindex-0.1.0/rewindex/snapshot/__init__.py +2 -0
  46. rewindex-0.1.0/rewindex/snapshot/engine.py +264 -0
  47. rewindex-0.1.0/rewindex/snapshot/opcodes.py +46 -0
  48. rewindex-0.1.0/rewindex/snapshot/reconstruct.py +66 -0
  49. rewindex-0.1.0/rewindex/snapshot/storage.py +56 -0
  50. rewindex-0.1.0/rewindex/ui/__init__.py +2 -0
  51. rewindex-0.1.0/rewindex/ui/diff_viewer.py +70 -0
  52. rewindex-0.1.0/rewindex/ui/rewind_prompt.py +56 -0
  53. rewindex-0.1.0/rewindex/ui/welcome.py +25 -0
  54. rewindex-0.1.0/rewindex/utils/__init__.py +0 -0
  55. rewindex-0.1.0/rewindex/utils/validation.py +60 -0
  56. rewindex-0.1.0/rewindex/version.py +60 -0
  57. rewindex-0.1.0/rewindex.egg-info/PKG-INFO +12 -0
  58. rewindex-0.1.0/rewindex.egg-info/SOURCES.txt +78 -0
  59. rewindex-0.1.0/rewindex.egg-info/dependency_links.txt +1 -0
  60. rewindex-0.1.0/rewindex.egg-info/entry_points.txt +2 -0
  61. rewindex-0.1.0/rewindex.egg-info/requires.txt +6 -0
  62. rewindex-0.1.0/rewindex.egg-info/top_level.txt +1 -0
  63. rewindex-0.1.0/setup.cfg +4 -0
  64. rewindex-0.1.0/tests/test_advanced_roundtrip.py +304 -0
  65. rewindex-0.1.0/tests/test_agent_aliases.py +134 -0
  66. rewindex-0.1.0/tests/test_agent_detection.py +59 -0
  67. rewindex-0.1.0/tests/test_config.py +62 -0
  68. rewindex-0.1.0/tests/test_daemon_log.py +124 -0
  69. rewindex-0.1.0/tests/test_db.py +84 -0
  70. rewindex-0.1.0/tests/test_missing_coverage.py +522 -0
  71. rewindex-0.1.0/tests/test_multiproject.py +253 -0
  72. rewindex-0.1.0/tests/test_new_fixes.py +224 -0
  73. rewindex-0.1.0/tests/test_opcodes.py +70 -0
  74. rewindex-0.1.0/tests/test_orphan_detection.py +183 -0
  75. rewindex-0.1.0/tests/test_rewind.py +60 -0
  76. rewindex-0.1.0/tests/test_rewind_navigation.py +196 -0
  77. rewindex-0.1.0/tests/test_same_folder.py +128 -0
  78. rewindex-0.1.0/tests/test_security_fixes.py +240 -0
  79. rewindex-0.1.0/tests/test_session.py +111 -0
  80. rewindex-0.1.0/tests/test_snapshot_roundtrip.py +120 -0
rewindex-0.1.0/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2026 Rewindex. All rights reserved.
2
+
3
+ PROPRIETARY SOFTWARE LICENSE
4
+
5
+ This software and its source code are the exclusive property of Rewindex.
6
+ All rights reserved. No part of this software may be reproduced, distributed,
7
+ modified, sublicensed, or used in any form without the prior written permission
8
+ of Rewindex.
9
+
10
+ UNAUTHORIZED USE PROHIBITED
11
+
12
+ This software is provided solely for use by authorized licensees under the
13
+ terms of a separate written agreement with Rewindex. Any use, copying,
14
+ modification, distribution, or disclosure of this software or its contents
15
+ without the express written consent of Rewindex is strictly prohibited and
16
+ may subject the violator to civil and criminal penalties.
17
+
18
+ THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ IMPLIED. REWINDEX SHALL NOT BE LIABLE FOR ANY DAMAGES ARISING FROM USE OF
20
+ THIS SOFTWARE.
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: rewindex
3
+ Version: 0.1.0
4
+ Summary: Automatic file snapshots for AI-assisted development
5
+ License: Proprietary
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: watchdog<7,>=4.0
8
+ Requires-Dist: questionary<3,>=2.0
9
+ Requires-Dist: click<9,>=8.0
10
+ Requires-Dist: pyyaml<7,>=6.0
11
+ Requires-Dist: psutil<6,>=5.9
12
+ Requires-Dist: rich<15,>=13.0
@@ -0,0 +1,433 @@
1
+ # Rewindex
2
+
3
+ ![version](https://img.shields.io/badge/version-0.1.0-blue)
4
+ ![python](https://img.shields.io/badge/python-3.10%2B-blue)
5
+ ![license](https://img.shields.io/badge/license-proprietary-red)
6
+
7
+ **Automatic file snapshots for AI-assisted development.**
8
+
9
+ Rewindex watches your project files and creates snapshots every time an AI agent (or you) makes a change. Rewind any file to any point in time — no commits needed, no manual saves, no data loss.
10
+
11
+ Think of it as an **undo history that never forgets**, built specifically for the workflow where AI agents write code on your behalf.
12
+
13
+ ---
14
+
15
+ ## Table of Contents
16
+
17
+ - [Why Rewindex](#why-rewindex)
18
+ - [How It Works](#how-it-works)
19
+ - [Installation](#installation)
20
+ - [Quick Start](#quick-start)
21
+ - [Commands](#commands)
22
+ - [Snapshot & History](#snapshot--history)
23
+ - [Rewind & Restore](#rewind--restore)
24
+ - [Project Management](#project-management)
25
+ - [Maintenance](#maintenance)
26
+ - [Configuration](#configuration)
27
+ - [Supported AI Agents](#supported-ai-agents)
28
+ - [Session Grouping](#session-grouping)
29
+ - [Snapshot Notes](#snapshot-notes)
30
+ - [Security](#security)
31
+ - [How Snapshots Work](#how-snapshots-work)
32
+ - [FAQ](#faq)
33
+ - [License](#license)
34
+
35
+ ---
36
+
37
+ ## Why Rewindex
38
+
39
+ When AI agents edit your code, changes happen fast. A single prompt can modify 10 files in seconds. If something goes wrong:
40
+
41
+ - **Git won't help** — you haven't committed yet
42
+ - **Ctrl+Z won't help** — the AI edited files outside your editor
43
+ - **File backups won't help** — they're too coarse-grained
44
+
45
+ Rewindex captures every change the moment it happens. You can see exactly what each agent changed, when, and rewind any file to any previous state.
46
+
47
+ ## How It Works
48
+
49
+ 1. A background daemon watches your project folder(s)
50
+ 2. When a file changes, Rewindex hashes it and creates a compressed snapshot
51
+ 3. Only the delta (diff) is stored — not the entire file
52
+ 4. An agent detector identifies which AI tool made the change
53
+ 5. You browse history, view diffs, and rewind files through the CLI
54
+
55
+ Everything stays on your machine. No cloud, no account, no telemetry.
56
+
57
+ ## Installation
58
+
59
+ > **Supported platforms: macOS and Linux only.** Windows is not yet supported.
60
+
61
+ ```bash
62
+ # pip
63
+ pip install rewindex
64
+
65
+ # pipx (recommended for CLI tools — keeps dependencies isolated)
66
+ pipx install rewindex
67
+
68
+ # uv
69
+ uv tool install rewindex
70
+ ```
71
+
72
+ Requires Python 3.10+.
73
+
74
+ ## Quick Start
75
+
76
+ ```bash
77
+ # 1. Set up your first project
78
+ rewindex init
79
+
80
+ # 2. Start the background daemon
81
+ rewindex start
82
+
83
+ # 3. Make changes (or let your AI agent work)
84
+ # Rewindex snapshots automatically in the background
85
+
86
+ # 4. View what happened
87
+ rewindex log
88
+
89
+ # 5. See what changed in a specific snapshot
90
+ rewindex diff 42
91
+
92
+ # 6. Rewind a file to a previous state
93
+ rewindex rewind 42
94
+
95
+ # 7. Check daemon status
96
+ rewindex status
97
+ ```
98
+
99
+ Non-interactive setup (for AI agents):
100
+
101
+ ```bash
102
+ rewindex init --project my-app --folder ~/projects/my-app
103
+ rewindex start
104
+ ```
105
+
106
+ ## Commands
107
+
108
+ ### Snapshot & History
109
+
110
+ #### `rewindex log`
111
+
112
+ View snapshot history grouped into sessions.
113
+
114
+ ```bash
115
+ rewindex log # session view (default)
116
+ rewindex log --detail # individual snapshots
117
+ rewindex log --agent claude # filter by agent
118
+ rewindex log --since "14:00" # since a specific time
119
+ rewindex log --from 39 --to 44 # index range
120
+ rewindex log --limit 100 # max entries (default: 50)
121
+ ```
122
+
123
+ #### `rewindex diff [INDEX]`
124
+
125
+ View file changes for a snapshot.
126
+
127
+ ```bash
128
+ rewindex diff # interactive — pick from list
129
+ rewindex diff 44 # show diff for snapshot #44
130
+ ```
131
+
132
+ #### `rewindex snap [LABEL]`
133
+
134
+ Manually trigger a snapshot of all watched files.
135
+
136
+ ```bash
137
+ rewindex snap # snapshot all changed files
138
+ rewindex snap "before refactor" # with a label
139
+ ```
140
+
141
+ #### `rewindex status`
142
+
143
+ Show daemon state, project info, snapshot counts, and disk usage.
144
+
145
+ ```bash
146
+ rewindex status
147
+ ```
148
+
149
+ Output:
150
+ ```
151
+ Rewindex is running (PID 12345)
152
+
153
+ my-app
154
+ → ~/projects/my-app
155
+ 142 snapshots · 28 files · latest #0142
156
+ Last activity: 2026-04-15 14:32:05
157
+ Disk usage: 3.2 MB
158
+ ```
159
+
160
+ ### Rewind & Restore
161
+
162
+ #### `rewindex rewind [INDEX] [FILES...]`
163
+
164
+ Restore files to a previous snapshot state.
165
+
166
+ ```bash
167
+ # Interactive — pick snapshot and files
168
+ rewindex rewind
169
+
170
+ # Rewind all files in snapshot #44
171
+ rewindex rewind 44
172
+
173
+ # Rewind specific file only
174
+ rewindex rewind 44 src/auth.py
175
+
176
+ # Preview diff without writing (read-only)
177
+ rewindex rewind --preview 44
178
+
179
+ # Rewind without confirmation (for AI agents)
180
+ rewindex rewind 44 --yes
181
+
182
+ # Rewind all files changed since snapshot #41
183
+ rewindex rewind --since 41 --yes
184
+
185
+ # Rewind last change by a specific agent
186
+ rewindex rewind --agent claude --last --yes
187
+ ```
188
+
189
+ #### `rewindex note INDEX TEXT`
190
+
191
+ Add a note to a snapshot.
192
+
193
+ ```bash
194
+ rewindex note 44 "refactored auth to JWT"
195
+ rewindex note latest "WIP — do not rewind"
196
+ ```
197
+
198
+ ### Project Management
199
+
200
+ #### `rewindex project add NAME FOLDER`
201
+
202
+ Add a new project to watch.
203
+
204
+ ```bash
205
+ rewindex project add backend ~/api
206
+ ```
207
+
208
+ #### `rewindex project list`
209
+
210
+ Show all configured projects and their folders.
211
+
212
+ #### `rewindex project remove NAME`
213
+
214
+ Stop watching a project. Snapshots are kept on disk.
215
+
216
+ ```bash
217
+ rewindex project remove backend
218
+ rewindex project remove backend --yes # skip confirmation
219
+ ```
220
+
221
+ To delete snapshot data, run `rewindex cleanup --orphans --apply` after removing.
222
+
223
+ #### `rewindex project add-folder NAME FOLDER`
224
+
225
+ Add an additional folder to an existing project.
226
+
227
+ ```bash
228
+ rewindex project add-folder my-app ~/shared-lib
229
+ ```
230
+
231
+ #### `rewindex project remove-folder NAME FOLDER`
232
+
233
+ Remove a folder from a project.
234
+
235
+ ```bash
236
+ rewindex project remove-folder my-app ~/shared-lib
237
+ ```
238
+
239
+ #### `rewindex project rename OLD NEW`
240
+
241
+ Rename a project. Updates the config, renames the snapshots directory, and updates all snapshot records in the database.
242
+
243
+ ```bash
244
+ rewindex project rename my-app my-app-v2
245
+ rewindex project rename my-app my-app-v2 --yes # skip confirmation
246
+ ```
247
+
248
+ ### Maintenance
249
+
250
+ #### `rewindex cleanup`
251
+
252
+ Identify snapshots that are safe to delete (already committed to git).
253
+
254
+ ```bash
255
+ rewindex cleanup # dry run — report only
256
+ rewindex cleanup --apply # actually delete
257
+ rewindex cleanup --orphans # show orphaned snapshot dirs
258
+ rewindex cleanup --orphans --apply # delete orphaned dirs
259
+ ```
260
+
261
+ #### `rewindex update`
262
+
263
+ Check for updates and sync config.
264
+
265
+ ```bash
266
+ rewindex update # check current version
267
+ rewindex update --latest # upgrade from PyPI
268
+ rewindex update --sync # add missing config defaults
269
+ ```
270
+
271
+ #### `rewindex start` / `rewindex stop`
272
+
273
+ Start or stop the background daemon.
274
+
275
+ ```bash
276
+ rewindex start
277
+ rewindex stop
278
+ ```
279
+
280
+ ## Configuration
281
+
282
+ Config file: `~/.rewindex/.rewindex.config.yaml`
283
+
284
+ ```yaml
285
+ projects:
286
+ - name: my-app
287
+ folders:
288
+ - ~/projects/my-app
289
+ - ~/projects/shared-lib
290
+
291
+ exclude:
292
+ - "node_modules/"
293
+ - ".git/"
294
+ - "__pycache__/"
295
+ - "venv/"
296
+ - ".venv/"
297
+ - "*.log"
298
+ - "*.tmp"
299
+
300
+ debounce_seconds: 5.0 # wait before snapshotting (group rapid saves)
301
+ session_gap_seconds: 20 # gap threshold for session grouping
302
+
303
+ history:
304
+ max_diffs: 600 # auto-trim after this many diffs per file
305
+ trim_count: 100 # number of oldest diffs removed per trim
306
+ ```
307
+
308
+ ### Security Exclusions (hardcoded, not configurable)
309
+
310
+ These files are **never** snapshotted:
311
+
312
+ - `.env`, `.env.*`, `*.secret`
313
+ - `id_rsa`, `id_ed25519`, `authorized_keys`
314
+ - `*.pem`, `*.key`, `*.p12`, `*.pfx`
315
+ - `.npmrc`, `.pypirc`, `.netrc`
316
+ - `*.kubeconfig`
317
+
318
+ ## Supported AI Agents
319
+
320
+ Rewindex auto-detects which AI agent is modifying your files:
321
+
322
+ | Agent | Detection |
323
+ |-------|-----------|
324
+ | Claude Code | Process name `claude` or node args |
325
+ | Cursor | Process name `cursor` |
326
+ | Windsurf | Process name `windsurf` |
327
+ | Aider | Process name `aider` or python args |
328
+ | Codex (OpenAI) | Process name `codex` or node args |
329
+ | Zed | Process name `zed` |
330
+ | OpenClaw | Process name `openclaw` or node args |
331
+ | OpenCode | Process name `opencode` |
332
+ | Augment Code | Process name `auggie` |
333
+ | Hermes | Process name `hermes` |
334
+ | Goose | Process name `goose` |
335
+ | Plandex | Process name `plandex` or `pdx` |
336
+ | ZeroClaw | Process name `zeroclaw` |
337
+ | Kilo | Process name `kilo` or node args |
338
+ | Pi | Process name `pi` or node args |
339
+ | nanobot | Process name `nanobot` |
340
+
341
+ If no agent is detected, changes are labeled as `manual`.
342
+
343
+ ## Session Grouping
344
+
345
+ `rewindex log` groups snapshots into sessions automatically:
346
+
347
+ - **Same agent + gap ≤ 20 seconds** = same session
348
+ - **Different agent** = always a new session
349
+
350
+ ```
351
+ #0042–#0044 14:30–14:32 Claude Code 3 files
352
+ src/auth.py
353
+ src/middleware.py
354
+ models/user.py
355
+
356
+ #0041 14:15 manual 1 file
357
+ config.py
358
+ ```
359
+
360
+ Use `rewindex log --detail` to see individual snapshots.
361
+
362
+ The threshold is configurable via `session_gap_seconds` in your config file.
363
+
364
+ ## Snapshot Notes
365
+
366
+ Add context to snapshots for future reference:
367
+
368
+ ```bash
369
+ rewindex note latest "refactored auth to use JWT"
370
+ rewindex note 42 "before breaking change"
371
+ ```
372
+
373
+ Notes appear in log output. Designed for both human and AI agent use — agents can annotate their changes automatically.
374
+
375
+ ## Security
376
+
377
+ - `~/.rewindex/` directory: `chmod 700` (owner only)
378
+ - `daemon.log`: `chmod 600` (owner only)
379
+ - Path traversal protection on all file operations
380
+ - Symlinks that point outside the project folder are ignored
381
+ - SQL injection prevented via parameterized queries throughout
382
+ - LIKE wildcards (`%`, `_`) escaped in agent filters
383
+ - Binary files detected and skipped (null byte check, same method as git)
384
+ - Text files larger than 10 MB are skipped
385
+ - Gzip bomb protection (50 MB decompressed limit)
386
+ - Config file bomb protection (1 MB limit)
387
+ - Log rotation via RotatingFileHandler (5 MB × 3 backups)
388
+ - PID verification before sending stop signal
389
+ - Autostart binary path validated (absolute path, owner check)
390
+
391
+ ## How Snapshots Work
392
+
393
+ 1. **Hash** — SHA-256 hash of the file content
394
+ 2. **Compare** — If hash matches the last snapshot, skip (no change)
395
+ 3. **Store** — First snapshot is a full copy (`.full.gz`), subsequent ones are diffs (`.diff.gz`)
396
+ 4. **Compress** — All snapshots are gzip compressed
397
+ 5. **Reconstruct** — To view or rewind, chain: load base → apply diffs in order
398
+
399
+ Storage is efficient: only deltas are stored, compressed. A typical project uses 1-5 MB for hundreds of snapshots.
400
+
401
+ ### Auto-Trim
402
+
403
+ When a file accumulates more than `max_diffs` (default: 600) diff snapshots, Rewindex automatically:
404
+
405
+ 1. Promotes the 100th oldest diff to a new full base
406
+ 2. Deletes everything before it
407
+ 3. History continues seamlessly — no user action needed
408
+
409
+ Hard safety cap: 1,000 diffs maximum per file in the reconstruction chain.
410
+
411
+ ## FAQ
412
+
413
+ **Q: Does Rewindex modify my files?**
414
+ A: Never. It only reads files to create snapshots. The only time it writes to your project folder is when you explicitly run `rewindex rewind`.
415
+
416
+ **Q: Does it send data anywhere?**
417
+ A: No. Everything is stored locally in `~/.rewindex/`. Cloud backup is planned for a future release but is opt-in.
418
+
419
+ **Q: How much disk space does it use?**
420
+ A: Very little. Diffs are compressed and typically 50-500 bytes each. Run `rewindex status` to check, or `rewindex cleanup` to free space.
421
+
422
+ **Q: Can I use it without AI agents?**
423
+ A: Yes. Rewindex snapshots any file change, whether made by an AI agent, your editor, or a script. Manual changes are labeled as `manual`.
424
+
425
+ **Q: What happens if I delete the database?**
426
+ A: The daemon creates a new one on restart and catches up by snapshotting all current files. Previous history is lost, but snapshot files remain on disk.
427
+
428
+ **Q: Does it work on Windows?**
429
+ A: Not yet. Currently supports Linux and macOS.
430
+
431
+ ## License
432
+
433
+ Proprietary. See LICENSE for details.
@@ -0,0 +1,28 @@
1
+ [project]
2
+ name = "rewindex"
3
+ version = "0.1.0"
4
+ description = "Automatic file snapshots for AI-assisted development"
5
+ requires-python = ">=3.10"
6
+ license = { text = "Proprietary" }
7
+ dependencies = [
8
+ "watchdog>=4.0,<7",
9
+ "questionary>=2.0,<3",
10
+ "click>=8.0,<9",
11
+ "pyyaml>=6.0,<7",
12
+ "psutil>=5.9,<6",
13
+ "rich>=13.0,<15",
14
+ ]
15
+
16
+ [project.scripts]
17
+ rewindex = "rewindex.cli:main"
18
+
19
+ [build-system]
20
+ requires = ["setuptools>=61", "wheel"]
21
+ build-backend = "setuptools.build_meta"
22
+
23
+ [tool.setuptools]
24
+ license-files = []
25
+
26
+ [tool.setuptools.packages.find]
27
+ where = ["."]
28
+ include = ["rewindex*"]
@@ -0,0 +1,4 @@
1
+ # Copyright (c) 2026 Rewindex. All rights reserved.
2
+ # Proprietary and confidential. Unauthorized use prohibited.
3
+
4
+ __version__ = "0.1.0"
@@ -0,0 +1,2 @@
1
+ # Copyright (c) 2026 Rewindex. All rights reserved.
2
+ # Proprietary and confidential. Unauthorized use prohibited.
@@ -0,0 +1,40 @@
1
+ # Copyright (c) 2026 Rewindex. All rights reserved.
2
+ # Proprietary and confidential. Unauthorized use prohibited.
3
+
4
+ import psutil
5
+
6
+ from .registry import KNOWN_AGENTS
7
+
8
+
9
+ def detect_agent() -> str:
10
+ """
11
+ Scan running processes and return the display name of the first
12
+ recognized AI agent found. Returns "manual" if none found.
13
+ Called once per file-change event — not a continuous poll.
14
+ """
15
+ try:
16
+ for proc in psutil.process_iter(["name", "cmdline"]):
17
+ name = (proc.info.get("name") or "").lower()
18
+ cmdline = proc.info.get("cmdline") or []
19
+ cmdline_str = " ".join(cmdline).lower()
20
+
21
+ rule = KNOWN_AGENTS.get(name)
22
+ if rule is None:
23
+ continue
24
+
25
+ if isinstance(rule, str):
26
+ return rule
27
+
28
+ # dict rule: check args_contains
29
+ if isinstance(rule, dict):
30
+ for entry in rule.get("args_contains", []):
31
+ if isinstance(entry, str):
32
+ if entry in cmdline_str:
33
+ return rule["name"]
34
+ elif isinstance(entry, dict):
35
+ if entry["fragment"] in cmdline_str:
36
+ return entry["name"]
37
+ except (psutil.Error, OSError, AttributeError, TypeError):
38
+ pass
39
+
40
+ return "manual"
@@ -0,0 +1,24 @@
1
+ # Copyright (c) 2026 Rewindex. All rights reserved.
2
+ # Proprietary and confidential. Unauthorized use prohibited.
3
+
4
+ from .aider import AiderPlugin
5
+ from .claude_code import ClaudeCodePlugin
6
+ from .cursor import CursorPlugin
7
+ from .nanobot import NanobotPlugin
8
+
9
+ _PLUGINS = [
10
+ AiderPlugin(),
11
+ ClaudeCodePlugin(),
12
+ CursorPlugin(),
13
+ NanobotPlugin(),
14
+ ]
15
+
16
+ # Map agent display name → plugin instance (case-insensitive prefix match)
17
+ _PLUGIN_MAP: dict[str, object] = {p.get_agent_name().lower(): p for p in _PLUGINS}
18
+
19
+
20
+ def get_plugin_for_agent(agent: str):
21
+ """Return plugin instance for the given agent name string, or None."""
22
+ # agent strings can be "Claude Code", "manual", "manual:label", etc.
23
+ agent_lower = agent.lower().split(":")[0].strip()
24
+ return _PLUGIN_MAP.get(agent_lower)
@@ -0,0 +1,9 @@
1
+ # Copyright (c) 2026 Rewindex. All rights reserved.
2
+ # Proprietary and confidential. Unauthorized use prohibited.
3
+
4
+ from .base import AgentPlugin
5
+
6
+
7
+ class AiderPlugin(AgentPlugin):
8
+ def get_agent_name(self) -> str:
9
+ return "Aider"
@@ -0,0 +1,18 @@
1
+ # Copyright (c) 2026 Rewindex. All rights reserved.
2
+ # Proprietary and confidential. Unauthorized use prohibited.
3
+
4
+ from abc import ABC, abstractmethod
5
+
6
+
7
+ class AgentPlugin(ABC):
8
+ @abstractmethod
9
+ def get_agent_name(self) -> str:
10
+ """Return the display name of this agent."""
11
+ ...
12
+
13
+ def on_file_change(self, filepath: str, diff: list) -> None:
14
+ """
15
+ Optional hook called after a snapshot is taken.
16
+ filepath: relative path of the changed file.
17
+ diff: list of opcodes from snapshot/opcodes.py generate().
18
+ """
@@ -0,0 +1,9 @@
1
+ # Copyright (c) 2026 Rewindex. All rights reserved.
2
+ # Proprietary and confidential. Unauthorized use prohibited.
3
+
4
+ from .base import AgentPlugin
5
+
6
+
7
+ class ClaudeCodePlugin(AgentPlugin):
8
+ def get_agent_name(self) -> str:
9
+ return "Claude Code"
@@ -0,0 +1,9 @@
1
+ # Copyright (c) 2026 Rewindex. All rights reserved.
2
+ # Proprietary and confidential. Unauthorized use prohibited.
3
+
4
+ from .base import AgentPlugin
5
+
6
+
7
+ class CursorPlugin(AgentPlugin):
8
+ def get_agent_name(self) -> str:
9
+ return "Cursor"
@@ -0,0 +1,9 @@
1
+ # Copyright (c) 2026 Rewindex. All rights reserved.
2
+ # Proprietary and confidential. Unauthorized use prohibited.
3
+
4
+ from .base import AgentPlugin
5
+
6
+
7
+ class NanobotPlugin(AgentPlugin):
8
+ def get_agent_name(self) -> str:
9
+ return "nanobot"