cli-router 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.
- cli_router-0.1.0/.github/workflows/publish.yml +68 -0
- cli_router-0.1.0/.github/workflows/test.yml +24 -0
- cli_router-0.1.0/.gitignore +224 -0
- cli_router-0.1.0/AGENTS.md +204 -0
- cli_router-0.1.0/CHANGELOG.md +5 -0
- cli_router-0.1.0/LICENSE +201 -0
- cli_router-0.1.0/PKG-INFO +172 -0
- cli_router-0.1.0/PRD.md +424 -0
- cli_router-0.1.0/README.md +145 -0
- cli_router-0.1.0/cli_router/__init__.py +3 -0
- cli_router-0.1.0/cli_router/artifacts.py +50 -0
- cli_router-0.1.0/cli_router/cli.py +96 -0
- cli_router-0.1.0/cli_router/config.py +139 -0
- cli_router-0.1.0/cli_router/extractors.py +39 -0
- cli_router-0.1.0/cli_router/failures.py +52 -0
- cli_router-0.1.0/cli_router/presets/__init__.py +1 -0
- cli_router-0.1.0/cli_router/presets/claude.yaml +14 -0
- cli_router-0.1.0/cli_router/presets/codex.yaml +9 -0
- cli_router-0.1.0/cli_router/presets/generic.yaml +60 -0
- cli_router-0.1.0/cli_router/presets/hermes.yaml +9 -0
- cli_router-0.1.0/cli_router/runner.py +75 -0
- cli_router-0.1.0/cli_router/tools.py +39 -0
- cli_router-0.1.0/cli_router/workflows.py +176 -0
- cli_router-0.1.0/examples/claude_codex.yaml +73 -0
- cli_router-0.1.0/examples/cli-router.yaml +45 -0
- cli_router-0.1.0/examples/local_models.yaml +43 -0
- cli_router-0.1.0/pyproject.toml +52 -0
- cli_router-0.1.0/tests/test_cli.py +78 -0
- cli_router-0.1.0/tests/test_config.py +49 -0
- cli_router-0.1.0/tests/test_extractors.py +18 -0
- cli_router-0.1.0/tests/test_failures.py +38 -0
- cli_router-0.1.0/tests/test_runner.py +42 -0
- cli_router-0.1.0/tests/test_workflows.py +155 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
test:
|
|
13
|
+
name: Test package
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
- uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
- name: Install test dependencies
|
|
24
|
+
run: python -m pip install --upgrade pip pytest
|
|
25
|
+
- name: Install package
|
|
26
|
+
run: python -m pip install -e .
|
|
27
|
+
- name: Run tests
|
|
28
|
+
run: python -m pytest tests -q
|
|
29
|
+
|
|
30
|
+
build:
|
|
31
|
+
name: Build distributions
|
|
32
|
+
runs-on: ubuntu-latest
|
|
33
|
+
needs: test
|
|
34
|
+
steps:
|
|
35
|
+
- uses: actions/checkout@v4
|
|
36
|
+
- uses: actions/setup-python@v5
|
|
37
|
+
with:
|
|
38
|
+
python-version: "3.12"
|
|
39
|
+
- name: Install build tools
|
|
40
|
+
run: python -m pip install --upgrade pip build twine
|
|
41
|
+
- name: Build package
|
|
42
|
+
run: python -m build
|
|
43
|
+
- name: Check distributions
|
|
44
|
+
run: python -m twine check dist/*
|
|
45
|
+
- name: Upload distributions
|
|
46
|
+
uses: actions/upload-artifact@v4
|
|
47
|
+
with:
|
|
48
|
+
name: dist
|
|
49
|
+
path: dist/
|
|
50
|
+
if-no-files-found: error
|
|
51
|
+
|
|
52
|
+
pypi-publish:
|
|
53
|
+
name: Publish CLI-Router to PyPI
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
needs: build
|
|
56
|
+
environment: pypi
|
|
57
|
+
permissions:
|
|
58
|
+
id-token: write
|
|
59
|
+
contents: read
|
|
60
|
+
|
|
61
|
+
steps:
|
|
62
|
+
- name: Download distributions
|
|
63
|
+
uses: actions/download-artifact@v4
|
|
64
|
+
with:
|
|
65
|
+
name: dist
|
|
66
|
+
path: dist/
|
|
67
|
+
- name: Publish package
|
|
68
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
strategy:
|
|
11
|
+
matrix:
|
|
12
|
+
python-version: ["3.10", "3.11", "3.12", "3.13"]
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: ${{ matrix.python-version }}
|
|
19
|
+
- name: Install test dependencies
|
|
20
|
+
run: python -m pip install --upgrade pip pytest
|
|
21
|
+
- name: Install package
|
|
22
|
+
run: python -m pip install -e .
|
|
23
|
+
- name: Test
|
|
24
|
+
run: python -m pytest tests -q
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# Installer logs
|
|
36
|
+
pip-log.txt
|
|
37
|
+
pip-delete-this-directory.txt
|
|
38
|
+
|
|
39
|
+
# Unit test / coverage reports
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
.coverage
|
|
44
|
+
.coverage.*
|
|
45
|
+
.cache
|
|
46
|
+
nosetests.xml
|
|
47
|
+
coverage.xml
|
|
48
|
+
*.cover
|
|
49
|
+
*.py.cover
|
|
50
|
+
.hypothesis/
|
|
51
|
+
.pytest_cache/
|
|
52
|
+
cover/
|
|
53
|
+
|
|
54
|
+
# Translations
|
|
55
|
+
*.mo
|
|
56
|
+
*.pot
|
|
57
|
+
|
|
58
|
+
# Django stuff:
|
|
59
|
+
*.log
|
|
60
|
+
local_settings.py
|
|
61
|
+
db.sqlite3
|
|
62
|
+
db.sqlite3-journal
|
|
63
|
+
|
|
64
|
+
# Flask stuff:
|
|
65
|
+
instance/
|
|
66
|
+
.webassets-cache
|
|
67
|
+
|
|
68
|
+
# Scrapy stuff:
|
|
69
|
+
.scrapy
|
|
70
|
+
|
|
71
|
+
# Sphinx documentation
|
|
72
|
+
docs/_build/
|
|
73
|
+
|
|
74
|
+
# PyBuilder
|
|
75
|
+
.pybuilder/
|
|
76
|
+
target/
|
|
77
|
+
|
|
78
|
+
# Jupyter Notebook
|
|
79
|
+
.ipynb_checkpoints
|
|
80
|
+
|
|
81
|
+
# IPython
|
|
82
|
+
profile_default/
|
|
83
|
+
ipython_config.py
|
|
84
|
+
|
|
85
|
+
# pyenv
|
|
86
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
87
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
88
|
+
# .python-version
|
|
89
|
+
|
|
90
|
+
# pipenv
|
|
91
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
92
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
93
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
94
|
+
# install all needed dependencies.
|
|
95
|
+
# Pipfile.lock
|
|
96
|
+
|
|
97
|
+
# UV
|
|
98
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
99
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
100
|
+
# commonly ignored for libraries.
|
|
101
|
+
# uv.lock
|
|
102
|
+
|
|
103
|
+
# poetry
|
|
104
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
105
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
106
|
+
# commonly ignored for libraries.
|
|
107
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
108
|
+
# poetry.lock
|
|
109
|
+
# poetry.toml
|
|
110
|
+
|
|
111
|
+
# pdm
|
|
112
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
113
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
114
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
115
|
+
# pdm.lock
|
|
116
|
+
# pdm.toml
|
|
117
|
+
.pdm-python
|
|
118
|
+
.pdm-build/
|
|
119
|
+
|
|
120
|
+
# pixi
|
|
121
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
122
|
+
# pixi.lock
|
|
123
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
124
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
125
|
+
.pixi
|
|
126
|
+
|
|
127
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
128
|
+
__pypackages__/
|
|
129
|
+
|
|
130
|
+
# Celery stuff
|
|
131
|
+
celerybeat-schedule
|
|
132
|
+
celerybeat.pid
|
|
133
|
+
|
|
134
|
+
# Redis
|
|
135
|
+
*.rdb
|
|
136
|
+
*.aof
|
|
137
|
+
*.pid
|
|
138
|
+
|
|
139
|
+
# RabbitMQ
|
|
140
|
+
mnesia/
|
|
141
|
+
rabbitmq/
|
|
142
|
+
rabbitmq-data/
|
|
143
|
+
|
|
144
|
+
# ActiveMQ
|
|
145
|
+
activemq-data/
|
|
146
|
+
|
|
147
|
+
# SageMath parsed files
|
|
148
|
+
*.sage.py
|
|
149
|
+
|
|
150
|
+
# Environments
|
|
151
|
+
.env
|
|
152
|
+
.envrc
|
|
153
|
+
.venv
|
|
154
|
+
env/
|
|
155
|
+
venv/
|
|
156
|
+
ENV/
|
|
157
|
+
env.bak/
|
|
158
|
+
venv.bak/
|
|
159
|
+
|
|
160
|
+
# Spyder project settings
|
|
161
|
+
.spyderproject
|
|
162
|
+
.spyproject
|
|
163
|
+
|
|
164
|
+
# Rope project settings
|
|
165
|
+
.ropeproject
|
|
166
|
+
|
|
167
|
+
# mkdocs documentation
|
|
168
|
+
/site
|
|
169
|
+
|
|
170
|
+
# mypy
|
|
171
|
+
.mypy_cache/
|
|
172
|
+
.dmypy.json
|
|
173
|
+
dmypy.json
|
|
174
|
+
|
|
175
|
+
# Pyre type checker
|
|
176
|
+
.pyre/
|
|
177
|
+
|
|
178
|
+
# pytype static type analyzer
|
|
179
|
+
.pytype/
|
|
180
|
+
|
|
181
|
+
# Cython debug symbols
|
|
182
|
+
cython_debug/
|
|
183
|
+
|
|
184
|
+
# PyCharm
|
|
185
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
186
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
187
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
188
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
189
|
+
# .idea/
|
|
190
|
+
|
|
191
|
+
# Abstra
|
|
192
|
+
# Abstra is an AI-powered process automation framework.
|
|
193
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
194
|
+
# Learn more at https://abstra.io/docs
|
|
195
|
+
.abstra/
|
|
196
|
+
|
|
197
|
+
# Visual Studio Code
|
|
198
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
199
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
200
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
201
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
202
|
+
# .vscode/
|
|
203
|
+
# Temporary file for partial code execution
|
|
204
|
+
tempCodeRunnerFile.py
|
|
205
|
+
|
|
206
|
+
# Ruff stuff:
|
|
207
|
+
.ruff_cache/
|
|
208
|
+
|
|
209
|
+
# PyPI configuration file
|
|
210
|
+
.pypirc
|
|
211
|
+
|
|
212
|
+
# CLI-Router local config and run artifacts
|
|
213
|
+
/cli-router.yaml
|
|
214
|
+
/.cli-router.yaml
|
|
215
|
+
/PLAN.md
|
|
216
|
+
/.cli-router/
|
|
217
|
+
|
|
218
|
+
# Marimo
|
|
219
|
+
marimo/_static/
|
|
220
|
+
marimo/_lsp/
|
|
221
|
+
__marimo__/
|
|
222
|
+
|
|
223
|
+
# Streamlit
|
|
224
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
Guidance for AI agents working in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Purpose
|
|
6
|
+
|
|
7
|
+
CLI-Router is a Python command-line orchestrator for external AI coding CLIs. It is intentionally programmatic and non-intelligent:
|
|
8
|
+
|
|
9
|
+
- It does not inspect repositories on its own unless a configured external tool does so.
|
|
10
|
+
- It does not semantically choose models or providers.
|
|
11
|
+
- It loads YAML config, renders prompt templates, runs configured commands, captures stdout/stderr, extracts configured output, writes artifacts, classifies failures, and applies configured fallback behavior.
|
|
12
|
+
|
|
13
|
+
Keep that boundary intact. New behavior should make orchestration more reliable or configurable, not turn this package into an agent.
|
|
14
|
+
|
|
15
|
+
## Source Layout
|
|
16
|
+
|
|
17
|
+
- `cli_router/cli.py`: argparse CLI entry point and command dispatch.
|
|
18
|
+
- `cli_router/config.py`: config discovery, merge, and validation.
|
|
19
|
+
- `cli_router/runner.py`: subprocess execution, placeholder rendering, command timeout handling.
|
|
20
|
+
- `cli_router/workflows.py`: `plan`, `implement`, and full `run` workflow execution.
|
|
21
|
+
- `cli_router/extractors.py`: text and JSON output extraction.
|
|
22
|
+
- `cli_router/failures.py`: external-tool failure classification and user-facing messages.
|
|
23
|
+
- `cli_router/artifacts.py`: run directory and artifact persistence.
|
|
24
|
+
- `cli_router/tools.py`: `tools list` and `tools test`.
|
|
25
|
+
- `cli_router/presets/`: packaged YAML defaults/presets.
|
|
26
|
+
- `examples/`: user-facing example configs.
|
|
27
|
+
- `tests/`: pytest coverage using fake subprocess CLIs, not real Claude/Codex calls.
|
|
28
|
+
|
|
29
|
+
## Development Commands
|
|
30
|
+
|
|
31
|
+
Use these from the repository root:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
python -m pytest tests -q
|
|
35
|
+
python -m cli_router.cli --help
|
|
36
|
+
python -m cli_router.cli check
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Package verification:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
python -m build
|
|
43
|
+
python -m twine check dist/*
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Editable install may fail on system Python environments protected by PEP 668. Use a virtual environment:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
python -m venv /tmp/cli-router-venv
|
|
50
|
+
/tmp/cli-router-venv/bin/python -m pip install -e .
|
|
51
|
+
/tmp/cli-router-venv/bin/cli-router --help
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`python -m build` and package install may need network access to fetch build dependencies.
|
|
55
|
+
|
|
56
|
+
## Configuration Semantics
|
|
57
|
+
|
|
58
|
+
Config lookup order is:
|
|
59
|
+
|
|
60
|
+
1. `./cli-router.yaml`
|
|
61
|
+
2. `./.cli-router.yaml`
|
|
62
|
+
3. `~/.config/cli-router/config.yaml`
|
|
63
|
+
4. Built-in defaults from `cli_router/presets/generic.yaml`
|
|
64
|
+
|
|
65
|
+
Config version is currently `version: 1`.
|
|
66
|
+
|
|
67
|
+
Supported placeholders are literal token replacements:
|
|
68
|
+
|
|
69
|
+
- `{prompt}`
|
|
70
|
+
- `{user_prompt}`
|
|
71
|
+
- `{plan_path}`
|
|
72
|
+
|
|
73
|
+
Do not switch to Python `str.format` semantics without a strong reason. Current literal replacement intentionally allows command snippets and prompts to contain unrelated braces.
|
|
74
|
+
|
|
75
|
+
Workflow stages use:
|
|
76
|
+
|
|
77
|
+
- `tool`: primary tool name.
|
|
78
|
+
- `fallback_tools`: optional ordered list of tool names to try after failed attempts.
|
|
79
|
+
- `input_template`: rendered prompt sent as `{prompt}` to the tool command.
|
|
80
|
+
- `output_file`: planner output path, usually `PLAN.md`.
|
|
81
|
+
|
|
82
|
+
Tool config supports:
|
|
83
|
+
|
|
84
|
+
- `command`: list-form command preferred; string-form is parsed with `shlex.split`.
|
|
85
|
+
- `output.format`: `text` or `json`.
|
|
86
|
+
- `output.extract`: top-level or dotted JSON field path.
|
|
87
|
+
- `timeout_seconds`: optional positive number; timeout returns exit code `124`.
|
|
88
|
+
|
|
89
|
+
## Runtime Artifacts
|
|
90
|
+
|
|
91
|
+
Runs write to `.cli-router/runs/<timestamp>/`.
|
|
92
|
+
|
|
93
|
+
Primary stage attempt artifacts use the stage id:
|
|
94
|
+
|
|
95
|
+
- `planner.stdout`
|
|
96
|
+
- `planner.stderr`
|
|
97
|
+
- `planner.extracted.md`
|
|
98
|
+
|
|
99
|
+
Fallback attempts include the tool name:
|
|
100
|
+
|
|
101
|
+
- `planner.codex-planner.stdout`
|
|
102
|
+
- `planner.codex-planner.stderr`
|
|
103
|
+
- `planner.codex-planner.extracted.md`
|
|
104
|
+
|
|
105
|
+
Every run writes `run.yaml` containing stage summaries, commands, return codes, extracted output, and `failure_kind`.
|
|
106
|
+
|
|
107
|
+
`.cli-router/runs/` is ignored by git. Do not commit generated run artifacts.
|
|
108
|
+
|
|
109
|
+
## Failure Behavior
|
|
110
|
+
|
|
111
|
+
Failure classification lives in `cli_router/failures.py`.
|
|
112
|
+
|
|
113
|
+
Current `failure_kind` values include:
|
|
114
|
+
|
|
115
|
+
- `usage_limit`
|
|
116
|
+
- `unsupported_model`
|
|
117
|
+
- `timeout`
|
|
118
|
+
- `command_not_found`
|
|
119
|
+
- `command_failed`
|
|
120
|
+
- `extraction_failed`
|
|
121
|
+
|
|
122
|
+
Known real-world messages are covered by tests, including:
|
|
123
|
+
|
|
124
|
+
- Claude: `You've hit your session limit ...`
|
|
125
|
+
- Claude/rate/quota variants containing usage limit, session limit, rate limit, quota, 429, or too many requests.
|
|
126
|
+
- Codex/OpenAI unsupported model messages such as `model is not supported`.
|
|
127
|
+
|
|
128
|
+
When adding a new failure classifier, add a focused test in `tests/test_failures.py` using the real observed stdout/stderr text when possible.
|
|
129
|
+
|
|
130
|
+
## Testing Rules
|
|
131
|
+
|
|
132
|
+
Prefer tests that use temporary configs and fake commands such as:
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
[sys.executable, "-c", "import json; print(json.dumps({'result': 'plan'}))"]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Do not require real Claude, Codex, Hermes, OpenAI, network access, or credentials in the normal test suite.
|
|
139
|
+
|
|
140
|
+
Use `tmp_path` and `monkeypatch.chdir(tmp_path)` for workflow tests that write `PLAN.md` or `.cli-router/runs/`.
|
|
141
|
+
|
|
142
|
+
When changing workflow behavior, cover:
|
|
143
|
+
|
|
144
|
+
- plan-only execution
|
|
145
|
+
- full planner-to-coder execution
|
|
146
|
+
- missing `PLAN.md` for `implement`
|
|
147
|
+
- nonzero command failures
|
|
148
|
+
- fallback ordering and artifact names
|
|
149
|
+
- failure classification and exit codes
|
|
150
|
+
|
|
151
|
+
When changing packaging, verify:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
python -m pytest tests -q
|
|
155
|
+
python -m build
|
|
156
|
+
python -m twine check dist/*
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Real External CLI Trials
|
|
160
|
+
|
|
161
|
+
Real Claude/Codex trials are useful but should be explicit and isolated.
|
|
162
|
+
|
|
163
|
+
Use a temporary directory such as `/tmp/cli-router-real-test`; do not run real provider trials in the repo root unless the user explicitly wants repo files changed.
|
|
164
|
+
|
|
165
|
+
Recommended safety controls:
|
|
166
|
+
|
|
167
|
+
- Add `timeout_seconds` to every real tool.
|
|
168
|
+
- For Codex trials, prefer `codex --ask-for-approval never exec --skip-git-repo-check --ephemeral --sandbox read-only ...`.
|
|
169
|
+
- Use harmless prompts and include "Do not edit files" in `input_template`.
|
|
170
|
+
- Expect provider/model availability to vary by account; classify and report unsupported model failures instead of treating them as code defects.
|
|
171
|
+
|
|
172
|
+
Known observed Codex behavior in this environment:
|
|
173
|
+
|
|
174
|
+
- Local Codex config default model was `gpt-5.5`.
|
|
175
|
+
- `gpt-5` and `gpt-5-codex` returned unsupported-model errors for the ChatGPT-backed account.
|
|
176
|
+
- `gpt-5.5` succeeded for both planner and coder in the real fallback trial.
|
|
177
|
+
|
|
178
|
+
These observations are environment-specific. Do not hardcode them into package defaults without a product decision.
|
|
179
|
+
|
|
180
|
+
## Design Constraints
|
|
181
|
+
|
|
182
|
+
- Keep the CLI stable: `cli-router run`, `plan`, `implement`, `check`, `config show`, `tools list`, `tools test <name>`.
|
|
183
|
+
- Keep dependencies minimal. Current runtime dependencies are `pyyaml` and `rich`; the CLI uses `argparse`.
|
|
184
|
+
- Prefer list-form subprocess commands in examples and tests.
|
|
185
|
+
- Preserve stdout/stderr exactly in artifacts. Do not redact or transform raw logs unless a user explicitly requests a privacy feature.
|
|
186
|
+
- Avoid hidden behavior. If CLI-Router retries or falls back, make that visible through summaries and `run.yaml`.
|
|
187
|
+
- Do not make built-in presets depend on provider credentials or paid services for basic `cli-router check`.
|
|
188
|
+
- Do not mutate user configuration unless implementing an explicit config-editing command.
|
|
189
|
+
|
|
190
|
+
## Git Hygiene
|
|
191
|
+
|
|
192
|
+
The worktree may contain user or generated changes. Do not revert files you did not intentionally change.
|
|
193
|
+
|
|
194
|
+
Generated files that should normally remain untracked:
|
|
195
|
+
|
|
196
|
+
- `.cli-router/runs/`
|
|
197
|
+
- `dist/`
|
|
198
|
+
- `build/`
|
|
199
|
+
- `*.egg-info/`
|
|
200
|
+
- `__pycache__/`
|
|
201
|
+
- `.pytest_cache/`
|
|
202
|
+
|
|
203
|
+
Before finalizing substantive changes, run `git status --short` and report any verification commands run.
|
|
204
|
+
|