agentpack-cli 0.3.0__tar.gz → 0.3.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.
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/.gitignore +5 -1
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/PKG-INFO +29 -10
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/README.md +28 -9
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/pyproject.toml +1 -1
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/__init__.py +1 -1
- agentpack_cli-0.3.1/src/agentpack/commands/init.py +408 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/install.py +6 -11
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/pack.py +1 -1
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/data/agentpack.md +1 -1
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/mcp_server.py +1 -1
- agentpack_cli-0.3.0/src/agentpack/commands/init.py +0 -186
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/LICENSE +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/adapters/__init__.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/adapters/antigravity.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/adapters/base.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/adapters/claude.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/adapters/codex.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/adapters/cursor.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/adapters/detect.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/adapters/generic.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/adapters/windsurf.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/__init__.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/dependency_graph.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/go_imports.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/java_imports.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/js_ts_imports.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/monorepo.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/python_imports.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/ranking.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/repo_map.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/role_inference.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/rust_imports.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/symbols.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/task_classifier.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/analysis/tests.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/application/__init__.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/application/pack_service.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/cli.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/__init__.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/_shared.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/benchmark.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/claude_cmd.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/diff.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/doctor.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/explain.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/hook_cmd.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/mcp_cmd.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/monitor.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/quickstart.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/repair.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/scan.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/stats.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/status.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/summarize.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/tune.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/commands/watch.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/__init__.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/bootstrap.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/cache.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/config.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/context_pack.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/diff.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/git.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/git_hooks.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/global_install.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/ignore.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/merkle.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/models.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/redactor.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/scanner.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/snapshot.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/token_estimator.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/core/vscode_tasks.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/installers/__init__.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/installers/antigravity.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/installers/claude.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/installers/codex.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/installers/cursor.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/installers/windsurf.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/integrations/__init__.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/integrations/agents.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/integrations/git_hooks.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/integrations/global_install.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/integrations/vscode_tasks.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/renderers/__init__.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/renderers/compact.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/renderers/markdown.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/renderers/receipts.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/session/__init__.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/session/state.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/summaries/__init__.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/summaries/base.py +0 -0
- {agentpack_cli-0.3.0 → agentpack_cli-0.3.1}/src/agentpack/summaries/offline.py +0 -0
|
@@ -12,6 +12,8 @@ env/
|
|
|
12
12
|
|
|
13
13
|
# agentpack:start
|
|
14
14
|
# AgentPack generated context/cache (safe to ignore)
|
|
15
|
+
.agentpack/*
|
|
16
|
+
!.agentpack/config.toml
|
|
15
17
|
.agentpack/cache/
|
|
16
18
|
.agentpack/snapshots/
|
|
17
19
|
.agentpack/context*
|
|
@@ -23,9 +25,11 @@ env/
|
|
|
23
25
|
.agentpack/session.json
|
|
24
26
|
.agentpack/task.md
|
|
25
27
|
.agentpack/benchmark_results.jsonl
|
|
28
|
+
.agentignore
|
|
26
29
|
.agent/skills/agentpack/
|
|
30
|
+
.vscode/tasks.json
|
|
31
|
+
GEMINI.md
|
|
27
32
|
# agentpack:end
|
|
28
|
-
|
|
29
33
|
.pytest_cache/
|
|
30
34
|
.mypy_cache/
|
|
31
35
|
.ruff_cache/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentpack-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Task-aware context packing for AI coding agents — Claude, Cursor, Windsurf, Codex, and Antigravity
|
|
5
5
|
License: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -44,7 +44,7 @@ Description-Content-Type: text/markdown
|
|
|
44
44
|
[](https://opensource.org/licenses/MIT)
|
|
45
45
|
[](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml)
|
|
46
46
|
|
|
47
|
-
> **Status: alpha (v0.3.
|
|
47
|
+
> **Status: alpha (v0.3.1).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Public benchmark proof exists for the current suite, but broader repo coverage is still growing. API may change before 1.0.
|
|
48
48
|
>
|
|
49
49
|
> **Platform note:** macOS and Linux are fully supported. Windows support is not yet implemented (git hooks use POSIX shell; the Claude Code session hooks use `python3`/`rm -f`). Contributions welcome.
|
|
50
50
|
|
|
@@ -80,11 +80,30 @@ AgentPack is useful when a repo is too large to paste, but a blank agent session
|
|
|
80
80
|
## Install
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
|
-
|
|
83
|
+
pipx install agentpack-cli
|
|
84
84
|
agentpack --version
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
Requires Python 3.10+. The PyPI package is `agentpack-cli`; the command is `agentpack`.
|
|
87
|
+
Requires Python 3.10+. The PyPI package is `agentpack-cli`; the command is `agentpack`. Use `pipx` for normal installs because many macOS/Linux Python distributions block global `pip install` with PEP 668's `externally-managed-environment` error. If you prefer `pip`, install inside a virtual environment.
|
|
88
|
+
|
|
89
|
+
Install `pipx` with your OS package manager first if needed:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# macOS
|
|
93
|
+
brew install pipx
|
|
94
|
+
|
|
95
|
+
# Ubuntu/Debian
|
|
96
|
+
sudo apt install pipx
|
|
97
|
+
|
|
98
|
+
# Fedora
|
|
99
|
+
sudo dnf install pipx
|
|
100
|
+
|
|
101
|
+
# Arch
|
|
102
|
+
sudo pacman -S python-pipx
|
|
103
|
+
|
|
104
|
+
# Then ensure pipx apps are on PATH
|
|
105
|
+
pipx ensurepath
|
|
106
|
+
```
|
|
88
107
|
|
|
89
108
|
JavaScript-heavy teams can install the npm wrapper:
|
|
90
109
|
|
|
@@ -454,7 +473,7 @@ jobs:
|
|
|
454
473
|
with:
|
|
455
474
|
python-version: "3.12"
|
|
456
475
|
|
|
457
|
-
- run: pip install agentpack-cli
|
|
476
|
+
- run: python -m pip install agentpack-cli
|
|
458
477
|
|
|
459
478
|
- name: Generate context pack
|
|
460
479
|
run: |
|
|
@@ -767,7 +786,7 @@ Uses `watchdog` if installed, falls back to polling. Context is refreshed whenev
|
|
|
767
786
|
|
|
768
787
|
Install watchdog for better performance:
|
|
769
788
|
```bash
|
|
770
|
-
|
|
789
|
+
pipx inject agentpack-cli watchdog
|
|
771
790
|
```
|
|
772
791
|
|
|
773
792
|
---
|
|
@@ -789,7 +808,7 @@ Requires an initialized project (`agentpack init`). Refreshes context, prints th
|
|
|
789
808
|
Run AgentPack as an MCP server — exposes context packing as tools that Claude Code (and any MCP-compatible agent) can call directly.
|
|
790
809
|
|
|
791
810
|
```bash
|
|
792
|
-
|
|
811
|
+
pipx inject agentpack-cli "agentpack-cli[mcp]"
|
|
793
812
|
agentpack mcp
|
|
794
813
|
```
|
|
795
814
|
|
|
@@ -1464,9 +1483,9 @@ Post-0.3 release focus: broader real-repo proof, npm publish reliability, and co
|
|
|
1464
1483
|
## Optional dependencies
|
|
1465
1484
|
|
|
1466
1485
|
```bash
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1486
|
+
pipx inject agentpack-cli watchdog # faster file watching for agentpack watch
|
|
1487
|
+
pipx inject agentpack-cli "agentpack-cli[mcp]" # expose agentpack as MCP server tools
|
|
1488
|
+
pipx inject agentpack-cli "agentpack-cli[all]" # watch + mcp
|
|
1470
1489
|
```
|
|
1471
1490
|
|
|
1472
1491
|
---
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://github.com/vishal2612200/agentpack/actions/workflows/ci.yml)
|
|
7
7
|
|
|
8
|
-
> **Status: alpha (v0.3.
|
|
8
|
+
> **Status: alpha (v0.3.1).** Works, tested, used in real sessions. Python and JavaScript/TypeScript are the best-supported languages. Public benchmark proof exists for the current suite, but broader repo coverage is still growing. API may change before 1.0.
|
|
9
9
|
>
|
|
10
10
|
> **Platform note:** macOS and Linux are fully supported. Windows support is not yet implemented (git hooks use POSIX shell; the Claude Code session hooks use `python3`/`rm -f`). Contributions welcome.
|
|
11
11
|
|
|
@@ -41,11 +41,30 @@ AgentPack is useful when a repo is too large to paste, but a blank agent session
|
|
|
41
41
|
## Install
|
|
42
42
|
|
|
43
43
|
```bash
|
|
44
|
-
|
|
44
|
+
pipx install agentpack-cli
|
|
45
45
|
agentpack --version
|
|
46
46
|
```
|
|
47
47
|
|
|
48
|
-
Requires Python 3.10+. The PyPI package is `agentpack-cli`; the command is `agentpack`.
|
|
48
|
+
Requires Python 3.10+. The PyPI package is `agentpack-cli`; the command is `agentpack`. Use `pipx` for normal installs because many macOS/Linux Python distributions block global `pip install` with PEP 668's `externally-managed-environment` error. If you prefer `pip`, install inside a virtual environment.
|
|
49
|
+
|
|
50
|
+
Install `pipx` with your OS package manager first if needed:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# macOS
|
|
54
|
+
brew install pipx
|
|
55
|
+
|
|
56
|
+
# Ubuntu/Debian
|
|
57
|
+
sudo apt install pipx
|
|
58
|
+
|
|
59
|
+
# Fedora
|
|
60
|
+
sudo dnf install pipx
|
|
61
|
+
|
|
62
|
+
# Arch
|
|
63
|
+
sudo pacman -S python-pipx
|
|
64
|
+
|
|
65
|
+
# Then ensure pipx apps are on PATH
|
|
66
|
+
pipx ensurepath
|
|
67
|
+
```
|
|
49
68
|
|
|
50
69
|
JavaScript-heavy teams can install the npm wrapper:
|
|
51
70
|
|
|
@@ -415,7 +434,7 @@ jobs:
|
|
|
415
434
|
with:
|
|
416
435
|
python-version: "3.12"
|
|
417
436
|
|
|
418
|
-
- run: pip install agentpack-cli
|
|
437
|
+
- run: python -m pip install agentpack-cli
|
|
419
438
|
|
|
420
439
|
- name: Generate context pack
|
|
421
440
|
run: |
|
|
@@ -728,7 +747,7 @@ Uses `watchdog` if installed, falls back to polling. Context is refreshed whenev
|
|
|
728
747
|
|
|
729
748
|
Install watchdog for better performance:
|
|
730
749
|
```bash
|
|
731
|
-
|
|
750
|
+
pipx inject agentpack-cli watchdog
|
|
732
751
|
```
|
|
733
752
|
|
|
734
753
|
---
|
|
@@ -750,7 +769,7 @@ Requires an initialized project (`agentpack init`). Refreshes context, prints th
|
|
|
750
769
|
Run AgentPack as an MCP server — exposes context packing as tools that Claude Code (and any MCP-compatible agent) can call directly.
|
|
751
770
|
|
|
752
771
|
```bash
|
|
753
|
-
|
|
772
|
+
pipx inject agentpack-cli "agentpack-cli[mcp]"
|
|
754
773
|
agentpack mcp
|
|
755
774
|
```
|
|
756
775
|
|
|
@@ -1425,9 +1444,9 @@ Post-0.3 release focus: broader real-repo proof, npm publish reliability, and co
|
|
|
1425
1444
|
## Optional dependencies
|
|
1426
1445
|
|
|
1427
1446
|
```bash
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1447
|
+
pipx inject agentpack-cli watchdog # faster file watching for agentpack watch
|
|
1448
|
+
pipx inject agentpack-cli "agentpack-cli[mcp]" # expose agentpack as MCP server tools
|
|
1449
|
+
pipx inject agentpack-cli "agentpack-cli[all]" # watch + mcp
|
|
1431
1450
|
```
|
|
1432
1451
|
|
|
1433
1452
|
---
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from agentpack.core.config import DEFAULT_CONFIG, CONFIG_TEMPLATE
|
|
11
|
+
from agentpack.core.ignore import DEFAULT_AGENTIGNORE
|
|
12
|
+
from agentpack.commands._shared import console, _root
|
|
13
|
+
from agentpack.integrations.agents import check_agent_integration, install_agent_integration
|
|
14
|
+
from agentpack.session.state import load_session, create_session, SESSION_FILE, TASK_FILE
|
|
15
|
+
|
|
16
|
+
_GITIGNORE_START = "# agentpack:start"
|
|
17
|
+
_GITIGNORE_END = "# agentpack:end"
|
|
18
|
+
_INIT_MODES = ("minimal", "balanced", "deep")
|
|
19
|
+
_INIT_AGENTS = ("auto", "claude", "cursor", "windsurf", "codex", "antigravity", "generic")
|
|
20
|
+
_AGENT_GITIGNORE_ENTRIES = {
|
|
21
|
+
"cursor": (".vscode/tasks.json",),
|
|
22
|
+
"windsurf": (".vscode/tasks.json",),
|
|
23
|
+
"antigravity": (".agent/skills/agentpack/", ".vscode/tasks.json", "GEMINI.md"),
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class InitResult:
|
|
29
|
+
path: str
|
|
30
|
+
action: str
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class InitHealth:
|
|
35
|
+
label: str
|
|
36
|
+
ok: bool
|
|
37
|
+
detail: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _repo_gitignore_entries(share_cache: bool = False, agent: str = "generic") -> list[str]:
|
|
41
|
+
entries = [
|
|
42
|
+
".agentpack/*",
|
|
43
|
+
"!.agentpack/config.toml",
|
|
44
|
+
]
|
|
45
|
+
if share_cache:
|
|
46
|
+
entries.extend(["!.agentpack/cache/", "!.agentpack/cache/**"])
|
|
47
|
+
else:
|
|
48
|
+
entries.append(".agentpack/cache/")
|
|
49
|
+
entries.extend(
|
|
50
|
+
[
|
|
51
|
+
".agentpack/snapshots/",
|
|
52
|
+
".agentpack/context*",
|
|
53
|
+
".agentpack/metrics.jsonl",
|
|
54
|
+
".agentpack/pack_metadata.json",
|
|
55
|
+
".agentpack/activity.log",
|
|
56
|
+
".agentpack/.gitignore",
|
|
57
|
+
".agentpack/.mcp_reminded",
|
|
58
|
+
".agentpack/session.json",
|
|
59
|
+
".agentpack/task.md",
|
|
60
|
+
".agentpack/benchmark_results.jsonl",
|
|
61
|
+
".agentignore",
|
|
62
|
+
]
|
|
63
|
+
)
|
|
64
|
+
entries.extend(_AGENT_GITIGNORE_ENTRIES.get(agent, ()))
|
|
65
|
+
return entries
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _repo_gitignore_block(share_cache: bool = False, agent: str = "generic") -> str:
|
|
69
|
+
return (
|
|
70
|
+
f"{_GITIGNORE_START}\n"
|
|
71
|
+
"# AgentPack generated context/cache (safe to ignore)\n"
|
|
72
|
+
+ "\n".join(_repo_gitignore_entries(share_cache, agent))
|
|
73
|
+
+ "\n"
|
|
74
|
+
f"{_GITIGNORE_END}\n"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _agentpack_gitignore_content(share_cache: bool = False) -> str:
|
|
79
|
+
entries = []
|
|
80
|
+
if not share_cache:
|
|
81
|
+
entries.append("cache/")
|
|
82
|
+
entries.extend(
|
|
83
|
+
[
|
|
84
|
+
"snapshots/",
|
|
85
|
+
"context.*",
|
|
86
|
+
"metrics.jsonl",
|
|
87
|
+
"pack_metadata.json",
|
|
88
|
+
"activity.log",
|
|
89
|
+
".mcp_reminded",
|
|
90
|
+
"session.json",
|
|
91
|
+
"task.md",
|
|
92
|
+
"benchmark_results.jsonl",
|
|
93
|
+
]
|
|
94
|
+
)
|
|
95
|
+
return "\n".join(entries) + "\n"
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _backup_file(path: Path) -> Path:
|
|
99
|
+
backup = path.with_name(f"{path.name}.bak")
|
|
100
|
+
index = 1
|
|
101
|
+
while backup.exists():
|
|
102
|
+
backup = path.with_name(f"{path.name}.bak.{index}")
|
|
103
|
+
index += 1
|
|
104
|
+
backup.write_text(path.read_text(encoding="utf-8"), encoding="utf-8")
|
|
105
|
+
return backup
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _write_text(root: Path, path: Path, content: str, *, force: bool = False, backups: list[InitResult] | None = None) -> str:
|
|
109
|
+
existed = path.exists()
|
|
110
|
+
if existed and path.read_text(encoding="utf-8") == content:
|
|
111
|
+
return "unchanged"
|
|
112
|
+
if force and existed and backups is not None:
|
|
113
|
+
backup = _backup_file(path)
|
|
114
|
+
backups.append(InitResult(str(backup.relative_to(root)), "created"))
|
|
115
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
116
|
+
path.write_text(content, encoding="utf-8")
|
|
117
|
+
return "updated" if existed else "created"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _patch_repo_gitignore(root: Path, share_cache: bool = False, agent: str = "generic", *, force: bool = False, backups: list[InitResult] | None = None) -> str:
|
|
121
|
+
gitignore = root / ".gitignore"
|
|
122
|
+
block = _repo_gitignore_block(share_cache, agent)
|
|
123
|
+
if not gitignore.exists():
|
|
124
|
+
gitignore.write_text(block, encoding="utf-8")
|
|
125
|
+
return "created"
|
|
126
|
+
|
|
127
|
+
content = gitignore.read_text(encoding="utf-8")
|
|
128
|
+
start = content.find(_GITIGNORE_START)
|
|
129
|
+
end = content.find(_GITIGNORE_END)
|
|
130
|
+
if start != -1 and end != -1 and end >= start:
|
|
131
|
+
end += len(_GITIGNORE_END)
|
|
132
|
+
replacement = block.rstrip()
|
|
133
|
+
updated = content[:start].rstrip() + "\n\n" + replacement + "\n" + content[end:].lstrip("\n")
|
|
134
|
+
if updated == content:
|
|
135
|
+
return "unchanged"
|
|
136
|
+
if force and backups is not None:
|
|
137
|
+
backup = _backup_file(gitignore)
|
|
138
|
+
backups.append(InitResult(str(backup.relative_to(root)), "created"))
|
|
139
|
+
gitignore.write_text(updated, encoding="utf-8")
|
|
140
|
+
return "updated"
|
|
141
|
+
|
|
142
|
+
prefix = content.rstrip() + "\n\n" if content.strip() else ""
|
|
143
|
+
if force and backups is not None:
|
|
144
|
+
backup = _backup_file(gitignore)
|
|
145
|
+
backups.append(InitResult(str(backup.relative_to(root)), "created"))
|
|
146
|
+
gitignore.write_text(prefix + block, encoding="utf-8")
|
|
147
|
+
return "updated"
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _install_agent_integration(root, agent: str) -> dict[str, str]:
|
|
151
|
+
"""Install repo-local agent integration files after `agentpack init`."""
|
|
152
|
+
return install_agent_integration(root, agent)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _print_agent_integration_results(results: dict[str, str]) -> None:
|
|
156
|
+
for key, action in results.items():
|
|
157
|
+
if action == "unchanged":
|
|
158
|
+
continue
|
|
159
|
+
if key.startswith("git:"):
|
|
160
|
+
console.print(f"[green].git/hooks/{key[4:]} {action}[/]")
|
|
161
|
+
elif key == "vscode:tasks":
|
|
162
|
+
console.print(f"[green].vscode/tasks.json {action}[/]")
|
|
163
|
+
else:
|
|
164
|
+
console.print(f"[green]{key} {action}[/]")
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _validate_init_options(agent: str, mode: str | None, budget: int) -> None:
|
|
168
|
+
if agent not in _INIT_AGENTS:
|
|
169
|
+
console.print(f"[yellow]Unknown agent: {agent}. Supported: {', '.join(_INIT_AGENTS)}[/]")
|
|
170
|
+
raise typer.Exit(1)
|
|
171
|
+
if mode is not None and mode not in _INIT_MODES:
|
|
172
|
+
console.print(f"[yellow]Unknown mode: {mode}. Supported: {', '.join(_INIT_MODES)}[/]")
|
|
173
|
+
raise typer.Exit(1)
|
|
174
|
+
if budget < 0:
|
|
175
|
+
console.print("[yellow]Budget must be 0 or greater.[/]")
|
|
176
|
+
raise typer.Exit(1)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _resolve_init_agent(root: Path, agent: str, *, force: bool = False) -> str:
|
|
180
|
+
if agent != "auto":
|
|
181
|
+
return agent
|
|
182
|
+
existing_session = load_session(root)
|
|
183
|
+
if existing_session is not None and not force:
|
|
184
|
+
return existing_session.agent
|
|
185
|
+
from agentpack.adapters.detect import detect_agent
|
|
186
|
+
|
|
187
|
+
return detect_agent(root)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _agent_integration_paths(agent: str) -> tuple[str, ...]:
|
|
191
|
+
if agent == "claude":
|
|
192
|
+
return ("CLAUDE.md", ".claude/settings.json", ".mcp.json")
|
|
193
|
+
if agent == "cursor":
|
|
194
|
+
return (".cursorrules", ".cursor/rules/agentpack.mdc", ".vscode/tasks.json")
|
|
195
|
+
if agent == "windsurf":
|
|
196
|
+
return (".windsurfrules", ".vscode/tasks.json")
|
|
197
|
+
if agent == "codex":
|
|
198
|
+
return ("AGENTS.md", ".codex/hooks.json")
|
|
199
|
+
if agent == "antigravity":
|
|
200
|
+
return ("GEMINI.md", ".vscode/tasks.json")
|
|
201
|
+
return ()
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _backup_existing_paths(root: Path, paths: tuple[str, ...], backups: list[InitResult]) -> None:
|
|
205
|
+
for rel in paths:
|
|
206
|
+
path = root / rel
|
|
207
|
+
if path.exists() and path.is_file():
|
|
208
|
+
backup = _backup_file(path)
|
|
209
|
+
backups.append(InitResult(str(backup.relative_to(root)), "created"))
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _planned_action(path: Path, expected: str | None = None) -> str:
|
|
213
|
+
if not path.exists():
|
|
214
|
+
return "create"
|
|
215
|
+
if expected is None:
|
|
216
|
+
return "update"
|
|
217
|
+
try:
|
|
218
|
+
return "unchanged" if path.read_text(encoding="utf-8") == expected else "update"
|
|
219
|
+
except OSError:
|
|
220
|
+
return "update"
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _print_dry_run(root: Path, agent: str, share_cache: bool, mode: str | None, budget: int) -> None:
|
|
224
|
+
console.print("[bold yellow]Dry run — no files will be changed.[/]\n")
|
|
225
|
+
items = [
|
|
226
|
+
InitResult(".agentpack/", "ensure"),
|
|
227
|
+
InitResult(".agentpack/snapshots/", "ensure"),
|
|
228
|
+
InitResult(".agentpack/cache/", "ensure"),
|
|
229
|
+
InitResult(".agentpack/.gitignore", _planned_action(root / ".agentpack" / ".gitignore")),
|
|
230
|
+
InitResult(".gitignore", _planned_action(root / ".gitignore")),
|
|
231
|
+
InitResult(".agentpack/config.toml", _planned_action(root / ".agentpack" / "config.toml")),
|
|
232
|
+
InitResult(".agentignore", _planned_action(root / ".agentignore", DEFAULT_AGENTIGNORE)),
|
|
233
|
+
InitResult(SESSION_FILE, _planned_action(root / SESSION_FILE)),
|
|
234
|
+
InitResult(TASK_FILE, _planned_action(root / TASK_FILE)),
|
|
235
|
+
]
|
|
236
|
+
for rel in _agent_integration_paths(agent):
|
|
237
|
+
items.append(InitResult(rel, _planned_action(root / rel)))
|
|
238
|
+
_print_init_summary("Dry Run", items)
|
|
239
|
+
console.print(f" Agent: {agent}")
|
|
240
|
+
console.print(f" Mode: {mode or DEFAULT_CONFIG.context.default_mode}")
|
|
241
|
+
console.print(f" Budget: {budget or DEFAULT_CONFIG.context.default_budget:,}")
|
|
242
|
+
console.print(f" Share cache: {'yes' if share_cache else 'no'}")
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _print_init_summary(title: str, results: list[InitResult]) -> None:
|
|
246
|
+
console.print(f"\n[bold]{title}[/]")
|
|
247
|
+
grouped: dict[str, list[str]] = {}
|
|
248
|
+
for result in results:
|
|
249
|
+
grouped.setdefault(result.action, []).append(result.path)
|
|
250
|
+
for action in ("created", "updated", "unchanged", "skipped", "ensure", "create", "update"):
|
|
251
|
+
paths = grouped.get(action)
|
|
252
|
+
if not paths:
|
|
253
|
+
continue
|
|
254
|
+
console.print(f" {action}: {', '.join(paths)}")
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _init_health(root: Path, agent: str) -> list[InitHealth]:
|
|
258
|
+
checks = [
|
|
259
|
+
InitHealth(".gitignore", (root / ".gitignore").exists(), "repo ignore file present"),
|
|
260
|
+
InitHealth(".agentpack/config.toml", (root / ".agentpack" / "config.toml").exists(), "config file present"),
|
|
261
|
+
InitHealth(".agentignore", (root / ".agentignore").exists(), "agent ignore file present"),
|
|
262
|
+
InitHealth(SESSION_FILE, (root / SESSION_FILE).exists(), "session file present"),
|
|
263
|
+
InitHealth(TASK_FILE, (root / TASK_FILE).exists(), "task file present"),
|
|
264
|
+
]
|
|
265
|
+
try:
|
|
266
|
+
from agentpack.core.config import load_config
|
|
267
|
+
|
|
268
|
+
load_config(root)
|
|
269
|
+
checks.append(InitHealth("config", True, "loads successfully"))
|
|
270
|
+
except Exception as exc:
|
|
271
|
+
checks.append(InitHealth("config", False, f"failed to load: {exc}"))
|
|
272
|
+
for check in check_agent_integration(root, agent):
|
|
273
|
+
checks.append(InitHealth(check.label, check.ok, check.detail))
|
|
274
|
+
return checks
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _print_health(root: Path, agent: str) -> None:
|
|
278
|
+
console.print("\n[bold]Init health[/]")
|
|
279
|
+
for check in _init_health(root, agent):
|
|
280
|
+
marker = "[green]✓[/]" if check.ok else "[yellow]![/]"
|
|
281
|
+
console.print(f" {marker} {check.label}: {check.detail}")
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def register(app: typer.Typer) -> None:
|
|
285
|
+
@app.command()
|
|
286
|
+
def init(
|
|
287
|
+
force: bool = typer.Option(False, "--force", help="Overwrite existing files."),
|
|
288
|
+
mode: Optional[str] = typer.Option(None, "--mode", help="Default pack mode (minimal|balanced|deep)."),
|
|
289
|
+
budget: int = typer.Option(0, "--budget", help="Default token budget (0 = keep default 25000)."),
|
|
290
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Skip interactive prompts, use defaults."),
|
|
291
|
+
silent: bool = typer.Option(False, "--silent", help="Suppress all output (for use in hooks/scripts)."),
|
|
292
|
+
share_cache: bool = typer.Option(False, "--share-cache", help="Commit summary cache to git (recommended for teams)."),
|
|
293
|
+
agent: str = typer.Option("auto", "--agent", help="Target agent (auto|claude|cursor|windsurf|codex|antigravity|generic)."),
|
|
294
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would change without writing files."),
|
|
295
|
+
health_check: bool = typer.Option(True, "--health-check/--no-health-check", help="Verify init outputs after setup."),
|
|
296
|
+
) -> None:
|
|
297
|
+
"""Initialize AgentPack in the current directory.
|
|
298
|
+
|
|
299
|
+
One-time setup. After this, just run `agentpack watch` — no other commands needed.
|
|
300
|
+
"""
|
|
301
|
+
if silent:
|
|
302
|
+
yes = True
|
|
303
|
+
console.quiet = True
|
|
304
|
+
_validate_init_options(agent, mode, budget)
|
|
305
|
+
root = _root()
|
|
306
|
+
resolved_agent = _resolve_init_agent(root, agent, force=force)
|
|
307
|
+
if dry_run:
|
|
308
|
+
_print_dry_run(root, resolved_agent, share_cache, mode, budget)
|
|
309
|
+
return
|
|
310
|
+
|
|
311
|
+
results: list[InitResult] = []
|
|
312
|
+
backups: list[InitResult] = []
|
|
313
|
+
agentpack_dir = root / ".agentpack"
|
|
314
|
+
agentpack_existed = agentpack_dir.exists()
|
|
315
|
+
snapshots_existed = (agentpack_dir / "snapshots").exists()
|
|
316
|
+
cache_existed = (agentpack_dir / "cache").exists()
|
|
317
|
+
agentpack_dir.mkdir(exist_ok=True)
|
|
318
|
+
(agentpack_dir / "snapshots").mkdir(exist_ok=True)
|
|
319
|
+
(agentpack_dir / "cache").mkdir(exist_ok=True)
|
|
320
|
+
results.extend(
|
|
321
|
+
[
|
|
322
|
+
InitResult(".agentpack/", "unchanged" if agentpack_existed else "created"),
|
|
323
|
+
InitResult(".agentpack/snapshots/", "unchanged" if snapshots_existed else "created"),
|
|
324
|
+
InitResult(".agentpack/cache/", "unchanged" if cache_existed else "created"),
|
|
325
|
+
]
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
gitignore = agentpack_dir / ".gitignore"
|
|
329
|
+
if not gitignore.exists() or force:
|
|
330
|
+
action = _write_text(root, gitignore, _agentpack_gitignore_content(share_cache), force=force, backups=backups)
|
|
331
|
+
results.append(InitResult(".agentpack/.gitignore", action))
|
|
332
|
+
else:
|
|
333
|
+
results.append(InitResult(".agentpack/.gitignore", "unchanged"))
|
|
334
|
+
|
|
335
|
+
gitignore_action = _patch_repo_gitignore(root, share_cache=share_cache, agent=resolved_agent, force=force, backups=backups)
|
|
336
|
+
results.append(InitResult(".gitignore", gitignore_action))
|
|
337
|
+
|
|
338
|
+
config_path_file = agentpack_dir / "config.toml"
|
|
339
|
+
if not config_path_file.exists() or force:
|
|
340
|
+
cfg = DEFAULT_CONFIG.model_copy(deep=True)
|
|
341
|
+
|
|
342
|
+
# Interactive mode selection
|
|
343
|
+
if not yes and mode is None and sys.stdin.isatty():
|
|
344
|
+
console.print("\n[bold]Choose default pack mode:[/]")
|
|
345
|
+
console.print(" [cyan]1[/] minimal — changed files + configs only (fastest, fewest tokens)")
|
|
346
|
+
console.print(" [cyan]2[/] balanced — + deps, tests, summaries [bold](recommended)[/]")
|
|
347
|
+
console.print(" [cyan]3[/] deep — + docs, more full files (most context)")
|
|
348
|
+
choice = typer.prompt("Mode", default="2")
|
|
349
|
+
mode_map = {"1": "minimal", "2": "balanced", "3": "deep",
|
|
350
|
+
"minimal": "minimal", "balanced": "balanced", "deep": "deep"}
|
|
351
|
+
cfg.context.default_mode = mode_map.get(choice.strip(), "balanced")
|
|
352
|
+
elif mode in ("minimal", "balanced", "deep"):
|
|
353
|
+
cfg.context.default_mode = mode
|
|
354
|
+
|
|
355
|
+
if budget > 0:
|
|
356
|
+
cfg.context.default_budget = budget
|
|
357
|
+
|
|
358
|
+
config_toml = CONFIG_TEMPLATE.replace(
|
|
359
|
+
'default_mode = "balanced"',
|
|
360
|
+
f'default_mode = "{cfg.context.default_mode}"',
|
|
361
|
+
)
|
|
362
|
+
if budget > 0:
|
|
363
|
+
config_toml = config_toml.replace(
|
|
364
|
+
"default_budget = 25000",
|
|
365
|
+
f"default_budget = {cfg.context.default_budget}",
|
|
366
|
+
)
|
|
367
|
+
action = _write_text(root, config_path_file, config_toml, force=force, backups=backups)
|
|
368
|
+
results.append(InitResult(".agentpack/config.toml", action))
|
|
369
|
+
else:
|
|
370
|
+
results.append(InitResult(".agentpack/config.toml", "unchanged"))
|
|
371
|
+
|
|
372
|
+
ignore_path = root / ".agentignore"
|
|
373
|
+
if not ignore_path.exists() or force:
|
|
374
|
+
action = _write_text(root, ignore_path, DEFAULT_AGENTIGNORE, force=force, backups=backups)
|
|
375
|
+
results.append(InitResult(".agentignore", action))
|
|
376
|
+
else:
|
|
377
|
+
results.append(InitResult(".agentignore", "unchanged"))
|
|
378
|
+
|
|
379
|
+
# Bootstrap session so `agentpack watch` works immediately — no separate `session start` needed
|
|
380
|
+
from agentpack.core.config import load_config
|
|
381
|
+
resolved_mode = load_config(root).context.default_mode
|
|
382
|
+
existing_session = load_session(root)
|
|
383
|
+
if existing_session is None or force:
|
|
384
|
+
create_session(root, agent=resolved_agent, mode=resolved_mode)
|
|
385
|
+
results.append(InitResult(SESSION_FILE, "created" if existing_session is None else "updated"))
|
|
386
|
+
results.append(InitResult(TASK_FILE, "created" if existing_session is None else "updated"))
|
|
387
|
+
else:
|
|
388
|
+
results.append(InitResult(SESSION_FILE, "unchanged"))
|
|
389
|
+
results.append(InitResult(TASK_FILE, "unchanged"))
|
|
390
|
+
|
|
391
|
+
if force:
|
|
392
|
+
_backup_existing_paths(root, _agent_integration_paths(resolved_agent), backups)
|
|
393
|
+
integration_results = _install_agent_integration(root, resolved_agent)
|
|
394
|
+
for key, action in integration_results.items():
|
|
395
|
+
if key.startswith("git:"):
|
|
396
|
+
results.append(InitResult(f".git/hooks/{key[4:]}", action))
|
|
397
|
+
elif key == "vscode:tasks":
|
|
398
|
+
results.append(InitResult(".vscode/tasks.json", action))
|
|
399
|
+
else:
|
|
400
|
+
results.append(InitResult(key, action))
|
|
401
|
+
|
|
402
|
+
if backups:
|
|
403
|
+
_print_init_summary("Backups", backups)
|
|
404
|
+
_print_init_summary("Init Summary", results)
|
|
405
|
+
if health_check:
|
|
406
|
+
_print_health(root, resolved_agent)
|
|
407
|
+
console.print(f"\n[bold green]AgentPack initialized.[/] [dim]agent={resolved_agent} mode={resolved_mode}[/]")
|
|
408
|
+
console.print("Run [bold]agentpack watch[/] to start auto-refreshing context.")
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import sys
|
|
4
3
|
from pathlib import Path
|
|
5
4
|
|
|
6
5
|
import typer
|
|
@@ -129,16 +128,12 @@ def register(app: typer.Typer) -> None:
|
|
|
129
128
|
if result.returncode == 0:
|
|
130
129
|
console.print("[green]agentpack installed globally.[/] Available as `agentpack` in any shell.")
|
|
131
130
|
else:
|
|
132
|
-
console.print("[
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
)
|
|
138
|
-
if result2.returncode != 0:
|
|
139
|
-
console.print(f"[red]Install failed:[/] {result2.stderr[:200]}")
|
|
140
|
-
raise typer.Exit(1)
|
|
141
|
-
console.print("[green]Installed via pip --user.[/]")
|
|
131
|
+
console.print("[red]pipx install failed.[/]")
|
|
132
|
+
console.print(result.stderr[:300])
|
|
133
|
+
console.print("Install pipx with your OS package manager, then retry: [bold]pipx ensurepath && pipx install agentpack-cli[/]")
|
|
134
|
+
console.print("Examples: [bold]brew install pipx[/], [bold]sudo apt install pipx[/], [bold]sudo dnf install pipx[/], [bold]sudo pacman -S python-pipx[/].")
|
|
135
|
+
console.print("Avoid global [bold]pip3 install[/] on system-managed Python; PEP 668 may block it.")
|
|
136
|
+
raise typer.Exit(1)
|
|
142
137
|
elif pipx and dry_run:
|
|
143
138
|
console.print("[dim]Would run: pipx install agentpack-cli[/]")
|
|
144
139
|
|
|
@@ -259,7 +259,7 @@ def _pack_watch(
|
|
|
259
259
|
from watchdog.events import FileSystemEventHandler
|
|
260
260
|
except ImportError:
|
|
261
261
|
console.print("[red]watchdog is required for --watch mode.[/]")
|
|
262
|
-
console.print("Install it: [bold]
|
|
262
|
+
console.print("Install it: [bold]pipx inject agentpack-cli watchdog[/]")
|
|
263
263
|
raise typer.Exit(1)
|
|
264
264
|
|
|
265
265
|
root = _root()
|
|
@@ -82,7 +82,7 @@ Then read `.agentpack/context.claude.md` in full.
|
|
|
82
82
|
### Step 1: Check agentpack is installed
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
|
-
agentpack --help 2>/dev/null ||
|
|
85
|
+
agentpack --help 2>/dev/null || pipx install agentpack-cli
|
|
86
86
|
```
|
|
87
87
|
|
|
88
88
|
### Step 2: Initialize if not already done
|
|
@@ -419,7 +419,7 @@ def serve() -> None:
|
|
|
419
419
|
except ImportError:
|
|
420
420
|
print(
|
|
421
421
|
"mcp package required for MCP server. "
|
|
422
|
-
"Install:
|
|
422
|
+
"Install: pipx inject agentpack-cli 'agentpack-cli[mcp]'",
|
|
423
423
|
file=sys.stderr,
|
|
424
424
|
)
|
|
425
425
|
sys.exit(1)
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import sys
|
|
4
|
-
from typing import Optional
|
|
5
|
-
|
|
6
|
-
import typer
|
|
7
|
-
|
|
8
|
-
from agentpack.core.config import DEFAULT_CONFIG, CONFIG_TEMPLATE
|
|
9
|
-
from agentpack.core.ignore import DEFAULT_AGENTIGNORE
|
|
10
|
-
from agentpack.commands._shared import console, _root
|
|
11
|
-
from agentpack.integrations.agents import install_agent_integration
|
|
12
|
-
from agentpack.session.state import load_session, create_session, SESSION_FILE, TASK_FILE
|
|
13
|
-
|
|
14
|
-
_GITIGNORE_START = "# agentpack:start"
|
|
15
|
-
_GITIGNORE_END = "# agentpack:end"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def _repo_gitignore_block(share_cache: bool = False) -> str:
|
|
19
|
-
cache_line = "" if share_cache else ".agentpack/cache/\n"
|
|
20
|
-
return (
|
|
21
|
-
f"{_GITIGNORE_START}\n"
|
|
22
|
-
"# AgentPack generated context/cache (safe to ignore)\n"
|
|
23
|
-
f"{cache_line}"
|
|
24
|
-
".agentpack/snapshots/\n"
|
|
25
|
-
".agentpack/context*\n"
|
|
26
|
-
".agentpack/metrics.jsonl\n"
|
|
27
|
-
".agentpack/pack_metadata.json\n"
|
|
28
|
-
".agentpack/activity.log\n"
|
|
29
|
-
".agentpack/.gitignore\n"
|
|
30
|
-
".agentpack/.mcp_reminded\n"
|
|
31
|
-
".agentpack/session.json\n"
|
|
32
|
-
".agentpack/task.md\n"
|
|
33
|
-
".agentpack/benchmark_results.jsonl\n"
|
|
34
|
-
".agent/skills/agentpack/\n"
|
|
35
|
-
f"{_GITIGNORE_END}\n"
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def _patch_repo_gitignore(root, share_cache: bool = False) -> str:
|
|
40
|
-
gitignore = root / ".gitignore"
|
|
41
|
-
block = _repo_gitignore_block(share_cache)
|
|
42
|
-
if not gitignore.exists():
|
|
43
|
-
gitignore.write_text(block, encoding="utf-8")
|
|
44
|
-
return "created"
|
|
45
|
-
|
|
46
|
-
content = gitignore.read_text(encoding="utf-8")
|
|
47
|
-
start = content.find(_GITIGNORE_START)
|
|
48
|
-
end = content.find(_GITIGNORE_END)
|
|
49
|
-
if start != -1 and end != -1 and end >= start:
|
|
50
|
-
end += len(_GITIGNORE_END)
|
|
51
|
-
replacement = block.rstrip()
|
|
52
|
-
updated = content[:start].rstrip() + "\n\n" + replacement + "\n" + content[end:].lstrip("\n")
|
|
53
|
-
if updated == content:
|
|
54
|
-
return "unchanged"
|
|
55
|
-
gitignore.write_text(updated, encoding="utf-8")
|
|
56
|
-
return "updated"
|
|
57
|
-
|
|
58
|
-
prefix = content.rstrip() + "\n\n" if content.strip() else ""
|
|
59
|
-
gitignore.write_text(prefix + block, encoding="utf-8")
|
|
60
|
-
return "updated"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def _install_agent_integration(root, agent: str) -> dict[str, str]:
|
|
64
|
-
"""Install repo-local agent integration files after `agentpack init`."""
|
|
65
|
-
return install_agent_integration(root, agent)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
def _print_agent_integration_results(results: dict[str, str]) -> None:
|
|
69
|
-
for key, action in results.items():
|
|
70
|
-
if action == "unchanged":
|
|
71
|
-
continue
|
|
72
|
-
if key.startswith("git:"):
|
|
73
|
-
console.print(f"[green].git/hooks/{key[4:]} {action}[/]")
|
|
74
|
-
elif key == "vscode:tasks":
|
|
75
|
-
console.print(f"[green].vscode/tasks.json {action}[/]")
|
|
76
|
-
else:
|
|
77
|
-
console.print(f"[green]{key} {action}[/]")
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def register(app: typer.Typer) -> None:
|
|
81
|
-
@app.command()
|
|
82
|
-
def init(
|
|
83
|
-
force: bool = typer.Option(False, "--force", help="Overwrite existing files."),
|
|
84
|
-
mode: Optional[str] = typer.Option(None, "--mode", help="Default pack mode (minimal|balanced|deep)."),
|
|
85
|
-
budget: int = typer.Option(0, "--budget", help="Default token budget (0 = keep default 25000)."),
|
|
86
|
-
yes: bool = typer.Option(False, "--yes", "-y", help="Skip interactive prompts, use defaults."),
|
|
87
|
-
silent: bool = typer.Option(False, "--silent", help="Suppress all output (for use in hooks/scripts)."),
|
|
88
|
-
share_cache: bool = typer.Option(False, "--share-cache", help="Commit summary cache to git (recommended for teams)."),
|
|
89
|
-
agent: str = typer.Option("auto", "--agent", help="Target agent (auto|claude|cursor|windsurf|codex|antigravity|generic)."),
|
|
90
|
-
) -> None:
|
|
91
|
-
"""Initialize AgentPack in the current directory.
|
|
92
|
-
|
|
93
|
-
One-time setup. After this, just run `agentpack watch` — no other commands needed.
|
|
94
|
-
"""
|
|
95
|
-
if silent:
|
|
96
|
-
yes = True
|
|
97
|
-
console.quiet = True
|
|
98
|
-
root = _root()
|
|
99
|
-
agentpack_dir = root / ".agentpack"
|
|
100
|
-
agentpack_dir.mkdir(exist_ok=True)
|
|
101
|
-
(agentpack_dir / "snapshots").mkdir(exist_ok=True)
|
|
102
|
-
(agentpack_dir / "cache").mkdir(exist_ok=True)
|
|
103
|
-
|
|
104
|
-
gitignore = agentpack_dir / ".gitignore"
|
|
105
|
-
if not gitignore.exists() or force:
|
|
106
|
-
# With --share-cache, cache/ is committed so teammates skip the summarize step
|
|
107
|
-
cache_line = "" if share_cache else ".agentpack/cache/\n"
|
|
108
|
-
gitignore.write_text(
|
|
109
|
-
f"{cache_line}.agentpack/snapshots/\n.agentpack/context.*\n.agentpack/metrics.jsonl\n"
|
|
110
|
-
)
|
|
111
|
-
console.print("[green]Created[/] .agentpack/.gitignore")
|
|
112
|
-
if share_cache:
|
|
113
|
-
console.print(" [dim]cache/ not gitignored — commit it so teammates skip agentpack summarize[/]")
|
|
114
|
-
else:
|
|
115
|
-
console.print("[dim]Skipped[/] .agentpack/.gitignore (exists)")
|
|
116
|
-
|
|
117
|
-
gitignore_action = _patch_repo_gitignore(root, share_cache=share_cache)
|
|
118
|
-
if gitignore_action == "created":
|
|
119
|
-
console.print("[green]Created[/] .gitignore [dim](AgentPack generated artifacts ignored)[/]")
|
|
120
|
-
elif gitignore_action == "updated":
|
|
121
|
-
console.print("[green]Updated[/] .gitignore [dim](AgentPack generated artifacts ignored)[/]")
|
|
122
|
-
else:
|
|
123
|
-
console.print("[dim]Skipped[/] .gitignore (AgentPack block unchanged)")
|
|
124
|
-
|
|
125
|
-
config_path_file = agentpack_dir / "config.toml"
|
|
126
|
-
if not config_path_file.exists() or force:
|
|
127
|
-
cfg = DEFAULT_CONFIG.model_copy(deep=True)
|
|
128
|
-
|
|
129
|
-
# Interactive mode selection
|
|
130
|
-
if not yes and mode is None and sys.stdin.isatty():
|
|
131
|
-
console.print("\n[bold]Choose default pack mode:[/]")
|
|
132
|
-
console.print(" [cyan]1[/] minimal — changed files + configs only (fastest, fewest tokens)")
|
|
133
|
-
console.print(" [cyan]2[/] balanced — + deps, tests, summaries [bold](recommended)[/]")
|
|
134
|
-
console.print(" [cyan]3[/] deep — + docs, more full files (most context)")
|
|
135
|
-
choice = typer.prompt("Mode", default="2")
|
|
136
|
-
mode_map = {"1": "minimal", "2": "balanced", "3": "deep",
|
|
137
|
-
"minimal": "minimal", "balanced": "balanced", "deep": "deep"}
|
|
138
|
-
cfg.context.default_mode = mode_map.get(choice.strip(), "balanced")
|
|
139
|
-
elif mode in ("minimal", "balanced", "deep"):
|
|
140
|
-
cfg.context.default_mode = mode
|
|
141
|
-
|
|
142
|
-
if budget > 0:
|
|
143
|
-
cfg.context.default_budget = budget
|
|
144
|
-
|
|
145
|
-
config_toml = CONFIG_TEMPLATE.replace(
|
|
146
|
-
'default_mode = "balanced"',
|
|
147
|
-
f'default_mode = "{cfg.context.default_mode}"',
|
|
148
|
-
)
|
|
149
|
-
if budget > 0:
|
|
150
|
-
config_toml = config_toml.replace(
|
|
151
|
-
"default_budget = 25000",
|
|
152
|
-
f"default_budget = {cfg.context.default_budget}",
|
|
153
|
-
)
|
|
154
|
-
config_path_file.parent.mkdir(parents=True, exist_ok=True)
|
|
155
|
-
config_path_file.write_text(config_toml)
|
|
156
|
-
console.print(f"[green]Created[/] .agentpack/config.toml [dim](mode: {cfg.context.default_mode}, budget: {cfg.context.default_budget:,})[/]")
|
|
157
|
-
else:
|
|
158
|
-
console.print("[dim]Skipped[/] .agentpack/config.toml (exists)")
|
|
159
|
-
|
|
160
|
-
ignore_path = root / ".agentignore"
|
|
161
|
-
if not ignore_path.exists() or force:
|
|
162
|
-
ignore_path.write_text(DEFAULT_AGENTIGNORE)
|
|
163
|
-
console.print("[green]Created[/] .agentignore")
|
|
164
|
-
else:
|
|
165
|
-
console.print("[dim]Skipped[/] .agentignore (exists)")
|
|
166
|
-
|
|
167
|
-
# Bootstrap session so `agentpack watch` works immediately — no separate `session start` needed
|
|
168
|
-
from agentpack.core.config import load_config
|
|
169
|
-
resolved_mode = load_config(root).context.default_mode
|
|
170
|
-
existing_session = load_session(root)
|
|
171
|
-
resolved_agent = agent
|
|
172
|
-
if existing_session is None or force:
|
|
173
|
-
from agentpack.adapters.detect import detect_agent
|
|
174
|
-
resolved_agent = agent if agent != "auto" else detect_agent(root)
|
|
175
|
-
create_session(root, agent=resolved_agent, mode=resolved_mode)
|
|
176
|
-
console.print(f"[green]Created[/] {SESSION_FILE} [dim]agent={resolved_agent} mode={resolved_mode}[/]")
|
|
177
|
-
console.print(f"[green]Created[/] {TASK_FILE} [dim]edit to set your task[/]")
|
|
178
|
-
else:
|
|
179
|
-
console.print(f"[dim]Skipped[/] {SESSION_FILE} (exists)")
|
|
180
|
-
if agent == "auto":
|
|
181
|
-
resolved_agent = existing_session.agent
|
|
182
|
-
|
|
183
|
-
_print_agent_integration_results(_install_agent_integration(root, resolved_agent))
|
|
184
|
-
|
|
185
|
-
console.print("\n[bold green]AgentPack initialized.[/]")
|
|
186
|
-
console.print("Run [bold]agentpack watch[/] to start auto-refreshing context.")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|