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.
- typer_agentic-0.1.1/.claude/hooks/session-start.sh +21 -0
- typer_agentic-0.1.1/.claude/settings.json +21 -0
- typer_agentic-0.1.1/.github/dependabot.yml +24 -0
- typer_agentic-0.1.1/.github/workflows/ci.yml +103 -0
- typer_agentic-0.1.1/.github/workflows/release.yml +58 -0
- typer_agentic-0.1.1/.gitignore +22 -0
- typer_agentic-0.1.1/CHANGELOG.md +14 -0
- typer_agentic-0.1.1/CLAUDE.md +53 -0
- typer_agentic-0.1.1/LICENSE +21 -0
- typer_agentic-0.1.1/PKG-INFO +177 -0
- typer_agentic-0.1.1/README.md +152 -0
- typer_agentic-0.1.1/SCHEMA.md +57 -0
- typer_agentic-0.1.1/docs/development.md +29 -0
- typer_agentic-0.1.1/docs/index.md +20 -0
- typer_agentic-0.1.1/docs/plans/README.md +3 -0
- typer_agentic-0.1.1/docs/plans/typer-agent-errors-spec.md +305 -0
- typer_agentic-0.1.1/pyproject.toml +190 -0
- typer_agentic-0.1.1/scripts/install-hooks.sh +9 -0
- typer_agentic-0.1.1/scripts/pre-commit.sh +32 -0
- typer_agentic-0.1.1/src/typer_agentic/__init__.py +51 -0
- typer_agentic-0.1.1/src/typer_agentic/builder.py +357 -0
- typer_agentic-0.1.1/src/typer_agentic/compat.py +177 -0
- typer_agentic-0.1.1/src/typer_agentic/config.py +137 -0
- typer_agentic-0.1.1/src/typer_agentic/example.py +83 -0
- typer_agentic-0.1.1/src/typer_agentic/intercept.py +159 -0
- typer_agentic-0.1.1/src/typer_agentic/introspect.py +174 -0
- typer_agentic-0.1.1/src/typer_agentic/payload.py +94 -0
- typer_agentic-0.1.1/src/typer_agentic/py.typed +0 -0
- typer_agentic-0.1.1/src/typer_agentic/render/__init__.py +15 -0
- typer_agentic-0.1.1/src/typer_agentic/render/json_format.py +11 -0
- typer_agentic-0.1.1/src/typer_agentic/render/markdown.py +124 -0
- typer_agentic-0.1.1/src/typer_agentic/repeat.py +113 -0
- typer_agentic-0.1.1/src/typer_agentic/skill.py +136 -0
- typer_agentic-0.1.1/src/typer_agentic/suggest.py +59 -0
- typer_agentic-0.1.1/src/typer_agentic/wording.py +62 -0
- typer_agentic-0.1.1/tests/__init__.py +0 -0
- typer_agentic-0.1.1/tests/conftest.py +119 -0
- typer_agentic-0.1.1/tests/fixture_app.py +71 -0
- typer_agentic-0.1.1/tests/golden/SKILL.md +93 -0
- typer_agentic-0.1.1/tests/golden/bad_choice.md +20 -0
- typer_agentic-0.1.1/tests/golden/bad_integer.md +18 -0
- typer_agentic-0.1.1/tests/golden/extra_argument.md +18 -0
- typer_agentic-0.1.1/tests/golden/missing_argument.md +18 -0
- typer_agentic-0.1.1/tests/golden/no_such_command.md +21 -0
- typer_agentic-0.1.1/tests/golden/no_such_option.json +78 -0
- typer_agentic-0.1.1/tests/golden/no_such_option.md +20 -0
- typer_agentic-0.1.1/tests/golden/option_requires_value.md +18 -0
- typer_agentic-0.1.1/tests/test_compat.py +145 -0
- typer_agentic-0.1.1/tests/test_config.py +145 -0
- typer_agentic-0.1.1/tests/test_e2e.py +70 -0
- typer_agentic-0.1.1/tests/test_intercept.py +221 -0
- typer_agentic-0.1.1/tests/test_introspect.py +155 -0
- typer_agentic-0.1.1/tests/test_render.py +160 -0
- typer_agentic-0.1.1/tests/test_repeat.py +111 -0
- typer_agentic-0.1.1/tests/test_skill.py +116 -0
- typer_agentic-0.1.1/tests/test_suggest.py +115 -0
- 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
|