slopkeeper 0.1.0__py3-none-any.whl
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.
Potentially problematic release.
This version of slopkeeper might be problematic. Click here for more details.
- slopkeeper/AGENT_GUIDE.md +305 -0
- slopkeeper/__init__.py +12 -0
- slopkeeper/__main__.py +8 -0
- slopkeeper/apply.py +598 -0
- slopkeeper/cli.py +789 -0
- slopkeeper/doctor.py +116 -0
- slopkeeper/draft.py +205 -0
- slopkeeper/inventory.py +255 -0
- slopkeeper/models.py +367 -0
- slopkeeper/plan.py +543 -0
- slopkeeper/references.py +260 -0
- slopkeeper/roots.py +111 -0
- slopkeeper/scaffold.py +188 -0
- slopkeeper/similar.py +369 -0
- slopkeeper/status.py +405 -0
- slopkeeper/strategies/hybrid.json +27 -0
- slopkeeper/strategies/projects.json +25 -0
- slopkeeper/strategies/role-bots.json +26 -0
- slopkeeper/strategies/topics.json +26 -0
- slopkeeper/strategies.py +37 -0
- slopkeeper/tui.py +929 -0
- slopkeeper-0.1.0.dist-info/METADATA +262 -0
- slopkeeper-0.1.0.dist-info/RECORD +27 -0
- slopkeeper-0.1.0.dist-info/WHEEL +5 -0
- slopkeeper-0.1.0.dist-info/entry_points.txt +2 -0
- slopkeeper-0.1.0.dist-info/licenses/LICENSE +21 -0
- slopkeeper-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SPDX-FileCopyrightText: 2026 Greg Brandt <brandt.greg@gmail.com>
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: MIT
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# AGENT_GUIDE — how to drive SlopKeeper
|
|
8
|
+
|
|
9
|
+
You are the calling agent. The user said something like "organize my
|
|
10
|
+
directory" or "clean up my workspace". This tool is your hands and your
|
|
11
|
+
seatbelt: it inventories, validates, moves and scaffolds. You classify.
|
|
12
|
+
The human approves. **Never generate or execute a plan without showing the
|
|
13
|
+
rendered markdown to the human first.**
|
|
14
|
+
|
|
15
|
+
(Humans can also drive slopkeeper themselves: bare `slopkeeper` with no subcommand
|
|
16
|
+
starts an interactive `/command` REPL. That is not your surface — drive
|
|
17
|
+
the JSON subcommands documented here; they are scriptable and stable.)
|
|
18
|
+
|
|
19
|
+
## Hard rules
|
|
20
|
+
|
|
21
|
+
1. **No plan, no moves.** Every file operation goes through a validated
|
|
22
|
+
plan JSON. There is no other write path.
|
|
23
|
+
2. **Dry-run is the default.** `apply` and `scaffold` and `undo` do nothing
|
|
24
|
+
destructive without `--execute`.
|
|
25
|
+
3. **The human gate.** Render the plan (`plan render`) and present it.
|
|
26
|
+
The human edits the plan JSON or approves. Re-validate after any edit.
|
|
27
|
+
4. **Respect reference risks.** `reference_risks` lists files cited from
|
|
28
|
+
files that are NOT moving. Prefer declaring the rewrite IN the plan
|
|
29
|
+
(`edits: [{file, find, replace}]` — applied after the moves, journaled,
|
|
30
|
+
undo-able; `edit.file` is the move-source path if the file moves,
|
|
31
|
+
else its current path, and `find` must occur exactly once). Otherwise:
|
|
32
|
+
move the citing files too, or get explicit human sign-off on the
|
|
33
|
+
breakage. Never silently accept. Citations from OUTSIDE the corpus
|
|
34
|
+
(skills, cron scripts, scratch harnesses) are never listed there —
|
|
35
|
+
apply scans the corpus only; sweep the directories that cite your
|
|
36
|
+
corpus with `refs --scan-path` (see "Authoring reference rewrites").
|
|
37
|
+
Recall limits: the scan is not exhaustive — basenames under 3
|
|
38
|
+
characters are never scanned; a citation from a file that shares the
|
|
39
|
+
cited file's basename is skipped (attribution would be ambiguous);
|
|
40
|
+
extensionless files and non-text extensions are never read. So sweep
|
|
41
|
+
critical directories with `slopkeeper refs --scan-path` too.
|
|
42
|
+
5. **Unresolved is honest.** Files you can't classify go in
|
|
43
|
+
`unresolved` with a reason — never guess. `keep` is for top-level
|
|
44
|
+
entries that stay (e.g. `docs/`, a watchlist).
|
|
45
|
+
6. **Corpus changed?** If apply raises `corpus_changed`, re-inventory and
|
|
46
|
+
regenerate the plan. `--force` only if the changes were yours and
|
|
47
|
+
expected. Staleness is content-based: a bare `touch` won't force a
|
|
48
|
+
re-plan, but a real edit — even one that keeps the byte size — will.
|
|
49
|
+
|
|
50
|
+
## The protocol
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
1. slopkeeper doctor <root>
|
|
54
|
+
→ strategy suggestion, token estimate, advice. Read it.
|
|
55
|
+
|
|
56
|
+
2. slopkeeper inventory <root> [--fast --summary | --out inv.json]
|
|
57
|
+
→ <800 files: full JSON with heads + cross-references (read it whole)
|
|
58
|
+
>800 files: --fast --summary, then per-dir inventories as needed
|
|
59
|
+
|
|
60
|
+
3. slopkeeper clusters <root>
|
|
61
|
+
→ similarity HINTS: content clusters, scrape pairs, dupes, hubs.
|
|
62
|
+
Name each cluster, merge/split by judgment. Never apply a cluster
|
|
63
|
+
as-is; and check `hubs` before moving a file cited by many others.
|
|
64
|
+
|
|
65
|
+
4. slopkeeper strategies show <suggested-id> (or your choice)
|
|
66
|
+
→ the partitioning rubric. Apply it. Strategies:
|
|
67
|
+
role-bots | topics | projects | hybrid (default for mixed corpora)
|
|
68
|
+
|
|
69
|
+
5. Write the plan JSON. Get the template:
|
|
70
|
+
slopkeeper plan schema
|
|
71
|
+
Record the inventory's fingerprint as based_on.corpus_fingerprint.
|
|
72
|
+
Cover every top-level entry: moves | keep | unresolved.
|
|
73
|
+
(keep accepts "name" or {"path": "name", "reason": "..."}.)
|
|
74
|
+
|
|
75
|
+
6. slopkeeper plan validate <plan.json> <root>
|
|
76
|
+
→ fix every error code it gives you. Then:
|
|
77
|
+
slopkeeper plan render <plan.json> <root> --out plan.md
|
|
78
|
+
→ HUMAN GATE: show plan.md. Wait for approval.
|
|
79
|
+
|
|
80
|
+
7. slopkeeper apply <plan.json> <root> (dry-run)
|
|
81
|
+
slopkeeper apply <plan.json> <root> --execute (for real)
|
|
82
|
+
→ note reference_risks; do rewrite steps now (plain edits, no tool).
|
|
83
|
+
A plan whose bytes differ from the last `plan render --out`
|
|
84
|
+
(e.g. hand-edited JSON) gets a `render_warning` in the result —
|
|
85
|
+
informational only: re-render or get explicit sign-off if the
|
|
86
|
+
human approved the earlier render.
|
|
87
|
+
|
|
88
|
+
8. slopkeeper scaffold <plan.json> <root> --execute
|
|
89
|
+
→ writes AGENTS.md roster + per-folder AGENTS.md/STATE.md.
|
|
90
|
+
Existing files are never overwritten; you get *.slopkeeper-suggested
|
|
91
|
+
to merge by hand.
|
|
92
|
+
|
|
93
|
+
9. Report: moved counts, risk resolutions, suggested-file merges needed,
|
|
94
|
+
journal path (for undo). Offer: slopkeeper undo <journal> <root>
|
|
95
|
+
(dry-run is the default; add --execute to actually restore)
|
|
96
|
+
|
|
97
|
+
PARTIAL APPLY: `apply --execute` can fail MID-RUN (an OSError on one
|
|
98
|
+
hop — permissions, disk full). The envelope then carries
|
|
99
|
+
"partial": true, the process exits non-zero, and next.command is
|
|
100
|
+
the exact undo for this journal. Read `errors`, fix the cause, run
|
|
101
|
+
the suggested `slopkeeper undo <journal> <root>` (dry-run is the default;
|
|
102
|
+
add --execute to actually restore), then re-apply. A source that
|
|
103
|
+
vanishes mid-apply is the same path: it lands in `errors` and the
|
|
104
|
+
run exits non-zero — never silent. Earlier hops stay journaled and
|
|
105
|
+
undoable throughout.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Re-tending (the recurring loop)
|
|
109
|
+
|
|
110
|
+
After the first sweep, `apply --execute` and `scaffold --execute` both
|
|
111
|
+
write `.slopkeeper/manifest.json`. From then on, a tending pass costs
|
|
112
|
+
tokens proportional to the slop shot in, not the corpus size:
|
|
113
|
+
|
|
114
|
+
```
|
|
115
|
+
1. slopkeeper status <root>
|
|
116
|
+
→ new_files (with title+head: triage them without opening anything),
|
|
117
|
+
modified, removed, stale folders, still-unresolved items.
|
|
118
|
+
|
|
119
|
+
2. slopkeeper draft <root> --out draft-plan.json
|
|
120
|
+
→ OPTIONAL but usually the fastest path: auto-drafts a PARTIAL plan
|
|
121
|
+
by nearest filed neighbor (TF-IDF cosine). Confident files get a
|
|
122
|
+
move with the neighbor + score as the reason; everything else is
|
|
123
|
+
unresolved WITH the reason ("best neighbor only 0.03"). Draft is
|
|
124
|
+
a guess with a citation — review every move, then edit.
|
|
125
|
+
The stdout envelope carries COUNTS only ({drafted_count,
|
|
126
|
+
unresolved_count, new_count, undrafted, out_path}) — the plan
|
|
127
|
+
body lives only in the --out file. The default path resolves
|
|
128
|
+
against your CWD and is REFUSED if it lands inside the corpus
|
|
129
|
+
(`draft_out_inside_root`) — a draft written into the root would
|
|
130
|
+
shift its fingerprint and the next apply would refuse with
|
|
131
|
+
corpus_changed. When new files exceed what the draft could see
|
|
132
|
+
(status's 2000-new_files cap), `undrafted` carries the count and
|
|
133
|
+
the reason, and the next-step hint says so. A zero-move draft
|
|
134
|
+
(everything went unresolved) is still a valid plan: validate it
|
|
135
|
+
and apply --execute — the backlog is recorded for the human.
|
|
136
|
+
|
|
137
|
+
3. Triage. Fix the draft: reclassify wrong moves, resolve the
|
|
138
|
+
unresolved ones (they're the actual judgment work), add folder
|
|
139
|
+
entries for any new folder the roster needs.
|
|
140
|
+
|
|
141
|
+
4. apply --execute → scaffold --execute (both record the manifest)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Notes:
|
|
145
|
+
- A delta plan should carry `"partial": true` at the top level (draft
|
|
146
|
+
plans already do) — no --allow-partial flags needed.
|
|
147
|
+
- `still_unresolved` in the status output is the human-decides backlog;
|
|
148
|
+
surface it every tend until it's empty.
|
|
149
|
+
- `skipped_bad_unresolved` counts backlog entries whose `path` was
|
|
150
|
+
null or missing — they are skipped (state-file content is data,
|
|
151
|
+
never a crash); the next re-record drops them from the manifest and
|
|
152
|
+
records the same count there.
|
|
153
|
+
- `modified` files are leads, not chores: a changed STATE.md or AGENTS.md
|
|
154
|
+
is the owner tending by hand — leave it alone; a changed runbook may
|
|
155
|
+
deserve a fresh look in your triage note.
|
|
156
|
+
|
|
157
|
+
## Tending several corpora on a schedule
|
|
158
|
+
|
|
159
|
+
Register the corpora you tend once; the registry is the loop's to-do
|
|
160
|
+
list, so a scheduled job is one call instead of a shell script. It lives
|
|
161
|
+
at `$XDG_CONFIG_HOME/slopkeeper/roots.json` (set `SLOPKEEPER_HOME` to
|
|
162
|
+
relocate it, e.g. for tests); content is sorted, unique, absolute paths.
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
slopkeeper roots add <dir> → register a corpus (idempotent; must exist)
|
|
166
|
+
slopkeeper roots remove <dir> → unregister (the corpus itself is untouched)
|
|
167
|
+
slopkeeper roots list → what is registered
|
|
168
|
+
|
|
169
|
+
slopkeeper status --all → ONE digest over every registered root:
|
|
170
|
+
per root {root, ok, never_tended, new_count, modified_count,
|
|
171
|
+
removed_count, stale_folders, unresolved_count}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The digest is counts, not listings — keep it skimmable, then drill into
|
|
175
|
+
whichever roots are nonzero with the ordinary per-root `status`. A
|
|
176
|
+
deleted root, a never-tended corpus, or a root whose status fails is a
|
|
177
|
+
row (`ok: false` carries the error), never a failed run — the other
|
|
178
|
+
roots always report. `--all` with an empty registry is a clean error:
|
|
179
|
+
add roots first.
|
|
180
|
+
|
|
181
|
+
## Authoring reference rewrites
|
|
182
|
+
|
|
183
|
+
When a move risks breaking citations from stay-put files:
|
|
184
|
+
|
|
185
|
+
```
|
|
186
|
+
1. slopkeeper refs <root> <basename>
|
|
187
|
+
→ exact citation sites: {file, line, text}.
|
|
188
|
+
|
|
189
|
+
2. Citations also live OUTSIDE the corpus — skills, AGENT_GUIDE-style
|
|
190
|
+
guides, cron scripts, scratch harnesses. reference_risks never lists
|
|
191
|
+
them (apply scans the corpus only), so sweep the dirs that cite your
|
|
192
|
+
corpus explicitly:
|
|
193
|
+
|
|
194
|
+
slopkeeper refs <root> <basename> --scan-path ~/skills --scan-path ~/cron
|
|
195
|
+
→ same {file, line, text} sites; every hit outside the corpus
|
|
196
|
+
carries "external": true and an absolute `file`. Dot-directories
|
|
197
|
+
and non-text files are skipped; the same per-file cap and size
|
|
198
|
+
limit as the in-tree scan apply (corpus_too_big refuses an
|
|
199
|
+
oversized dir, scan_path_missing a bad one).
|
|
200
|
+
|
|
201
|
+
3. Add to the plan:
|
|
202
|
+
"edits": [{"file": "<citing file (move-source path if it moves)>",
|
|
203
|
+
"find": "<exact text from the citation line, unique>",
|
|
204
|
+
"replace": "<new path after the move>",
|
|
205
|
+
"reason": "fix citation"}]
|
|
206
|
+
find must occur exactly once in the file (edit_ambiguous otherwise);
|
|
207
|
+
if the content drifted, you get edit_stale. The target must be valid
|
|
208
|
+
UTF-8 (edit_not_utf8 otherwise) — non-UTF-8 files are refused, never
|
|
209
|
+
re-encoded. Edits run after the moves,
|
|
210
|
+
are journaled, and undo reverses them.
|
|
211
|
+
|
|
212
|
+
4. To rewrite a citation in a file OUTSIDE the corpus, declare the
|
|
213
|
+
directory it lives under and use an ABSOLUTE edit path:
|
|
214
|
+
|
|
215
|
+
"external_roots": ["/abs/dir"],
|
|
216
|
+
"edits": [{"file": "/abs/dir/harness.sh",
|
|
217
|
+
"find": "tmp/docs/infra/config.yaml",
|
|
218
|
+
"replace": "docs/infra/config.yaml",
|
|
219
|
+
"reason": "external citation"}]
|
|
220
|
+
|
|
221
|
+
Every external_roots entry must be an absolute, existing directory
|
|
222
|
+
(plan_bad_external_root otherwise). With no external_roots declared,
|
|
223
|
+
an edit outside the root is rejected (path_escape) — the old rule,
|
|
224
|
+
unchanged. External edits run after the in-root ones, are journaled
|
|
225
|
+
under their absolute path, and undo reverses them. `plan render`
|
|
226
|
+
marks external edits so the human sees writes outside the corpus.
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
A citing file covered by an edit no longer counts as a reference risk,
|
|
230
|
+
so `--strict-references` is usable when all rewrites are declared —
|
|
231
|
+
in-root and external alike.
|
|
232
|
+
|
|
233
|
+
## Error codes you will see
|
|
234
|
+
|
|
235
|
+
| code | meaning | your move |
|
|
236
|
+
|---|---|---|
|
|
237
|
+
| `plan_incomplete` | top-level entries uncovered | add to moves/keep/unresolved |
|
|
238
|
+
| `plan_no_moves` | `moves` is not a list, or is empty on a non-partial plan | a plan needs at least one move — unless it carries `"partial": true` (a zero-move partial plan is valid: the draft filed everything as unresolved; validate it and `apply --execute` records the backlog) |
|
|
239
|
+
| `plan_stale_src` | a source vanished | re-inventory, regenerate |
|
|
240
|
+
| `corpus_changed` | fingerprint mismatch | re-inventory; `--force` only if yours |
|
|
241
|
+
| `dst_exists` | destination occupied | resolve collision in plan |
|
|
242
|
+
| `referenced_moves` | strict mode: refs would break | rewrite refs first |
|
|
243
|
+
| `plan_nested_moves` | dir + its contents both moved | move dir whole OR its files |
|
|
244
|
+
| `plan_bad_keep` | keep entry is neither string nor {path} object | use `"name"` or `{"path": "name", "reason": "..."}` |
|
|
245
|
+
| `plan_bad_unresolved` | an unresolved entry has no non-empty string `path` (null, missing, or blank) | every entry is `{"path": "<file>", "reason": "<why it waits>"}` — fix or drop it |
|
|
246
|
+
| `edit_stale` | edit target missing or find-text gone | update the edit in the plan |
|
|
247
|
+
| `edit_ambiguous` | find text occurs more than once | make find unique; split the edit |
|
|
248
|
+
| `edit_not_utf8` | edit target is not valid UTF-8 (e.g. latin-1); editing would corrupt it | convert the file to UTF-8 first, or rewrite the citation by hand |
|
|
249
|
+
| `plan_bad_external_root` | external_roots entry is not an absolute, existing directory | create or fix the directory, or omit external_roots |
|
|
250
|
+
| `scan_path_missing` | `refs --scan-path` directory does not exist | create it or fix the path |
|
|
251
|
+
| `no_manifest` | draft with no prior tend | run a first tend first |
|
|
252
|
+
| `draft_out_inside_root` | `draft --out` resolves inside the corpus root — a draft written into the corpus shifts its fingerprint (the next apply would fail `corpus_changed`) | write the draft outside the corpus: run from another directory (the default `draft-plan.json` resolves against your CWD) or pass an explicit path elsewhere |
|
|
253
|
+
| `path_escape` | path outside root, contains a null byte, or is the root itself | use relative paths to real files; for edits outside the root, declare `external_roots` |
|
|
254
|
+
| `manifest_unreadable` / `manifest_bad_schema` | `.slopkeeper/manifest.json` corrupt | delete it; `status --record` rebuilds |
|
|
255
|
+
| `tending_in_progress` | another tend holds `.slopkeeper/tend.lock` (execute-mode apply/scaffold/undo, `status --record`) | wait for the other tend, or check for a dead agent's session; the OS frees the lock on process death |
|
|
256
|
+
| `root_not_dir` | `roots add` target is not an existing directory | register corpora that exist; SlopKeeper doesn't create them |
|
|
257
|
+
| `root_not_registered` | `roots remove` for a path not in the registry | check `slopkeeper roots list` |
|
|
258
|
+
| `root_missing` (in `status --all` data) | a registered root is no longer a directory | re-add its new path, or `roots remove` if it's gone |
|
|
259
|
+
| `roots_unreadable` / `roots_bad_schema` | roots registry file corrupt/foreign | delete it; re-add roots |
|
|
260
|
+
| `no_roots` | `status --all` with an empty registry | `roots add` first, or run `status <root>` directly |
|
|
261
|
+
| `no_root` | `status` with neither a root nor `--all` | name a root or use `--all` |
|
|
262
|
+
| `similarity_cap` | clusters handed more than 3000 files (the O(n²) matcher would crawl) | partition per-directory: `slopkeeper clusters <root>/<subdir>`, or organize smaller scopes |
|
|
263
|
+
| `plan_not_json` | plan file is not valid JSON | fix the JSON syntax and re-validate |
|
|
264
|
+
| `plan_unreadable` | plan file cannot be read | check the path exists and is readable |
|
|
265
|
+
| `plan_not_object` | plan JSON parses but is not an object | the plan is a JSON object with `schema` and `moves` — not a bare list |
|
|
266
|
+
| `plan_missing_field` | plan is missing a required field | add it; see `slopkeeper plan schema` |
|
|
267
|
+
| `plan_bad_schema` | `plan.schema` is not the current schema | regenerate from the current template: `slopkeeper plan schema` |
|
|
268
|
+
| `plan_wrong_root` | the plan's `corpus_root` names a different corpus | re-target or regenerate the plan for this root |
|
|
269
|
+
| `plan_bad_move` | a move lacks `from`/`to` | every move is `{"from": "...", "to": "..."}` |
|
|
270
|
+
| `plan_noop_move` | a move's `from` equals its `to` | drop the no-op move |
|
|
271
|
+
| `plan_dup_src` | two moves share a source | one move per file |
|
|
272
|
+
| `plan_dup_dst` | two moves share a destination | resolve the collision — chains (a→b, b→c) are fine, two files onto one path are not |
|
|
273
|
+
| `plan_dst_under_src` | a destination is inside another move's source | move the directory whole, or flatten the plan |
|
|
274
|
+
| `plan_bad_edit` | an edit lacks `file`/`find`/`replace` | every edit carries all three; see `slopkeeper plan schema` |
|
|
275
|
+
| `plan_bad_folder` | a folder entry has no `path` | every folder entry names a path |
|
|
276
|
+
| `plan_bad_kind` | folder `kind` is unknown | kind is one of bot, case, topic, project, archive, scratch |
|
|
277
|
+
| `move_cycle` | the moves form a cycle (a→b→a) | remove the circular moves from the plan |
|
|
278
|
+
| `symlink_escape` | a path resolves outside the root via a symlink | remove the symlink or move its target explicitly |
|
|
279
|
+
| `no_corpus` | the argument to `doctor` is not a directory | pass the directory you want to organize |
|
|
280
|
+
| `no_plan` | REPL: no compiled plan to act on (`/apply`/`/render`/`/scaffold` before `/plan`, or decisions changed since) | `/plan` first — it compiles decisions, validates and saves; `/load` + `/plan` for an agent-authored plan |
|
|
281
|
+
| `no_such_move` | REPL: not a move number | `/moves` lists the working plan's moves |
|
|
282
|
+
| `no_basename` | REPL: `/refs` called with no basename | `/refs <basename>` |
|
|
283
|
+
| `no_path` | REPL: a required path argument is missing or empty | see the usage line in the message |
|
|
284
|
+
| `no_such_journal` | REPL: not a journal index (or a non-numeric one) | `/undo` lists journals; `/undo <n> [!]` |
|
|
285
|
+
| `cat_unreadable` | REPL: the `/cat` target cannot be read | check the path (it is relative to the corpus root) |
|
|
286
|
+
| `edit_unreadable` | an edit target cannot be read | check the file exists and is readable |
|
|
287
|
+
| `journal_unreadable` | the journal file cannot be read | check the journal path |
|
|
288
|
+
| `journal_empty` | the journal contains no entries | nothing to undo |
|
|
289
|
+
| `corpus_too_big` | a corpus or scan dir exceeds its cap (2000 for the refs scan, 3000 filed files for draft) | partition per-directory, scan a narrower dir, or grep by hand |
|
|
290
|
+
| `unknown_strategy` | no strategy with that id | `slopkeeper strategies` for the available ids |
|
|
291
|
+
|
|
292
|
+
## Sizing
|
|
293
|
+
|
|
294
|
+
- Inventory: at ≤ 800 files the full JSON (heads + cross-references)
|
|
295
|
+
is one read — read it whole. Above that a full read is too big
|
|
296
|
+
(~1000 files ≈ 150k tokens): use `--fast --summary`, then
|
|
297
|
+
`inventory <subdir>` for the dirs you plan to reorganize, plus
|
|
298
|
+
title/head from the summary. Inventory refuses past 8000 files.
|
|
299
|
+
- Reference scanning: apply and `refs` scan at most 2000 files — past
|
|
300
|
+
that the result is `reference_scan: "skipped_corpus_too_big"`,
|
|
301
|
+
never a silent empty risk set. (The inventory's embedded
|
|
302
|
+
cross-reference scan only runs at ≤ 800 files.)
|
|
303
|
+
- Clusters and draft: 3000 files. Past it `clusters` refuses with
|
|
304
|
+
`similarity_cap` (draft: `corpus_too_big`) — partition
|
|
305
|
+
per-directory instead.
|
slopkeeper/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Greg Brandt <brandt.greg@gmail.com>
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: MIT
|
|
4
|
+
|
|
5
|
+
"""slopkeeper — inventory, partition and scaffold messy file corpora.
|
|
6
|
+
|
|
7
|
+
Agent-first CLI: the calling agent is the brain; this tool is the hands
|
|
8
|
+
and the seatbelt. No LLM calls, no network, stdlib only.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__version__ = "0.1.0"
|
|
12
|
+
VERSION = __version__
|