atk-cli 0.2.1__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {atk_cli-0.2.1 → atk_cli-0.3.0}/.gitignore +7 -1
- atk_cli-0.3.0/CONTRIBUTING.md +90 -0
- atk_cli-0.3.0/Makefile +49 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/PKG-INFO +1 -1
- {atk_cli-0.2.1 → atk_cli-0.3.0}/skills/create-atk-plugin/SKILL.md +16 -0
- atk_cli-0.3.0/skills/release/SKILL.md +289 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/cli.py +48 -0
- atk_cli-0.3.0/src/atk/commands/doctor.py +75 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/git.py +103 -9
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/init.py +18 -7
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_add.py +16 -10
- atk_cli-0.3.0/tests/test_doctor.py +81 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_git.py +133 -16
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_init.py +5 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_remove.py +5 -4
- atk_cli-0.2.1/CONTRIBUTING.md +0 -51
- atk_cli-0.2.1/Makefile +0 -29
- {atk_cli-0.2.1 → atk_cli-0.3.0}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/.github/pull_request_template.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/.github/workflows/ci.yml +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/.github/workflows/publish.yml +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/CODE_OF_CONDUCT.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/LICENSE +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/README.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/SECURITY.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/assets/demo-hero.gif +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/assets/demo-search.gif +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/assets/demo-status.gif +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/assets/logo.png +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/assets/tapes/demo-hero.tape +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/assets/tapes/demo-search.tape +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/assets/tapes/demo-status.tape +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/README.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/ROADMAP.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/backlog.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/legacy/backup-feature-spec.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/legacy/cli-architecture.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/legacy/service-yaml-spec.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/phases/phase-1-core-cli.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/phases/phase-11-git-sync.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/phases/phase-2-lifecycle.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/phases/phase-3-configuration.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/phases/phase-4-plugin-sources.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/specs/atk-spec.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/specs/commands-spec.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/specs/git-sync-spec.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/specs/home-spec.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/specs/mcp-agent-configure-spec.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/specs/plugin-schema.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/docs/specs/registry-spec.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/pyproject.toml +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/__init__.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/add.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/agents/__init__.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/agents/auggie_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/agents/claude_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/agents/codex_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/agents/gemini_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/agents/managed_section.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/agents/opencode_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/agents/symlink_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/banner.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/bootstrap.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/cli_logger.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/commands/__init__.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/commands/lifecycle.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/commands/mcp.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/commands/plug.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/commands/preconditions.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/commands/run.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/commands/search.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/commands/status.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/commands/upgrade.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/env.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/errors.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/exit_codes.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/fetch.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/git_source.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/home.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/lifecycle.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/manifest_schema.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/mcp.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/mcp_agents.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/mcp_configure.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/plugin.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/plugin_schema.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/registry.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/registry_schema.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/remove.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/sanitize.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/setup.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/source.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/update_check.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/upgrade.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/src/atk/validation.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/__init__.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/agents/__init__.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/agents/test_auggie_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/agents/test_claude_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/agents/test_codex_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/agents/test_gemini_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/agents/test_opencode_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/agents/test_symlink_skill.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/commands/__init__.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/commands/test_cli.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/commands/test_search.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/commands/test_status.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/conftest.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/full-plugin/README.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/full-plugin/SKILL.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/full-plugin/install.sh +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/full-plugin/mcp-server.sh +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/full-plugin/plugin.yaml +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/full-plugin/start.sh +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/full-plugin/status.sh +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/full-plugin/stop.sh +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/invalid-plugin/plugin.yaml +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/minimal-plugin/plugin.yaml +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/skill-only-plugin/SKILL.md +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/fixtures/plugins/skill-only-plugin/plugin.yaml +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_env.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_errors.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_git_proxy.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_git_source.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_home.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_lifecycle.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_manifest_schema.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_mcp.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_plug.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_plugin.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_plugin_schema.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_registry.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_repo_status.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_run.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_sanitize.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_setup.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_source.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_update_check.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_upgrade.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/tests/test_version.py +0 -0
- {atk_cli-0.2.1 → atk_cli-0.3.0}/uv.lock +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Contributing to ATK
|
|
2
|
+
|
|
3
|
+
Thanks for your interest. ATK is an early-stage tool — contributions are welcome.
|
|
4
|
+
|
|
5
|
+
## Getting started
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/Svtoo/atk
|
|
9
|
+
cd atk
|
|
10
|
+
uv sync --dev
|
|
11
|
+
make install-hooks # Install pre-commit hook
|
|
12
|
+
make sync-skills # Copy skills to agent directories
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Development workflow
|
|
16
|
+
|
|
17
|
+
### Pre-commit hook
|
|
18
|
+
|
|
19
|
+
`make install-hooks` installs a git pre-commit hook that:
|
|
20
|
+
|
|
21
|
+
1. Runs `make check` (ruff lint, mypy type-check, pytest) — commit is blocked if any fail
|
|
22
|
+
2. Runs `make sync-skills` — copies skills to agent-specific directories
|
|
23
|
+
|
|
24
|
+
The hook is local (`.git/hooks/pre-commit`) and not tracked in git. Run `make install-hooks` after cloning.
|
|
25
|
+
|
|
26
|
+
### Skills
|
|
27
|
+
|
|
28
|
+
ATK includes skills (structured prompts) for AI coding agents. The source of truth is `skills/<name>/SKILL.md` at the project root. Skills are agent-agnostic — they work with any agent that supports structured prompts.
|
|
29
|
+
|
|
30
|
+
`make sync-skills` copies skills to agent-specific directories where they are discoverable:
|
|
31
|
+
|
|
32
|
+
| Agent | Target directory | Discovery |
|
|
33
|
+
|-------|-----------------|-----------|
|
|
34
|
+
| Claude Code | `.claude/skills/<name>/SKILL.md` | Auto-discovered as `/name` slash commands |
|
|
35
|
+
|
|
36
|
+
The agent directories (`.claude/skills/`) are gitignored — they contain derived copies, not source files. Always edit `skills/<name>/SKILL.md`, never the copies.
|
|
37
|
+
|
|
38
|
+
To add a new skill: create `skills/<your-skill>/SKILL.md` and run `make sync-skills`.
|
|
39
|
+
|
|
40
|
+
## Making changes
|
|
41
|
+
|
|
42
|
+
- Open an issue first for non-trivial changes.
|
|
43
|
+
- Keep PRs focused — one concern per PR.
|
|
44
|
+
- Match existing code style (ruff + mypy strict).
|
|
45
|
+
|
|
46
|
+
## Tests
|
|
47
|
+
|
|
48
|
+
All code changes must include automated tests.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
# Run the full test suite
|
|
52
|
+
uv run pytest
|
|
53
|
+
|
|
54
|
+
# Lint and type-check
|
|
55
|
+
uv run ruff check src tests
|
|
56
|
+
uv run mypy src
|
|
57
|
+
|
|
58
|
+
# All of the above in one command
|
|
59
|
+
make check
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
If you're adding a command or changing CLI behaviour, also test it manually:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uv run atk <your command>
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The CI must pass before a PR is merged.
|
|
69
|
+
|
|
70
|
+
## Releases
|
|
71
|
+
|
|
72
|
+
Releases are managed via the `/release` skill (or `make release` for manual local builds). See `skills/release/SKILL.md` for the full process.
|
|
73
|
+
|
|
74
|
+
Key points:
|
|
75
|
+
- Versioning uses **hatch-vcs** — version is derived from git tags (`vX.Y.Z`), not hardcoded
|
|
76
|
+
- CI runs on push to `main`; publish to PyPI triggers on tag push matching `v*.*.*`
|
|
77
|
+
- PyPI does not allow re-uploads — once a version is published, it is permanent
|
|
78
|
+
|
|
79
|
+
## Submitting a PR
|
|
80
|
+
|
|
81
|
+
1. Fork and create a branch from `main`.
|
|
82
|
+
2. Write or update tests for your change.
|
|
83
|
+
3. Run `make check` — all must pass.
|
|
84
|
+
4. Open a PR with a clear description of what changed and why.
|
|
85
|
+
|
|
86
|
+
## Adding a registry plugin
|
|
87
|
+
|
|
88
|
+
Submit a PR to [atk-registry](https://github.com/Svtoo/atk-registry), not this repo.
|
|
89
|
+
See the registry README for schema requirements.
|
|
90
|
+
|
atk_cli-0.3.0/Makefile
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
.PHONY: test check release install-local install-pypi uninstall sync-skills install-hooks
|
|
2
|
+
|
|
3
|
+
# TDD cycle - run often
|
|
4
|
+
test:
|
|
5
|
+
uv run pytest
|
|
6
|
+
|
|
7
|
+
# Pre-commit validation - lint, type check, tests
|
|
8
|
+
check:
|
|
9
|
+
uv run ruff check src tests
|
|
10
|
+
uv run mypy src
|
|
11
|
+
uv run pytest
|
|
12
|
+
|
|
13
|
+
# Sync skills from skills/ to agent-specific directories
|
|
14
|
+
# Source of truth: skills/<name>/SKILL.md
|
|
15
|
+
# Currently supported agents: Claude Code (.claude/skills/)
|
|
16
|
+
sync-skills:
|
|
17
|
+
@mkdir -p .claude/skills
|
|
18
|
+
@for dir in skills/*/; do \
|
|
19
|
+
name=$$(basename "$$dir"); \
|
|
20
|
+
mkdir -p ".claude/skills/$$name"; \
|
|
21
|
+
cp "$$dir"SKILL.md ".claude/skills/$$name/SKILL.md" 2>/dev/null || true; \
|
|
22
|
+
done
|
|
23
|
+
@echo "Skills synced to .claude/skills/"
|
|
24
|
+
|
|
25
|
+
# Install git pre-commit hook that runs check + sync-skills
|
|
26
|
+
install-hooks:
|
|
27
|
+
@echo '#!/bin/sh' > .git/hooks/pre-commit
|
|
28
|
+
@echo 'make check || exit 1' >> .git/hooks/pre-commit
|
|
29
|
+
@echo 'make sync-skills' >> .git/hooks/pre-commit
|
|
30
|
+
@chmod +x .git/hooks/pre-commit
|
|
31
|
+
@echo "Pre-commit hook installed."
|
|
32
|
+
|
|
33
|
+
# Build and publish to PyPI
|
|
34
|
+
release:
|
|
35
|
+
uv build
|
|
36
|
+
uv publish
|
|
37
|
+
|
|
38
|
+
# Install from local source — editable, so changes are live without reinstalling
|
|
39
|
+
install-local:
|
|
40
|
+
uv tool install --editable . --reinstall
|
|
41
|
+
|
|
42
|
+
# Uninstall (works after either install-local or install-pypi)
|
|
43
|
+
uninstall:
|
|
44
|
+
uv tool uninstall atk-cli
|
|
45
|
+
|
|
46
|
+
# Install stable release from PyPI
|
|
47
|
+
install-pypi:
|
|
48
|
+
uv tool install atk-cli --reinstall
|
|
49
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: atk-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: AI Toolkit - Manage AI development tools through a git-backed, declarative manifest
|
|
5
5
|
Project-URL: Homepage, https://github.com/Svtoo/atk
|
|
6
6
|
Project-URL: Repository, https://github.com/Svtoo/atk
|
|
@@ -248,6 +248,22 @@ mcp:
|
|
|
248
248
|
**Idempotency rule**: Always build from scratch. Always `rm -rf` and fresh clone/install — no conditional
|
|
249
249
|
"if exists, pull; else clone" logic.
|
|
250
250
|
|
|
251
|
+
**External package managers silently skip when already installed — you MUST force re-install.** Bare
|
|
252
|
+
`uv tool install pkg@latest`, `npm install -g pkg@latest`, and `pip install pkg` are no-ops once the
|
|
253
|
+
package is present, even if upstream has shipped a newer version. The user runs `atk install <plugin>`
|
|
254
|
+
expecting an upgrade and gets nothing. Required flags per manager:
|
|
255
|
+
|
|
256
|
+
| Manager | Wrong (silently skips) | Right (always pulls latest) |
|
|
257
|
+
|---|---|---|
|
|
258
|
+
| uv tool | `uv tool install pkg@latest` | `uv tool install pkg@latest --reinstall` |
|
|
259
|
+
| Homebrew | `brew install pkg` | `brew upgrade pkg \|\| brew install pkg` |
|
|
260
|
+
| npm global | `npm install -g pkg@latest` | `npm install -g pkg@latest --force` |
|
|
261
|
+
| pipx | `pipx install pkg` | `pipx install pkg --force` |
|
|
262
|
+
| cargo | `cargo install pkg` | `cargo install pkg --force` |
|
|
263
|
+
|
|
264
|
+
If your install relies on a `curl ... \| sh` install script (codanna-style), that's usually fine —
|
|
265
|
+
those scripts typically replace the binary unconditionally. But verify before shipping.
|
|
266
|
+
|
|
251
267
|
Use `set -e` in `install.sh`: fail fast on errors.
|
|
252
268
|
|
|
253
269
|
### start
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release
|
|
3
|
+
description: Create a new ATK release. Handles pre-flight checks, changelog generation, tagging, pushing, CI verification, and PyPI publication confirmation. Use when asked to cut a release, publish a new version, or ship changes.
|
|
4
|
+
argument-hint: "[version, e.g. 0.3.0]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# ATK Release Process
|
|
8
|
+
|
|
9
|
+
You are performing a release of the `atk-cli` package. This is a critical operation — follow every step in order. Do not skip steps. Do not assume success — verify it.
|
|
10
|
+
|
|
11
|
+
## Versioning Strategy
|
|
12
|
+
|
|
13
|
+
ATK uses **hatch-vcs**: the version is derived from git tags at build time, not hardcoded.
|
|
14
|
+
|
|
15
|
+
- Tags follow `vX.Y.Z` (e.g., `v0.2.1`)
|
|
16
|
+
- **Patch** (`Z`): bug fixes, small features, non-breaking changes
|
|
17
|
+
- **Minor** (`Y`): new user-facing features, new commands, behavioral changes
|
|
18
|
+
- **Major** (`X`): breaking changes to CLI interface, manifest schema, or plugin API
|
|
19
|
+
- Pre-`v1.0.0`: minor bumps are for significant milestones; patches are the norm
|
|
20
|
+
|
|
21
|
+
PyPI receives the version without the `v` prefix (e.g., `0.2.1`).
|
|
22
|
+
|
|
23
|
+
**CRITICAL**: PyPI does not allow re-uploads. If a version is published, it is permanent. Never force-move a tag that has been pushed. If a mistake is published, bump to the next patch.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Step 0: Determine Version
|
|
28
|
+
|
|
29
|
+
If version was provided as argument, use `v$ARGUMENTS`. Otherwise:
|
|
30
|
+
|
|
31
|
+
1. Run `git tag --sort=-v:refname | head -1` to find the current latest tag
|
|
32
|
+
2. Run `git log <latest-tag>..HEAD --oneline` to see what changed
|
|
33
|
+
3. Classify the changes:
|
|
34
|
+
- Bug fixes only → **patch bump**
|
|
35
|
+
- New features, new commands → **minor bump**
|
|
36
|
+
- Breaking CLI/schema changes → **major bump**
|
|
37
|
+
4. Present the proposed version to Sasha with reasoning. Wait for confirmation before proceeding.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Step 1: Pre-Flight Checks
|
|
42
|
+
|
|
43
|
+
Run ALL of these checks. If ANY fail, stop and report.
|
|
44
|
+
|
|
45
|
+
### 1a. Clean working tree
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
git status --short
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
EXPECT: Empty output (no uncommitted changes). If dirty, stop — all changes must be committed before release.
|
|
52
|
+
|
|
53
|
+
### 1b. On main branch
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git branch --show-current
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
EXPECT: `main`. Releases are only cut from main.
|
|
60
|
+
|
|
61
|
+
### 1c. Up to date with remote
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
git fetch origin main
|
|
65
|
+
git rev-list HEAD..origin/main --count
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
EXPECT: `0` (no commits on origin/main that we don't have). If behind, stop — pull first.
|
|
69
|
+
|
|
70
|
+
### 1d. Local checks pass
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
make check
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
EXPECT: Exit 0. This runs ruff (lint), mypy (types), and pytest (tests). All must be green.
|
|
77
|
+
|
|
78
|
+
### 1e. CI is green on HEAD
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
gh run list --branch main --limit 3
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
EXPECT: The most recent CI run for HEAD is `completed` / `success`. If CI hasn't run for the current HEAD yet, wait or trigger it.
|
|
85
|
+
|
|
86
|
+
### 1f. No open blockers
|
|
87
|
+
|
|
88
|
+
Check if there are any issues/PRs that should block this release:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
gh issue list --state open --label "blocker" --limit 5
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
If blockers exist, report them and ask Sasha whether to proceed.
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Step 2: Changelog Analysis
|
|
99
|
+
|
|
100
|
+
Generate a thorough changelog by analyzing what changed since the last release.
|
|
101
|
+
|
|
102
|
+
### 2a. Identify the last release
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
git tag --sort=-v:refname | head -1
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 2b. List all commits since last release
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
git log <last-tag>..HEAD --oneline --no-merges
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### 2c. Detailed diff summary
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
git diff <last-tag>..HEAD --stat
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 2d. Categorize changes
|
|
121
|
+
|
|
122
|
+
Read each commit and the diff. Organize into categories:
|
|
123
|
+
|
|
124
|
+
- **New Features**: new commands, new config options, new behaviors
|
|
125
|
+
- **Bug Fixes**: corrections to existing behavior
|
|
126
|
+
- **Breaking Changes**: anything that changes existing CLI interface, manifest format, or plugin contract
|
|
127
|
+
- **Documentation**: spec updates, roadmap changes, new phase docs
|
|
128
|
+
- **Internal**: refactors, test improvements, CI changes
|
|
129
|
+
|
|
130
|
+
### 2e. Draft release notes
|
|
131
|
+
|
|
132
|
+
Write release notes in this format:
|
|
133
|
+
|
|
134
|
+
```markdown
|
|
135
|
+
## What's New
|
|
136
|
+
|
|
137
|
+
### <Feature Name>
|
|
138
|
+
<2-3 sentence description with usage example>
|
|
139
|
+
|
|
140
|
+
### <Feature Name>
|
|
141
|
+
...
|
|
142
|
+
|
|
143
|
+
## Bug Fixes
|
|
144
|
+
- <one-liner per fix>
|
|
145
|
+
|
|
146
|
+
## Breaking Changes
|
|
147
|
+
- <one-liner with migration guidance>
|
|
148
|
+
|
|
149
|
+
## Upgrade
|
|
150
|
+
|
|
151
|
+
\```bash
|
|
152
|
+
pip install --upgrade atk-cli
|
|
153
|
+
# or
|
|
154
|
+
uv tool upgrade atk-cli
|
|
155
|
+
\```
|
|
156
|
+
|
|
157
|
+
<Any migration notes if needed>
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Present the draft to Sasha for review before proceeding.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Step 3: Manual Testing
|
|
165
|
+
|
|
166
|
+
Before tagging, manually verify the key changes work. This is NOT optional.
|
|
167
|
+
|
|
168
|
+
For each new feature or significant change:
|
|
169
|
+
1. State what you're testing and why
|
|
170
|
+
2. Run the actual command against a real (or temporary) ATK Home
|
|
171
|
+
3. Verify the output matches expectations
|
|
172
|
+
4. Report PASS or FAIL with evidence
|
|
173
|
+
|
|
174
|
+
At minimum, always test:
|
|
175
|
+
- `atk --version` outputs a valid version
|
|
176
|
+
- `atk status` works without errors (if ATK Home exists)
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Step 4: Tag and Push
|
|
181
|
+
|
|
182
|
+
Only proceed here if all previous steps passed and Sasha approved the release notes.
|
|
183
|
+
|
|
184
|
+
### 4a. Create the tag
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
git tag v<VERSION>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### 4b. Push the commit and tag
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
git push origin main
|
|
194
|
+
git push origin v<VERSION>
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
The tag push triggers the Publish to PyPI workflow automatically.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Step 5: Wait for CI
|
|
202
|
+
|
|
203
|
+
Both workflows must succeed before the release is complete.
|
|
204
|
+
|
|
205
|
+
### 5a. Monitor CI workflow (triggered by push to main)
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
gh run list --branch main --limit 3
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Wait until the CI run for the current commit shows `completed` / `success`.
|
|
212
|
+
|
|
213
|
+
### 5b. Monitor Publish workflow (triggered by tag push)
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
gh run list --workflow publish.yml --limit 3
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Wait until the publish run shows `completed` / `success`.
|
|
220
|
+
|
|
221
|
+
If either workflow fails:
|
|
222
|
+
1. Check logs: `gh run view <run-id> --log | tail -50`
|
|
223
|
+
2. Report the failure to Sasha
|
|
224
|
+
3. Do NOT proceed to creating the GitHub release
|
|
225
|
+
4. If the publish failed, the tag is "burned" — PyPI may or may not have the version. Check with `curl -s https://pypi.org/pypi/atk-cli/<VERSION>/json` before deciding next steps.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Step 6: Verify PyPI Publication
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
curl -s https://pypi.org/pypi/atk-cli/<VERSION>/json | python3 -c "import sys,json; d=json.load(sys.stdin); print('Version:', d['info']['version']); print('Summary:', d['info']['summary'])"
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
EXPECT: Version matches the release version. If this fails, the publish did not actually land on PyPI — investigate before creating the GitHub release.
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Step 7: Create GitHub Release
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
gh release create v<VERSION> --title "v<VERSION> — <Short Title>" --notes "<release notes from Step 2>"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Use a HEREDOC for multi-line notes:
|
|
246
|
+
|
|
247
|
+
```bash
|
|
248
|
+
gh release create v<VERSION> --title "v<VERSION> — <Title>" --notes "$(cat <<'EOF'
|
|
249
|
+
<release notes here>
|
|
250
|
+
EOF
|
|
251
|
+
)"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Step 8: Post-Release Verification
|
|
257
|
+
|
|
258
|
+
### 8a. Verify the release page exists
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
gh release view v<VERSION>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### 8b. Verify installability
|
|
265
|
+
|
|
266
|
+
```bash
|
|
267
|
+
uv pip install atk-cli==<VERSION> --dry-run
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### 8c. Report completion
|
|
271
|
+
|
|
272
|
+
Summarize to Sasha:
|
|
273
|
+
- Version released
|
|
274
|
+
- PyPI URL: `https://pypi.org/project/atk-cli/<VERSION>/`
|
|
275
|
+
- GitHub release URL: `https://github.com/Svtoo/atk/releases/tag/v<VERSION>`
|
|
276
|
+
- CI status: all green
|
|
277
|
+
- Publish status: confirmed on PyPI
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Failure Recovery
|
|
282
|
+
|
|
283
|
+
| Failure | Recovery |
|
|
284
|
+
|---------|----------|
|
|
285
|
+
| CI fails after tag push | Fix the issue, push fix to main, bump to next patch, re-tag |
|
|
286
|
+
| Publish fails but PyPI has the version | The version is burned — create GitHub release as-is |
|
|
287
|
+
| Publish fails and PyPI does NOT have it | Fix issue, delete the tag (`git tag -d v<X> && git push origin :refs/tags/v<X>`), re-tag same commit |
|
|
288
|
+
| Wrong content released | Cannot re-upload to PyPI. Bump patch, fix, release again |
|
|
289
|
+
| Tag pushed to wrong commit | If not yet on PyPI: delete remote tag, re-tag correct commit. If on PyPI: bump patch |
|
|
@@ -12,6 +12,7 @@ from rich.markdown import Markdown
|
|
|
12
12
|
from atk import __version__, cli_logger, exit_codes
|
|
13
13
|
from atk.add import AddCancelledError, InstallFailedError, add_plugin
|
|
14
14
|
from atk.banner import print_banner
|
|
15
|
+
from atk.commands.doctor import run_doctor
|
|
15
16
|
from atk.commands.lifecycle import run_lifecycle_cli, run_restart_single_cli, run_uninstall_cli
|
|
16
17
|
from atk.commands.plug import plug_plugin, unplug_plugin
|
|
17
18
|
from atk.commands.preconditions import (
|
|
@@ -361,6 +362,53 @@ def setup(
|
|
|
361
362
|
raise typer.Exit(exit_codes.SUCCESS)
|
|
362
363
|
|
|
363
364
|
|
|
365
|
+
@app.command()
|
|
366
|
+
def doctor() -> None:
|
|
367
|
+
"""Repair ATK Home: keep secrets gitignored and untrack any that leaked.
|
|
368
|
+
|
|
369
|
+
Fixes the historical bug where a local plugin's .env could be tracked
|
|
370
|
+
because its .gitignore exemption was ordered after the secret rule.
|
|
371
|
+
Safe to run anytime; idempotent.
|
|
372
|
+
"""
|
|
373
|
+
atk_home = require_initialized_home()
|
|
374
|
+
manifest = load_manifest(atk_home)
|
|
375
|
+
if manifest.config.auto_commit:
|
|
376
|
+
require_git()
|
|
377
|
+
|
|
378
|
+
result = run_doctor(
|
|
379
|
+
atk_home,
|
|
380
|
+
auto_commit=manifest.config.auto_commit,
|
|
381
|
+
auto_push=manifest.config.auto_push,
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
if result.gitignore_fixed:
|
|
385
|
+
cli_logger.success("Re-ordered .gitignore so secret rules stay last")
|
|
386
|
+
else:
|
|
387
|
+
cli_logger.info(".gitignore already keeps secrets last")
|
|
388
|
+
|
|
389
|
+
if result.untracked_secrets:
|
|
390
|
+
cli_logger.warning(
|
|
391
|
+
f"Untracked {len(result.untracked_secrets)} secret file(s) "
|
|
392
|
+
"(kept on disk):"
|
|
393
|
+
)
|
|
394
|
+
for secret in result.untracked_secrets:
|
|
395
|
+
cli_logger.dim(f" • {secret}")
|
|
396
|
+
cli_logger.warning(
|
|
397
|
+
"These were committed before — ROTATE the affected keys, and note "
|
|
398
|
+
"they remain in git history (rewrite history if you need them gone)."
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
if result.committed:
|
|
402
|
+
cli_logger.success("Committed the repair")
|
|
403
|
+
elif (result.gitignore_fixed or result.untracked_secrets) and not manifest.config.auto_commit:
|
|
404
|
+
cli_logger.info("Changes staged — commit them when ready (auto_commit is off)")
|
|
405
|
+
|
|
406
|
+
if not result.gitignore_fixed and not result.untracked_secrets:
|
|
407
|
+
cli_logger.success("Nothing to fix — ATK Home is healthy")
|
|
408
|
+
|
|
409
|
+
raise typer.Exit(exit_codes.SUCCESS)
|
|
410
|
+
|
|
411
|
+
|
|
364
412
|
@app.command()
|
|
365
413
|
def mcp(
|
|
366
414
|
plugin: Annotated[
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""`atk doctor` — repair an ATK Home's .gitignore and untrack leaked secrets.
|
|
2
|
+
|
|
3
|
+
Historical bug: per-plugin `!plugins/<name>/**` exemptions were appended AFTER
|
|
4
|
+
the `*.env` rule. Because .gitignore is last-match-wins, those exemptions
|
|
5
|
+
re-included plugin .env files, which `atk add`'s auto-commit then committed.
|
|
6
|
+
|
|
7
|
+
This migration re-orders the secret rules last (idempotent) and untracks any
|
|
8
|
+
secret files that already slipped into the repo. It does NOT rotate keys or
|
|
9
|
+
rewrite history — both are surfaced to the user as follow-ups.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
from atk.git import (
|
|
18
|
+
git_add,
|
|
19
|
+
git_commit,
|
|
20
|
+
git_push,
|
|
21
|
+
git_rm_cached,
|
|
22
|
+
list_tracked_secrets,
|
|
23
|
+
normalize_gitignore,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class DoctorResult:
|
|
29
|
+
"""Outcome of a doctor run, for the CLI to report."""
|
|
30
|
+
|
|
31
|
+
gitignore_fixed: bool
|
|
32
|
+
untracked_secrets: list[str] = field(default_factory=list)
|
|
33
|
+
committed: bool = False
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def run_doctor(
|
|
37
|
+
atk_home: Path,
|
|
38
|
+
*,
|
|
39
|
+
auto_commit: bool,
|
|
40
|
+
auto_push: bool = False,
|
|
41
|
+
) -> DoctorResult:
|
|
42
|
+
"""Repair the ATK Home .gitignore and untrack any committed secret files.
|
|
43
|
+
|
|
44
|
+
Order matters: normalise the .gitignore FIRST so the secret files become
|
|
45
|
+
ignored, THEN `git rm --cached` them — that way the subsequent stage can't
|
|
46
|
+
re-add a now-ignored file. Commits the repair when auto_commit is on,
|
|
47
|
+
mirroring the add/remove commands.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
atk_home: Path to the (initialized) ATK Home directory.
|
|
51
|
+
auto_commit: Whether to commit the repair (from manifest config).
|
|
52
|
+
auto_push: Whether to push after committing (subordinate to commit).
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
DoctorResult describing what changed.
|
|
56
|
+
"""
|
|
57
|
+
gitignore_fixed = normalize_gitignore(atk_home)
|
|
58
|
+
|
|
59
|
+
tracked_secrets = list_tracked_secrets(atk_home)
|
|
60
|
+
if tracked_secrets:
|
|
61
|
+
git_rm_cached(atk_home, tracked_secrets)
|
|
62
|
+
if gitignore_fixed:
|
|
63
|
+
git_add(atk_home, [".gitignore"])
|
|
64
|
+
|
|
65
|
+
committed = False
|
|
66
|
+
if (gitignore_fixed or tracked_secrets) and auto_commit:
|
|
67
|
+
committed = git_commit(atk_home, "atk doctor: keep secrets out of git")
|
|
68
|
+
if committed and auto_push:
|
|
69
|
+
git_push(atk_home)
|
|
70
|
+
|
|
71
|
+
return DoctorResult(
|
|
72
|
+
gitignore_fixed=gitignore_fixed,
|
|
73
|
+
untracked_secrets=tracked_secrets,
|
|
74
|
+
committed=committed,
|
|
75
|
+
)
|