kctl-claude 0.2.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.
- kctl_claude-0.2.0/.gitignore +33 -0
- kctl_claude-0.2.0/PKG-INFO +17 -0
- kctl_claude-0.2.0/README.md +88 -0
- kctl_claude-0.2.0/pyproject.toml +45 -0
- kctl_claude-0.2.0/skills/claude-admin/SKILL.md +164 -0
- kctl_claude-0.2.0/src/kctl_claude/__init__.py +3 -0
- kctl_claude-0.2.0/src/kctl_claude/__main__.py +5 -0
- kctl_claude-0.2.0/src/kctl_claude/cli.py +137 -0
- kctl_claude-0.2.0/src/kctl_claude/commands/__init__.py +0 -0
- kctl_claude-0.2.0/src/kctl_claude/commands/api_cmd.py +97 -0
- kctl_claude-0.2.0/src/kctl_claude/commands/backup_cmd.py +53 -0
- kctl_claude-0.2.0/src/kctl_claude/commands/config_cmd.py +436 -0
- kctl_claude-0.2.0/src/kctl_claude/commands/doctor_cmd.py +207 -0
- kctl_claude-0.2.0/src/kctl_claude/commands/env_cmd.py +83 -0
- kctl_claude-0.2.0/src/kctl_claude/commands/setup_cmd.py +170 -0
- kctl_claude-0.2.0/src/kctl_claude/commands/skill_cmd.py +76 -0
- kctl_claude-0.2.0/src/kctl_claude/commands/status_cmd.py +244 -0
- kctl_claude-0.2.0/src/kctl_claude/commands/sync_cmd.py +83 -0
- kctl_claude-0.2.0/src/kctl_claude/commands/verify_cmd.py +111 -0
- kctl_claude-0.2.0/src/kctl_claude/core/__init__.py +0 -0
- kctl_claude-0.2.0/src/kctl_claude/core/callbacks.py +55 -0
- kctl_claude-0.2.0/src/kctl_claude/core/checks.py +249 -0
- kctl_claude-0.2.0/src/kctl_claude/core/config.py +21 -0
- kctl_claude-0.2.0/src/kctl_claude/core/exceptions.py +17 -0
- kctl_claude-0.2.0/src/kctl_claude/core/output.py +39 -0
- kctl_claude-0.2.0/src/kctl_claude/core/paths.py +47 -0
- kctl_claude-0.2.0/src/kctl_claude/core/plugins.py +7 -0
- kctl_claude-0.2.0/src/kctl_claude/core/runner.py +48 -0
- kctl_claude-0.2.0/tests/__init__.py +0 -0
- kctl_claude-0.2.0/tests/conftest.py +61 -0
- kctl_claude-0.2.0/tests/test_api_cmd.py +113 -0
- kctl_claude-0.2.0/tests/test_callbacks.py +62 -0
- kctl_claude-0.2.0/tests/test_checks.py +207 -0
- kctl_claude-0.2.0/tests/test_config.py +69 -0
- kctl_claude-0.2.0/tests/test_config_cmd.py +80 -0
- kctl_claude-0.2.0/tests/test_doctor_cmd.py +131 -0
- kctl_claude-0.2.0/tests/test_output.py +41 -0
- kctl_claude-0.2.0/tests/test_paths.py +61 -0
- kctl_claude-0.2.0/tests/test_runner.py +53 -0
- kctl_claude-0.2.0/tests/test_smoke.py +79 -0
- kctl_claude-0.2.0/tests/test_status_cmd.py +85 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
*.egg
|
|
6
|
+
dist/
|
|
7
|
+
build/
|
|
8
|
+
.eggs/
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
venv/
|
|
13
|
+
|
|
14
|
+
# IDE
|
|
15
|
+
.idea/
|
|
16
|
+
.vscode/
|
|
17
|
+
*.swp
|
|
18
|
+
*.swo
|
|
19
|
+
|
|
20
|
+
# Testing
|
|
21
|
+
.pytest_cache/
|
|
22
|
+
.coverage
|
|
23
|
+
htmlcov/
|
|
24
|
+
.mypy_cache/
|
|
25
|
+
.ruff_cache/
|
|
26
|
+
|
|
27
|
+
# OS
|
|
28
|
+
.DS_Store
|
|
29
|
+
Thumbs.db
|
|
30
|
+
|
|
31
|
+
# Environment
|
|
32
|
+
.env
|
|
33
|
+
.env.local
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kctl-claude
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Kodemeio Claude Code CLI — manage local and remote Claude Code environments
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: httpx>=0.28.0
|
|
7
|
+
Requires-Dist: kctl-lib>=0.4.0
|
|
8
|
+
Requires-Dist: pydantic>=2.10.0
|
|
9
|
+
Requires-Dist: pyyaml>=6.0.2
|
|
10
|
+
Requires-Dist: rich>=13.9.0
|
|
11
|
+
Requires-Dist: typer>=0.15.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: mypy>=1.14.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: pytest-httpx>=0.35.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest>=8.3.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: ruff>=0.9.0; extra == 'dev'
|
|
17
|
+
Requires-Dist: types-pyyaml>=6.0.0; extra == 'dev'
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# kctl-claude
|
|
2
|
+
|
|
3
|
+
Kodemeio Claude Code CLI — manage local and remote Claude Code environments.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
uv tool install .
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
kctl-claude config init
|
|
15
|
+
kctl-claude status
|
|
16
|
+
kctl-claude doctor
|
|
17
|
+
kctl-claude env list
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Command Groups
|
|
21
|
+
|
|
22
|
+
| Group | Description |
|
|
23
|
+
|-------|-------------|
|
|
24
|
+
| `config` | Manage CLI configuration and profiles |
|
|
25
|
+
| `api` | SDK REST API operations |
|
|
26
|
+
| `backup` | Backup and restore Claude Code runtime |
|
|
27
|
+
| `doctor` | Diagnostics and health checks |
|
|
28
|
+
| `env` | Environment file management |
|
|
29
|
+
| `setup` | Setup Claude Code on local, VPS, or Docker |
|
|
30
|
+
| `status` | Status dashboard and health checks |
|
|
31
|
+
| `sync` | Sync config between local `~/.claude` and repo |
|
|
32
|
+
| `verify` | Verify Claude Code config completeness |
|
|
33
|
+
|
|
34
|
+
Top-level commands: `completions`, `update`
|
|
35
|
+
|
|
36
|
+
## Configuration
|
|
37
|
+
|
|
38
|
+
Config lives in `~/.config/kodemeio/config.yaml` under the `claude` service key.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
# Initialize a profile
|
|
42
|
+
kctl-claude config init
|
|
43
|
+
|
|
44
|
+
# Add a named profile
|
|
45
|
+
kctl-claude config add work --url https://api.anthropic.com --api-key $ANTHROPIC_API_KEY
|
|
46
|
+
|
|
47
|
+
# Switch active profile
|
|
48
|
+
kctl-claude config use work
|
|
49
|
+
|
|
50
|
+
# Show current profile (secrets masked)
|
|
51
|
+
kctl-claude config show
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Global Options
|
|
55
|
+
|
|
56
|
+
| Option | Short | Description |
|
|
57
|
+
|--------|-------|-------------|
|
|
58
|
+
| `--json` | | Output as JSON |
|
|
59
|
+
| `--quiet` | `-q` | Suppress info messages |
|
|
60
|
+
| `--verbose` | `-v` | Show debug output |
|
|
61
|
+
| `--version` | `-V` | Show version and exit |
|
|
62
|
+
|
|
63
|
+
## Shell Completions
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
# Generate completions (prints to stdout)
|
|
67
|
+
kctl-claude completions zsh
|
|
68
|
+
kctl-claude completions bash
|
|
69
|
+
kctl-claude completions fish
|
|
70
|
+
|
|
71
|
+
# Install completions automatically
|
|
72
|
+
kctl-claude completions zsh --install
|
|
73
|
+
kctl-claude completions bash --install
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Self-Update
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
kctl-claude update
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
uv run pytest tests/ -v
|
|
86
|
+
uv run ruff check src/
|
|
87
|
+
uv run mypy src/
|
|
88
|
+
```
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kctl-claude"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Kodemeio Claude Code CLI — manage local and remote Claude Code environments"
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"kctl-lib>=0.4.0",
|
|
12
|
+
"typer>=0.15.0",
|
|
13
|
+
"rich>=13.9.0",
|
|
14
|
+
"pydantic>=2.10.0",
|
|
15
|
+
"pyyaml>=6.0.2",
|
|
16
|
+
"httpx>=0.28.0",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
[project.optional-dependencies]
|
|
20
|
+
dev = [
|
|
21
|
+
"pytest>=8.3.0",
|
|
22
|
+
"pytest-httpx>=0.35.0",
|
|
23
|
+
"ruff>=0.9.0",
|
|
24
|
+
"mypy>=1.14.0",
|
|
25
|
+
"types-PyYAML>=6.0.0",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
[project.scripts]
|
|
29
|
+
kctl-claude = "kctl_claude.cli:_run"
|
|
30
|
+
|
|
31
|
+
[tool.uv.sources]
|
|
32
|
+
kctl-lib = { workspace = true }
|
|
33
|
+
|
|
34
|
+
[project.entry-points."kctl_claude.plugins"]
|
|
35
|
+
|
|
36
|
+
[tool.hatch.build.targets.wheel]
|
|
37
|
+
packages = ["src/kctl_claude"]
|
|
38
|
+
|
|
39
|
+
[tool.ruff]
|
|
40
|
+
target-version = "py312"
|
|
41
|
+
line-length = 120
|
|
42
|
+
|
|
43
|
+
[tool.mypy]
|
|
44
|
+
python_version = "3.12"
|
|
45
|
+
strict = true
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: claude-admin
|
|
3
|
+
description: >
|
|
4
|
+
Claude Code environment management via kctl-claude CLI (12 groups, ~33 commands).
|
|
5
|
+
MUST use for ANY kctl-claude operation.
|
|
6
|
+
Triggers on: "api", "backup", "check", "compare", "completions", "config", "current", "deploy", "diff", "docker", "doctor", "env", "generate", "health", "init", "kctl-claude", "local", "profile", "profiles", "pull", "push", "remove", "restore", "secrets", "setup", "skill", "status", "sync", "task", "test", "update", "updates", "validate", "verify".
|
|
7
|
+
Auto-generated: 2026-04-05
|
|
8
|
+
registry_hash: 7578bca2463c
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# claude-admin — kctl-claude CLI Reference
|
|
12
|
+
|
|
13
|
+
> Auto-generated from `kctl-claude` command registry. Do not edit manually.
|
|
14
|
+
> To regenerate: `kctl-claude skill generate`
|
|
15
|
+
> To add custom content: edit `SKILL.extra.md` in the same directory.
|
|
16
|
+
|
|
17
|
+
## Overview
|
|
18
|
+
|
|
19
|
+
**CLI:** `kctl-claude`
|
|
20
|
+
**Command groups:** 12
|
|
21
|
+
**Total commands:** ~33
|
|
22
|
+
**Install:** `cd cli && uv tool install --editable .`
|
|
23
|
+
|
|
24
|
+
## Global Options
|
|
25
|
+
|
|
26
|
+
| Flag | Description |
|
|
27
|
+
|------|-------------|
|
|
28
|
+
| `--json` | JSON output |
|
|
29
|
+
| `--quiet`, `-q` | Suppress info messages |
|
|
30
|
+
| `--format`, `-f` | Output format: pretty/json/csv/yaml |
|
|
31
|
+
| `--no-header` | Omit CSV header row |
|
|
32
|
+
| `--profile`, `-p` | Config profile name |
|
|
33
|
+
| `--version`, `-V` | Show version |
|
|
34
|
+
|
|
35
|
+
## Command Reference
|
|
36
|
+
|
|
37
|
+
### `kctl-claude api`
|
|
38
|
+
|
|
39
|
+
SDK REST API operations.
|
|
40
|
+
|
|
41
|
+
| Command | Description |
|
|
42
|
+
|---------|-------------|
|
|
43
|
+
| `api health` | Check SDK API health. |
|
|
44
|
+
| `api task <prompt> [--workspace] [--bare]` | Send a task to Claude Code via SDK API. |
|
|
45
|
+
|
|
46
|
+
### `kctl-claude backup`
|
|
47
|
+
|
|
48
|
+
Backup and restore Claude Code runtime.
|
|
49
|
+
|
|
50
|
+
| Command | Description |
|
|
51
|
+
|---------|-------------|
|
|
52
|
+
| `backup create` | Backup container runtime volume. |
|
|
53
|
+
| `backup restore <backup_dir>` | Restore runtime from backup. |
|
|
54
|
+
|
|
55
|
+
### `kctl-claude completions`
|
|
56
|
+
|
|
57
|
+
Generate or install shell completions.
|
|
58
|
+
|
|
59
|
+
Usage: `kctl-claude completions [--shell] [--install]`
|
|
60
|
+
|
|
61
|
+
### `kctl-claude config`
|
|
62
|
+
|
|
63
|
+
Manage CLI configuration and profiles.
|
|
64
|
+
|
|
65
|
+
| Command | Description |
|
|
66
|
+
|---------|-------------|
|
|
67
|
+
| `config add <name> [--config_dir] [--backup_dir] [--set_default]` | Add or update a profile's Claude configuration. |
|
|
68
|
+
| `config current` | Show the active profile and its Claude configuration. |
|
|
69
|
+
| `config init [--config_dir] [--backup_dir] [--name]` | Initialize CLI configuration (interactive if no flags given). |
|
|
70
|
+
| `config profiles` | List all profiles. |
|
|
71
|
+
| `config remove <name> [--force] [--service_only]` | Remove a profile or just its Claude config. |
|
|
72
|
+
| `config set <key> <value> [--profile_arg]` | Set a configuration value for the current service. |
|
|
73
|
+
| `config show` | Show full configuration. |
|
|
74
|
+
| `config test` | Verify configuration is valid. |
|
|
75
|
+
| `config use <name>` | Switch the default profile. |
|
|
76
|
+
| `config validate` | Validate the current configuration. |
|
|
77
|
+
|
|
78
|
+
**Examples:**
|
|
79
|
+
```bash
|
|
80
|
+
kctl-claude config set config_dir /path/to/dir
|
|
81
|
+
kctl-claude config set backup_dir /path/to/backups
|
|
82
|
+
kctl-claude config set default_profile work
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### `kctl-claude doctor`
|
|
86
|
+
|
|
87
|
+
Diagnostics and health checks.
|
|
88
|
+
|
|
89
|
+
### `kctl-claude env`
|
|
90
|
+
|
|
91
|
+
Environment file management.
|
|
92
|
+
|
|
93
|
+
| Command | Description |
|
|
94
|
+
|---------|-------------|
|
|
95
|
+
| `env check` | Validate .env file. |
|
|
96
|
+
| `env deploy` | Deploy .env to Hetzner server. |
|
|
97
|
+
| `env generate` | Interactive .env generator. |
|
|
98
|
+
|
|
99
|
+
### `kctl-claude setup`
|
|
100
|
+
|
|
101
|
+
Setup Claude Code on local, VPS, or Docker.
|
|
102
|
+
|
|
103
|
+
| Command | Description |
|
|
104
|
+
|---------|-------------|
|
|
105
|
+
| `setup compare` | Compare setup modes side-by-side. |
|
|
106
|
+
| `setup docker` | Show Docker deployment commands. |
|
|
107
|
+
| `setup init` | Run session initialization (git, SSH, tmux, kctl tools). |
|
|
108
|
+
| `setup local` | Setup local development environment (laptop). |
|
|
109
|
+
| `setup vps [--check] [--skip_repos]` | Setup VPS/bare-metal server (requires sudo). |
|
|
110
|
+
|
|
111
|
+
### `kctl-claude skill`
|
|
112
|
+
|
|
113
|
+
Claude Code skill management.
|
|
114
|
+
|
|
115
|
+
| Command | Description |
|
|
116
|
+
|---------|-------------|
|
|
117
|
+
| `skill generate [--output] [--install] [--check]` | Auto-generate SKILL.md from CLI command registry. |
|
|
118
|
+
|
|
119
|
+
**Examples:**
|
|
120
|
+
```bash
|
|
121
|
+
kctl-claude skill generate
|
|
122
|
+
kctl-claude skill generate --install
|
|
123
|
+
kctl-claude skill generate --check
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### `kctl-claude status`
|
|
127
|
+
|
|
128
|
+
Status dashboard and health checks.
|
|
129
|
+
|
|
130
|
+
| Command | Description |
|
|
131
|
+
|---------|-------------|
|
|
132
|
+
| `status health` | Quick health check (exit code 0=ok, 1=issues). |
|
|
133
|
+
| `status updates` | Check for Claude Code updates and show changelog link. |
|
|
134
|
+
|
|
135
|
+
### `kctl-claude sync`
|
|
136
|
+
|
|
137
|
+
Sync config between local ~/.claude and repo.
|
|
138
|
+
|
|
139
|
+
| Command | Description |
|
|
140
|
+
|---------|-------------|
|
|
141
|
+
| `sync diff` | Preview what push would change (dry-run). |
|
|
142
|
+
| `sync pull` | Sync repo config -> local (update local environment). |
|
|
143
|
+
| `sync push` | Sync local config -> repo (for Docker deployment). |
|
|
144
|
+
| `sync secrets` | Deploy kctl credentials to Hetzner. |
|
|
145
|
+
|
|
146
|
+
### `kctl-claude update`
|
|
147
|
+
|
|
148
|
+
Check for updates and upgrade kctl-claude.
|
|
149
|
+
|
|
150
|
+
### `kctl-claude verify`
|
|
151
|
+
|
|
152
|
+
Verify Claude Code config completeness.
|
|
153
|
+
|
|
154
|
+
## Configuration
|
|
155
|
+
|
|
156
|
+
Shared config: `~/.config/kodemeio/config.yaml`
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
kctl-claude config init # Interactive setup
|
|
160
|
+
kctl-claude config show # Show current config
|
|
161
|
+
kctl-claude config profiles # List profiles
|
|
162
|
+
kctl-claude config current # Show active profile
|
|
163
|
+
kctl-claude config validate # Verify config
|
|
164
|
+
```
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""Main CLI entry point for kctl-claude."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
from typing import Annotated
|
|
8
|
+
|
|
9
|
+
import typer
|
|
10
|
+
|
|
11
|
+
from kctl_claude import __version__
|
|
12
|
+
from kctl_claude.commands.api_cmd import app as api_app
|
|
13
|
+
from kctl_claude.commands.backup_cmd import app as backup_app
|
|
14
|
+
from kctl_claude.commands.config_cmd import app as config_app
|
|
15
|
+
from kctl_claude.commands.doctor_cmd import app as doctor_app
|
|
16
|
+
from kctl_claude.commands.env_cmd import app as env_app
|
|
17
|
+
from kctl_claude.commands.setup_cmd import app as setup_app
|
|
18
|
+
from kctl_claude.commands.status_cmd import app as status_app
|
|
19
|
+
from kctl_claude.commands.sync_cmd import app as sync_app
|
|
20
|
+
from kctl_claude.commands.verify_cmd import app as verify_app
|
|
21
|
+
from kctl_claude.core.callbacks import AppContext
|
|
22
|
+
from kctl_claude.core.plugins import ENTRY_POINT_GROUP, discover_and_load_plugins
|
|
23
|
+
from kctl_claude.commands.skill_cmd import app as skill_app
|
|
24
|
+
from kctl_lib.self_update import notify_if_outdated
|
|
25
|
+
|
|
26
|
+
app = typer.Typer(
|
|
27
|
+
name="kctl-claude",
|
|
28
|
+
help="Kodemeio Claude Code CLI — manage local and remote Claude Code environments.",
|
|
29
|
+
invoke_without_command=True,
|
|
30
|
+
rich_markup_mode="rich",
|
|
31
|
+
pretty_exceptions_enable=False,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Register command groups
|
|
35
|
+
app.add_typer(config_app, name="config")
|
|
36
|
+
app.add_typer(status_app, name="status")
|
|
37
|
+
app.add_typer(sync_app, name="sync")
|
|
38
|
+
app.add_typer(verify_app, name="verify")
|
|
39
|
+
app.add_typer(api_app, name="api")
|
|
40
|
+
app.add_typer(env_app, name="env")
|
|
41
|
+
app.add_typer(backup_app, name="backup")
|
|
42
|
+
app.add_typer(setup_app, name="setup")
|
|
43
|
+
app.add_typer(doctor_app, name="doctor")
|
|
44
|
+
app.add_typer(skill_app, name="skill", hidden=True)
|
|
45
|
+
|
|
46
|
+
# Discover and load plugins
|
|
47
|
+
discover_and_load_plugins(app, ENTRY_POINT_GROUP)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@app.callback()
|
|
51
|
+
def main(
|
|
52
|
+
ctx: typer.Context,
|
|
53
|
+
json_output: Annotated[bool, typer.Option("--json", help="JSON output")] = False,
|
|
54
|
+
quiet: Annotated[bool, typer.Option("--quiet", "-q", help="Suppress info messages")] = False,
|
|
55
|
+
verbose: Annotated[bool, typer.Option("--verbose", "-v", help="Show debug output")] = False,
|
|
56
|
+
version: Annotated[bool, typer.Option("--version", "-V", help="Show version")] = False,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""Global options applied to all commands."""
|
|
59
|
+
if version:
|
|
60
|
+
typer.echo(f"kctl-claude {__version__}")
|
|
61
|
+
raise typer.Exit()
|
|
62
|
+
|
|
63
|
+
ctx.ensure_object(dict)
|
|
64
|
+
ctx.obj = AppContext(json_mode=json_output, quiet=quiet, verbose=verbose)
|
|
65
|
+
notify_if_outdated(ctx.obj.output, "kctl-claude", __version__)
|
|
66
|
+
|
|
67
|
+
# Record command in history (non-blocking, best-effort)
|
|
68
|
+
try:
|
|
69
|
+
from kctl_lib.history import HistoryStore
|
|
70
|
+
|
|
71
|
+
store = HistoryStore("kctl-claude")
|
|
72
|
+
store.ensure_schema(
|
|
73
|
+
[
|
|
74
|
+
"""CREATE TABLE IF NOT EXISTS commands (
|
|
75
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
76
|
+
command TEXT NOT NULL,
|
|
77
|
+
timestamp TEXT NOT NULL
|
|
78
|
+
)"""
|
|
79
|
+
]
|
|
80
|
+
)
|
|
81
|
+
store.record(table="commands", command=" ".join(sys.argv[1:]))
|
|
82
|
+
except Exception:
|
|
83
|
+
pass # History recording is best-effort
|
|
84
|
+
|
|
85
|
+
# If no subcommand, show help
|
|
86
|
+
if ctx.invoked_subcommand is None and not version:
|
|
87
|
+
typer.echo(ctx.get_help())
|
|
88
|
+
raise typer.Exit()
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@app.command()
|
|
92
|
+
def completions(
|
|
93
|
+
shell: Annotated[str, typer.Argument(help="Shell type: zsh, bash, fish")] = "zsh",
|
|
94
|
+
install: Annotated[bool, typer.Option("--install", help="Install completions")] = False,
|
|
95
|
+
) -> None:
|
|
96
|
+
"""Generate or install shell completions."""
|
|
97
|
+
from kctl_lib.completions import get_completion_script, install_completions
|
|
98
|
+
|
|
99
|
+
if install:
|
|
100
|
+
path = install_completions("kctl-claude", shell)
|
|
101
|
+
if path:
|
|
102
|
+
typer.echo(f"Completions installed to {path}")
|
|
103
|
+
else:
|
|
104
|
+
typer.echo(f"Could not install completions for {shell}", err=True)
|
|
105
|
+
raise typer.Exit(code=1)
|
|
106
|
+
else:
|
|
107
|
+
script = get_completion_script("kctl-claude", shell)
|
|
108
|
+
typer.echo(script)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@app.command("update")
|
|
112
|
+
def update_cmd(ctx: typer.Context) -> None:
|
|
113
|
+
"""Check for updates and upgrade kctl-claude."""
|
|
114
|
+
actx: AppContext = ctx.obj
|
|
115
|
+
out = actx.output
|
|
116
|
+
|
|
117
|
+
from kctl_lib.self_update import check_update
|
|
118
|
+
from kctl_lib.self_update import update as do_update
|
|
119
|
+
|
|
120
|
+
latest = check_update("kctl-claude", __version__)
|
|
121
|
+
if latest:
|
|
122
|
+
out.info(f"Updating to {latest}...")
|
|
123
|
+
do_update("kctl-claude")
|
|
124
|
+
out.success(f"Updated to {latest}")
|
|
125
|
+
else:
|
|
126
|
+
out.success("Already up to date")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _run() -> None:
|
|
130
|
+
"""Entry point with error handling."""
|
|
131
|
+
try:
|
|
132
|
+
app()
|
|
133
|
+
except KeyboardInterrupt:
|
|
134
|
+
sys.exit(130)
|
|
135
|
+
except Exception as e:
|
|
136
|
+
typer.echo(f"Error: {e}", err=True)
|
|
137
|
+
sys.exit(1)
|
|
File without changes
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""kctl-claude api — interact with the SDK REST API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Annotated
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from kctl_claude.core.callbacks import AppContext
|
|
11
|
+
|
|
12
|
+
app = typer.Typer(help="SDK REST API operations.")
|
|
13
|
+
|
|
14
|
+
DEFAULT_URL = "http://localhost:3100"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _base_url() -> str:
|
|
18
|
+
import os
|
|
19
|
+
|
|
20
|
+
return os.environ.get("SDK_BASE_URL", DEFAULT_URL)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _api_key() -> str:
|
|
24
|
+
import os
|
|
25
|
+
|
|
26
|
+
return os.environ.get("SDK_API_KEY", "")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@app.command()
|
|
30
|
+
def health(ctx: typer.Context) -> None:
|
|
31
|
+
"""Check SDK API health."""
|
|
32
|
+
actx: AppContext = ctx.obj
|
|
33
|
+
out = actx.output
|
|
34
|
+
try:
|
|
35
|
+
resp = httpx.get(f"{_base_url()}/health", timeout=5)
|
|
36
|
+
try:
|
|
37
|
+
data = resp.json()
|
|
38
|
+
except Exception:
|
|
39
|
+
data = {}
|
|
40
|
+
if actx.json_mode:
|
|
41
|
+
out.raw_json(data)
|
|
42
|
+
else:
|
|
43
|
+
out.success(f"Status: {data.get('status', 'unknown')}")
|
|
44
|
+
out.success(f"Tasks: {data.get('inFlight', 0)}/{data.get('maxConcurrent', 3)} in flight")
|
|
45
|
+
except (httpx.ConnectError, httpx.TimeoutException) as e:
|
|
46
|
+
out.error(f"SDK API unreachable: {e}")
|
|
47
|
+
raise typer.Exit(code=1) from None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@app.command()
|
|
51
|
+
def task(
|
|
52
|
+
ctx: typer.Context,
|
|
53
|
+
prompt: Annotated[str, typer.Argument(help="Task prompt for Claude Code")],
|
|
54
|
+
workspace: Annotated[str, typer.Option("--workspace", "-w", help="Workspace name")] = "",
|
|
55
|
+
bare: Annotated[bool, typer.Option("--bare", help="Skip auto-discovery")] = False,
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Send a task to Claude Code via SDK API."""
|
|
58
|
+
actx: AppContext = ctx.obj
|
|
59
|
+
out = actx.output
|
|
60
|
+
|
|
61
|
+
# Validate workspace path to prevent traversal attacks
|
|
62
|
+
if workspace and (".." in workspace or workspace.startswith("/")):
|
|
63
|
+
out.error("Invalid workspace path")
|
|
64
|
+
raise typer.Exit(code=1)
|
|
65
|
+
|
|
66
|
+
headers = {"Content-Type": "application/json"}
|
|
67
|
+
key = _api_key()
|
|
68
|
+
if key:
|
|
69
|
+
headers["Authorization"] = f"Bearer {key}"
|
|
70
|
+
|
|
71
|
+
body: dict = {"prompt": prompt}
|
|
72
|
+
if workspace:
|
|
73
|
+
body["workspace"] = workspace
|
|
74
|
+
if bare:
|
|
75
|
+
body["bare"] = True
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
resp = httpx.post(f"{_base_url()}/task", json=body, headers=headers, timeout=310)
|
|
79
|
+
try:
|
|
80
|
+
data = resp.json()
|
|
81
|
+
except Exception:
|
|
82
|
+
data = {}
|
|
83
|
+
if actx.json_mode:
|
|
84
|
+
out.raw_json(data)
|
|
85
|
+
else:
|
|
86
|
+
if resp.status_code == 200:
|
|
87
|
+
out.success("Task completed")
|
|
88
|
+
out.text(data.get("result", str(data)))
|
|
89
|
+
else:
|
|
90
|
+
out.error(f"Error {resp.status_code}: {data.get('error', str(data))}")
|
|
91
|
+
raise typer.Exit(code=1)
|
|
92
|
+
except httpx.TimeoutException:
|
|
93
|
+
out.error("Task timed out (310s)")
|
|
94
|
+
raise typer.Exit(code=1) from None
|
|
95
|
+
except httpx.ConnectError as e:
|
|
96
|
+
out.error(f"SDK API unreachable: {e}")
|
|
97
|
+
raise typer.Exit(code=1) from None
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""kctl-claude backup — runtime volume backup/restore."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import subprocess
|
|
6
|
+
from typing import Annotated
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
from kctl_lib.exceptions import CommandError
|
|
10
|
+
|
|
11
|
+
from kctl_claude.core.callbacks import AppContext
|
|
12
|
+
from kctl_claude.core.runner import run_script
|
|
13
|
+
|
|
14
|
+
app = typer.Typer(help="Backup and restore Claude Code runtime.")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.command("create")
|
|
18
|
+
def create_backup(ctx: typer.Context) -> None:
|
|
19
|
+
"""Backup container runtime volume."""
|
|
20
|
+
actx: AppContext = ctx.obj
|
|
21
|
+
out = actx.output
|
|
22
|
+
repo = actx.repo_dir
|
|
23
|
+
if not repo:
|
|
24
|
+
out.error("Repo not found.")
|
|
25
|
+
raise typer.Exit(code=1)
|
|
26
|
+
script = repo / "scripts" / "backup-runtime.sh"
|
|
27
|
+
try:
|
|
28
|
+
run_script(script, capture=False)
|
|
29
|
+
out.success("Backup created")
|
|
30
|
+
except CommandError as e:
|
|
31
|
+
out.error(f"Backup failed (exit {e.returncode}): {e.stderr}")
|
|
32
|
+
raise typer.Exit(code=1) from None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@app.command()
|
|
36
|
+
def restore(
|
|
37
|
+
ctx: typer.Context,
|
|
38
|
+
backup_dir: Annotated[str, typer.Argument(help="Path to backup directory")],
|
|
39
|
+
) -> None:
|
|
40
|
+
"""Restore runtime from backup."""
|
|
41
|
+
actx: AppContext = ctx.obj
|
|
42
|
+
out = actx.output
|
|
43
|
+
out.info(f"Restoring from {backup_dir}...")
|
|
44
|
+
result = subprocess.run(
|
|
45
|
+
["docker", "cp", f"{backup_dir}/.", "kodemeio-claude:/home/dev/.claude/"],
|
|
46
|
+
capture_output=True,
|
|
47
|
+
text=True,
|
|
48
|
+
)
|
|
49
|
+
if result.returncode == 0:
|
|
50
|
+
out.success("Restore complete")
|
|
51
|
+
else:
|
|
52
|
+
out.error(f"Restore failed (exit {result.returncode}): {result.stderr.strip()}")
|
|
53
|
+
raise typer.Exit(code=1)
|