typer-agentic 0.1.1__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 (57) hide show
  1. typer_agentic-0.1.1/.claude/hooks/session-start.sh +21 -0
  2. typer_agentic-0.1.1/.claude/settings.json +21 -0
  3. typer_agentic-0.1.1/.github/dependabot.yml +24 -0
  4. typer_agentic-0.1.1/.github/workflows/ci.yml +103 -0
  5. typer_agentic-0.1.1/.github/workflows/release.yml +58 -0
  6. typer_agentic-0.1.1/.gitignore +22 -0
  7. typer_agentic-0.1.1/CHANGELOG.md +14 -0
  8. typer_agentic-0.1.1/CLAUDE.md +53 -0
  9. typer_agentic-0.1.1/LICENSE +21 -0
  10. typer_agentic-0.1.1/PKG-INFO +177 -0
  11. typer_agentic-0.1.1/README.md +152 -0
  12. typer_agentic-0.1.1/SCHEMA.md +57 -0
  13. typer_agentic-0.1.1/docs/development.md +29 -0
  14. typer_agentic-0.1.1/docs/index.md +20 -0
  15. typer_agentic-0.1.1/docs/plans/README.md +3 -0
  16. typer_agentic-0.1.1/docs/plans/typer-agent-errors-spec.md +305 -0
  17. typer_agentic-0.1.1/pyproject.toml +190 -0
  18. typer_agentic-0.1.1/scripts/install-hooks.sh +9 -0
  19. typer_agentic-0.1.1/scripts/pre-commit.sh +32 -0
  20. typer_agentic-0.1.1/src/typer_agentic/__init__.py +51 -0
  21. typer_agentic-0.1.1/src/typer_agentic/builder.py +357 -0
  22. typer_agentic-0.1.1/src/typer_agentic/compat.py +177 -0
  23. typer_agentic-0.1.1/src/typer_agentic/config.py +137 -0
  24. typer_agentic-0.1.1/src/typer_agentic/example.py +83 -0
  25. typer_agentic-0.1.1/src/typer_agentic/intercept.py +159 -0
  26. typer_agentic-0.1.1/src/typer_agentic/introspect.py +174 -0
  27. typer_agentic-0.1.1/src/typer_agentic/payload.py +94 -0
  28. typer_agentic-0.1.1/src/typer_agentic/py.typed +0 -0
  29. typer_agentic-0.1.1/src/typer_agentic/render/__init__.py +15 -0
  30. typer_agentic-0.1.1/src/typer_agentic/render/json_format.py +11 -0
  31. typer_agentic-0.1.1/src/typer_agentic/render/markdown.py +124 -0
  32. typer_agentic-0.1.1/src/typer_agentic/repeat.py +113 -0
  33. typer_agentic-0.1.1/src/typer_agentic/skill.py +136 -0
  34. typer_agentic-0.1.1/src/typer_agentic/suggest.py +59 -0
  35. typer_agentic-0.1.1/src/typer_agentic/wording.py +62 -0
  36. typer_agentic-0.1.1/tests/__init__.py +0 -0
  37. typer_agentic-0.1.1/tests/conftest.py +119 -0
  38. typer_agentic-0.1.1/tests/fixture_app.py +71 -0
  39. typer_agentic-0.1.1/tests/golden/SKILL.md +93 -0
  40. typer_agentic-0.1.1/tests/golden/bad_choice.md +20 -0
  41. typer_agentic-0.1.1/tests/golden/bad_integer.md +18 -0
  42. typer_agentic-0.1.1/tests/golden/extra_argument.md +18 -0
  43. typer_agentic-0.1.1/tests/golden/missing_argument.md +18 -0
  44. typer_agentic-0.1.1/tests/golden/no_such_command.md +21 -0
  45. typer_agentic-0.1.1/tests/golden/no_such_option.json +78 -0
  46. typer_agentic-0.1.1/tests/golden/no_such_option.md +20 -0
  47. typer_agentic-0.1.1/tests/golden/option_requires_value.md +18 -0
  48. typer_agentic-0.1.1/tests/test_compat.py +145 -0
  49. typer_agentic-0.1.1/tests/test_config.py +145 -0
  50. typer_agentic-0.1.1/tests/test_e2e.py +70 -0
  51. typer_agentic-0.1.1/tests/test_intercept.py +221 -0
  52. typer_agentic-0.1.1/tests/test_introspect.py +155 -0
  53. typer_agentic-0.1.1/tests/test_render.py +160 -0
  54. typer_agentic-0.1.1/tests/test_repeat.py +111 -0
  55. typer_agentic-0.1.1/tests/test_skill.py +116 -0
  56. typer_agentic-0.1.1/tests/test_suggest.py +115 -0
  57. typer_agentic-0.1.1/uv.lock +602 -0
@@ -0,0 +1,21 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$PWD}"
5
+ cd "$CLAUDE_PROJECT_DIR"
6
+
7
+ # Report venv status for the user's outer shell.
8
+ # Affects both user-typed `! cmd` and Claude's Bash subprocesses (which inherit the parent shell's env).
9
+ # When unactivated, prefer `uv run <tool>` for venv binaries.
10
+ project_venv="$CLAUDE_PROJECT_DIR/.venv"
11
+ if [ -z "${VIRTUAL_ENV:-}" ]; then
12
+ echo "venv not activated in the user's outer shell — \`! cmd\` will not see .venv/bin. Suggest activating: fish \`source .venv/bin/activate.fish\`, bash/zsh \`source .venv/bin/activate\`."
13
+ elif [ "$VIRTUAL_ENV" != "$project_venv" ]; then
14
+ echo "VIRTUAL_ENV is '$VIRTUAL_ENV' but project venv is '$project_venv'."
15
+ fi
16
+
17
+ # Remote-only setup (Claude Code on the web)
18
+ if [ "${CLAUDE_CODE_REMOTE:-}" = "true" ]; then
19
+ uv sync --quiet
20
+ bash scripts/install-hooks.sh
21
+ fi
@@ -0,0 +1,21 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(uv run tyf:*)",
5
+ "Bash(uv run biston:*)",
6
+ "Bash(uv run poe:*)"
7
+ ]
8
+ },
9
+ "hooks": {
10
+ "SessionStart": [
11
+ {
12
+ "hooks": [
13
+ {
14
+ "type": "command",
15
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh"
16
+ }
17
+ ]
18
+ }
19
+ ]
20
+ }
21
+ }
@@ -0,0 +1,24 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "uv"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ day: "monday"
8
+ open-pull-requests-limit: 10
9
+ labels:
10
+ - "dependencies"
11
+ - "python"
12
+ groups:
13
+ minor-and-patch:
14
+ update-types:
15
+ - "minor"
16
+ - "patch"
17
+ - package-ecosystem: "github-actions"
18
+ directory: "/"
19
+ schedule:
20
+ interval: "weekly"
21
+ day: "monday"
22
+ labels:
23
+ - "dependencies"
24
+ - "ci"
@@ -0,0 +1,103 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ lint:
18
+ name: Lint
19
+ runs-on: ubuntu-latest
20
+ steps:
21
+ - uses: actions/checkout@v7
22
+ - uses: astral-sh/setup-uv@v7
23
+ - run: uv sync --frozen
24
+ - run: uv run ruff check .
25
+
26
+ format:
27
+ name: Format
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v7
31
+ - uses: astral-sh/setup-uv@v7
32
+ - run: uv sync --frozen
33
+ - run: uv run ruff format --check .
34
+
35
+ typecheck:
36
+ name: Type Check
37
+ runs-on: ubuntu-latest
38
+ steps:
39
+ - uses: actions/checkout@v7
40
+ - uses: astral-sh/setup-uv@v7
41
+ - run: uv sync --frozen
42
+ - run: uv run ty check
43
+
44
+ vulns:
45
+ name: Dependency Vulnerabilities
46
+ runs-on: ubuntu-latest
47
+ steps:
48
+ - uses: actions/checkout@v7
49
+ - uses: astral-sh/setup-uv@v7
50
+ - run: uv sync --frozen
51
+ - run: uvx pysentry-rs .
52
+ continue-on-error: true
53
+
54
+ dead-code:
55
+ name: Dead Code Detection
56
+ runs-on: ubuntu-latest
57
+ steps:
58
+ - uses: actions/checkout@v7
59
+ - uses: astral-sh/setup-uv@v7
60
+ - run: uv sync --frozen
61
+ - run: uv run vulture src
62
+
63
+ unused-deps:
64
+ name: Unused Dependencies
65
+ runs-on: ubuntu-latest
66
+ steps:
67
+ - uses: actions/checkout@v7
68
+ - uses: astral-sh/setup-uv@v7
69
+ - run: uv sync --frozen
70
+ - run: uv run deptry .
71
+
72
+ clones:
73
+ name: Clone Detection
74
+ runs-on: ubuntu-latest
75
+ steps:
76
+ - uses: actions/checkout@v7
77
+ - uses: astral-sh/setup-uv@v7
78
+ - run: uv sync --frozen
79
+ - run: uv run biston scan .
80
+ continue-on-error: true
81
+
82
+ test:
83
+ name: Test (py${{ matrix.python }})
84
+ runs-on: ubuntu-latest
85
+ strategy:
86
+ fail-fast: false
87
+ matrix:
88
+ python: ["3.11", "3.12", "3.13", "3.14"]
89
+ steps:
90
+ - uses: actions/checkout@v7
91
+ - uses: astral-sh/setup-uv@v7
92
+ with:
93
+ python-version: ${{ matrix.python }}
94
+ - run: uv sync --frozen
95
+ - run: uv run pytest -q --tb=short
96
+
97
+ test-compat:
98
+ name: Test (typer 0.25, external click)
99
+ runs-on: ubuntu-latest
100
+ steps:
101
+ - uses: actions/checkout@v7
102
+ - uses: astral-sh/setup-uv@v7
103
+ - run: uv run --no-project --with "typer==0.25.*" --with pytest --with . pytest tests -q -o addopts= -p no:cacheprovider
@@ -0,0 +1,58 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ build:
14
+ name: Build distribution
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@v7
18
+ - uses: astral-sh/setup-uv@v7
19
+ - run: uv build
20
+ - run: uvx twine check dist/*
21
+ - uses: actions/upload-artifact@v7
22
+ with:
23
+ name: dist
24
+ path: dist/
25
+
26
+ publish:
27
+ name: Publish to PyPI
28
+ needs: build
29
+ runs-on: ubuntu-latest
30
+ if: startsWith(github.ref, 'refs/tags/')
31
+ environment:
32
+ name: pypi
33
+ url: https://pypi.org/p/typer-agentic
34
+ permissions:
35
+ id-token: write
36
+ steps:
37
+ - uses: actions/download-artifact@v8
38
+ with:
39
+ name: dist
40
+ path: dist/
41
+ - uses: pypa/gh-action-pypi-publish@release/v1
42
+
43
+ github-release:
44
+ name: Create GitHub Release
45
+ needs: publish
46
+ runs-on: ubuntu-latest
47
+ if: startsWith(github.ref, 'refs/tags/')
48
+ permissions:
49
+ contents: write
50
+ steps:
51
+ - uses: actions/download-artifact@v8
52
+ with:
53
+ name: dist
54
+ path: dist/
55
+ - uses: softprops/action-gh-release@v3
56
+ with:
57
+ files: dist/*
58
+ generate_release_notes: true
@@ -0,0 +1,22 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .eggs/
8
+ *.egg
9
+ .venv/
10
+ venv/
11
+ .env
12
+ *.so
13
+ .coverage
14
+ htmlcov/
15
+ .pytest_cache/
16
+ .ruff_cache/
17
+ .mypy_cache/
18
+
19
+ # workflow scratch dirs
20
+ cf/
21
+ cml/
22
+ di/
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ Initial release.
6
+
7
+ - `agent_errors(app)` wrapper: structured, retry-oriented usage errors when an agent drives the CLI; Typer's own output for humans.
8
+ - Auto-detection via `CLAUDECODE` and friends, `AGENT_ERRORS` env toggle, `--agent-errors` / `--human-errors` sentinel flags.
9
+ - Markdown (default) and JSON renderers; `typer-agentic/v1` schema.
10
+ - Parameter inventory with normalised types and choices, ranked suggestions, one synthesised corrected example.
11
+ - De-escalation copy in `copy.py`.
12
+ - `compat.py`: works with vendored (`typer._click`, Typer >= 0.26) and external Click; transparent passthrough if neither resolves.
13
+ - Optional repeat-failure escalation (`repeat_detection=True`).
14
+ - `--agent-skill` / `render_skill()` SKILL.md emitter.
@@ -0,0 +1,53 @@
1
+ # typer-agentic
2
+
3
+ Typer extension that makes CLIs friendly to coding agents. Package: `src/typer_agentic/`, published to PyPI as `typer-agentic`.
4
+
5
+ Design and build prompts live in `docs/plans/`. Read the current plan before starting non-trivial work.
6
+
7
+ ## Commands
8
+
9
+ - `uv run poe check` — lint, typecheck, dead code, unused deps, clones, then fail-fast tests
10
+ - `uv run poe check-fast` — lint + typecheck only
11
+ - `uv run poe fix` — auto-format and fix lint issues
12
+ - `uv run poe test` — tests with coverage (parallel)
13
+ - `uv run poe check-all` — every check, report all failures
14
+ - `uv run poe release` — bump patch version, tag, push (`level=minor|major` to override)
15
+
16
+ ## Code Search (`tyf`)
17
+
18
+ This project has `tyf` (ty-find) — type-aware code search that gives LSP-quality results by symbol name. Prefer it over grep for Python symbols. Reserve grep for string literals, config values, TODOs, non-Python files.
19
+
20
+ - `uv run tyf show <name>` — definition + signature + usages (flags: `-d` docs, `-r` refs, `-t` test refs, `--all`)
21
+ - `uv run tyf find <Symbol>` — locate definition
22
+ - `uv run tyf refs <name>` — find all usages
23
+ - `uv run tyf members <Class>` — view class API
24
+ - `uv run tyf calls <name>` — call tree (`--in` for callers)
25
+ - `uv run tyf list <file.py>` — file outline
26
+
27
+ All commands accept multiple symbols — batch to save tool calls.
28
+
29
+ ## Clone Detection (`biston`)
30
+
31
+ Structural clone detector for Python — finds functions that are structurally similar even when names/literals/argument order differ. Run after producing multiple similar functions, or when refactoring, to spot extraction opportunities.
32
+
33
+ - `uv run biston scan --suggest .` — find clones with anti-unified template proposals
34
+ - `uv run biston scan --tests-only .` — test-duplication scan
35
+ - `uv run biston overview .` — condensed file-centric summary
36
+ - `uv run biston guide triage` — what to do with findings
37
+
38
+ ## Stack
39
+
40
+ uv, ruff (lint/format), ty (type check), tyf (code search), biston (clone detection), pytest (+xdist, randomly), poethepoet (task runner), hatchling (build)
41
+
42
+ ## Development Workflow
43
+
44
+ - TDD: failing test first, then implementation. Bug fixes include a regression test.
45
+ - All public behavior must have tests in `tests/`.
46
+ - When a test fails, diagnose before changing it. Default assumption: the test is right. Never weaken an assertion just to make it pass.
47
+ - **IMPORTANT**: After completing any task, run the `/python-review` skill. Apply all 🔴 Must Fix and 🟡 Should Fix findings before marking work complete.
48
+
49
+ ## Notes
50
+
51
+ - ty is in beta — may produce false positives. Prefer `# ty: ignore[rule]` over blanket suppression.
52
+ - Pre-commit hook auto-fixes and restages files. Only blocks on unfixable issues.
53
+ - Keep `typer` the only runtime dependency unless a plan explicitly adds one.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mojzis
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,177 @@
1
+ Metadata-Version: 2.5
2
+ Name: typer-agentic
3
+ Version: 0.1.1
4
+ Summary: Typer extension that makes CLIs friendly to coding agents
5
+ Project-URL: homepage, https://github.com/mojzis/typer-agentic
6
+ Project-URL: repository, https://github.com/mojzis/typer-agentic
7
+ Project-URL: documentation, https://github.com/mojzis/typer-agentic#readme
8
+ Author-email: mojzis <mojzisek@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: agent,claude-code,cli,llm,typer
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: typer>=0.24
24
+ Description-Content-Type: text/markdown
25
+
26
+ # typer-agentic
27
+
28
+ A [Typer](https://typer.tiangolo.com/) extension that makes CLIs friendly to coding agents.
29
+
30
+ When an LLM agent misspells a flag or omits an argument, Typer prints a Rich panel and exits 2. The agent then either burns turns re-reading `--help`, or, worse, gives up on the tool and starts editing files by hand. `typer-agentic` intercepts usage errors and, **only when an agent is driving**, replaces the panel with a compact block: what went wrong, the valid parameters with types and choices, exactly one corrected example, and calm instructions that keep the agent on the retry path. Humans keep Typer's stock output, byte for byte.
31
+
32
+ ## Quickstart
33
+
34
+ ```python
35
+ from typer_agentic import agent_errors
36
+
37
+ app = typer.Typer()
38
+ ...
39
+ main = agent_errors(app) # use `main` as your [project.scripts] entry point
40
+ ```
41
+
42
+ Nothing else changes. `agent_errors(app)` returns a zero-argument callable, never mutates the app, and never monkeypatches Typer or Click.
43
+
44
+ ## Before / after
45
+
46
+ `myapp sync --verbos`, Typer 0.27 (stock):
47
+
48
+ ```
49
+ Usage: myapp sync [OPTIONS] {path}
50
+ Try 'myapp sync --help' for help.
51
+ ╭─ Error ────────────────────────────────────────────────────────────╮
52
+ │ No such option: --verbos (Possible options: --verbose) │
53
+ ╰────────────────────────────────────────────────────────────────────╯
54
+ ```
55
+
56
+ Same command with `CLAUDECODE=1` in the environment (Claude Code sets it for you):
57
+
58
+ ```
59
+ ✗ Usage error in `myapp sync`: No such option: --verbos
60
+
61
+ This is a recoverable input mistake, not a bug in the tool. Do not switch tools, edit files or state to work around it, or invent flags; apply the one change below and re-run the command.
62
+
63
+ Did you mean: --verbose
64
+
65
+ Fix and retry (one change): Replace '--verbos' with '--verbose'. Run the corrected example.
66
+
67
+ ```
68
+ myapp sync ./PATH --verbose
69
+ ```
70
+
71
+ Valid options:
72
+ --verbose, -v flag Enable verbose output.
73
+ --count INTEGER How many.
74
+ --env CHOICE[dev|prod] Target environment.
75
+
76
+ Required arguments: PATH
77
+
78
+ Full reference: myapp sync --help
79
+ ```
80
+
81
+ Honest note: Typer already prints a did-you-mean hint. What this adds is the typed inventory, the runnable example, and the de-escalation copy. If you only want the box-drawing gone, `TYPER_USE_RICH=0` does that with no dependency.
82
+
83
+ ## Mode resolution
84
+
85
+ `mode="auto"` (the default) picks agent or human output per invocation. Highest precedence first:
86
+
87
+ | # | Signal | Result |
88
+ |---|---|---|
89
+ | 1 | `AgentErrorsConfig(mode="agent" \| "human")` | forced |
90
+ | 2 | `--agent-errors` / `--human-errors` on the command line | agent / human |
91
+ | 3 | `AGENT_ERRORS` env var (`1/true/yes/on` vs `0/false/no/off/empty`) | agent / human |
92
+ | 4 | Any of `CLAUDECODE`, `CLAUDE_CODE`, `CLAUDE_CODE_ENTRYPOINT`, `CODEX`, `CODEX_CLI`, `CURSOR`, `CURSOR_SESSION_ID`, `OPENCODE`, `AGENT` set and non-empty | agent |
93
+ | 5 | `tty_heuristic=True` and stderr is not a TTY | agent |
94
+ | 6 | default | human |
95
+
96
+ The sentinel flags are consumed by the wrapper before Typer parses anything, so they work even when the rest of the command line is broken. They do not appear in `--help`.
97
+
98
+ ## Format
99
+
100
+ Markdown (default) is written for a model to read: stable first line `✗ Usage error in \`<command>\`: …`, fixed section order, no ANSI. JSON is for harnesses that parse stderr and retry automatically: select it with `AgentErrorsConfig(format="json")` or `AGENT_ERRORS_FORMAT=json`. The JSON document is the only thing written to the stream. See [SCHEMA.md](SCHEMA.md).
101
+
102
+ For agent-harness authors: grep the first line for `✗ Usage error`, or set `AGENT_ERRORS_FORMAT=json` and `json.loads(stderr)`.
103
+
104
+ ## Configuration
105
+
106
+ ```python
107
+ from typer_agentic import AgentErrorsConfig
108
+
109
+ main = agent_errors(
110
+ app,
111
+ config=AgentErrorsConfig(
112
+ mode="auto", # "agent" | "human" to force
113
+ format="markdown", # or "json"
114
+ stream="stderr", # or "stdout"
115
+ env_var="AGENT_ERRORS",
116
+ format_env_var="AGENT_ERRORS_FORMAT",
117
+ flag="--agent-errors", # None disables
118
+ human_flag="--human-errors", # None disables
119
+ skill_flag="--agent-skill", # None disables
120
+ max_suggestions=3,
121
+ include_hidden=False, # list hidden params too
122
+ tty_heuristic=False,
123
+ agent_detect_env_vars=(...), # see table above
124
+ intercept_click_exceptions=False,
125
+ repeat_detection=False, # see below
126
+ repeat_state_dir=None, # defaults to $TMPDIR
127
+ ),
128
+ )
129
+ ```
130
+
131
+ ## SKILL.md for your CLI
132
+
133
+ Errors are the reactive channel. `myapp --agent-skill` prints an [Agent Skill](https://docs.claude.com/en/docs/agents-and-tools/agent-skills) describing every visible command, argument, option and one example per command, so the agent has the inventory before it guesses:
134
+
135
+ ```
136
+ myapp --agent-skill > .claude/skills/myapp/SKILL.md
137
+ ```
138
+
139
+ `render_skill(app)` returns the same text as a string. The library never writes files itself.
140
+
141
+ ## Repeat-failure escalation (opt-in)
142
+
143
+ Each CLI run is a fresh process, so "the agent is looping" needs a little state. With `repeat_detection=True` the wrapper writes a small JSON record (`$TMPDIR/typer-agentic-<uid>/<prog>.json`, 10-minute TTL, atomic replace) and on the second identical failure adds: *stop retrying variations, read `--help`, rebuild from the example*. On the third it adds: *if that does not resolve it, report the exact error to the user instead of working around it*. Any I/O problem silently disables the feature.
144
+
145
+ ## The copy
146
+
147
+ All agent-facing text lives in `typer_agentic/wording.py`. If you customise it, keep the rules the defaults follow: one sentence classifying the failure as a recoverable input mistake; one sentence forbidding the panic behaviours (switching tools, editing state around the CLI, inventing flags); exactly one next action; calm, imperative, no exclamation marks, no apologies, no "please"; identical wording across error types.
148
+
149
+ ## Compatibility
150
+
151
+ - Python 3.11+, `typer>=0.24`, no other runtime dependency. `click` is never imported unconditionally.
152
+ - Typer 0.26+ bundles its own Click under `typer._click`; its exception classes are unrelated to `click.exceptions`. `typer_agentic.compat` resolves whichever hierarchies are present and catches all of them.
153
+ - If resolution fails on some future Typer, `agent_errors(app)` becomes a transparent passthrough and emits one `RuntimeWarning` per process (only in agent mode). Your CLI keeps working; only the feature degrades.
154
+ - Agent mode drives Click's `make_context` / `invoke` loop directly (not `main(standalone_mode=False)`, whose return value cannot distinguish `typer.Exit(n)` from a command returning `n`), so exit codes match stock Typer: `Exit(n)` → `n`, normal return → 0, `Abort` → 1, Ctrl-C → 130. Shell completion is delegated to Typer untouched.
155
+ - Not intercepted, by design: runtime exceptions inside command bodies (they propagate with Typer's pretty-exception hook applied), `NoArgsIsHelpError` (help is printed as usual), `--help` / `--version` / completion.
156
+ - The built-in `--help`, `--install-completion` and `--show-completion` options are omitted from the inventory.
157
+
158
+ The test suite runs against Typer 0.25 (external Click) and the current release: `uv run poe test-compat`.
159
+
160
+ ## Development
161
+
162
+ ```
163
+ uv sync
164
+ uv run poe setup # install git hooks
165
+ uv run poe check # lint, typecheck, dead code, deps, clones, tests
166
+ uv run poe fix # auto-format + fix lint
167
+ uv run poe test # tests with coverage
168
+ uv run poe test-compat
169
+ ```
170
+
171
+ Golden files under `tests/golden/` are regenerated with `UPDATE_GOLDEN=1 uv run pytest`.
172
+
173
+ Release: `uv run poe release` (or `level=minor uv run poe release`) bumps the version, tags, and pushes. The tag triggers the PyPI publish workflow.
174
+
175
+ ## License
176
+
177
+ MIT
@@ -0,0 +1,152 @@
1
+ # typer-agentic
2
+
3
+ A [Typer](https://typer.tiangolo.com/) extension that makes CLIs friendly to coding agents.
4
+
5
+ When an LLM agent misspells a flag or omits an argument, Typer prints a Rich panel and exits 2. The agent then either burns turns re-reading `--help`, or, worse, gives up on the tool and starts editing files by hand. `typer-agentic` intercepts usage errors and, **only when an agent is driving**, replaces the panel with a compact block: what went wrong, the valid parameters with types and choices, exactly one corrected example, and calm instructions that keep the agent on the retry path. Humans keep Typer's stock output, byte for byte.
6
+
7
+ ## Quickstart
8
+
9
+ ```python
10
+ from typer_agentic import agent_errors
11
+
12
+ app = typer.Typer()
13
+ ...
14
+ main = agent_errors(app) # use `main` as your [project.scripts] entry point
15
+ ```
16
+
17
+ Nothing else changes. `agent_errors(app)` returns a zero-argument callable, never mutates the app, and never monkeypatches Typer or Click.
18
+
19
+ ## Before / after
20
+
21
+ `myapp sync --verbos`, Typer 0.27 (stock):
22
+
23
+ ```
24
+ Usage: myapp sync [OPTIONS] {path}
25
+ Try 'myapp sync --help' for help.
26
+ ╭─ Error ────────────────────────────────────────────────────────────╮
27
+ │ No such option: --verbos (Possible options: --verbose) │
28
+ ╰────────────────────────────────────────────────────────────────────╯
29
+ ```
30
+
31
+ Same command with `CLAUDECODE=1` in the environment (Claude Code sets it for you):
32
+
33
+ ```
34
+ ✗ Usage error in `myapp sync`: No such option: --verbos
35
+
36
+ This is a recoverable input mistake, not a bug in the tool. Do not switch tools, edit files or state to work around it, or invent flags; apply the one change below and re-run the command.
37
+
38
+ Did you mean: --verbose
39
+
40
+ Fix and retry (one change): Replace '--verbos' with '--verbose'. Run the corrected example.
41
+
42
+ ```
43
+ myapp sync ./PATH --verbose
44
+ ```
45
+
46
+ Valid options:
47
+ --verbose, -v flag Enable verbose output.
48
+ --count INTEGER How many.
49
+ --env CHOICE[dev|prod] Target environment.
50
+
51
+ Required arguments: PATH
52
+
53
+ Full reference: myapp sync --help
54
+ ```
55
+
56
+ Honest note: Typer already prints a did-you-mean hint. What this adds is the typed inventory, the runnable example, and the de-escalation copy. If you only want the box-drawing gone, `TYPER_USE_RICH=0` does that with no dependency.
57
+
58
+ ## Mode resolution
59
+
60
+ `mode="auto"` (the default) picks agent or human output per invocation. Highest precedence first:
61
+
62
+ | # | Signal | Result |
63
+ |---|---|---|
64
+ | 1 | `AgentErrorsConfig(mode="agent" \| "human")` | forced |
65
+ | 2 | `--agent-errors` / `--human-errors` on the command line | agent / human |
66
+ | 3 | `AGENT_ERRORS` env var (`1/true/yes/on` vs `0/false/no/off/empty`) | agent / human |
67
+ | 4 | Any of `CLAUDECODE`, `CLAUDE_CODE`, `CLAUDE_CODE_ENTRYPOINT`, `CODEX`, `CODEX_CLI`, `CURSOR`, `CURSOR_SESSION_ID`, `OPENCODE`, `AGENT` set and non-empty | agent |
68
+ | 5 | `tty_heuristic=True` and stderr is not a TTY | agent |
69
+ | 6 | default | human |
70
+
71
+ The sentinel flags are consumed by the wrapper before Typer parses anything, so they work even when the rest of the command line is broken. They do not appear in `--help`.
72
+
73
+ ## Format
74
+
75
+ Markdown (default) is written for a model to read: stable first line `✗ Usage error in \`<command>\`: …`, fixed section order, no ANSI. JSON is for harnesses that parse stderr and retry automatically: select it with `AgentErrorsConfig(format="json")` or `AGENT_ERRORS_FORMAT=json`. The JSON document is the only thing written to the stream. See [SCHEMA.md](SCHEMA.md).
76
+
77
+ For agent-harness authors: grep the first line for `✗ Usage error`, or set `AGENT_ERRORS_FORMAT=json` and `json.loads(stderr)`.
78
+
79
+ ## Configuration
80
+
81
+ ```python
82
+ from typer_agentic import AgentErrorsConfig
83
+
84
+ main = agent_errors(
85
+ app,
86
+ config=AgentErrorsConfig(
87
+ mode="auto", # "agent" | "human" to force
88
+ format="markdown", # or "json"
89
+ stream="stderr", # or "stdout"
90
+ env_var="AGENT_ERRORS",
91
+ format_env_var="AGENT_ERRORS_FORMAT",
92
+ flag="--agent-errors", # None disables
93
+ human_flag="--human-errors", # None disables
94
+ skill_flag="--agent-skill", # None disables
95
+ max_suggestions=3,
96
+ include_hidden=False, # list hidden params too
97
+ tty_heuristic=False,
98
+ agent_detect_env_vars=(...), # see table above
99
+ intercept_click_exceptions=False,
100
+ repeat_detection=False, # see below
101
+ repeat_state_dir=None, # defaults to $TMPDIR
102
+ ),
103
+ )
104
+ ```
105
+
106
+ ## SKILL.md for your CLI
107
+
108
+ Errors are the reactive channel. `myapp --agent-skill` prints an [Agent Skill](https://docs.claude.com/en/docs/agents-and-tools/agent-skills) describing every visible command, argument, option and one example per command, so the agent has the inventory before it guesses:
109
+
110
+ ```
111
+ myapp --agent-skill > .claude/skills/myapp/SKILL.md
112
+ ```
113
+
114
+ `render_skill(app)` returns the same text as a string. The library never writes files itself.
115
+
116
+ ## Repeat-failure escalation (opt-in)
117
+
118
+ Each CLI run is a fresh process, so "the agent is looping" needs a little state. With `repeat_detection=True` the wrapper writes a small JSON record (`$TMPDIR/typer-agentic-<uid>/<prog>.json`, 10-minute TTL, atomic replace) and on the second identical failure adds: *stop retrying variations, read `--help`, rebuild from the example*. On the third it adds: *if that does not resolve it, report the exact error to the user instead of working around it*. Any I/O problem silently disables the feature.
119
+
120
+ ## The copy
121
+
122
+ All agent-facing text lives in `typer_agentic/wording.py`. If you customise it, keep the rules the defaults follow: one sentence classifying the failure as a recoverable input mistake; one sentence forbidding the panic behaviours (switching tools, editing state around the CLI, inventing flags); exactly one next action; calm, imperative, no exclamation marks, no apologies, no "please"; identical wording across error types.
123
+
124
+ ## Compatibility
125
+
126
+ - Python 3.11+, `typer>=0.24`, no other runtime dependency. `click` is never imported unconditionally.
127
+ - Typer 0.26+ bundles its own Click under `typer._click`; its exception classes are unrelated to `click.exceptions`. `typer_agentic.compat` resolves whichever hierarchies are present and catches all of them.
128
+ - If resolution fails on some future Typer, `agent_errors(app)` becomes a transparent passthrough and emits one `RuntimeWarning` per process (only in agent mode). Your CLI keeps working; only the feature degrades.
129
+ - Agent mode drives Click's `make_context` / `invoke` loop directly (not `main(standalone_mode=False)`, whose return value cannot distinguish `typer.Exit(n)` from a command returning `n`), so exit codes match stock Typer: `Exit(n)` → `n`, normal return → 0, `Abort` → 1, Ctrl-C → 130. Shell completion is delegated to Typer untouched.
130
+ - Not intercepted, by design: runtime exceptions inside command bodies (they propagate with Typer's pretty-exception hook applied), `NoArgsIsHelpError` (help is printed as usual), `--help` / `--version` / completion.
131
+ - The built-in `--help`, `--install-completion` and `--show-completion` options are omitted from the inventory.
132
+
133
+ The test suite runs against Typer 0.25 (external Click) and the current release: `uv run poe test-compat`.
134
+
135
+ ## Development
136
+
137
+ ```
138
+ uv sync
139
+ uv run poe setup # install git hooks
140
+ uv run poe check # lint, typecheck, dead code, deps, clones, tests
141
+ uv run poe fix # auto-format + fix lint
142
+ uv run poe test # tests with coverage
143
+ uv run poe test-compat
144
+ ```
145
+
146
+ Golden files under `tests/golden/` are regenerated with `UPDATE_GOLDEN=1 uv run pytest`.
147
+
148
+ Release: `uv run poe release` (or `level=minor uv run poe release`) bumps the version, tags, and pushes. The tag triggers the PyPI publish workflow.
149
+
150
+ ## License
151
+
152
+ MIT