modus-operandi 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.
- modus_operandi-0.1.0/LICENSE +21 -0
- modus_operandi-0.1.0/PKG-INFO +140 -0
- modus_operandi-0.1.0/README.md +116 -0
- modus_operandi-0.1.0/pyproject.toml +73 -0
- modus_operandi-0.1.0/pyproject.toml.orig +69 -0
- modus_operandi-0.1.0/src/modus_operandi/__init__.py +24 -0
- modus_operandi-0.1.0/src/modus_operandi/bootstrap.py +52 -0
- modus_operandi-0.1.0/src/modus_operandi/cli.py +313 -0
- modus_operandi-0.1.0/src/modus_operandi/config.py +199 -0
- modus_operandi-0.1.0/src/modus_operandi/data/config.example.yml +37 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/adr-task-id.sh +32 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/adr_utils.py +74 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/agent-step.sh +38 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/agent_call.py +34 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/agent_log_tailer.py +121 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/buffered_emitter.py +156 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/check_implementation.py +109 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/check_plan_deviation.py +43 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/check_questions.py +55 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/check_review.py +149 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/clear-feedback.sh +12 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/config_invocation.py +197 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/determine-scope.sh +57 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/display.py +89 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/editor.py +97 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/engine_output.py +95 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/feedback_editor.py +136 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/feedback_gate.py +86 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/gate_state.py +89 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/implement-pass-check.sh +23 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/implement-retry.sh +25 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/latency_table.py +223 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/live_lines.py +339 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/live_monitor.py +201 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/name-task.sh +90 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/notify.py +55 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/pass-check.sh +27 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/prompt_subst.sh +20 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/pty_spawn.py +155 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/review-check.sh +83 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/review-task-id.sh +22 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/run-agent-cursor.sh +208 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/run-agent.sh +268 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/run_finish.py +102 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/run_id_discoverer.py +30 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/run_pipeline.py +283 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/run_state.py +33 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/run_statistics.py +274 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/save_adr.py +130 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/session_store.sh +137 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/show-file.sh +38 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/stdout_reader.py +117 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/step_result_poller.py +62 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/table_format.py +103 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/task_utils.py +25 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/usage_parser.py +142 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/validate_inputs.py +114 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/warm-planner.sh +47 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/workflow_info.py +89 -0
- modus_operandi-0.1.0/src/modus_operandi/data/pipeline_scripts/wrapper_cli.py +57 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/adr/bug-review.md +3 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/adr/comment-fix.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/adr/comment-review.md +3 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/adr/executor-questions.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/adr/implement-continue.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/adr/implement-retry.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/adr/implement.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/adr/planner-agreement.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/adr/planner-answers.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/adr/review.md +3 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/adr/srp-review.md +40 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/bug-fix.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/fix-all.md +7 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/fix.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/review/bug-rereview.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/review/bug-review.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/review/comment-fix.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/review/comment-rereview.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/review/comment-review.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/review/review-rereview.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/review/review.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/review/srp-rereview.md +25 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/review/srp-review.md +42 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/review/warmup.md +12 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/srp-fix.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/task/research.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/task/study-revise.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/task/study.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/task/write-adr.md +99 -0
- modus_operandi-0.1.0/src/modus_operandi/data/prompts/task/write-plan.md +1 -0
- modus_operandi-0.1.0/src/modus_operandi/data/roles/executor.md +14 -0
- modus_operandi-0.1.0/src/modus_operandi/data/roles/planner.md +13 -0
- modus_operandi-0.1.0/src/modus_operandi/data/victory.wav +0 -0
- modus_operandi-0.1.0/src/modus_operandi/data/workflows/review-pipeline.yml +107 -0
- modus_operandi-0.1.0/src/modus_operandi/data/workflows/task-pipeline.yml +341 -0
- modus_operandi-0.1.0/src/modus_operandi/edit_command.py +113 -0
- modus_operandi-0.1.0/src/modus_operandi/editor.py +97 -0
- modus_operandi-0.1.0/src/modus_operandi/exceptions/__init__.py +17 -0
- modus_operandi-0.1.0/src/modus_operandi/exceptions/edit_requested.py +6 -0
- modus_operandi-0.1.0/src/modus_operandi/exceptions/help_requested.py +5 -0
- modus_operandi-0.1.0/src/modus_operandi/exceptions/invalid_invocation.py +6 -0
- modus_operandi-0.1.0/src/modus_operandi/exceptions/uninstall_requested.py +12 -0
- modus_operandi-0.1.0/src/modus_operandi/installer.py +74 -0
- modus_operandi-0.1.0/src/modus_operandi/installer_cli.py +165 -0
- modus_operandi-0.1.0/src/modus_operandi/paths.py +117 -0
- modus_operandi-0.1.0/src/modus_operandi/proc.py +25 -0
- modus_operandi-0.1.0/src/modus_operandi/prompt.py +12 -0
- modus_operandi-0.1.0/src/modus_operandi/render.py +303 -0
- modus_operandi-0.1.0/src/modus_operandi/tool_discovery.py +11 -0
- modus_operandi-0.1.0/src/modus_operandi/uninstall.py +184 -0
- modus_operandi-0.1.0/src/modus_operandi/verify.py +230 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Denis Riazantsev (metacodeine@gmail.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: modus-operandi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Spec-driven planner -> executor pipelines for opencode and cursor
|
|
5
|
+
Author: Denis Riazantsev
|
|
6
|
+
Author-email: Denis Riazantsev <metacodeine@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Operating System :: POSIX
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
19
|
+
Requires-Dist: specify-cli>=0.16
|
|
20
|
+
Requires-Dist: pyyaml>=6.0
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Project-URL: repository, https://github.com/DenisRyazantsev/modus-operandi
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# Modus Operandi 🤌
|
|
26
|
+
|
|
27
|
+
Automate spec-driven "planner -> executor" workflows in LLM clients (opencode
|
|
28
|
+
and cursor): task planning, task implementing and code review.
|
|
29
|
+
|
|
30
|
+
## Quick start
|
|
31
|
+
|
|
32
|
+
```shell
|
|
33
|
+
pip install modus-operandi
|
|
34
|
+
modus-operandi task "<task-description>"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
No configuration needed: the defaults use the free OpenCode Zen models
|
|
38
|
+
(`opencode/big-pickle` for planning and review, `opencode/deepseek-v4-flash-free`
|
|
39
|
+
for implementation), which work out of the box. To switch models or backends,
|
|
40
|
+
run `modus-operandi edit`.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
`modus-operandi` works right after install: the first run renders the pipeline
|
|
45
|
+
files (agents, scripts, workflows, prompts) into `~/.config/modus-operandi/` and
|
|
46
|
+
`~/.config/opencode/`, and creates `~/.config/modus-operandi/config.yml` from the
|
|
47
|
+
example. Updates arrive through PyPI (`pip install -U modus-operandi`); the render
|
|
48
|
+
is re-applied automatically on the next run.
|
|
49
|
+
|
|
50
|
+
Requirements: Python >= 3.12, and one backend CLI on PATH — `opencode`
|
|
51
|
+
(default) or `cursor-agent` (see `--backend cursor` below).
|
|
52
|
+
|
|
53
|
+
The dev flow (`python3 install.py` from a checkout) additionally installs a
|
|
54
|
+
checkout-based `modus-operandi` command into `~/.local/bin` and keeps that
|
|
55
|
+
directory on PATH in your existing shell rc files (`~/.bashrc`, `~/.zshrc`,
|
|
56
|
+
`~/.profile`), so the command is available in every newly opened terminal.
|
|
57
|
+
|
|
58
|
+
## Usage
|
|
59
|
+
|
|
60
|
+
### Task
|
|
61
|
+
|
|
62
|
+
If you have a workflow like this
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
1. Studying the motivation of the task with a planner-agent
|
|
66
|
+
2. Creating an ADR
|
|
67
|
+
3. Writing an implementation plan for the executor
|
|
68
|
+
4. The executor-agent asking clarifying questions about the task
|
|
69
|
+
5. The planner-agent answering these questions
|
|
70
|
+
6. Implementing the task
|
|
71
|
+
7. Reviewing for SRP violations
|
|
72
|
+
8. Reviewing for bugs
|
|
73
|
+
9. General review (correctness and quality)
|
|
74
|
+
10. Comment review (readability)
|
|
75
|
+
11. Fixing the issues found
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The ADR records the decision (what and why) and is published to the
|
|
79
|
+
`architecture/` directory at the end of the run in its final version; the
|
|
80
|
+
implementation plan stays internal to the executor.
|
|
81
|
+
|
|
82
|
+
then you can use this command
|
|
83
|
+
|
|
84
|
+
```shell
|
|
85
|
+
modus-operandi task "<task-description>"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Review
|
|
89
|
+
|
|
90
|
+
If you have a workflow like this
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
1. Reviewing the whole project for SRP violations
|
|
94
|
+
2. Reviewing the whole project for bugs
|
|
95
|
+
3. Comment review (readability)
|
|
96
|
+
4. Fixing the issues found
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
then you can use this command
|
|
100
|
+
|
|
101
|
+
```shell
|
|
102
|
+
modus-operandi review
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
To review only the changes between the current branch and the default branch:
|
|
106
|
+
|
|
107
|
+
```shell
|
|
108
|
+
modus-operandi review --branch-diff
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Backend
|
|
112
|
+
|
|
113
|
+
By default, opencode is used. If you need the cursor backend, use the
|
|
114
|
+
`--backend cursor` flag:
|
|
115
|
+
|
|
116
|
+
```shell
|
|
117
|
+
modus-operandi --backend cursor task "<task-description>"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Config
|
|
121
|
+
|
|
122
|
+
```shell
|
|
123
|
+
modus-operandi edit
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
opens `~/.config/modus-operandi/config.yml` in your terminal editor. On save and
|
|
127
|
+
close it validates the file: a valid config is applied, an invalid one is
|
|
128
|
+
rolled back and the error is reported with its line number; a config left
|
|
129
|
+
unchanged is not re-applied.
|
|
130
|
+
|
|
131
|
+
## Uninstall
|
|
132
|
+
|
|
133
|
+
```shell
|
|
134
|
+
modus-operandi uninstall
|
|
135
|
+
pip uninstall modus-operandi
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
removes the rendered pipeline files (`~/.config/modus-operandi` and the modus-operandi
|
|
139
|
+
files under `~/.config/opencode`) — run `pip uninstall modus-operandi` afterwards
|
|
140
|
+
to remove the package itself.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Modus Operandi 🤌
|
|
2
|
+
|
|
3
|
+
Automate spec-driven "planner -> executor" workflows in LLM clients (opencode
|
|
4
|
+
and cursor): task planning, task implementing and code review.
|
|
5
|
+
|
|
6
|
+
## Quick start
|
|
7
|
+
|
|
8
|
+
```shell
|
|
9
|
+
pip install modus-operandi
|
|
10
|
+
modus-operandi task "<task-description>"
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
No configuration needed: the defaults use the free OpenCode Zen models
|
|
14
|
+
(`opencode/big-pickle` for planning and review, `opencode/deepseek-v4-flash-free`
|
|
15
|
+
for implementation), which work out of the box. To switch models or backends,
|
|
16
|
+
run `modus-operandi edit`.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
`modus-operandi` works right after install: the first run renders the pipeline
|
|
21
|
+
files (agents, scripts, workflows, prompts) into `~/.config/modus-operandi/` and
|
|
22
|
+
`~/.config/opencode/`, and creates `~/.config/modus-operandi/config.yml` from the
|
|
23
|
+
example. Updates arrive through PyPI (`pip install -U modus-operandi`); the render
|
|
24
|
+
is re-applied automatically on the next run.
|
|
25
|
+
|
|
26
|
+
Requirements: Python >= 3.12, and one backend CLI on PATH — `opencode`
|
|
27
|
+
(default) or `cursor-agent` (see `--backend cursor` below).
|
|
28
|
+
|
|
29
|
+
The dev flow (`python3 install.py` from a checkout) additionally installs a
|
|
30
|
+
checkout-based `modus-operandi` command into `~/.local/bin` and keeps that
|
|
31
|
+
directory on PATH in your existing shell rc files (`~/.bashrc`, `~/.zshrc`,
|
|
32
|
+
`~/.profile`), so the command is available in every newly opened terminal.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
### Task
|
|
37
|
+
|
|
38
|
+
If you have a workflow like this
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
1. Studying the motivation of the task with a planner-agent
|
|
42
|
+
2. Creating an ADR
|
|
43
|
+
3. Writing an implementation plan for the executor
|
|
44
|
+
4. The executor-agent asking clarifying questions about the task
|
|
45
|
+
5. The planner-agent answering these questions
|
|
46
|
+
6. Implementing the task
|
|
47
|
+
7. Reviewing for SRP violations
|
|
48
|
+
8. Reviewing for bugs
|
|
49
|
+
9. General review (correctness and quality)
|
|
50
|
+
10. Comment review (readability)
|
|
51
|
+
11. Fixing the issues found
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The ADR records the decision (what and why) and is published to the
|
|
55
|
+
`architecture/` directory at the end of the run in its final version; the
|
|
56
|
+
implementation plan stays internal to the executor.
|
|
57
|
+
|
|
58
|
+
then you can use this command
|
|
59
|
+
|
|
60
|
+
```shell
|
|
61
|
+
modus-operandi task "<task-description>"
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Review
|
|
65
|
+
|
|
66
|
+
If you have a workflow like this
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
1. Reviewing the whole project for SRP violations
|
|
70
|
+
2. Reviewing the whole project for bugs
|
|
71
|
+
3. Comment review (readability)
|
|
72
|
+
4. Fixing the issues found
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
then you can use this command
|
|
76
|
+
|
|
77
|
+
```shell
|
|
78
|
+
modus-operandi review
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
To review only the changes between the current branch and the default branch:
|
|
82
|
+
|
|
83
|
+
```shell
|
|
84
|
+
modus-operandi review --branch-diff
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Backend
|
|
88
|
+
|
|
89
|
+
By default, opencode is used. If you need the cursor backend, use the
|
|
90
|
+
`--backend cursor` flag:
|
|
91
|
+
|
|
92
|
+
```shell
|
|
93
|
+
modus-operandi --backend cursor task "<task-description>"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Config
|
|
97
|
+
|
|
98
|
+
```shell
|
|
99
|
+
modus-operandi edit
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
opens `~/.config/modus-operandi/config.yml` in your terminal editor. On save and
|
|
103
|
+
close it validates the file: a valid config is applied, an invalid one is
|
|
104
|
+
rolled back and the error is reported with its line number; a config left
|
|
105
|
+
unchanged is not re-applied.
|
|
106
|
+
|
|
107
|
+
## Uninstall
|
|
108
|
+
|
|
109
|
+
```shell
|
|
110
|
+
modus-operandi uninstall
|
|
111
|
+
pip uninstall modus-operandi
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
removes the rendered pipeline files (`~/.config/modus-operandi` and the modus-operandi
|
|
115
|
+
files under `~/.config/opencode`) — run `pip uninstall modus-operandi` afterwards
|
|
116
|
+
to remove the package itself.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build >= 0.12.1, <0.13.0"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "modus-operandi"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Spec-driven planner -> executor pipelines for opencode and cursor"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Environment :: Console",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: POSIX",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
23
|
+
"Topic :: Software Development :: Build Tools",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"specify-cli>=0.16",
|
|
27
|
+
"pyyaml>=6.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[[project.authors]]
|
|
31
|
+
name = "Denis Riazantsev"
|
|
32
|
+
email = "metacodeine@gmail.com"
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
repository = "https://github.com/DenisRyazantsev/modus-operandi"
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
modus-operandi = "modus_operandi.cli:main"
|
|
39
|
+
|
|
40
|
+
[dependency-groups]
|
|
41
|
+
dev = [
|
|
42
|
+
"pytest~=9.1.1",
|
|
43
|
+
"ruff~=0.16.4",
|
|
44
|
+
"mypy~=2.3.1",
|
|
45
|
+
"types-PyYAML==6.0.12.20260815",
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
[tool.ruff]
|
|
49
|
+
target-version = "py312"
|
|
50
|
+
line-length = 100
|
|
51
|
+
extend-include = [".github/scripts/*.py"]
|
|
52
|
+
|
|
53
|
+
[tool.ruff.lint]
|
|
54
|
+
select = [
|
|
55
|
+
"E",
|
|
56
|
+
"F",
|
|
57
|
+
"I",
|
|
58
|
+
"W",
|
|
59
|
+
"B",
|
|
60
|
+
"UP",
|
|
61
|
+
"SIM",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[tool.mypy]
|
|
65
|
+
python_version = "3.12"
|
|
66
|
+
strict = true
|
|
67
|
+
explicit_package_bases = true
|
|
68
|
+
files = ["."]
|
|
69
|
+
mypy_path = [
|
|
70
|
+
"src",
|
|
71
|
+
"src/modus_operandi/data/pipeline_scripts",
|
|
72
|
+
".",
|
|
73
|
+
]
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["uv_build >= 0.12.1, <0.13.0"]
|
|
3
|
+
build-backend = "uv_build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "modus-operandi"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Spec-driven planner -> executor pipelines for opencode and cursor"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Denis Riazantsev", email = "metacodeine@gmail.com" },
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: POSIX",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Programming Language :: Python :: 3.14",
|
|
26
|
+
"Topic :: Software Development :: Build Tools",
|
|
27
|
+
]
|
|
28
|
+
dependencies = ["specify-cli>=0.16", "pyyaml>=6.0"]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
repository = "https://github.com/DenisRyazantsev/modus-operandi"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
modus-operandi = "modus_operandi.cli:main"
|
|
35
|
+
|
|
36
|
+
[dependency-groups]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest~=9.1.1",
|
|
39
|
+
"ruff~=0.16.4",
|
|
40
|
+
"mypy~=2.3.1",
|
|
41
|
+
# types-PyYAML does not follow semver: the version is the targeted
|
|
42
|
+
# PyYAML version + a typeshed calver (date) suffix, and any stub update
|
|
43
|
+
# can change type-checking results. Pin exactly.
|
|
44
|
+
"types-PyYAML==6.0.12.20260815",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
[tool.ruff]
|
|
48
|
+
target-version = "py312"
|
|
49
|
+
line-length = 100
|
|
50
|
+
lint.select = ["E", "F", "I", "W", "B", "UP", "SIM"]
|
|
51
|
+
# CI scripts live in the hidden .github/ tree, which ruff skips by default;
|
|
52
|
+
# they are first-class code and must be linted like everything else.
|
|
53
|
+
extend-include = [".github/scripts/*.py"]
|
|
54
|
+
|
|
55
|
+
[tool.mypy]
|
|
56
|
+
python_version = "3.12"
|
|
57
|
+
strict = true
|
|
58
|
+
# The standalone data scripts are not part of the modus_operandi package (no
|
|
59
|
+
# __init__.py under data/): with explicit package bases mypy maps them only
|
|
60
|
+
# through mypy_path, so their sibling imports resolve to the same module
|
|
61
|
+
# names the scripts use at runtime.
|
|
62
|
+
explicit_package_bases = true
|
|
63
|
+
# Everything is checked: the whole repo tree, no whitelist, no exclude.
|
|
64
|
+
files = ["."]
|
|
65
|
+
# The standalone scripts import each other from the same directory (e.g.
|
|
66
|
+
# save_adr.py imports adr_utils/agent_call/task_utils); point mypy at it so
|
|
67
|
+
# those sibling imports resolve. "src" is the explicit package base of the
|
|
68
|
+
# modus_operandi package.
|
|
69
|
+
mypy_path = ["src", "src/modus_operandi/data/pipeline_scripts", "."]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""modus-operandi package: the launcher and the install/bootstrap flow.
|
|
2
|
+
|
|
3
|
+
The package carries the pipeline artifacts as package data under
|
|
4
|
+
``data/`` (pipeline scripts, prompts, workflows, the config example and the
|
|
5
|
+
victory sound) and renders them into the user config base on first run
|
|
6
|
+
(``modus_operandi.cli.ensure_installed``). ``__version__`` is mirrored in
|
|
7
|
+
``pyproject.toml``; both are bumped together on release.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .paths import Paths
|
|
11
|
+
|
|
12
|
+
# The package's public surface: the install layout type and the
|
|
13
|
+
# user-facing install failure. (The explicit __all__ marks Paths as a
|
|
14
|
+
# re-export of the paths.py alias; InstallError and __version__ are
|
|
15
|
+
# defined in this module.)
|
|
16
|
+
__all__ = ["InstallError", "Paths", "__version__"]
|
|
17
|
+
|
|
18
|
+
__version__ = "0.1.0"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class InstallError(Exception):
|
|
22
|
+
"""User-facing failure of the install/bootstrap flow."""
|
|
23
|
+
|
|
24
|
+
pass
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"""Bootstrap: materialize the rendered installation on first run / update.
|
|
2
|
+
|
|
3
|
+
One responsibility: check the ``install-version.txt`` marker against the
|
|
4
|
+
package ``__version__`` and, when the rendered artifacts are missing or
|
|
5
|
+
stale, render them from the package data (prerequisites check + apply) and
|
|
6
|
+
report one status line. The launcher (cli.py) owns the argument parsing and
|
|
7
|
+
dispatch; this module owns the installation state — it changes together with
|
|
8
|
+
installer/verify, not with the command surface.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
from . import InstallError, Paths, __version__, installer, verify
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def ensure_installed(layout: Paths, check_prereqs: bool = True) -> int:
|
|
19
|
+
"""Bootstrap the rendered installation from the package data.
|
|
20
|
+
|
|
21
|
+
Reads ``install-version.txt`` (config base /modus-operandi/install-version.txt):
|
|
22
|
+
when the file is missing or its version differs from the package
|
|
23
|
+
``__version__``, checks the prerequisites and renders every artifact from
|
|
24
|
+
the package data (``apply()`` records the marker), then prints exactly
|
|
25
|
+
one status line. Returns 0 on success (including an up-to-date
|
|
26
|
+
installation) and 1 on error, with the reason on stderr.
|
|
27
|
+
|
|
28
|
+
``check_prereqs=False`` skips the tool-presence checks: the `edit` flow
|
|
29
|
+
never runs the backend CLI, so a user who uninstalled it (or switched
|
|
30
|
+
configs) must still be able to edit the config and re-render the
|
|
31
|
+
artifacts.
|
|
32
|
+
"""
|
|
33
|
+
marker = layout["install_version"]
|
|
34
|
+
if marker.exists() and marker.read_text(encoding="utf-8").strip() == __version__:
|
|
35
|
+
return 0
|
|
36
|
+
was_installed = marker.exists()
|
|
37
|
+
if check_prereqs:
|
|
38
|
+
try:
|
|
39
|
+
verify.check_prerequisites(layout)
|
|
40
|
+
except InstallError as exc:
|
|
41
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
42
|
+
return 1
|
|
43
|
+
try:
|
|
44
|
+
installer.apply(layout)
|
|
45
|
+
except (InstallError, OSError, ValueError, KeyError) as exc:
|
|
46
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
47
|
+
return 1
|
|
48
|
+
if was_installed:
|
|
49
|
+
print(f"modus-operandi: updated to {__version__}", flush=True)
|
|
50
|
+
else:
|
|
51
|
+
print(f"modus-operandi: installed to {layout['config_dir']}", flush=True)
|
|
52
|
+
return 0
|