cli-router 0.3.1__tar.gz → 0.3.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.
Files changed (77) hide show
  1. {cli_router-0.3.1 → cli_router-0.3.2}/AGENTS.md +50 -4
  2. {cli_router-0.3.1 → cli_router-0.3.2}/CHANGELOG.md +9 -0
  3. cli_router-0.3.2/MEMORY.md +60 -0
  4. {cli_router-0.3.1 → cli_router-0.3.2}/PKG-INFO +93 -5
  5. {cli_router-0.3.1 → cli_router-0.3.2}/README.md +91 -4
  6. cli_router-0.3.2/STATE.md +55 -0
  7. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/__init__.py +1 -1
  8. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/cli.py +81 -11
  9. cli_router-0.3.2/cli_router/config.py +310 -0
  10. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/failures.py +27 -0
  11. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/presets/generic.yaml +2 -1
  12. cli_router-0.3.2/cli_router/receipts.py +185 -0
  13. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/runner.py +125 -8
  14. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/tools.py +18 -3
  15. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/tui.py +9 -2
  16. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/workflows.py +76 -16
  17. cli_router-0.3.2/docs/releasing.md +182 -0
  18. {cli_router-0.3.1 → cli_router-0.3.2}/examples/claude_codex.yaml +9 -2
  19. {cli_router-0.3.1 → cli_router-0.3.2}/examples/cli-router.yaml +2 -1
  20. {cli_router-0.3.1 → cli_router-0.3.2}/examples/local_models.yaml +2 -1
  21. {cli_router-0.3.1 → cli_router-0.3.2}/pyproject.toml +2 -1
  22. cli_router-0.3.2/tests/test_cli.py +471 -0
  23. cli_router-0.3.2/tests/test_config.py +364 -0
  24. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_failures.py +12 -0
  25. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_runner.py +117 -0
  26. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_tui.py +3 -0
  27. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_workflows.py +402 -0
  28. cli_router-0.3.1/cli_router/config.py +0 -172
  29. cli_router-0.3.1/tests/test_cli.py +0 -218
  30. cli_router-0.3.1/tests/test_config.py +0 -144
  31. {cli_router-0.3.1 → cli_router-0.3.2}/.claude/skills/tui-design/SKILL.md +0 -0
  32. {cli_router-0.3.1 → cli_router-0.3.2}/.claude/skills/tui-design/references/cli-basics.md +0 -0
  33. {cli_router-0.3.1 → cli_router-0.3.2}/.claude/skills/tui-design/references/ecosystem-go.md +0 -0
  34. {cli_router-0.3.1 → cli_router-0.3.2}/.claude/skills/tui-design/references/ecosystem-python.md +0 -0
  35. {cli_router-0.3.1 → cli_router-0.3.2}/.claude/skills/tui-design/references/ecosystem-rust.md +0 -0
  36. {cli_router-0.3.1 → cli_router-0.3.2}/.claude/skills/tui-design/references/ecosystem-typescript.md +0 -0
  37. {cli_router-0.3.1 → cli_router-0.3.2}/.claude/skills/tui-design/references/exemplar-apps.md +0 -0
  38. {cli_router-0.3.1 → cli_router-0.3.2}/.claude/skills/tui-design/references/interaction-patterns.md +0 -0
  39. {cli_router-0.3.1 → cli_router-0.3.2}/.claude/skills/tui-design/references/visual-patterns.md +0 -0
  40. {cli_router-0.3.1 → cli_router-0.3.2}/.github/actionlint.yaml +0 -0
  41. {cli_router-0.3.1 → cli_router-0.3.2}/.github/dependabot.yml +0 -0
  42. {cli_router-0.3.1 → cli_router-0.3.2}/.github/workflows/agent-drift-watchdog.yml +0 -0
  43. {cli_router-0.3.1 → cli_router-0.3.2}/.github/workflows/publish.yml +0 -0
  44. {cli_router-0.3.1 → cli_router-0.3.2}/.github/workflows/test.yml +0 -0
  45. {cli_router-0.3.1 → cli_router-0.3.2}/.gitignore +0 -0
  46. {cli_router-0.3.1 → cli_router-0.3.2}/LICENSE +0 -0
  47. {cli_router-0.3.1 → cli_router-0.3.2}/PRD.md +0 -0
  48. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/artifacts.py +0 -0
  49. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/doctor.py +0 -0
  50. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/extractors.py +0 -0
  51. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/logs.py +0 -0
  52. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/modelcache.py +0 -0
  53. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/models.py +0 -0
  54. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/presets/__init__.py +0 -0
  55. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/presets/claude.yaml +0 -0
  56. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/presets/codex.yaml +0 -0
  57. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/presets/grok.yaml +0 -0
  58. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/presets/hermes.yaml +0 -0
  59. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/runs.py +0 -0
  60. {cli_router-0.3.1 → cli_router-0.3.2}/cli_router/streamfmt.py +0 -0
  61. {cli_router-0.3.1 → cli_router-0.3.2}/docs/agent-loops/self-hosted-runner.md +0 -0
  62. {cli_router-0.3.1 → cli_router-0.3.2}/scripts/agent/apply_drift_fix.sh +0 -0
  63. {cli_router-0.3.1 → cli_router-0.3.2}/scripts/agent/build_prompt.py +0 -0
  64. {cli_router-0.3.1 → cli_router-0.3.2}/scripts/agent/detect_drift.py +0 -0
  65. {cli_router-0.3.1 → cli_router-0.3.2}/scripts/agent/preflight.py +0 -0
  66. {cli_router-0.3.1 → cli_router-0.3.2}/scripts/agent/validate_patch.py +0 -0
  67. {cli_router-0.3.1 → cli_router-0.3.2}/scripts/check_release.py +0 -0
  68. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_agent_scripts.py +0 -0
  69. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_doctor.py +0 -0
  70. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_extractors.py +0 -0
  71. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_logs.py +0 -0
  72. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_modelcache.py +0 -0
  73. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_models.py +0 -0
  74. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_release.py +0 -0
  75. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_runs.py +0 -0
  76. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_streamfmt.py +0 -0
  77. {cli_router-0.3.1 → cli_router-0.3.2}/tests/test_version.py +0 -0
@@ -12,6 +12,23 @@ CLI-Router is a Python command-line orchestrator for external AI coding CLIs. It
12
12
 
13
13
  Keep that boundary intact. New behavior should make orchestration more reliable or configurable, not turn this package into an agent.
14
14
 
15
+ ## Agent Orientation and Continuity
16
+
17
+ This repository keeps agent-neutral continuity files in the repository root. Every coding agent should use them, regardless of vendor or runtime:
18
+
19
+ 1. Read `AGENTS.md` for normative repository rules.
20
+ 2. Read `STATE.md` for the last verified project, release, CI, and operational state. Confirm time-sensitive claims against the repository or service before acting.
21
+ 3. Read `MEMORY.md` for durable lessons and known operational traps. Treat it as accumulated guidance, not a substitute for direct verification.
22
+ 4. Follow task-specific documentation under `docs/`, especially `docs/releasing.md` for releases.
23
+
24
+ Keep the files distinct:
25
+
26
+ - Update `AGENTS.md` when repository-wide working rules or invariants change.
27
+ - Update `STATE.md` after releases or when the current branch, version, CI, publishing, or known-blocker state materially changes.
28
+ - Update `MEMORY.md` when a reusable lesson is learned about this repository or its development environment.
29
+ - Never record tokens, passwords, private keys, one-time device codes, credential contents, or other secrets in any repository file. Paths to credential stores and safe verification commands are acceptable.
30
+ - If continuity documentation conflicts with executable evidence, prefer the evidence and correct the stale documentation in the same change when practical.
31
+
15
32
  ## Source Layout
16
33
 
17
34
  - `cli_router/cli.py`: argparse CLI entry point and command dispatch.
@@ -23,6 +40,7 @@ Keep that boundary intact. New behavior should make orchestration more reliable
23
40
  - `cli_router/failures.py`: external-tool failure classification and user-facing messages.
24
41
  - `cli_router/artifacts.py`: run directory and artifact persistence.
25
42
  - `cli_router/tools.py`: `tools list` and `tools test`.
43
+ - `cli_router/receipts.py`: schema-versioned JSON receipts for automation-facing CLI output.
26
44
  - `cli_router/presets/`: packaged YAML defaults/presets.
27
45
  - `examples/`: user-facing example configs.
28
46
  - `tests/`: pytest coverage using fake subprocess CLIs, not real Claude/Codex calls.
@@ -84,13 +102,16 @@ Config lookup order is:
84
102
  4. `~/.config/cli-router/config.yaml`
85
103
  5. Built-in defaults from `cli_router/presets/generic.yaml`
86
104
 
87
- Config version is currently `version: 1`.
105
+ Config version is currently `version: 2`; legacy `version: 1` remains supported. Version 2 requires `requires_cli_router`, which gives older routers a hard failure on the schema version rather than letting them ignore an unknown compatibility key.
88
106
 
89
107
  Supported placeholders are literal token replacements:
90
108
 
91
109
  - `{prompt}`
92
110
  - `{user_prompt}`
93
111
  - `{plan_path}`
112
+ - `{previous_output}`
113
+ - `{all_stage_outputs}`
114
+ - `{target_root}`
94
115
 
95
116
  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.
96
117
 
@@ -98,7 +119,8 @@ Workflow stages use:
98
119
 
99
120
  - `id`: unique stage id within the workflow. The list order is the default execution order.
100
121
  - `tool`: primary tool name.
101
- - `fallback_tools`: optional ordered list of tool names to try after failed attempts.
122
+ - `fallback_tools`: optional ordered list of tool names or `{tool, on}` policy mappings. Fallback is fail-closed: only `auth_required`, `usage_limit`, `timeout`, and `transport_failure` may be named, and legacy string entries use that same safe operational set.
123
+ - `max_fallback_attempts`: optional nonnegative cap on fallback subprocesses actually started. Nonmatching policies are skipped without consuming the cap; a failed fallback's failure kind becomes the trigger for later policies. Fallback must never run after semantic/generic command failures, `unsupported_model`, `extraction_failed`, or configuration errors.
102
124
  - `enabled`: optional boolean; `false` skips the stage in default `run` and `implement`, while explicit `--stages` can still select it.
103
125
  - `input_template`: rendered prompt sent as `{prompt}` to the tool command.
104
126
  - `output_file`: planner output path, usually `PLAN.md`.
@@ -124,6 +146,16 @@ Tool config supports:
124
146
  - `output.extract`: top-level or dotted JSON field path.
125
147
  - `timeout_seconds`: optional positive number; timeout returns exit code `124`.
126
148
  - `provider`, `model`, and `effort`: optional TUI metadata for Model Config display/editing. These do not replace `command`.
149
+ - `cwd`: optional working directory with literal placeholder replacement; workflows expose `{target_root}` as the router invocation directory.
150
+ - `environment_mode`: `inherit` (default) or `allowlist`; allowlist mode starts with only names in `environment_allowlist`.
151
+ - `environment`: string mapping applied after the inherited/allowlisted base; values support literal placeholders.
152
+ - `environment_unset`: environment names removed after overrides.
153
+ - `stdin`: `inherit` (default) or `closed`.
154
+ - `redact_environment_values`: environment names whose nonempty values are replaced in captured/streamed output and recorded commands before artifact persistence. Without this explicit setting, preserve raw stdout/stderr.
155
+
156
+ Top-level `requires_cli_router` accepts a PEP 440 specifier such as `">=0.3.2,<0.4.0"`. It is mandatory for config version 2. All config-loading commands must fail before execution when the running version is incompatible.
157
+
158
+ Stable machine-readable output uses a single JSON object with `schema_version: 1`. `--json` is supported for `--version`, `check`, `plan`, `run`, `implement`, and `tools test`; preserve existing field names and semantics when extending the receipt schema.
127
159
 
128
160
  Diagnostic defaults support:
129
161
 
@@ -176,7 +208,7 @@ Fallback attempts include the tool name:
176
208
  - `planner.codex-planner.stderr`
177
209
  - `planner.codex-planner.extracted.md`
178
210
 
179
- Every run writes `run.yaml` containing stage summaries, commands, return codes, extracted output, `failure_kind`, workflow start/finish timestamps, total duration, and per-stage duration metrics.
211
+ Every run writes `run.yaml` containing stage summaries, commands, return codes, extracted output, `failure_kind`, workflow start/finish timestamps, total duration, and per-stage duration metrics. Fallback attempt records also contain original-primary provenance (`primary_tool`, `primary_failure_kind`), immediate-trigger provenance (`trigger_tool`, `trigger_failure_kind`), `fallback_tool`, `fallback_reason`, and `fallback_attempt`.
180
212
 
181
213
  Persistent diagnostics write under `~/.cli-router/logs/` by default:
182
214
 
@@ -198,6 +230,8 @@ Current `failure_kind` values include:
198
230
  - `command_not_found`
199
231
  - `command_failed`
200
232
  - `extraction_failed`
233
+ - `transport_failure`
234
+ - `configuration_error`
201
235
 
202
236
  Known real-world messages are covered by tests, including:
203
237
 
@@ -262,7 +296,7 @@ These observations are environment-specific. Do not hardcode them into package d
262
296
  ## Design Constraints
263
297
 
264
298
  - Keep the CLI stable: `cli-router run`, `plan`, `implement`, `check`, `config show`, `tools list`, `tools test <name>`.
265
- - Keep dependencies minimal. Current runtime dependencies are `pyyaml` and `rich`; the CLI uses `argparse`. This is why the TUI remains Rich-based instead of adopting Textual without an explicit product decision.
299
+ - Keep dependencies minimal. Current runtime dependencies are `packaging`, `pyyaml`, and `rich`; the CLI uses `argparse`. This is why the TUI remains Rich-based instead of adopting Textual without an explicit product decision.
266
300
  - Prefer list-form subprocess commands in examples and tests.
267
301
  - Preserve stdout/stderr exactly in artifacts. Do not redact or transform raw logs unless a user explicitly requests a privacy feature.
268
302
  - Avoid hidden behavior. If CLI-Router retries or falls back, make that visible through summaries and `run.yaml`.
@@ -285,3 +319,15 @@ Generated files that should normally remain untracked:
285
319
  - `.pytest_cache/`
286
320
 
287
321
  Before finalizing substantive changes, run `git status --short` and report any verification commands run.
322
+
323
+ ## Protected Branch and Release Workflow
324
+
325
+ `main` is protected. Direct pushes are expected to fail with `GH006`; release metadata and all other changes must go through a pull request and the required checks. Do not bypass protection merely to complete a release.
326
+
327
+ Use `docs/releasing.md` as the canonical release runbook. Important invariants:
328
+
329
+ - Create the release commit on a branch, merge it through a green pull request, synchronize local `main`, and only then create the annotated release tag. The tag must point to the actual merged `main` commit, not a pre-merge or squash-source commit.
330
+ - Pushing a tag does not publish the package. `.github/workflows/publish.yml` runs when a GitHub Release is published.
331
+ - PyPI publishing uses GitHub Actions trusted publishing. Do not collect or add a local PyPI password/token for the normal release path.
332
+ - Git transport and GitHub API authentication are separate. This repository's `origin` uses SSH, while `gh` stores its API credential persistently in the user's GitHub CLI configuration. A successful SSH test does not prove that `gh` is authenticated.
333
+ - Before GitHub API work, run `gh auth status`. If the stored credential is invalid, reauthenticate once with `gh auth login`, verify the result, and reuse the persistent credential; do not start repeated login flows without checking status.
@@ -2,6 +2,15 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.3.2 - 2026-07-19
6
+
7
+ - Added fail-closed conditional fallback policies with per-tool `on` failure allowlists, `max_fallback_attempts`, transport-failure classification, and fallback provenance in run manifests. Legacy string fallbacks now use only the safe operational failure set and never retry semantic, extraction, unsupported-model, or configuration failures.
8
+ - Fixed conditional fallback selection to scan past nonmatching policies, count only started fallback subprocesses against `max_fallback_attempts`, carry failed fallback classifications into later policy checks, and record both original-primary and immediate-trigger provenance.
9
+ - Added schema-versioned JSON receipts for `--version`, `check`, `plan`, `run`, `implement`, and `tools test`, including config checksums, run/stage/model details, failure and fallback data, durations, outcomes, and artifact paths.
10
+ - Captured config source and effective merged-config checksums immutably at load time so receipts remain bound to the configuration actually executed, and added provider reasoning effort to workflow and tool-test stage receipts.
11
+ - Added tool-level cwd, environment allowlisting/overrides/unsets, closed stdin, and configured environment-value redaction for commands and captured/streamed output.
12
+ - Added config schema v2 with mandatory `requires_cli_router` PEP 440 compatibility declarations, while retaining v1 support. Older routers reject the v2 schema instead of silently ignoring its safety requirement.
13
+
5
14
  ## 0.3.1 - 2026-07-19
6
15
 
7
16
  - Made workflow outcomes fail closed: `run` and `implement` now reject empty resolved stage lists, and `stop_on_failure: false` preserves the first failed stage as the overall result even when later stages succeed.
@@ -0,0 +1,60 @@
1
+ # Repository Memory
2
+
3
+ This file preserves durable, high-signal lessons for maintainers and coding agents. It must never contain credential values, private keys, one-time device codes, or other secrets. Current facts belong in `STATE.md`; normative rules belong in `AGENTS.md`.
4
+
5
+ ## GitHub Authentication Is Split
6
+
7
+ - Git transport and GitHub API authentication are independent. The repository remote uses SSH, so `ssh -T git@github.com` can succeed while `gh auth status` fails.
8
+ - `gh` API authentication should persist across sessions in the user's GitHub CLI configuration. In this WSL environment the credential store is `/home/razor/.config/gh/hosts.yml`.
9
+ - Never read, print, commit, or relay the token. Verify it safely with `gh auth status`; `gh auth token` may be used only with output suppressed when a script must test readability.
10
+ - An SSH success message exits nonzero because GitHub does not provide shell access. Judge `ssh -T git@github.com` by its message, not only its exit code.
11
+ - `gh auth setup-git` configures HTTPS Git credentials; it does not repair GitHub API authentication and is unnecessary for this repository's SSH remote.
12
+
13
+ ### 2026-07-19 Authentication Incident
14
+
15
+ - The saved `gh` credential existed but GitHub rejected it as invalid. SSH push authentication remained healthy.
16
+ - A single browser/device flow with `gh auth login --hostname github.com --git-protocol ssh --web` replaced the invalid API credential. After the user authorized it, `gh auth status` and suppressed token-readability checks succeeded without another login.
17
+ - The correct response to a future failure is: check `gh auth status`, distinguish API auth from SSH auth and network failures, then reauthenticate only if GitHub reports the stored API credential invalid.
18
+ - Device authorization codes are transient secrets. Show them only to the user during the active flow; never save them in repository documentation, logs, commits, or chat summaries intended for reuse.
19
+
20
+ ## Protected Main Changes the Release Order
21
+
22
+ - Direct pushes to `main` fail with `GH006`; this is expected protection, not an authentication failure.
23
+ - Start release metadata work on a release branch, push it, open a pull request, and wait for all required jobs.
24
+ - The repository commonly squash-merges pull requests. A tag created on the source commit before the squash would not point to the final `main` commit.
25
+ - Create the annotated version tag only after fetching the merged `main`. Verify the tag's peeled commit matches `origin/main` before pushing it.
26
+ - If an unpushed local tag points to the source commit, it is safe to recreate it at the identical merged tree. If a tag has already been pushed or a release may have published, stop and assess before rewriting it; do not force-update a public release tag casually.
27
+
28
+ ## Publishing Mechanics
29
+
30
+ - A tag push alone does not publish CLI-Router. Publishing starts when a non-draft GitHub Release is published for the tag.
31
+ - `.github/workflows/publish.yml` performs tests, release-identity validation, build, Twine checks, artifact handoff, and PyPI trusted publishing.
32
+ - Trusted publishing uses GitHub OIDC. A normal release should not need `TWINE_PASSWORD`, `PYPI_API_TOKEN`, or a manual upload from a maintainer machine.
33
+ - Monitor the release workflow through the final `Publish CLI-Router to PyPI` job. A successful build job alone does not prove publication.
34
+ - Independently verify publication from a fresh virtual environment using the public PyPI index and the exact version. Check both `importlib.metadata.version("cli-router")` and `cli_router.__version__`, then run `cli-router check`.
35
+
36
+ ## Local Verification Environment
37
+
38
+ - System Python may be protected by PEP 668 or lack package metadata and development tools. An uninstalled checkout can make `tests/test_version.py` fail even when the source versions agree.
39
+ - Use a temporary virtual environment, install `-e ".[dev]"`, and run tests, Ruff, mypy, coverage, CLI smoke checks, release identity, build, and Twine there.
40
+ - Build release artifacts in a fresh temporary output directory to avoid confusing stale `dist/` files with the current release.
41
+ - Network isolation can prevent build dependencies from resolving. Treat dependency-download failures separately from code failures and rerun with the environment's approved network mechanism.
42
+
43
+ ## PyYAML Treats `on` as a YAML 1.1 Boolean
44
+
45
+ - PyYAML's safe loader parses an unquoted `on:` mapping key as boolean `true`, even though users naturally write conditional fallback policies with `on:`.
46
+ - CLI-Router normalizes boolean `true` back to the documented `on` key inside fallback policy mappings before validation. Preserve this normalization unless the entire loader is deliberately migrated to YAML 1.2 semantics.
47
+
48
+ ## Compatibility Keys Need a Schema Bootstrap
49
+
50
+ - A router released before `requires_cli_router` existed can ignore that unknown top-level key, so the key alone cannot protect a safety-sensitive config from old binaries.
51
+ - Config version 2 is the bootstrap boundary: old routers that only accept version 1 fail immediately, while new routers require and evaluate `requires_cli_router`. Keep legacy version 1 support, including treating configs with no explicit version as v1.
52
+
53
+ ## Fallback Policies Filter; Attempt Caps Count Processes
54
+
55
+ - A nonmatching conditional fallback policy is skipped, not treated as a failed attempt and not charged against `max_fallback_attempts`.
56
+ - After a fallback subprocess fails, its classified failure becomes the immediate trigger for later policies. Keep original-primary provenance separate from this immediate-trigger provenance so multi-hop chains remain auditable.
57
+
58
+ ## Config Receipt Identity Is a Load-Time Snapshot
59
+
60
+ - Receipt identity must hash the exact source bytes read and the canonical effective merged config while loading. Never reread the source path when emitting a receipt: another process can replace the file between execution and receipt emission.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cli-router
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Summary: A simple Python CLI router for AI planning and coding agents.
5
5
  Project-URL: Homepage, https://github.com/coolrazor007/cli-router
6
6
  Project-URL: Repository, https://github.com/coolrazor007/cli-router
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3.14
22
22
  Classifier: Topic :: Software Development
23
23
  Classifier: Topic :: Utilities
24
24
  Requires-Python: >=3.10
25
+ Requires-Dist: packaging>=24.0
25
26
  Requires-Dist: pyyaml>=6.0
26
27
  Requires-Dist: rich>=13.0
27
28
  Provides-Extra: dev
@@ -61,6 +62,8 @@ python -m pip install -e .
61
62
 
62
63
  ```bash
63
64
  cli-router --help
65
+ cli-router --version
66
+ cli-router --version --json
64
67
  cli-router
65
68
  cli-router tui
66
69
  cli-router plan "Add a health check endpoint"
@@ -69,9 +72,11 @@ cli-router run "Add a health check endpoint" --stages planner,review,coder
69
72
  cli-router implement
70
73
  cli-router implement --stages coder,review
71
74
  cli-router check
75
+ cli-router check --json
72
76
  cli-router config show
73
77
  cli-router tools list
74
78
  cli-router tools test claude-planner
79
+ cli-router tools test claude-planner --json
75
80
  cli-router runs
76
81
  cli-router runs show 2026-07-07T14-22-10
77
82
  ```
@@ -130,10 +135,18 @@ CLI-Router looks for config in this order:
130
135
 
131
136
  The TUI persists first-run setup and TUI edits to `~/.cli-router/config.yaml`. Project-local config files still take precedence when present.
132
137
 
138
+ Project configs can require a compatible CLI-Router release with a PEP 440 version range. Every command, including `check`, fails before execution when the running package is outside the range. Use config `version: 2` with this declaration for a hard compatibility boundary: pre-feature routers reject the schema version instead of ignoring an unknown top-level key.
139
+
140
+ ```yaml
141
+ version: 2
142
+ requires_cli_router: ">=0.3.2,<0.4.0"
143
+ ```
144
+
133
145
  Minimal example:
134
146
 
135
147
  ```yaml
136
- version: 1
148
+ version: 2
149
+ requires_cli_router: ">=0.3.2,<0.4.0"
137
150
 
138
151
  defaults:
139
152
  plan_file: PLAN.md
@@ -159,6 +172,43 @@ tools:
159
172
  format: json
160
173
  extract: result
161
174
 
175
+ grok-reviewer:
176
+ type: grok
177
+ cwd: "{target_root}"
178
+ environment_mode: allowlist
179
+ environment_allowlist:
180
+ - HOME
181
+ - USER
182
+ - LOGNAME
183
+ - PATH
184
+ - GROK_HOME
185
+ environment:
186
+ TERM: dumb
187
+ environment_unset:
188
+ - GITHUB_TOKEN
189
+ - SSH_AUTH_SOCK
190
+ stdin: closed
191
+ redact_environment_values:
192
+ - GROK_TOKEN
193
+ command:
194
+ - grok
195
+ - --single
196
+ - "{prompt}"
197
+ output:
198
+ format: text
199
+
200
+ codex-planner:
201
+ type: codex
202
+ timeout_seconds: 120
203
+ command:
204
+ - codex
205
+ - --ask-for-approval
206
+ - never
207
+ - exec
208
+ - "{prompt}"
209
+ output:
210
+ format: text
211
+
162
212
  codex-coder:
163
213
  type: codex
164
214
  timeout_seconds: 120
@@ -221,7 +271,13 @@ workflows:
221
271
  - id: planner
222
272
  tool: claude-planner
223
273
  fallback_tools:
224
- - codex-planner
274
+ - tool: codex-planner
275
+ on:
276
+ - auth_required
277
+ - usage_limit
278
+ - timeout
279
+ - transport_failure
280
+ max_fallback_attempts: 1
225
281
  input_template: |
226
282
  You are the planning model for a coding-agent handoff.
227
283
 
@@ -251,7 +307,7 @@ workflows:
251
307
  {user_prompt}
252
308
  ```
253
309
 
254
- Command args and templates support `{prompt}`, `{user_prompt}`, and `{plan_path}` placeholders.
310
+ Command args, `cwd`, and configured environment values use literal placeholder replacement. Supported runtime placeholders include `{prompt}`, `{user_prompt}`, `{plan_path}`, `{previous_output}`, `{all_stage_outputs}`, and `{target_root}`. `{target_root}` is the directory from which CLI-Router was invoked.
255
311
 
256
312
  Workflow stages are modular:
257
313
 
@@ -272,7 +328,35 @@ Workflow stages are modular:
272
328
  - A stage with `output_file` writes its extracted output to that file.
273
329
  - A stage with `updates_plan: true` makes later `{plan_path}` placeholders point to its `output_file`.
274
330
 
275
- When a stage command fails, CLI-Router records stdout/stderr and classifies common failures. Usage-limit messages such as provider quota, rate-limit, or 429 errors are reported as usage-limit failures. Authentication failures such as provider login errors are reported as `auth_required` and include the provider's first error line. Commands can set `timeout_seconds`; timed-out commands are recorded with exit code `124`. A stage can define `fallback_tools` to try alternate configured tools in order after a failed primary tool.
331
+ When a stage command fails, CLI-Router records stdout/stderr and classifies common failures. Usage-limit messages such as provider quota, rate-limit, or 429 errors are reported as usage-limit failures. Authentication failures such as provider login errors are reported as `auth_required` and include the provider's first error line. Network/connection errors are classified as `transport_failure`. Commands can set `timeout_seconds`; timed-out commands are recorded with exit code `124`.
332
+
333
+ Fallback is fail-closed and only available for the operational failure kinds `auth_required`, `usage_limit`, `timeout`, and `transport_failure`. A structured fallback selects the allowed kinds with `on`; `max_fallback_attempts` caps how many alternates may run. Legacy string entries remain supported as shorthand for the full safe operational set. CLI-Router never falls back after generic command/semantic failures, unsupported-model configuration, malformed structured output (`extraction_failed`), or runtime configuration errors. Successful extracted verdicts such as `FAIL`, `PASS_WITH_WARNINGS`, or `INCONCLUSIVE` are final stage output and do not trigger fallback.
334
+
335
+ Fallback policies are scanned in configured order. Policies that do not allow the current failure are skipped without consuming `max_fallback_attempts`; the cap counts only fallback subprocesses actually started. After a fallback fails, its failure kind becomes the trigger evaluated by later policies.
336
+
337
+ Each fallback attempt records the original `primary_tool` and `primary_failure_kind`, the immediate `trigger_tool` and `trigger_failure_kind`, `fallback_tool`, `fallback_reason: allowed_failure_kind`, and the one-based `fallback_attempt` in `run.yaml`.
338
+
339
+ Tool execution inherits the router process environment and stdin by default for backward compatibility. `environment_mode: allowlist` starts from only `environment_allowlist`; `environment` then adds or overrides values, and `environment_unset` removes named values. `stdin: closed` connects the child to the null device. `redact_environment_values` names environment variables whose nonempty values are replaced in captured stdout, stderr, streamed output, and the recorded command before artifacts are written. This is an explicit privacy mode: without it, raw output remains unchanged.
340
+
341
+ ## Machine-readable output
342
+
343
+ `--json` is supported by `--version`, `check`, `plan`, `run`, `implement`, and `tools test` (place it after the subcommand for command-specific use). Each invocation prints exactly one JSON object with `schema_version: 1`. The stable envelope includes:
344
+
345
+ - CLI-Router version and command.
346
+ - Config source, the SHA-256 checksum of the exact source bytes loaded, and the SHA-256 checksum of the canonical effective merged config. These values are captured at load time and do not change if the source file is rewritten during execution.
347
+ - Workflow, run ID/directory, duration, exit code, overall outcome, and error.
348
+ - Whether fallback was used and why.
349
+ - Per-attempt stage, tool, provider/model/reasoning effort, attempt number, exit code, failure kind, duration, fallback provenance, and artifact paths.
350
+
351
+ Examples:
352
+
353
+ ```bash
354
+ cli-router --version --json
355
+ cli-router check --json
356
+ cli-router tools test grok-reviewer --json
357
+ cli-router run "Review this target" --json
358
+ cli-router implement --json
359
+ ```
276
360
 
277
361
  ## Artifacts
278
362
 
@@ -322,3 +406,7 @@ python -m pip install --upgrade build twine
322
406
  python -m build
323
407
  python -m twine check dist/*
324
408
  ```
409
+
410
+ The protected-branch, tagging, GitHub Release, trusted PyPI publishing, authentication, and public-package verification procedure is documented in the [release runbook](https://github.com/coolrazor007/cli-router/blob/main/docs/releasing.md).
411
+
412
+ For cross-agent continuity, repository rules live in [AGENTS.md](https://github.com/coolrazor007/cli-router/blob/main/AGENTS.md), the last verified operational state lives in [STATE.md](https://github.com/coolrazor007/cli-router/blob/main/STATE.md), and durable environment lessons live in [MEMORY.md](https://github.com/coolrazor007/cli-router/blob/main/MEMORY.md). These files are intentionally tool- and vendor-neutral.
@@ -26,6 +26,8 @@ python -m pip install -e .
26
26
 
27
27
  ```bash
28
28
  cli-router --help
29
+ cli-router --version
30
+ cli-router --version --json
29
31
  cli-router
30
32
  cli-router tui
31
33
  cli-router plan "Add a health check endpoint"
@@ -34,9 +36,11 @@ cli-router run "Add a health check endpoint" --stages planner,review,coder
34
36
  cli-router implement
35
37
  cli-router implement --stages coder,review
36
38
  cli-router check
39
+ cli-router check --json
37
40
  cli-router config show
38
41
  cli-router tools list
39
42
  cli-router tools test claude-planner
43
+ cli-router tools test claude-planner --json
40
44
  cli-router runs
41
45
  cli-router runs show 2026-07-07T14-22-10
42
46
  ```
@@ -95,10 +99,18 @@ CLI-Router looks for config in this order:
95
99
 
96
100
  The TUI persists first-run setup and TUI edits to `~/.cli-router/config.yaml`. Project-local config files still take precedence when present.
97
101
 
102
+ Project configs can require a compatible CLI-Router release with a PEP 440 version range. Every command, including `check`, fails before execution when the running package is outside the range. Use config `version: 2` with this declaration for a hard compatibility boundary: pre-feature routers reject the schema version instead of ignoring an unknown top-level key.
103
+
104
+ ```yaml
105
+ version: 2
106
+ requires_cli_router: ">=0.3.2,<0.4.0"
107
+ ```
108
+
98
109
  Minimal example:
99
110
 
100
111
  ```yaml
101
- version: 1
112
+ version: 2
113
+ requires_cli_router: ">=0.3.2,<0.4.0"
102
114
 
103
115
  defaults:
104
116
  plan_file: PLAN.md
@@ -124,6 +136,43 @@ tools:
124
136
  format: json
125
137
  extract: result
126
138
 
139
+ grok-reviewer:
140
+ type: grok
141
+ cwd: "{target_root}"
142
+ environment_mode: allowlist
143
+ environment_allowlist:
144
+ - HOME
145
+ - USER
146
+ - LOGNAME
147
+ - PATH
148
+ - GROK_HOME
149
+ environment:
150
+ TERM: dumb
151
+ environment_unset:
152
+ - GITHUB_TOKEN
153
+ - SSH_AUTH_SOCK
154
+ stdin: closed
155
+ redact_environment_values:
156
+ - GROK_TOKEN
157
+ command:
158
+ - grok
159
+ - --single
160
+ - "{prompt}"
161
+ output:
162
+ format: text
163
+
164
+ codex-planner:
165
+ type: codex
166
+ timeout_seconds: 120
167
+ command:
168
+ - codex
169
+ - --ask-for-approval
170
+ - never
171
+ - exec
172
+ - "{prompt}"
173
+ output:
174
+ format: text
175
+
127
176
  codex-coder:
128
177
  type: codex
129
178
  timeout_seconds: 120
@@ -186,7 +235,13 @@ workflows:
186
235
  - id: planner
187
236
  tool: claude-planner
188
237
  fallback_tools:
189
- - codex-planner
238
+ - tool: codex-planner
239
+ on:
240
+ - auth_required
241
+ - usage_limit
242
+ - timeout
243
+ - transport_failure
244
+ max_fallback_attempts: 1
190
245
  input_template: |
191
246
  You are the planning model for a coding-agent handoff.
192
247
 
@@ -216,7 +271,7 @@ workflows:
216
271
  {user_prompt}
217
272
  ```
218
273
 
219
- Command args and templates support `{prompt}`, `{user_prompt}`, and `{plan_path}` placeholders.
274
+ Command args, `cwd`, and configured environment values use literal placeholder replacement. Supported runtime placeholders include `{prompt}`, `{user_prompt}`, `{plan_path}`, `{previous_output}`, `{all_stage_outputs}`, and `{target_root}`. `{target_root}` is the directory from which CLI-Router was invoked.
220
275
 
221
276
  Workflow stages are modular:
222
277
 
@@ -237,7 +292,35 @@ Workflow stages are modular:
237
292
  - A stage with `output_file` writes its extracted output to that file.
238
293
  - A stage with `updates_plan: true` makes later `{plan_path}` placeholders point to its `output_file`.
239
294
 
240
- When a stage command fails, CLI-Router records stdout/stderr and classifies common failures. Usage-limit messages such as provider quota, rate-limit, or 429 errors are reported as usage-limit failures. Authentication failures such as provider login errors are reported as `auth_required` and include the provider's first error line. Commands can set `timeout_seconds`; timed-out commands are recorded with exit code `124`. A stage can define `fallback_tools` to try alternate configured tools in order after a failed primary tool.
295
+ When a stage command fails, CLI-Router records stdout/stderr and classifies common failures. Usage-limit messages such as provider quota, rate-limit, or 429 errors are reported as usage-limit failures. Authentication failures such as provider login errors are reported as `auth_required` and include the provider's first error line. Network/connection errors are classified as `transport_failure`. Commands can set `timeout_seconds`; timed-out commands are recorded with exit code `124`.
296
+
297
+ Fallback is fail-closed and only available for the operational failure kinds `auth_required`, `usage_limit`, `timeout`, and `transport_failure`. A structured fallback selects the allowed kinds with `on`; `max_fallback_attempts` caps how many alternates may run. Legacy string entries remain supported as shorthand for the full safe operational set. CLI-Router never falls back after generic command/semantic failures, unsupported-model configuration, malformed structured output (`extraction_failed`), or runtime configuration errors. Successful extracted verdicts such as `FAIL`, `PASS_WITH_WARNINGS`, or `INCONCLUSIVE` are final stage output and do not trigger fallback.
298
+
299
+ Fallback policies are scanned in configured order. Policies that do not allow the current failure are skipped without consuming `max_fallback_attempts`; the cap counts only fallback subprocesses actually started. After a fallback fails, its failure kind becomes the trigger evaluated by later policies.
300
+
301
+ Each fallback attempt records the original `primary_tool` and `primary_failure_kind`, the immediate `trigger_tool` and `trigger_failure_kind`, `fallback_tool`, `fallback_reason: allowed_failure_kind`, and the one-based `fallback_attempt` in `run.yaml`.
302
+
303
+ Tool execution inherits the router process environment and stdin by default for backward compatibility. `environment_mode: allowlist` starts from only `environment_allowlist`; `environment` then adds or overrides values, and `environment_unset` removes named values. `stdin: closed` connects the child to the null device. `redact_environment_values` names environment variables whose nonempty values are replaced in captured stdout, stderr, streamed output, and the recorded command before artifacts are written. This is an explicit privacy mode: without it, raw output remains unchanged.
304
+
305
+ ## Machine-readable output
306
+
307
+ `--json` is supported by `--version`, `check`, `plan`, `run`, `implement`, and `tools test` (place it after the subcommand for command-specific use). Each invocation prints exactly one JSON object with `schema_version: 1`. The stable envelope includes:
308
+
309
+ - CLI-Router version and command.
310
+ - Config source, the SHA-256 checksum of the exact source bytes loaded, and the SHA-256 checksum of the canonical effective merged config. These values are captured at load time and do not change if the source file is rewritten during execution.
311
+ - Workflow, run ID/directory, duration, exit code, overall outcome, and error.
312
+ - Whether fallback was used and why.
313
+ - Per-attempt stage, tool, provider/model/reasoning effort, attempt number, exit code, failure kind, duration, fallback provenance, and artifact paths.
314
+
315
+ Examples:
316
+
317
+ ```bash
318
+ cli-router --version --json
319
+ cli-router check --json
320
+ cli-router tools test grok-reviewer --json
321
+ cli-router run "Review this target" --json
322
+ cli-router implement --json
323
+ ```
241
324
 
242
325
  ## Artifacts
243
326
 
@@ -287,3 +370,7 @@ python -m pip install --upgrade build twine
287
370
  python -m build
288
371
  python -m twine check dist/*
289
372
  ```
373
+
374
+ The protected-branch, tagging, GitHub Release, trusted PyPI publishing, authentication, and public-package verification procedure is documented in the [release runbook](https://github.com/coolrazor007/cli-router/blob/main/docs/releasing.md).
375
+
376
+ For cross-agent continuity, repository rules live in [AGENTS.md](https://github.com/coolrazor007/cli-router/blob/main/AGENTS.md), the last verified operational state lives in [STATE.md](https://github.com/coolrazor007/cli-router/blob/main/STATE.md), and durable environment lessons live in [MEMORY.md](https://github.com/coolrazor007/cli-router/blob/main/MEMORY.md). These files are intentionally tool- and vendor-neutral.
@@ -0,0 +1,55 @@
1
+ # Repository State
2
+
3
+ Last verified: 2026-07-19 (America/Los_Angeles)
4
+
5
+ This file records the current known state for maintainers and coding agents. Verify remote and time-sensitive facts before relying on them, and update this file when the state materially changes.
6
+
7
+ ## Current Release
8
+
9
+ - Current published package version: `0.3.1`. Draft PR #8 prepares package and runtime identity `0.3.2`; it is not yet merged, tagged, released, or published.
10
+ - Annotated tag `v0.3.1` points to release commit `a37b4960dd7467f19bdb767502c13dcdc522326f`, which was the verified `main` and `origin/main` tip when 0.3.1 was published.
11
+ - GitHub Release: <https://github.com/coolrazor007/cli-router/releases/tag/v0.3.1>
12
+ - PyPI package: `cli-router==0.3.1`, independently installed from the public PyPI index and verified to report metadata and runtime version `0.3.1`.
13
+ - The `Unreleased` changelog section is empty after the 0.3.1 release; draft PR #8 stages its accepted changes under a dated 0.3.2 release section.
14
+ - Issue #1, the empty-stage false green, is closed as completed and represented in the 0.3.1 changelog.
15
+
16
+ ## Repository and CI
17
+
18
+ - Default branch: `main`.
19
+ - `main` is protected and rejects direct pushes. Changes must be merged by pull request.
20
+ - Required pull-request jobs currently comprise Python 3.10, 3.11, 3.12, 3.13, and 3.14 tests plus `Quality and package`.
21
+ - The quality job runs Ruff, mypy, actionlint, branch-aware coverage with an 80% floor, package build, and Twine checks.
22
+ - The 0.3.1 release PR was #6 and all six required jobs passed before merge.
23
+
24
+ ## Publishing
25
+
26
+ - `.github/workflows/publish.yml` is triggered by a published GitHub Release, not by a tag push alone.
27
+ - The publish workflow reruns the Python 3.10-3.14 test matrix and release-identity validation, builds and checks the distributions, and publishes through PyPI trusted publishing using GitHub OIDC.
28
+ - The successful 0.3.1 publish run is <https://github.com/coolrazor007/cli-router/actions/runs/29706059649>.
29
+ - Normal releases do not require a local PyPI API token or a manual `twine upload`.
30
+
31
+ ## Local Environment
32
+
33
+ - Repository remote: `git@github.com:coolrazor007/cli-router.git`.
34
+ - Git pushes authenticate with the existing SSH key.
35
+ - GitHub API operations authenticate separately through `gh`.
36
+ - As of the last verification, `gh auth status` succeeds for `coolrazor007`, and `gh` can read its persistently stored credential from `/home/razor/.config/gh/hosts.yml`.
37
+ - Do not copy credential contents or transient device codes into this file.
38
+
39
+ ## Verification Baseline
40
+
41
+ The 0.3.1 release was verified with:
42
+
43
+ - 185 tests passing locally.
44
+ - Ruff passing.
45
+ - mypy passing for 22 source files.
46
+ - Branch-aware coverage meeting the configured 80% floor.
47
+ - CLI help and `cli-router check` smoke tests passing.
48
+ - Release identity validation passing for `v0.3.1`.
49
+ - Wheel and source distribution build and Twine checks passing.
50
+ - A built-wheel installation smoke test passing.
51
+ - A separate clean installation of `cli-router==0.3.1` from public PyPI reporting metadata and runtime version `0.3.1`, with `cli-router check` returning `Configuration OK`.
52
+
53
+ ## Known Blockers
54
+
55
+ - No known blocker remains for the 0.3.1 release.
@@ -1,3 +1,3 @@
1
1
  """CLI-Router package."""
2
2
 
3
- __version__ = "0.3.1"
3
+ __version__ = "0.3.2"