modus-operandi 0.2.0__tar.gz → 0.2.2__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.
Potentially problematic release.
This version of modus-operandi might be problematic. Click here for more details.
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/PKG-INFO +8 -1
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/README.md +7 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/pyproject.toml +1 -1
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/pyproject.toml.orig +1 -1
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/__init__.py +1 -1
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/config.py +77 -6
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/config_invocation.py +71 -15
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/editor.py +12 -8
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/feedback_editor.py +8 -8
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/run_pipeline.py +18 -7
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/workflows/task-pipeline.yml +3 -3
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/editor.py +12 -8
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/LICENSE +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/bootstrap.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/cli.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/config.example.yml +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/adr-task-id.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/adr_utils.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/agent-step.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/agent_call.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/agent_log_tailer.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/buffered_emitter.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/check_implementation.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/check_plan_deviation.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/check_questions.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/check_review.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/clear-feedback.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/determine-scope.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/display.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/engine_output.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/feedback_gate.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/gate_state.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/implement-pass-check.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/implement-retry.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/latency_table.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/live_lines.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/live_monitor.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/name-task.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/notify.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/pass-check.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/prompt_subst.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/pty_spawn.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/review-check.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/review-task-id.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/run-agent-cursor.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/run-agent.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/run_finish.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/run_id_discoverer.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/run_state.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/run_statistics.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/save_adr.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/session_store.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/show-file.sh +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/stdout_reader.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/step_result_poller.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/table_format.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/task_utils.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/usage_parser.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/validate_inputs.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/workflow_info.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/wrapper_cli.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/bug-review.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/comment-fix.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/comment-review.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/executor-questions.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/implement-continue.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/implement-retry.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/implement.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/planner-agreement.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/planner-answers.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/review.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/srp-review.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/bug-fix.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/fix-all.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/fix.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/bug-rereview.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/bug-review.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/comment-fix.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/comment-rereview.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/comment-review.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/review-rereview.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/review.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/srp-rereview.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/srp-review.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/srp-fix.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/task/research.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/task/study-revise.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/task/study.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/task/write-adr.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/task/write-plan.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/roles/executor.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/roles/planner.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/roles/reviewer-bugs.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/roles/reviewer-comment.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/roles/reviewer-review.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/roles/reviewer-srp.md +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/victory.wav +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/workflows/review-pipeline.yml +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/edit_command.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/exceptions/__init__.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/exceptions/edit_requested.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/exceptions/help_requested.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/exceptions/invalid_invocation.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/exceptions/uninstall_requested.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/installer.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/installer_cli.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/paths.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/proc.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/prompt.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/render.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/tool_discovery.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/uninstall.py +0 -0
- {modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/verify.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: modus-operandi
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Spec-driven planner -> executor pipelines for opencode and cursor
|
|
5
5
|
Author: Denis Riazantsev
|
|
6
6
|
Author-email: Denis Riazantsev <metacodeine@gmail.com>
|
|
@@ -24,6 +24,9 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
|
|
25
25
|
# Modus Operandi 🤌
|
|
26
26
|
|
|
27
|
+
[](https://pypi.org/project/modus-operandi/)
|
|
28
|
+
[](https://pypi.org/project/modus-operandi/)
|
|
29
|
+
|
|
27
30
|
Automate spec-driven "planner -> executor" workflows in LLM clients (opencode
|
|
28
31
|
and cursor): task planning, task implementing and code review.
|
|
29
32
|
|
|
@@ -120,6 +123,10 @@ By default, opencode is used. If you need the cursor backend, use the
|
|
|
120
123
|
modus-operandi --backend cursor task "<task-description>"
|
|
121
124
|
```
|
|
122
125
|
|
|
126
|
+
Cursor runs on its default `composer-2` model for planning, review and
|
|
127
|
+
implementation when no `cursor:` section is configured yet; set your own
|
|
128
|
+
model slugs with `modus-operandi edit`.
|
|
129
|
+
|
|
123
130
|
### Config
|
|
124
131
|
|
|
125
132
|
```shell
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Modus Operandi 🤌
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/modus-operandi/)
|
|
4
|
+
[](https://pypi.org/project/modus-operandi/)
|
|
5
|
+
|
|
3
6
|
Automate spec-driven "planner -> executor" workflows in LLM clients (opencode
|
|
4
7
|
and cursor): task planning, task implementing and code review.
|
|
5
8
|
|
|
@@ -96,6 +99,10 @@ By default, opencode is used. If you need the cursor backend, use the
|
|
|
96
99
|
modus-operandi --backend cursor task "<task-description>"
|
|
97
100
|
```
|
|
98
101
|
|
|
102
|
+
Cursor runs on its default `composer-2` model for planning, review and
|
|
103
|
+
implementation when no `cursor:` section is configured yet; set your own
|
|
104
|
+
model slugs with `modus-operandi edit`.
|
|
105
|
+
|
|
99
106
|
### Config
|
|
100
107
|
|
|
101
108
|
```shell
|
|
@@ -23,6 +23,24 @@ DEFAULT_ADR_DIR = "architecture"
|
|
|
23
23
|
DEFAULT_BACKEND = "opencode"
|
|
24
24
|
BACKENDS = ("opencode", "cursor")
|
|
25
25
|
|
|
26
|
+
# The shipped per-backend role model defaults, mirroring config.example.yml
|
|
27
|
+
# (opencode: the free OpenCode Zen big-pickle; cursor: Cursor's default
|
|
28
|
+
# composer-2). apply_defaults fills the ACTIVE backend's missing role models
|
|
29
|
+
# from these, so switching backends with `modus-operandi edit` works out of
|
|
30
|
+
# the box even on a config created before the backend's section existed
|
|
31
|
+
# (e.g. a legacy pre-cursor config); an existing value (even a placeholder)
|
|
32
|
+
# is never overwritten, so validation still catches placeholders.
|
|
33
|
+
DEFAULT_MODELS: dict[str, dict[str, dict[str, str]]] = {
|
|
34
|
+
"opencode": {
|
|
35
|
+
"planner": {"provider": "opencode", "model": "big-pickle", "reasoning": DEFAULT_REASONING},
|
|
36
|
+
"executor": {"provider": "opencode", "model": "big-pickle", "reasoning": DEFAULT_REASONING},
|
|
37
|
+
},
|
|
38
|
+
"cursor": {
|
|
39
|
+
"planner": {"model": "composer-2"},
|
|
40
|
+
"executor": {"model": "composer-2"},
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
26
44
|
DEFAULT_CONFIG: dict[str, Any] = {
|
|
27
45
|
"backend": DEFAULT_BACKEND,
|
|
28
46
|
"workflow": {
|
|
@@ -51,17 +69,27 @@ def load_config(path: str | Path) -> dict[str, Any]:
|
|
|
51
69
|
# without special-casing yaml types. A root that is valid YAML but not a
|
|
52
70
|
# mapping (a bare scalar or a list) is the same class of user error, and
|
|
53
71
|
# dict(raw) would otherwise raise an unhandled TypeError instead.
|
|
72
|
+
# The file is read as BYTES so PyYAML detects the encodings a desktop
|
|
73
|
+
# editor can save (UTF-8, UTF-16/UTF-32 with BOM); a BOM-less UTF-16 save
|
|
74
|
+
# falls back to an explicit decode — otherwise `modus-operandi edit`
|
|
75
|
+
# rejected (and rolled back) a config saved as UTF-16 by a macOS editor.
|
|
54
76
|
# PyYAML is a declared wheel dependency (pyproject.toml `dependencies`),
|
|
55
77
|
# so the import is unconditional.
|
|
56
|
-
with open(path, encoding="utf-8") as fh:
|
|
57
|
-
text = fh.read()
|
|
58
78
|
try:
|
|
59
|
-
raw =
|
|
60
|
-
except
|
|
79
|
+
raw = Path(path).read_bytes()
|
|
80
|
+
except OSError as exc:
|
|
81
|
+
raise InstallError(f"cannot read config.yml: {exc}") from exc
|
|
82
|
+
try:
|
|
83
|
+
try:
|
|
84
|
+
parsed = yaml.safe_load(raw)
|
|
85
|
+
except yaml.YAMLError:
|
|
86
|
+
parsed = yaml.safe_load(raw.decode("utf-16"))
|
|
87
|
+
except (UnicodeDecodeError, yaml.YAMLError) as exc:
|
|
61
88
|
raise InstallError(f"invalid config.yml: {exc}") from exc
|
|
62
|
-
|
|
89
|
+
raw_cfg = parsed or {}
|
|
90
|
+
if not isinstance(raw_cfg, dict):
|
|
63
91
|
raise InstallError("invalid config.yml: top-level must be a mapping")
|
|
64
|
-
return dict(
|
|
92
|
+
return dict(raw_cfg)
|
|
65
93
|
|
|
66
94
|
|
|
67
95
|
def opencode_models_complete(cfg: dict[str, Any]) -> bool:
|
|
@@ -84,6 +112,45 @@ def opencode_models_complete(cfg: dict[str, Any]) -> bool:
|
|
|
84
112
|
)
|
|
85
113
|
|
|
86
114
|
|
|
115
|
+
def _fill_default_models(cfg: dict[str, Any], backend: str) -> None:
|
|
116
|
+
"""Fill the ACTIVE backend's missing role models with the shipped defaults.
|
|
117
|
+
|
|
118
|
+
Only the active backend is completed (DEFAULT_MODELS): switching backends
|
|
119
|
+
with `modus-operandi edit` must not fail on a config that predates the
|
|
120
|
+
target backend's section (e.g. a legacy config without a cursor section),
|
|
121
|
+
while the inactive section stays untouched — an empty opencode section
|
|
122
|
+
keeps rendering no agent files. Existing values are never overwritten (a
|
|
123
|
+
placeholder value stays and fails validation), and a structurally invalid
|
|
124
|
+
section is left alone for the per-section validation below to reject.
|
|
125
|
+
"""
|
|
126
|
+
defaults = DEFAULT_MODELS.get(backend)
|
|
127
|
+
if defaults is None:
|
|
128
|
+
return
|
|
129
|
+
section = cfg.get(backend)
|
|
130
|
+
if section is None:
|
|
131
|
+
section = {}
|
|
132
|
+
if not isinstance(section, dict):
|
|
133
|
+
return
|
|
134
|
+
models = section.get("models")
|
|
135
|
+
if models is None:
|
|
136
|
+
models = {}
|
|
137
|
+
if not isinstance(models, dict):
|
|
138
|
+
return
|
|
139
|
+
for role in ("planner", "executor"):
|
|
140
|
+
model = models.get(role)
|
|
141
|
+
if model is None:
|
|
142
|
+
model = {}
|
|
143
|
+
if not isinstance(model, dict):
|
|
144
|
+
continue
|
|
145
|
+
merged = dict(defaults[role])
|
|
146
|
+
for key, value in model.items():
|
|
147
|
+
if value is not None:
|
|
148
|
+
merged[key] = value
|
|
149
|
+
models[role] = merged
|
|
150
|
+
section["models"] = models
|
|
151
|
+
cfg[backend] = section
|
|
152
|
+
|
|
153
|
+
|
|
87
154
|
def apply_defaults(raw: Any) -> dict[str, Any]:
|
|
88
155
|
# Same guard as load_config: raw may be None (treated as empty config) but
|
|
89
156
|
# any other non-mapping top level is a user error, not a TypeError.
|
|
@@ -98,6 +165,10 @@ def apply_defaults(raw: Any) -> dict[str, Any]:
|
|
|
98
165
|
cfg["opencode"] = {"models": cfg["models"]}
|
|
99
166
|
del cfg["models"]
|
|
100
167
|
cfg.setdefault("backend", DEFAULT_BACKEND)
|
|
168
|
+
# Complete the active backend's models (see _fill_default_models): the
|
|
169
|
+
# active section is always usable out of the box, the inactive one is
|
|
170
|
+
# validated structurally below, nothing more.
|
|
171
|
+
_fill_default_models(cfg, cfg["backend"])
|
|
101
172
|
workflow = cfg.get("workflow") or {}
|
|
102
173
|
if not isinstance(workflow, dict):
|
|
103
174
|
raise InstallError("invalid config.yml: workflow must be a mapping")
|
|
@@ -26,6 +26,24 @@ CONFIG_PATH = Path(
|
|
|
26
26
|
# fallback for an invalid config value).
|
|
27
27
|
BACKENDS = ("opencode", "cursor")
|
|
28
28
|
|
|
29
|
+
# The shipped per-backend default role model slugs, mirroring
|
|
30
|
+
# modus_operandi/config.py (DEFAULT_MODELS). The wrapper reads the raw YAML
|
|
31
|
+
# without apply_defaults, so a model missing from the ACTIVE backend's
|
|
32
|
+
# section (a legacy config without a cursor section, a --backend override on
|
|
33
|
+
# a config that does not configure the backend) falls back to the shipped
|
|
34
|
+
# default instead of exporting an empty MO_*_MODEL that fails deep in
|
|
35
|
+
# run-agent.sh. Keep the two in sync.
|
|
36
|
+
_DEFAULT_MODELS: dict[str, dict[str, dict[str, str]]] = {
|
|
37
|
+
"opencode": {
|
|
38
|
+
"planner": {"model": "big-pickle"},
|
|
39
|
+
"executor": {"model": "big-pickle"},
|
|
40
|
+
},
|
|
41
|
+
"cursor": {
|
|
42
|
+
"planner": {"model": "composer-2"},
|
|
43
|
+
"executor": {"model": "composer-2"},
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
|
|
29
47
|
|
|
30
48
|
def normalize_config(cfg: dict[str, Any]) -> dict[str, Any]:
|
|
31
49
|
"""Fold the legacy top-level `models:` into `opencode.models`.
|
|
@@ -90,10 +108,14 @@ def sound_settings(cfg: dict[str, Any], config_path: Path) -> tuple[bool, str]:
|
|
|
90
108
|
|
|
91
109
|
|
|
92
110
|
def role_model(cfg: dict[str, Any], backend: str, role: str) -> str:
|
|
93
|
-
"""Model slug of a role under the active backend
|
|
111
|
+
"""Model slug of a role under the active backend.
|
|
94
112
|
|
|
95
113
|
The opencode agent files carry the opencode model; the exported value is
|
|
96
|
-
only consumed by the cursor branch of run-agent.sh/name-task.sh.
|
|
114
|
+
only consumed by the cursor branch of run-agent.sh/name-task.sh. A model
|
|
115
|
+
missing from the active backend's section falls back to the shipped
|
|
116
|
+
default (_DEFAULT_MODELS, mirroring the installer's apply_defaults), so a
|
|
117
|
+
legacy config or a `--backend` override on a config that does not
|
|
118
|
+
configure the backend still runs instead of exporting an empty value.
|
|
97
119
|
"""
|
|
98
120
|
section = cfg.get("cursor") if backend == "cursor" else cfg.get("opencode")
|
|
99
121
|
if not isinstance(section, dict):
|
|
@@ -107,30 +129,64 @@ def role_model(cfg: dict[str, Any], backend: str, role: str) -> str:
|
|
|
107
129
|
model = models.get(role)
|
|
108
130
|
model = model if isinstance(model, dict) else {}
|
|
109
131
|
value = model.get("model")
|
|
110
|
-
|
|
132
|
+
if isinstance(value, str) and value:
|
|
133
|
+
return value
|
|
134
|
+
default = _DEFAULT_MODELS.get(backend, {}).get(role, {}).get("model")
|
|
135
|
+
return default if isinstance(default, str) else ""
|
|
111
136
|
|
|
112
137
|
|
|
113
138
|
def load_config(config_path: Path | None = None) -> dict[str, Any]:
|
|
114
|
-
"""Read the installed config.yml into a dict, or {}
|
|
139
|
+
"""Read the installed config.yml into a dict, or {} when it is missing.
|
|
115
140
|
|
|
116
141
|
config_path defaults to the module's CONFIG_PATH; the run-pipeline entry
|
|
117
142
|
passes its own CONFIG_PATH explicitly so a runtime override (e.g. tests
|
|
118
|
-
pointing the wrapper at another file) is honored. A
|
|
119
|
-
|
|
120
|
-
|
|
143
|
+
pointing the wrapper at another file) is honored. A MISSING file degrades
|
|
144
|
+
to the documented defaults, so a hand-invoked wrapper (or a config
|
|
145
|
+
deleted after install) still runs. An EXISTING file that cannot be read
|
|
146
|
+
or parsed raises ValueError with a user-facing explanation: silently
|
|
147
|
+
running the pipeline with the opencode defaults on a broken config is
|
|
148
|
+
what hid backend misconfiguration (a config that fails to load made
|
|
149
|
+
`backend: cursor` invisible, so the run used opencode and exported empty
|
|
150
|
+
role models), so a broken config now aborts before the run starts —
|
|
151
|
+
mirroring sound_settings. The file is read as BYTES: PyYAML detects the
|
|
152
|
+
UTF-8 and UTF-16/UTF-32 (with BOM) encodings a desktop text editor can
|
|
153
|
+
save, so a macOS TextEdit UTF-16 save still parses instead of failing.
|
|
121
154
|
"""
|
|
122
155
|
path = config_path if config_path is not None else CONFIG_PATH
|
|
123
156
|
try:
|
|
124
|
-
|
|
125
|
-
except
|
|
157
|
+
raw = path.read_bytes()
|
|
158
|
+
except FileNotFoundError:
|
|
126
159
|
return {}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
except Exception:
|
|
160
|
+
except OSError as exc:
|
|
161
|
+
raise ValueError(f"cannot read config {path}: {exc}") from exc
|
|
162
|
+
data = _safe_yaml_load(raw)
|
|
163
|
+
if data is None:
|
|
132
164
|
return {}
|
|
133
|
-
|
|
165
|
+
if not isinstance(data, dict):
|
|
166
|
+
raise ValueError(
|
|
167
|
+
f"invalid config.yml at {path}: top-level must be a mapping, got {type(data).__name__}"
|
|
168
|
+
)
|
|
169
|
+
return data
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _safe_yaml_load(raw: bytes) -> Any:
|
|
173
|
+
"""Parse config bytes into a value, tolerating editor-saved encodings.
|
|
174
|
+
|
|
175
|
+
PyYAML given BYTES detects UTF-8 (plain and with BOM) and UTF-16/UTF-32
|
|
176
|
+
with BOM natively; a BOM-less UTF-16 file (an editor that saves
|
|
177
|
+
"UTF-16 LE") falls back to an explicit decode. Raises ValueError with
|
|
178
|
+
the parse error when neither works.
|
|
179
|
+
"""
|
|
180
|
+
import yaml # pyproject.toml declares pyyaml as a package dependency
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
return yaml.safe_load(raw)
|
|
184
|
+
except yaml.YAMLError:
|
|
185
|
+
pass
|
|
186
|
+
try:
|
|
187
|
+
return yaml.safe_load(raw.decode("utf-16"))
|
|
188
|
+
except (UnicodeDecodeError, yaml.YAMLError) as exc:
|
|
189
|
+
raise ValueError(f"invalid config.yml: cannot parse the file: {exc}") from exc
|
|
134
190
|
|
|
135
191
|
|
|
136
192
|
def build_specify_invocation(
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/editor.py
RENAMED
|
@@ -58,21 +58,25 @@ def resolve_editor() -> list[str] | None:
|
|
|
58
58
|
def resolve_feedback_editor() -> tuple[str, list[str]] | None:
|
|
59
59
|
"""Resolve the platform editor for the feedback gates (ADR-0011).
|
|
60
60
|
|
|
61
|
-
Returns (mode, cmd): mode is "
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
Returns (mode, cmd): mode is "detached" — launch the editor in a
|
|
62
|
+
separate window and leave the gate interactive (the user presses
|
|
63
|
+
`continue` in the terminal) — or "waited" — run the editor blocking in
|
|
64
|
+
the terminal and answer the gate with `continue` on close. None means
|
|
65
|
+
no editor is available at all.
|
|
65
66
|
|
|
66
|
-
macOS: TextEdit via `open -a TextEdit
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
macOS: TextEdit via `open -a TextEdit` (always installed), detached —
|
|
68
|
+
the -W flag is deliberately NOT used: it waits for the whole app to
|
|
69
|
+
quit (closing the document window is not enough, and an already-running
|
|
70
|
+
TextEdit makes `open -W` block until THAT instance exits), which hung
|
|
71
|
+
the run at the feedback gate with no menu on screen. Linux: GNOME Text
|
|
72
|
+
Editor in a separate window — Flatpak first (checked via `flatpak info
|
|
69
73
|
org.gnome.TextEditor`), then the RPM binary, then the generic desktop
|
|
70
74
|
opener (gio, then xdg-open) — all detached, since these launchers return
|
|
71
75
|
immediately. Fallback: the terminal chain ($VISUAL, $EDITOR, nano, vi),
|
|
72
76
|
waited.
|
|
73
77
|
"""
|
|
74
78
|
if sys.platform == "darwin":
|
|
75
|
-
return "
|
|
79
|
+
return "detached", ["open", "-a", "TextEdit"]
|
|
76
80
|
if sys.platform.startswith("linux"):
|
|
77
81
|
if shutil.which("flatpak"):
|
|
78
82
|
try:
|
|
@@ -68,14 +68,14 @@ def open_feedback_editor(
|
|
|
68
68
|
in the platform editor and, on editor close,
|
|
69
69
|
answers the gate with `continue` so the workflow continues (the revise
|
|
70
70
|
step reads feedback.md).
|
|
71
|
-
The editor resolution (editor.py) returns a mode: "
|
|
72
|
-
TextEdit
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
False on fallback — no TTY, no editor, or a detached launch —
|
|
77
|
-
file is still created, forwarding is left running and the
|
|
78
|
-
interactive for manual input.
|
|
71
|
+
The editor resolution (editor.py) returns a mode: "detached" (macOS
|
|
72
|
+
TextEdit, Linux GUI in a separate window) launches the editor and
|
|
73
|
+
leaves the gate interactive — the user closes the window and presses
|
|
74
|
+
`continue`; "waited" (the terminal chain) runs the editor blocking and
|
|
75
|
+
the wrapper answers the gate. Returns True when the wrapper answered
|
|
76
|
+
the gate; False on fallback — no TTY, no editor, or a detached launch —
|
|
77
|
+
where the file is still created, forwarding is left running and the
|
|
78
|
+
gate stays interactive for manual input.
|
|
79
79
|
"""
|
|
80
80
|
task_dir = Path.cwd() / state_dir / "tasks" / "current"
|
|
81
81
|
source_doc: Path | None = None
|
|
@@ -18,11 +18,15 @@ to specify (keeping sys.stdin.isatty() True inside it) and terminal input is
|
|
|
18
18
|
forwarded into the master end, so interactive gates keep working while the
|
|
19
19
|
wrapper can answer the feedback gate itself. When that gate opens
|
|
20
20
|
the wrapper creates <state_dir>/tasks/current/feedback.md (never overwriting
|
|
21
|
-
an existing file)
|
|
22
|
-
|
|
23
|
-
the
|
|
24
|
-
(
|
|
25
|
-
|
|
21
|
+
an existing file) and opens it in the platform editor — macOS TextEdit and
|
|
22
|
+
the Linux GUI open in a separate window and the run NEVER waits for the
|
|
23
|
+
editor: the gate stays interactive and the user answers it in the terminal
|
|
24
|
+
(`continue`/`abort`) after closing the editor; without a GUI the editor runs
|
|
25
|
+
in the terminal ($VISUAL, then $EDITOR, then nano, then vi) and the wrapper
|
|
26
|
+
answers the gate with `continue` on editor close so the workflow resumes
|
|
27
|
+
(study-revise reads feedback.md). If no editor can run (non-TTY or none
|
|
28
|
+
found), the file is still created and the gate stays interactive for manual
|
|
29
|
+
input. On a non-TTY run no pty is used and specify
|
|
26
30
|
keeps the inherited stdin, so gates keep going PAUSED exactly as before.
|
|
27
31
|
|
|
28
32
|
When the run finishes (success, failure or abort alike) the wrapper prints a
|
|
@@ -218,8 +222,15 @@ def main() -> int:
|
|
|
218
222
|
# Map the installed config + argv to the specify invocation; main() only
|
|
219
223
|
# orchestrates the live run from the result. CONFIG_PATH is passed
|
|
220
224
|
# explicitly so a runtime override of this module's CONFIG_PATH (e.g. by
|
|
221
|
-
# tests) is honored by the config read.
|
|
222
|
-
|
|
225
|
+
# tests) is honored by the config read. An existing but unreadable or
|
|
226
|
+
# invalid config aborts with an explanation instead of silently running
|
|
227
|
+
# with the opencode defaults (a broken config used to make `backend:
|
|
228
|
+
# cursor` invisible and export empty role models).
|
|
229
|
+
try:
|
|
230
|
+
cfg = load_config(CONFIG_PATH)
|
|
231
|
+
except ValueError as exc:
|
|
232
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
233
|
+
return 1
|
|
223
234
|
# The sound section is validated before the run starts (ADR-0018): an
|
|
224
235
|
# invalid sound setting aborts with an explanation, not a silent degrade.
|
|
225
236
|
try:
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/workflows/task-pipeline.yml
RENAMED
|
@@ -121,9 +121,9 @@ steps:
|
|
|
121
121
|
message: >-
|
|
122
122
|
Feedback on the motivation study is collected in
|
|
123
123
|
{{ inputs.state_dir }}/tasks/current/feedback.md — the wrapper opens it
|
|
124
|
-
in your editor
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
in your editor; the run never waits for the editor: edit and
|
|
125
|
+
save the file, close the editor window, then choose continue
|
|
126
|
+
in the terminal. If no editor opened, create the file manually
|
|
127
127
|
and write your feedback, then choose continue. The planner
|
|
128
128
|
will update study.md accordingly.
|
|
129
129
|
options: [continue, abort]
|
|
@@ -58,21 +58,25 @@ def resolve_editor() -> list[str] | None:
|
|
|
58
58
|
def resolve_feedback_editor() -> tuple[str, list[str]] | None:
|
|
59
59
|
"""Resolve the platform editor for the feedback gates (ADR-0011).
|
|
60
60
|
|
|
61
|
-
Returns (mode, cmd): mode is "
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
Returns (mode, cmd): mode is "detached" — launch the editor in a
|
|
62
|
+
separate window and leave the gate interactive (the user presses
|
|
63
|
+
`continue` in the terminal) — or "waited" — run the editor blocking in
|
|
64
|
+
the terminal and answer the gate with `continue` on close. None means
|
|
65
|
+
no editor is available at all.
|
|
65
66
|
|
|
66
|
-
macOS: TextEdit via `open -a TextEdit
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
macOS: TextEdit via `open -a TextEdit` (always installed), detached —
|
|
68
|
+
the -W flag is deliberately NOT used: it waits for the whole app to
|
|
69
|
+
quit (closing the document window is not enough, and an already-running
|
|
70
|
+
TextEdit makes `open -W` block until THAT instance exits), which hung
|
|
71
|
+
the run at the feedback gate with no menu on screen. Linux: GNOME Text
|
|
72
|
+
Editor in a separate window — Flatpak first (checked via `flatpak info
|
|
69
73
|
org.gnome.TextEditor`), then the RPM binary, then the generic desktop
|
|
70
74
|
opener (gio, then xdg-open) — all detached, since these launchers return
|
|
71
75
|
immediately. Fallback: the terminal chain ($VISUAL, $EDITOR, nano, vi),
|
|
72
76
|
waited.
|
|
73
77
|
"""
|
|
74
78
|
if sys.platform == "darwin":
|
|
75
|
-
return "
|
|
79
|
+
return "detached", ["open", "-a", "TextEdit"]
|
|
76
80
|
if sys.platform.startswith("linux"):
|
|
77
81
|
if shutil.which("flatpak"):
|
|
78
82
|
try:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/adr_utils.py
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/agent-step.sh
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/agent_call.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/display.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/gate_state.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/live_lines.py
RENAMED
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/name-task.sh
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/notify.py
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/pass-check.sh
RENAMED
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/pty_spawn.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/run-agent.sh
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/run_finish.py
RENAMED
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/run_state.py
RENAMED
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/save_adr.py
RENAMED
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/show-file.sh
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/pipeline_scripts/task_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/bug-review.md
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/comment-fix.md
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/comment-review.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/implement-retry.md
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/implement.md
RENAMED
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/planner-answers.md
RENAMED
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/adr/srp-review.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/bug-rereview.md
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/bug-review.md
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/comment-fix.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/review.md
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/srp-rereview.md
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/review/srp-review.md
RENAMED
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/task/research.md
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/task/study-revise.md
RENAMED
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/task/write-adr.md
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/prompts/task/write-plan.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/roles/reviewer-bugs.md
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/roles/reviewer-comment.md
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/roles/reviewer-review.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/data/workflows/review-pipeline.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/exceptions/edit_requested.py
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/exceptions/help_requested.py
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/exceptions/invalid_invocation.py
RENAMED
|
File without changes
|
{modus_operandi-0.2.0 → modus_operandi-0.2.2}/src/modus_operandi/exceptions/uninstall_requested.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|