squalor-slicer 1.0.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 (92) hide show
  1. squalor_slicer-1.0.0/LICENSE +202 -0
  2. squalor_slicer-1.0.0/PKG-INFO +457 -0
  3. squalor_slicer-1.0.0/README.md +435 -0
  4. squalor_slicer-1.0.0/pyproject.toml +40 -0
  5. squalor_slicer-1.0.0/setup.cfg +4 -0
  6. squalor_slicer-1.0.0/src/slicer/__init__.py +10 -0
  7. squalor_slicer-1.0.0/src/slicer/__main__.py +8 -0
  8. squalor_slicer-1.0.0/src/slicer/ai.py +134 -0
  9. squalor_slicer-1.0.0/src/slicer/check.py +57 -0
  10. squalor_slicer-1.0.0/src/slicer/cli.py +1957 -0
  11. squalor_slicer-1.0.0/src/slicer/config.py +287 -0
  12. squalor_slicer-1.0.0/src/slicer/errors.py +89 -0
  13. squalor_slicer-1.0.0/src/slicer/graph.py +178 -0
  14. squalor_slicer-1.0.0/src/slicer/ids.py +110 -0
  15. squalor_slicer-1.0.0/src/slicer/jsonio.py +80 -0
  16. squalor_slicer-1.0.0/src/slicer/legacy.py +292 -0
  17. squalor_slicer-1.0.0/src/slicer/migrator.py +386 -0
  18. squalor_slicer-1.0.0/src/slicer/model.py +491 -0
  19. squalor_slicer-1.0.0/src/slicer/ops.py +1192 -0
  20. squalor_slicer-1.0.0/src/slicer/outline.py +235 -0
  21. squalor_slicer-1.0.0/src/slicer/prose.py +88 -0
  22. squalor_slicer-1.0.0/src/slicer/py.typed +0 -0
  23. squalor_slicer-1.0.0/src/slicer/render.py +452 -0
  24. squalor_slicer-1.0.0/src/slicer/store.py +389 -0
  25. squalor_slicer-1.0.0/src/slicer/sync.py +118 -0
  26. squalor_slicer-1.0.0/src/slicer/templates/roadmap.md +13 -0
  27. squalor_slicer-1.0.0/src/slicer/templates/row.md +1 -0
  28. squalor_slicer-1.0.0/src/slicer/templates/slice.md +5 -0
  29. squalor_slicer-1.0.0/src/slicer/templates.py +21 -0
  30. squalor_slicer-1.0.0/src/slicer/tui.py +1069 -0
  31. squalor_slicer-1.0.0/src/slicer/tui_style.py +133 -0
  32. squalor_slicer-1.0.0/src/slicer/tui_wizard.py +247 -0
  33. squalor_slicer-1.0.0/src/slicer/vcs.py +267 -0
  34. squalor_slicer-1.0.0/src/slicer/verify.py +185 -0
  35. squalor_slicer-1.0.0/src/squalor_slicer.egg-info/PKG-INFO +457 -0
  36. squalor_slicer-1.0.0/src/squalor_slicer.egg-info/SOURCES.txt +90 -0
  37. squalor_slicer-1.0.0/src/squalor_slicer.egg-info/dependency_links.txt +1 -0
  38. squalor_slicer-1.0.0/src/squalor_slicer.egg-info/entry_points.txt +2 -0
  39. squalor_slicer-1.0.0/src/squalor_slicer.egg-info/top_level.txt +1 -0
  40. squalor_slicer-1.0.0/tests/test_add_options.py +110 -0
  41. squalor_slicer-1.0.0/tests/test_ai.py +240 -0
  42. squalor_slicer-1.0.0/tests/test_atomic.py +99 -0
  43. squalor_slicer-1.0.0/tests/test_batch.py +135 -0
  44. squalor_slicer-1.0.0/tests/test_boundary.py +149 -0
  45. squalor_slicer-1.0.0/tests/test_claim.py +147 -0
  46. squalor_slicer-1.0.0/tests/test_cli.py +1186 -0
  47. squalor_slicer-1.0.0/tests/test_code_location.py +72 -0
  48. squalor_slicer-1.0.0/tests/test_config.py +140 -0
  49. squalor_slicer-1.0.0/tests/test_corruption.py +143 -0
  50. squalor_slicer-1.0.0/tests/test_depends_validation.py +120 -0
  51. squalor_slicer-1.0.0/tests/test_deps.py +61 -0
  52. squalor_slicer-1.0.0/tests/test_docs_coverage.py +57 -0
  53. squalor_slicer-1.0.0/tests/test_edit.py +193 -0
  54. squalor_slicer-1.0.0/tests/test_effort.py +197 -0
  55. squalor_slicer-1.0.0/tests/test_errors.py +325 -0
  56. squalor_slicer-1.0.0/tests/test_find.py +112 -0
  57. squalor_slicer-1.0.0/tests/test_goals.py +55 -0
  58. squalor_slicer-1.0.0/tests/test_handoff.py +200 -0
  59. squalor_slicer-1.0.0/tests/test_ids.py +343 -0
  60. squalor_slicer-1.0.0/tests/test_import.py +413 -0
  61. squalor_slicer-1.0.0/tests/test_isolation.py +84 -0
  62. squalor_slicer-1.0.0/tests/test_lean.py +150 -0
  63. squalor_slicer-1.0.0/tests/test_legacy_index.py +48 -0
  64. squalor_slicer-1.0.0/tests/test_legacy_slice.py +86 -0
  65. squalor_slicer-1.0.0/tests/test_merge.py +379 -0
  66. squalor_slicer-1.0.0/tests/test_migrate.py +437 -0
  67. squalor_slicer-1.0.0/tests/test_next.py +136 -0
  68. squalor_slicer-1.0.0/tests/test_next_offset.py +92 -0
  69. squalor_slicer-1.0.0/tests/test_note.py +84 -0
  70. squalor_slicer-1.0.0/tests/test_ops.py +739 -0
  71. squalor_slicer-1.0.0/tests/test_outline.py +129 -0
  72. squalor_slicer-1.0.0/tests/test_packaging.py +55 -0
  73. squalor_slicer-1.0.0/tests/test_park.py +129 -0
  74. squalor_slicer-1.0.0/tests/test_parser_cache.py +49 -0
  75. squalor_slicer-1.0.0/tests/test_prose.py +276 -0
  76. squalor_slicer-1.0.0/tests/test_ready.py +315 -0
  77. squalor_slicer-1.0.0/tests/test_remove.py +319 -0
  78. squalor_slicer-1.0.0/tests/test_render.py +170 -0
  79. squalor_slicer-1.0.0/tests/test_schema.py +165 -0
  80. squalor_slicer-1.0.0/tests/test_scoring.py +215 -0
  81. squalor_slicer-1.0.0/tests/test_sort.py +50 -0
  82. squalor_slicer-1.0.0/tests/test_start.py +290 -0
  83. squalor_slicer-1.0.0/tests/test_status.py +65 -0
  84. squalor_slicer-1.0.0/tests/test_status_keys.py +61 -0
  85. squalor_slicer-1.0.0/tests/test_store.py +69 -0
  86. squalor_slicer-1.0.0/tests/test_sync.py +112 -0
  87. squalor_slicer-1.0.0/tests/test_tables.py +209 -0
  88. squalor_slicer-1.0.0/tests/test_tui.py +656 -0
  89. squalor_slicer-1.0.0/tests/test_tui_style.py +274 -0
  90. squalor_slicer-1.0.0/tests/test_tui_wizard.py +395 -0
  91. squalor_slicer-1.0.0/tests/test_unscored_hint.py +76 -0
  92. squalor_slicer-1.0.0/tests/test_worktree_claims.py +207 -0
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2026 Squalor LLC
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
@@ -0,0 +1,457 @@
1
+ Metadata-Version: 2.4
2
+ Name: squalor-slicer
3
+ Version: 1.0.0
4
+ Summary: Roadmap and slice manager for the review -> slice -> implement loop
5
+ Author: Squalor LLC
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/squalor-xyz/slicer
8
+ Project-URL: Source, https://github.com/squalor-xyz/slicer
9
+ Project-URL: Issues, https://github.com/squalor-xyz/slicer/issues
10
+ Classifier: Environment :: Console
11
+ Classifier: License :: OSI Approved :: Apache Software License
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Programming Language :: Python :: 3.14
18
+ Requires-Python: >=3.11
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Dynamic: license-file
22
+
23
+ # slicer
24
+
25
+ Roadmap and slice management for the review → slice → implement → done loop.
26
+
27
+ slicer is AI-friendly: a coding agent can review a project, turn accepted findings into
28
+ an importable roadmap, and work through bounded slices using commands with JSON output.
29
+ For a new project, start with goals and acceptance criteria instead of review findings.
30
+ The AI supplies the review and planning; slicer stores, validates, prioritizes, and
31
+ renders the work. It runs locally without an AI service or API key.
32
+
33
+ Run `slicer ai instructions` for a concise agent quick start, or add `--json` for
34
+ machine-readable output. It works before initialization and reads no project state.
35
+
36
+ Start with the [worked workflows](docs/getting-started.md#worked-workflows), use the
37
+ [agent prompts](docs/agents.md#reusable-prompts), or follow the
38
+ [contributor workflow](AGENTS.md#working-on-the-roadmap) to work on slicer itself.
39
+ See the [changelog](CHANGELOG.md) for notable changes by release.
40
+
41
+ State is **JSON**. The markdown under `.slicer/render/` is generated output — readable,
42
+ committed, and never parsed back. Edit through the commands or the TUI, not by hand.
43
+
44
+ Stdlib Python 3.11+, no dependencies.
45
+
46
+ ## Install
47
+
48
+ Python 3.11+, no dependencies. Install it to use slicer; clone it to change slicer.
49
+
50
+ ### Install it (no clone)
51
+
52
+ Into its own virtual environment from PyPI. The distribution is `squalor-slicer`
53
+ because the `slicer` name on PyPI is taken; the command it installs is still `slicer`.
54
+
55
+ ```sh
56
+ python3 -m venv ~/.slicer-venv
57
+ ~/.slicer-venv/bin/pip install squalor-slicer
58
+ ln -s ~/.slicer-venv/bin/slicer ~/.local/bin/slicer # or anywhere on your PATH
59
+ ```
60
+
61
+ `slicer --help` confirms it. Update with
62
+ `~/.slicer-venv/bin/pip install -U squalor-slicer`; uninstall by
63
+ deleting the venv and the symlink.
64
+
65
+ If you prefer a single command, `pip install --user squalor-slicer` also works, with two
66
+ caveats: on macOS Homebrew and recent Debian/Ubuntu/Fedora the system Python is
67
+ "externally managed" (PEP 668) and rejects it — use the venv above instead — and the user
68
+ scripts directory must be on your `PATH` (`~/.local/bin` on Linux,
69
+ `~/Library/Python/3.11/bin` on macOS). Uninstall with `pip uninstall squalor-slicer`. If
70
+ you already use [pipx](https://pipx.pypa.io) or [uv](https://docs.astral.sh/uv/),
71
+ `pipx install squalor-slicer` or `uv tool install squalor-slicer` install it isolated and
72
+ on your `PATH` in one step.
73
+
74
+ To try unreleased main instead of the latest release, replace the package name with
75
+ `git+https://github.com/squalor-xyz/slicer`.
76
+
77
+ ### Develop on it (clone + editable)
78
+
79
+ To hack on slicer itself, use an editable install so the command tracks your checkout:
80
+
81
+ ```sh
82
+ git clone git@github.com:squalor-xyz/slicer.git
83
+ cd slicer
84
+ python3 -m venv .venv
85
+ .venv/bin/pip install -e .
86
+ ln -s "$PWD/.venv/bin/slicer" ~/.local/bin/slicer # or anywhere on your PATH
87
+ ```
88
+
89
+ The install is editable, so `slicer` follows the checkout. `slicer --help` confirms it.
90
+
91
+ ## Use it on a project
92
+
93
+ ```sh
94
+ cd any-repo
95
+ slicer init # creates .slicer/
96
+ slicer add "Parse the config file" --size M --tree core
97
+ slicer promote S01 # give it a slice file from the template
98
+ slicer edit S01 --section Why --text "Load settings before starting the app"
99
+ slicer render # regenerate .slicer/render/
100
+ slicer check # the gate: exit 1 if anything drifted
101
+ ```
102
+
103
+ `add` appends a roadmap row; `promote` gives it a slice file.
104
+
105
+ Got a whole roadmap to load? Write it as a markdown outline and import it in one go:
106
+
107
+ ```sh
108
+ slicer import --skeleton > roadmap.md # a template, built from your config
109
+ slicer import roadmap.md --dry-run # validate; writes nothing
110
+ slicer import roadmap.md # apply
111
+ ```
112
+
113
+ Each `##` heading is an item, optional `key: value` lines carry its size, tree and
114
+ dependencies, and `###` sections become the slice itself — so one file can produce a
115
+ fully written roadmap. It is a one-way ramp: the file is yours to delete afterwards.
116
+
117
+ Already running this workflow by hand in markdown? `slicer migrate --from docs/slices`
118
+ converts an existing tree that is **already in slicer's legacy format**. It refuses to
119
+ write anything unless every file round-trips byte for byte, so a document slicer cannot
120
+ reproduce is never half-migrated.
121
+
122
+ **→ [docs/getting-started.md](docs/getting-started.md)** walks through all of this with
123
+ real output. [docs/import.md](docs/import.md) is the outline format;
124
+ [docs/agents.md](docs/agents.md) is how to drive slicer from an AI agent;
125
+ [docs/migrate-format.md](docs/migrate-format.md) is the legacy grammar; and
126
+ [docs/configuration.md](docs/configuration.md) is every config key.
127
+
128
+ ## Commands
129
+
130
+ | | |
131
+ |---|---|
132
+ | `ai instructions` | agent quick start, available without a project; supports `--json` |
133
+ | `ai skill` | the same loop and exit rules as a `SKILL.md` for Claude Code, Codex, and Grok |
134
+ | `init [--force]` | create `.slicer/` with config and templates; `--force` rewrites an existing config and templates only |
135
+ | `setup-git` | print the two `git config` lines that enable the `slicer-generated` render merge driver in this clone (`slicer setup-git \| sh` applies them); needs no project |
136
+ | `import FILE [--dry-run] [--force]` | bulk-load a roadmap from a markdown outline |
137
+ | `import --skeleton` | print an outline template built from your config |
138
+ | `migrate --from DIR [--dry-run] [--force]` | convert an existing legacy markdown tree; `--force` replaces an existing roadmap |
139
+ | `add TITLE [--id/--size/--tree/--findings/--status/--pass/--importance/--urgency/--effort/--depends-on/--short-title]` | append a roadmap item (no slice file yet); repeat `--depends-on ID` for multiple dependencies. `--effort` is 1–3 and optional; an open item left at importance 2, urgency 2 and no effort gets a stderr hint |
140
+ | `promote ID [--file/--stdin] [--boundary TEXT] [--force]` | give an item a slice file; a one-item outline fills its sections in one call. `--force` overwrites an existing slice |
141
+ | `move ID --before/--after/--to` | reorder the queue; position is the manual priority, and breaks score ties |
142
+ | `sort [--by score\|effort] [--render]` | reorder the whole queue in one step. `score` (default) persists `list --sort score`. `effort` persists `list --sort effort`: lightest estimate first, unset last |
143
+ | `next [-n N] [--start] [--show\|--ready [--section NAME ...]]` | one eligible item at offset N (default 0), with its effective score and status; `--start` marks it started. `--show` adds the full item and its slice. `--ready` returns item identity, the slice, and blocked ids. Repeat `--section` with `--ready` to return the scope boundary and those sections only. An item started or claimed in a sibling Git worktree is skipped and reported (`in_work_elsewhere`), unless this checkout has it too |
144
+ | `next-id` | the id the next `add` or `import` would take, without allocating it |
145
+ | `list [--all] [--status/--tree/--pass/--flag] [--sort score\|effort]` | the queue in `next`'s order: unblocked started, then unblocked open, then the other visible rows, each by effective score. The text table has a CLAIM column: the local owner, `*` for locally in-progress with no claim, `wt:NAME` for work in a sibling worktree, or `-`. `wt:NAME+N` means N more worktrees. `--json` includes `claim` (`{"owner", "at"}` or null) and `in_work_elsewhere` (an array of `{worktree, owner}`). Done and retired items are omitted unless `--all` is set or `--status` names them. Repeat `--flag` to keep an item that has any of those flags. Flags are free-form labels set with `set --flag`. `--sort score` is a flat score sort. `--sort effort` orders estimates 1–3 and puts unset items last, without writing state |
146
+ | `find PATTERN [--in FIELDS]` | search items by text (id, title, findings and slice bodies by default); shows the matched field and a snippet |
147
+ | `deps [ID] [--format mermaid]` | dependencies: unblocked open items, or one item's waits-on/blocked-by/dependents; `--format mermaid` renders the graph |
148
+ | `show ID [--section NAME ...] [--context]` | print one slice or selected sections; `--context` adds title, dependencies, and scope boundary |
149
+ | `set ID [ID ...] --title/--short-title/--size/--tree/--findings/--status/--pass/--depends-on/--flag/--no-flags/--group/--importance/--urgency/--effort/--no-effort` | change fields. `--no-effort` clears an estimate. `add` and `set` refuse an unknown, self, retired, or cycle-closing `--depends-on` and write nothing |
150
+ | `edit ID (--section NAME / --boundary) [--text/--file/--stdin]` | edit a section or scope boundary; sections also accept `--append` |
151
+ | `note ID [--text/--file/--stdin] [--render]` | append a dated note to any item — no slice needed (shows in `show`/`render`, unlike `done --note`) |
152
+ | `prose list / show REF / edit REF` | read and edit the roadmap's own prose |
153
+ | `prose add-pass KEY / drop-pass KEY` | open or close a pass group |
154
+ | `goals` | print the project's goals and non-goals together; supports `--json` |
155
+ | `start ID [ID ...] [--note TEXT]` | mark an item in progress and claim it (owner and time). The owner is `claim_owner` in config, otherwise the git user name, otherwise the worktree name. A second start does not refresh the claim |
156
+ | `release ID [ID ...]` | clear a claim without changing status. An item that was in progress stays in progress and lists as `*` |
157
+ | `handoff ID [ID ...] [--note TEXT]` | hand a started slice to review: status becomes `review_status` and the claim is cleared. `next` skips review items and dependents stay blocked until `done`; a reviewer finds them with `list --status review` and claims one with `start` |
158
+ | `done ID [ID ...]` / `park ID [ID ...]` / `unpark ID [ID ...]` `[--note TEXT]` | change status; `--note` records a one-line *history* entry (for a durable note on the item, use `slicer note`); `done` moves the file with `git mv` and clears a claim |
159
+ | `remove ID --reason "…"` | retire an obsolete item; the id stays claimed |
160
+ | `remove ID --purge` | delete outright, for something that never should have existed |
161
+ | `remove ID --purge/--reason --dry-run` | preview the removal and its fallout (dependents, id fate); write nothing |
162
+ | `remove ID ... --force` | retire or purge despite dependents, or a done item |
163
+ | `render` | regenerate `.slicer/render/` (ROADMAP.md, a browser-viewable ROADMAP.html, and one file per slice) |
164
+ | `sync [--check]` | rewrite derived lines in other documents |
165
+ | `verify` | check the index for consistency, and against `git log` (unless `git_check` is off) |
166
+ | `check [--diff]` | the CI gate: render staleness, sync drift, integrity |
167
+ | `stats` / `log [--limit N] [--item ID] [--action A]` | counts + completion % and per-tree progress; history, newest first (`--limit` defaults to 20; `--item`/`--action` scope it; `set` records old→new values) |
168
+ | `status` | the front door: next item, progress census, and blockers in one view (`--json`) |
169
+ | `tui` / `ui` | browse, read, reorder and edit interactively (two names for the same command) |
170
+
171
+ The sibling worktree signal comes from local `git worktree list` and each checkout's
172
+ `.slicer/index.json`. It sees worktrees on this machine only, not work on another machine.
173
+
174
+ `slicer next -n 1` returns the item after the current next item. Offsets are
175
+ nonnegative integers: `-n 0` is the same as `next`. Eligible started items come
176
+ before eligible open items; each group uses descending effective priority with
177
+ roadmap order breaking ties. Skipping an item does not complete it or unblock its
178
+ dependents. The command returns one item, including rows without slice files;
179
+ an exhausted offset exits 2 (JSON returns `item: null` and blocked details).
180
+ The text form of `slicer list` labels its columns: number, id, status, size,
181
+ effort, score, quadrant, and title. An unset effort appears as `-`. When the
182
+ default status filter hides done or retired items, a final line counts the
183
+ matching hidden rows and points to `--all`. `--all` and `--status` suppress that
184
+ notice; `--json` remains an array of the visible item records.
185
+ `slicer next --ready` is a bounded pickup of that same item: `id`, `title`,
186
+ `status`, `depends_on`, `effective_score`, `path`, the slice when the item has
187
+ one, and every blocked id. The agent loop uses
188
+ `slicer next --ready --section "Implement" --section "Check" --json --lean`.
189
+ The headings are examples; pass the section names the project configures.
190
+ Repeat `--section NAME` with `--ready` to keep the
191
+ scope boundary and those section bodies; omit it and the slice stays complete.
192
+ `--section` without `--ready` is a usage error. A row with no slice still says
193
+ to run `promote`. An empty queue uses the same exit 2 result as `next`.
194
+ Pass either `--ready` or `--show`. The text form of `--ready` stays the
195
+ identity, the boundary, and the section headings.
196
+
197
+ In the TUI, the queue opens in the same ranked order as `slicer list`: eligible
198
+ started items, then eligible open items, then the other visible rows, each by
199
+ effective score, with parked items last. Done and retired stay hidden until
200
+ you clear the filter or ask for them. `o` sorts that view by ranked order, ID, title, status, size,
201
+ importance, urgency, effective score, or effort, ascending or descending. The
202
+ choice lasts for the session and does not rewrite the stored queue. `J`, `K`,
203
+ `T`, and `M` still move items in stored order, and only when no filter or
204
+ search is active. `tab` moves between the queue and the detail pane, `e` opens `$EDITOR` on
205
+ whatever is selected there — an item field (size, trees, findings, depends, importance,
206
+ urgency), a slice section, its scope boundary, a note (edit it, or empty to remove; the
207
+ `+ add a note` line adds one), or a prose block — `s` starts the selected item and `a` adds a
208
+ new one. Press `w` for the guided roadmap wizard; an empty roadmap offers it once
209
+ when the TUI starts.
210
+
211
+ The wizard collects an optional roadmap heading and each item's title, size, trees,
212
+ findings, importance, urgency, group, and dependencies (comma-separated exact titles,
213
+ including later draft items). Enter advances and Shift-Tab goes back. Each configured
214
+ section offers `e` to open `$EDITOR`, or Enter to leave its body as it is. Every item
215
+ gets a slice, even when its section bodies are empty.
216
+
217
+ After adding items, review the answers with Up/Down and Enter to revisit a field or
218
+ section, add another item, or select **Save roadmap**. Esc asks before discarding draft
219
+ answers. Nothing is saved until the final save; validation errors keep the draft for
220
+ correction. A supplied heading is prepended to existing roadmap preamble prose; a blank
221
+ heading leaves it unchanged. Saving generates the normal roadmap output without a
222
+ separate outline file. The project must already be initialized with `slicer init`.
223
+
224
+ The main TUI screen keeps common shortcuts visible below the status and feedback
225
+ lines: pane switching, editing, adding, starting/completing items, search, filters,
226
+ show all, jump, movement, help, and quit. Hints use one row when they fit or two at
227
+ 80 columns, and stay visible after actions. These are fixed defaults; `?` opens
228
+ the complete shortcut list. Prompts and overlays show their own instructions.
229
+
230
+ The TUI initially hides the project's configured done status. View controls:
231
+
232
+ | Key | Action |
233
+ |---|---|
234
+ | `/` | Search IDs, full titles, and short titles as you type; Enter accepts, Esc cancels |
235
+ | `f` | Filter by status, tree, pass, importance, and urgency |
236
+ | `c` | Clear search and all filters, including the default hide-done filter |
237
+ | `g` | Jump to an ID; hidden targets are revealed by clearing search and filters |
238
+ | `J` / `K` | Reorder the selected item down / up |
239
+ | `T` | Move the selected item to the top |
240
+ | `M` | Move the selected item to a numbered position |
241
+ | `?` | Open help; arrows or `j/k` scroll, `?` or Esc closes |
242
+
243
+ In the filter panel, arrows or `j/k` navigate, Space toggles choices, Enter applies,
244
+ and Esc cancels. Each group offers Any; tree and pass also offer `(none)`. Multiple
245
+ choices within a group match any selected value; different groups must all match.
246
+ Importance and urgency use the item's assigned values (1–3), not inherited priority.
247
+ Search is case-insensitive literal text and combines with the filters.
248
+
249
+ The status line shows matching/total item counts and active restrictions. Roadmap
250
+ prose stays accessible below the items and is excluded from those counts. Filters
251
+ last only for this session. `J/K` reorder down/up, `T` moves to the top and `M` moves to
252
+ a numbered position; reordering requires clearing all restrictions with `c`. Filtering
253
+ itself preserves queue order.
254
+
255
+ Queue and Details headings mark the focused pane with `>`. Focused selections use
256
+ reverse/bold; inactive selections retain a marker and bold text. Queue rows show
257
+ `P:22`-style base priority scores (importance × 10 + urgency); an axis of 3 emphasizes
258
+ the score without reordering items. The detail pane retains the score and quadrant.
259
+
260
+ When supported, cyan marks headings/started work, green marks done/success, yellow
261
+ marks parked/blocked work and high priority, and red marks errors. Labels and `!`
262
+ blocked markers remain visible without color. Feedback uses `OK:`, `Error:`, and
263
+ `Info:` prefixes; unchanged actions and cancellations are informational. Set
264
+ `NO_COLOR=1` for monochrome; unsupported terminals also fall back automatically.
265
+ Below 80 columns or 10 rows, the TUI shows a resize prompt and preserves the session.
266
+
267
+ Every command except the interactive `tui`/`ui` takes `--json`, including the failures — an agent calls `slicer next
268
+ --json` rather than parsing markdown, and reads `{"error": {"code": ...}}` rather than
269
+ prose. Exit codes: `0` fine, `1` drift or a failed check, `2` usage, validation, or nothing to do, `3` internal or state (`corrupt`, `locked`, `io`, `config`, `schema_too_new`).
270
+ See [docs/agents.md](docs/agents.md).
271
+
272
+ Every command that changes state — `add`, `set`, `start`, `done`, `move`, `sort`, `promote`,
273
+ `edit`, `note`, `remove`, `park`, `unpark`, `import`, `migrate`, and the `prose` edits — takes `--render`
274
+ to regenerate `.slicer/render/` in the same step, so a mutation and its render are one
275
+ command. By default the change is saved first and rendered after; add `--strict` to require
276
+ the render to succeed first, so a change that cannot be rendered is rolled back rather than
277
+ landed (this is how `done --render` already behaves). The agent loop passes
278
+ `--render --strict` on `start` and on slice edits. `done` stays `--render`.
279
+
280
+ Items carry an Eisenhower-style priority: an `--importance` and an `--urgency` (each 1–3),
281
+ combined into a score (importance leads). A blocker of a critical item inherits its
282
+ priority, so `slicer next` and `slicer list --sort score` surface the blockers of
283
+ important work first, while the stored queue order stays whatever `move` set.
284
+
285
+ **slicer never commits, pushes or tags.** `git` access is allowlisted to
286
+ `rev-parse`, `status`, `log`, `mv`, `ls-files`, and the read-only queries
287
+ `worktree list --porcelain`, `branch --all`, and `config --get user.name`. `start` uses
288
+ the branch and worktree queries to warn when another checkout already refers to the slice;
289
+ the exit code does not change, and
290
+ `next` stays silent. Writing subcommands cannot be reached from the code at all.
291
+
292
+ ### Batch changes
293
+
294
+ `done`, `start`, `release`, `park`, `unpark`, and `set` accept multiple IDs.
295
+ For example, `slicer set S01 S02 --urgency 3 --render` applies the same fields to
296
+ both items. Use `slicer done -` to read whitespace-separated IDs from stdin.
297
+ See [batch changes](docs/getting-started.md#batch-changes) for validation and output rules.
298
+
299
+ ## What lives in `.slicer/`
300
+
301
+ ```
302
+ config.json paths, statuses, fields, templates, sync targets
303
+ index.json the ordered queue plus roadmap prose (preamble, goals, non-goals, epilogue)
304
+ slices/<ID>.json one open slice: title, lead, sections (ordered list)
305
+ slices/done/<ID>.json finished slices
306
+ slices/retired/<ID>.json obsolete slices, with the reason on the item
307
+ templates/*.md render templates, yours to edit
308
+ render/ GENERATED - ROADMAP.md, ROADMAP.html (browser-viewable), and one file per slice
309
+ log.jsonl append-only history of status changes
310
+ .gitattributes union-merges log.jsonl, and keeps generated render/ on merge (see below)
311
+ ```
312
+
313
+ Sections are an ordered **list**, not a map: real slices carry headings no schema names,
314
+ sometimes more than once, and their order is part of the document.
315
+
316
+ Landing work on parallel branches touches these files: `log.jsonl` is append-only and
317
+ union-merges automatically (via the generated `.gitattributes`), so both sides' entries
318
+ survive without a conflict. `index.json` is the source of truth — a genuine overlap there is
319
+ yours to resolve. Anything under `render/` is a projection of `index.json`, so after resolving
320
+ a merge just re-run `slicer render` (and `slicer check` will flag it if you forget) rather than
321
+ merging the generated markdown by hand.
322
+
323
+ The `.gitattributes` also points `render/` at a `slicer-generated` merge driver that keeps the
324
+ current branch's copy instead of writing conflict markers — but a driver name only resolves
325
+ once the clone defines it. Run `slicer setup-git` to print the two lines (or `slicer setup-git
326
+ | sh` to apply them), once per clone — slicer's git allowlist cannot run `git config` for you:
327
+
328
+ ```sh
329
+ git config merge.slicer-generated.name "keep the current branch's generated files"
330
+ git config merge.slicer-generated.driver true
331
+ ```
332
+
333
+ Either way, re-run `slicer render` after resolving `index.json` so the kept files match it.
334
+
335
+ The scope boundary is a separate field on each slice. It renders after metadata and
336
+ before sections, so section edits cannot remove it. Use `slicer edit ID --boundary`
337
+ with `--text`, `--file`, `--stdin`, or the editor to change the full paragraph. Empty
338
+ text clears it. `promote --boundary TEXT` overrides the source/default boundary.
339
+
340
+ ## Removing an item
341
+
342
+ Removal is two different acts, so `remove` has two modes.
343
+
344
+ `remove ID --reason "superseded by S30"` **retires** it: the status becomes `retired`, the
345
+ slice moves to `.slicer/slices/retired/`, and the row keeps rendering with the reason
346
+ beside it. The id stays claimed. This is for something that existed and was cited — a
347
+ commit or a review that names it must still resolve to something that explains itself.
348
+
349
+ `remove ID --purge` **deletes** it: the item and its slice file go. This is for a mistyped
350
+ `add`. The id comes back only when it was the most recently allocated *and* no commit
351
+ subject mentions it — that is undoing an allocation, not reusing an identifier. Any
352
+ earlier id stays burned, and the output says which happened and why.
353
+
354
+ Both refuse when another item depends on it, or when it is `done`; `--force` overrides and
355
+ names the rule it overrode. After a forced purge, `slicer check` reports the dangling
356
+ dependency it left behind. Add `--dry-run` to either mode to preview the outcome first — the
357
+ dependents that would dangle and whether a purge would free or burn the id — without writing;
358
+ it turns that surprise into a decision.
359
+
360
+ Retiring needs a status to move into. A tracking directory created before `remove` existed
361
+ gains a `retired` status automatically on load — only that one key, so a project that
362
+ dropped some other status does not get it back.
363
+
364
+ ## Roadmap prose
365
+
366
+ A roadmap carries text that belongs to no slice: an opening note, a heading and prose
367
+ around each pass group, and a closing section. `slicer prose` addresses those blocks:
368
+
369
+ ```
370
+ preamble the opening note
371
+ goals project goals (see below)
372
+ non_goals project non-goals (see below)
373
+ pass.<key>.heading the group's markdown heading
374
+ pass.<key>.intro prose above the group's table
375
+ pass.<key>.outro prose below it
376
+ epilogue the closing section
377
+ ```
378
+
379
+ `slicer prose list` names every block in the order it renders. Both `edit` and `prose edit`
380
+ take exactly one of `--text`, `--file`, or `--stdin`, or open `$EDITOR` when none is
381
+ supplied. In replacement mode, `--text` preserves the argument exactly, including
382
+ newlines, and `--text ""` clears the body. Section editing also accepts `--append` with an
383
+ explicit source: it joins old and new text with one blank line, removing boundary
384
+ newline characters. Empty appended content leaves the body unchanged.
385
+ For example, `slicer prose edit preamble --text "Current priorities"` replaces the preamble.
386
+ A pass group is opened with `prose add-pass 6 --heading
387
+ "# ..."` and closed with `drop-pass`, which refuses while any item is still filed under
388
+ it. Items are filed with `slicer add --pass 6` or moved with `slicer set <id> --pass 6`.
389
+
390
+ A declared pass renders even with no items yet, so a group can be opened before its first
391
+ slice exists.
392
+
393
+ ## Goals and non-goals
394
+
395
+ The backlog says what is queued; **goals and non-goals** say what the project is *for*, so
396
+ humans and AI agents can judge what belongs on the backlog at all. They are two roadmap
397
+ prose blocks (`goals`, `non_goals`), edited like any other prose and rendered near the top
398
+ of `ROADMAP.md`:
399
+
400
+ ```
401
+ slicer goals print both, or slicer goals --json for agents
402
+ slicer prose edit goals record or revise them (--text/--file/--stdin/$EDITOR)
403
+ slicer prose edit non_goals
404
+ ```
405
+
406
+ `slicer check` keeps the rendered copy current. Set direction with the owner — do not
407
+ infer it from the backlog.
408
+
409
+ slicer's own goals and non-goals: run `slicer goals`, or read them at the top of
410
+ [the roadmap](.slicer/render/ROADMAP.md). In short, slicer is a small, dependency-light,
411
+ file-based store for a project's roadmap, goals, and issues — canonical JSON that humans
412
+ and AI agents plan from, projected deterministically to markdown — and is *not* a
413
+ real-time, multi-user collaboration tool (coordination happens through git).
414
+
415
+ ## Configuration
416
+
417
+ Everything project-specific is in `.slicer/config.json` — status vocabulary and how each
418
+ one renders, the section list `promote` seeds, the scope-boundary marker, which flags
419
+ exclude an item from derived pointers, and the `sync` targets. Nothing is compiled into
420
+ the tool, so slicer works on a repo with no review protocol at all.
421
+
422
+ Every key, its default, and which ones are unsafe to change once items exist:
423
+ [docs/configuration.md](docs/configuration.md). Two to know up front — `id.prefix` and
424
+ `id.width` can change before the first item exists. After allocation the index owns the
425
+ scheme; changing the config does not renumber items, and a mismatch fails `check`.
426
+
427
+ ## Tests
428
+
429
+ ```sh
430
+ python3 -m unittest discover -s tests -t tests
431
+ ```
432
+
433
+ No install step and no dependencies — the suite puts `src/` on `sys.path` itself.
434
+
435
+ `tests/fixtures/legacy/` is a synthetic 14-slice tree for a project that does not
436
+ exist. It is shaped to exercise the awkward parts of the legacy format — a middot
437
+ inside a findings value, singular and plural tree keys, a collective trees cell,
438
+ headings no schema names, prose between the tables — and the suite proves every file
439
+ round-trips through the parser byte for byte.
440
+
441
+ Set `SLICER_LEGACY_TREE=/path/to/docs/slices` to additionally prove the migrator
442
+ against a live markdown tree of your own. That test is skipped when the variable is
443
+ unset, and it is the only way to exercise the migrator against real, messily
444
+ hand-written markdown — worth running before changing `legacy.py` or `migrator.py`.
445
+
446
+ ## Status
447
+
448
+ slicer manages its own roadmap: `.slicer/` in this repository is a worked
449
+ example you can read, and `.slicer/render/ROADMAP.md` is what it renders to. Run
450
+ `slicer --version` for the installed version, and see the [changelog](CHANGELOG.md).
451
+
452
+ [docs/getting-started.md](docs/getting-started.md) is the walkthrough, and
453
+ [docs/agents.md](docs/agents.md) covers driving slicer from an agent.
454
+ [ARCHITECTURE.md](ARCHITECTURE.md) explains the layering and the invariants.
455
+ [AGENTS.md](AGENTS.md) has the commands and the house style.
456
+
457
+ Apache-2.0. Stdlib Python, no dependencies, and none planned.