atk-cli 0.2.0__tar.gz → 0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. {atk_cli-0.2.0 → atk_cli-0.2.1}/PKG-INFO +1 -1
  2. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/ROADMAP.md +5 -0
  3. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/backlog.md +2 -3
  4. atk_cli-0.2.1/docs/phases/phase-11-git-sync.md +88 -0
  5. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/specs/commands-spec.md +37 -0
  6. atk_cli-0.2.1/docs/specs/git-sync-spec.md +156 -0
  7. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/specs/home-spec.md +3 -0
  8. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/add.py +25 -7
  9. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/cli.py +32 -4
  10. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/commands/status.py +55 -0
  11. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/git.py +218 -0
  12. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/init.py +1 -1
  13. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/manifest_schema.py +4 -0
  14. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/remove.py +5 -2
  15. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/upgrade.py +3 -1
  16. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_add.py +48 -0
  17. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_git.py +303 -0
  18. atk_cli-0.2.1/tests/test_git_proxy.py +72 -0
  19. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_manifest_schema.py +19 -0
  20. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_mcp.py +2 -2
  21. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_plug.py +0 -2
  22. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_remove.py +47 -0
  23. atk_cli-0.2.1/tests/test_repo_status.py +109 -0
  24. {atk_cli-0.2.0 → atk_cli-0.2.1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  25. {atk_cli-0.2.0 → atk_cli-0.2.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  26. {atk_cli-0.2.0 → atk_cli-0.2.1}/.github/pull_request_template.md +0 -0
  27. {atk_cli-0.2.0 → atk_cli-0.2.1}/.github/workflows/ci.yml +0 -0
  28. {atk_cli-0.2.0 → atk_cli-0.2.1}/.github/workflows/publish.yml +0 -0
  29. {atk_cli-0.2.0 → atk_cli-0.2.1}/.gitignore +0 -0
  30. {atk_cli-0.2.0 → atk_cli-0.2.1}/CODE_OF_CONDUCT.md +0 -0
  31. {atk_cli-0.2.0 → atk_cli-0.2.1}/CONTRIBUTING.md +0 -0
  32. {atk_cli-0.2.0 → atk_cli-0.2.1}/LICENSE +0 -0
  33. {atk_cli-0.2.0 → atk_cli-0.2.1}/Makefile +0 -0
  34. {atk_cli-0.2.0 → atk_cli-0.2.1}/README.md +0 -0
  35. {atk_cli-0.2.0 → atk_cli-0.2.1}/SECURITY.md +0 -0
  36. {atk_cli-0.2.0 → atk_cli-0.2.1}/assets/demo-hero.gif +0 -0
  37. {atk_cli-0.2.0 → atk_cli-0.2.1}/assets/demo-search.gif +0 -0
  38. {atk_cli-0.2.0 → atk_cli-0.2.1}/assets/demo-status.gif +0 -0
  39. {atk_cli-0.2.0 → atk_cli-0.2.1}/assets/logo.png +0 -0
  40. {atk_cli-0.2.0 → atk_cli-0.2.1}/assets/tapes/demo-hero.tape +0 -0
  41. {atk_cli-0.2.0 → atk_cli-0.2.1}/assets/tapes/demo-search.tape +0 -0
  42. {atk_cli-0.2.0 → atk_cli-0.2.1}/assets/tapes/demo-status.tape +0 -0
  43. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/README.md +0 -0
  44. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/legacy/backup-feature-spec.md +0 -0
  45. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/legacy/cli-architecture.md +0 -0
  46. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/legacy/service-yaml-spec.md +0 -0
  47. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/phases/phase-1-core-cli.md +0 -0
  48. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/phases/phase-2-lifecycle.md +0 -0
  49. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/phases/phase-3-configuration.md +0 -0
  50. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/phases/phase-4-plugin-sources.md +0 -0
  51. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/specs/atk-spec.md +0 -0
  52. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/specs/mcp-agent-configure-spec.md +0 -0
  53. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/specs/plugin-schema.md +0 -0
  54. {atk_cli-0.2.0 → atk_cli-0.2.1}/docs/specs/registry-spec.md +0 -0
  55. {atk_cli-0.2.0 → atk_cli-0.2.1}/pyproject.toml +0 -0
  56. {atk_cli-0.2.0 → atk_cli-0.2.1}/skills/create-atk-plugin/SKILL.md +0 -0
  57. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/__init__.py +0 -0
  58. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/agents/__init__.py +0 -0
  59. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/agents/auggie_skill.py +0 -0
  60. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/agents/claude_skill.py +0 -0
  61. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/agents/codex_skill.py +0 -0
  62. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/agents/gemini_skill.py +0 -0
  63. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/agents/managed_section.py +0 -0
  64. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/agents/opencode_skill.py +0 -0
  65. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/agents/symlink_skill.py +0 -0
  66. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/banner.py +0 -0
  67. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/bootstrap.py +0 -0
  68. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/cli_logger.py +0 -0
  69. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/commands/__init__.py +0 -0
  70. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/commands/lifecycle.py +0 -0
  71. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/commands/mcp.py +0 -0
  72. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/commands/plug.py +0 -0
  73. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/commands/preconditions.py +0 -0
  74. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/commands/run.py +0 -0
  75. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/commands/search.py +0 -0
  76. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/commands/upgrade.py +0 -0
  77. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/env.py +0 -0
  78. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/errors.py +0 -0
  79. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/exit_codes.py +0 -0
  80. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/fetch.py +0 -0
  81. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/git_source.py +0 -0
  82. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/home.py +0 -0
  83. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/lifecycle.py +0 -0
  84. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/mcp.py +0 -0
  85. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/mcp_agents.py +0 -0
  86. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/mcp_configure.py +0 -0
  87. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/plugin.py +0 -0
  88. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/plugin_schema.py +0 -0
  89. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/registry.py +0 -0
  90. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/registry_schema.py +0 -0
  91. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/sanitize.py +0 -0
  92. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/setup.py +0 -0
  93. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/source.py +0 -0
  94. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/update_check.py +0 -0
  95. {atk_cli-0.2.0 → atk_cli-0.2.1}/src/atk/validation.py +0 -0
  96. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/__init__.py +0 -0
  97. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/agents/__init__.py +0 -0
  98. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/agents/test_auggie_skill.py +0 -0
  99. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/agents/test_claude_skill.py +0 -0
  100. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/agents/test_codex_skill.py +0 -0
  101. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/agents/test_gemini_skill.py +0 -0
  102. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/agents/test_opencode_skill.py +0 -0
  103. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/agents/test_symlink_skill.py +0 -0
  104. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/commands/__init__.py +0 -0
  105. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/commands/test_cli.py +0 -0
  106. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/commands/test_search.py +0 -0
  107. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/commands/test_status.py +0 -0
  108. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/conftest.py +0 -0
  109. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/full-plugin/README.md +0 -0
  110. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/full-plugin/SKILL.md +0 -0
  111. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/full-plugin/install.sh +0 -0
  112. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/full-plugin/mcp-server.sh +0 -0
  113. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/full-plugin/plugin.yaml +0 -0
  114. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/full-plugin/start.sh +0 -0
  115. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/full-plugin/status.sh +0 -0
  116. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/full-plugin/stop.sh +0 -0
  117. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/invalid-plugin/plugin.yaml +0 -0
  118. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/minimal-plugin/plugin.yaml +0 -0
  119. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/skill-only-plugin/SKILL.md +0 -0
  120. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/fixtures/plugins/skill-only-plugin/plugin.yaml +0 -0
  121. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_env.py +0 -0
  122. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_errors.py +0 -0
  123. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_git_source.py +0 -0
  124. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_home.py +0 -0
  125. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_init.py +0 -0
  126. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_lifecycle.py +0 -0
  127. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_plugin.py +0 -0
  128. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_plugin_schema.py +0 -0
  129. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_registry.py +0 -0
  130. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_run.py +0 -0
  131. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_sanitize.py +0 -0
  132. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_setup.py +0 -0
  133. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_source.py +0 -0
  134. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_update_check.py +0 -0
  135. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_upgrade.py +0 -0
  136. {atk_cli-0.2.0 → atk_cli-0.2.1}/tests/test_version.py +0 -0
  137. {atk_cli-0.2.0 → atk_cli-0.2.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atk-cli
3
- Version: 0.2.0
3
+ Version: 0.2.1
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
@@ -25,6 +25,7 @@
25
25
  | 8 | MCP Management | ✅ | Auto-install to Claude Code, Codex, Gemini, etc. |
26
26
  | 9 | Plug/Unplug | ✅ | `atk plug`/`unplug` — unified agent wiring |
27
27
  | 10 | Data Backup | ⏳ | Backup/restore plugin data |
28
+ | 11 | Git Sync | 🔄 | Remote sync, auto-push, repo status |
28
29
 
29
30
  ## Phase Summaries
30
31
 
@@ -83,6 +84,10 @@ Deprecates `atk mcp add`/`atk mcp remove`. `atk mcp <plugin>` reverts to its ori
83
84
 
84
85
  Backup and restore plugin data (databases, state files).
85
86
 
87
+ ### Phase 11: Git Sync 🔄
88
+
89
+ `atk git` proxy command for managing the .atk repository without navigating to it. `auto_push` config parameter for automatic push after mutations. Enhanced `atk status` with repository section showing branch, remote, sync state, and working directory status.
90
+
86
91
  ---
87
92
 
88
93
  ## Navigation
@@ -8,10 +8,9 @@ This document collects ideas, deferred features, and future enhancements. Items
8
8
  For the master plan, see `ROADMAP.md`.
9
9
  ---
10
10
 
11
- ## Git push
11
+ ## ~~Git push~~ -> Promoted to Phase 11
12
12
 
13
- For .atk registries with remote repositories, enable a config parameter alongside with auto_commit and auto_push to automatically push changes to the remote.
14
- also add command to pull changes from the remote. just a proxy for git, but useful so that the user does not need to navigate to the .atk directory to pull/push changes.
13
+ Addressed by Phase 11: Git Sync. `atk git` proxy command replaces dedicated push/pull commands. `auto_push` config parameter enables automatic push after mutations. `atk status` shows repository state. See `ROADMAP.md` Phase 11 and `specs/git-sync-spec.md`.
15
14
  ---
16
15
  ## ~~Manage Agents.md and other prompts through atk~~ → Promoted to Phase 9
17
16
 
@@ -0,0 +1,88 @@
1
+ # Phase 11: Git Sync
2
+
3
+ > **Status**: In Progress
4
+ > **Last Updated**: 2026-04-14
5
+
6
+ Remote synchronization for ATK Home. Users can add a git remote, auto-push after mutations, and see repository state in `atk status`.
7
+
8
+ ## Goals
9
+
10
+ 1. Users can manage the .atk git repo without navigating to the directory
11
+ 2. Mutations can automatically push to a remote after committing
12
+ 3. `atk status` shows the git repository state alongside plugin status
13
+
14
+ ---
15
+
16
+ ## Scenarios
17
+
18
+ ### Scenario 1: Git Proxy
19
+
20
+ **User story:** I want to manage my .atk repo's git remote without `cd ~/.atk`.
21
+
22
+ **Flow:**
23
+ 1. `atk git remote add origin git@github.com:user/dotfiles-atk.git`
24
+ 2. ATK runs `git remote add origin ...` in ATK_HOME
25
+ 3. Git output passed through to terminal
26
+
27
+ **Edge cases:**
28
+ - ATK Home not initialized -> exit 3
29
+ - Git not available -> exit 7
30
+ - Invalid git arguments -> git's own error, git's exit code
31
+
32
+ ### Scenario 2: Auto-Push After Mutation
33
+
34
+ **User story:** I want my plugin changes to automatically sync to my remote.
35
+
36
+ **Flow:**
37
+ 1. Edit `manifest.yaml` to set `auto_push: true`
38
+ 2. `atk add openmemory`
39
+ 3. ATK adds plugin, commits (auto_commit), pushes (auto_push)
40
+ 4. Remote now has the latest state
41
+
42
+ **Edge cases:**
43
+ - No remote configured -> warn, don't error
44
+ - Push fails (auth, network) -> warn, don't error; commit already succeeded
45
+ - `auto_commit: false` with `auto_push: true` -> no push (push requires commit)
46
+
47
+ ### Scenario 3: Repository Status
48
+
49
+ **User story:** I want to see if my .atk repo is in sync with the remote.
50
+
51
+ **Flow:**
52
+ 1. `atk status`
53
+ 2. ATK shows plugin table (existing behavior)
54
+ 3. ATK shows Repository section: branch, remote, ahead/behind, last commit, working dir
55
+
56
+ **Edge cases:**
57
+ - No remote -> show `(none)` for remote, omit sync line
58
+ - No tracking branch -> show `(no tracking branch)` for sync
59
+ - Empty repo (no commits) -> gracefully degrade, show what's available
60
+ - Git queries fail -> skip failing fields, show what's available
61
+
62
+ ---
63
+
64
+ ## Tasks
65
+
66
+ - [ ] Write git-sync-spec.md
67
+ - [ ] Update ROADMAP.md with Phase 11
68
+ - [ ] Update commands-spec.md with `atk git` command
69
+ - [ ] Update home-spec.md with `auto_push` in manifest schema
70
+ - [ ] Update backlog.md — promote "Git push" item
71
+ - [ ] Add git helper functions to `git.py` (push, branch, remote info, ahead/behind, working dir status)
72
+ - [ ] Add `auto_push` field to `ConfigSection` in `manifest_schema.py`
73
+ - [ ] Add `atk git` proxy command to `cli.py`
74
+ - [ ] Add repository section to `atk status` output
75
+ - [ ] Wire auto-push into `add.py`, `remove.py`, `upgrade.py`
76
+ - [ ] Update initial manifest in `init.py` with `auto_push: false`
77
+ - [ ] Tests for all new functions and behaviors
78
+
79
+ ## Acceptance Criteria
80
+
81
+ - `atk git remote add origin <url>` successfully adds a remote to .atk
82
+ - `atk git push` pushes .atk commits to the remote
83
+ - `atk git log --oneline` shows .atk commit history
84
+ - Setting `auto_push: true` in manifest causes mutations to push after commit
85
+ - Auto-push failure warns but does not fail the mutation
86
+ - `atk status` shows Repository section with branch, remote, sync, last commit, working dir
87
+ - `atk status` with no remote shows `(none)` gracefully
88
+ - `make check` passes (ruff + mypy + pytest)
@@ -62,6 +62,7 @@ flowchart TB
62
62
  plug[atk plug]
63
63
  mcp[atk mcp]
64
64
  help[atk help]
65
+ git[atk git]
65
66
  end
66
67
 
67
68
  init --> add
@@ -711,6 +712,42 @@ Continue? [y/N]:
711
712
 
712
713
  ---
713
714
 
715
+ ## `atk git [args...]`
716
+
717
+ Run git commands in ATK Home. Thin proxy — passes all arguments to `git` executed in ATK_HOME.
718
+
719
+ **Arguments:**
720
+ - `[args...]`: Any git arguments (passed through verbatim)
721
+
722
+ **Usage:**
723
+ ```bash
724
+ atk git remote add origin git@github.com:user/dotfiles-atk.git
725
+ atk git remote show origin
726
+ atk git push
727
+ atk git pull
728
+ atk git log --oneline -5
729
+ atk git status
730
+ atk git diff
731
+ ```
732
+
733
+ **Behavior:**
734
+ 1. Validate ATK Home is initialized (exit 3 if not)
735
+ 2. Require git available (exit 7 if not)
736
+ 3. Execute `git <args>` in ATK_HOME with stdin/stdout/stderr passed through
737
+ 4. Return git's exit code
738
+
739
+ **Notes:**
740
+ - No argument validation — git handles its own errors
741
+ - Interactive commands work (stdin is passed through)
742
+ - Destructive commands are the user's responsibility
743
+
744
+ **Exit Codes:**
745
+ - 3: ATK Home not initialized
746
+ - 7: Git not available
747
+ - (other): Git's own exit code passed through
748
+
749
+ ---
750
+
714
751
  # Deferred to Future
715
752
 
716
753
  The following commands and features are documented in `atk-future.md`:
@@ -0,0 +1,156 @@
1
+ # Git Sync Specification
2
+
3
+ > **Status**: Approved
4
+ > **Last Updated**: 2026-04-14
5
+
6
+ ## Overview
7
+
8
+ ATK Home is a git-backed repository. Phase 1 introduced `auto_commit` — every mutation creates a local commit. Git Sync extends this with remote synchronization: users can add a remote, auto-push after mutations, and see the repository state in `atk status`.
9
+
10
+ ## Design Principles
11
+
12
+ 1. **Proxy, don't reinvent.** `atk git` passes arguments straight to `git` in ATK_HOME. Users already know git.
13
+ 2. **Push is best-effort.** Auto-push failures warn but never fail the mutation. The commit already succeeded locally.
14
+ 3. **Sensible defaults.** `auto_push` defaults to `false`. Users opt in after adding a remote.
15
+
16
+ ## `atk git [args...]`
17
+
18
+ Thin proxy that runs `git <args>` in ATK_HOME.
19
+
20
+ ### Behavior
21
+
22
+ 1. Resolve ATK_HOME (env var or `~/.atk/`)
23
+ 2. Validate ATK Home is initialized (exit 3 if not)
24
+ 3. Require git available (exit 7 if not)
25
+ 4. Execute `git <args>` in ATK_HOME with stdin/stdout/stderr passed through (no capture)
26
+ 5. Return git's exit code
27
+
28
+ ### Examples
29
+
30
+ ```bash
31
+ atk git remote add origin git@github.com:user/dotfiles-atk.git
32
+ atk git remote show origin
33
+ atk git push
34
+ atk git pull
35
+ atk git log --oneline -5
36
+ atk git status
37
+ atk git diff
38
+ ```
39
+
40
+ ### Edge Cases
41
+
42
+ - No arguments (`atk git`) — passes no args to git, which prints git help. Acceptable.
43
+ - Interactive commands (`atk git rebase -i`) — works because stdin/stdout are passed through.
44
+ - Destructive commands (`atk git reset --hard`) — user's responsibility. ATK does not guard against this.
45
+
46
+ ## `auto_push` Configuration
47
+
48
+ New field in the manifest `config` section.
49
+
50
+ ### Manifest Schema
51
+
52
+ ```yaml
53
+ config:
54
+ auto_commit: true # Existing (default: true)
55
+ auto_push: false # New (default: false)
56
+ ```
57
+
58
+ ### Rules
59
+
60
+ | `auto_commit` | `auto_push` | Remote exists | Behavior |
61
+ |---------------|-------------|---------------|----------|
62
+ | true | true | yes | Commit + push |
63
+ | true | true | no | Commit only, warn "no remote configured" |
64
+ | true | false | any | Commit only (current behavior) |
65
+ | false | true | any | Neither — `auto_push` requires `auto_commit` |
66
+ | false | false | any | Neither (current behavior) |
67
+
68
+ ### Push Semantics
69
+
70
+ - Pushes the current branch to its upstream tracking branch
71
+ - Command: `git push` (no force, no explicit remote/branch — uses git defaults)
72
+ - On failure: print warning to stderr, do not exit with error
73
+ - On success: silent (consistent with auto_commit behavior)
74
+
75
+ ## Enhanced `atk status` — Repository Section
76
+
77
+ After the plugin status table, `atk status` displays a repository section.
78
+
79
+ ### Output Format
80
+
81
+ **With remote:**
82
+ ```
83
+ Repository:
84
+ Branch: main
85
+ Remote: origin -> git@github.com:user/dotfiles-atk.git
86
+ Sync: 2 ahead, 0 behind
87
+ Last commit: Add plugin 'sasha-rules' (2m ago)
88
+ Working dir: clean
89
+ ```
90
+
91
+ **Without remote:**
92
+ ```
93
+ Repository:
94
+ Branch: main
95
+ Remote: (none)
96
+ Last commit: Add plugin 'sasha-rules' (2m ago)
97
+ Working dir: clean
98
+ ```
99
+
100
+ **With dirty working directory:**
101
+ ```
102
+ Repository:
103
+ Branch: main
104
+ Remote: origin -> git@github.com:user/dotfiles-atk.git
105
+ Sync: 0 ahead, 0 behind
106
+ Last commit: Initialize ATK Home (3d ago)
107
+ Working dir: 1 modified, 2 untracked
108
+ ```
109
+
110
+ **No remote tracking branch:**
111
+ ```
112
+ Repository:
113
+ Branch: main
114
+ Remote: origin -> git@github.com:user/dotfiles-atk.git
115
+ Sync: (no tracking branch)
116
+ Last commit: Add plugin 'openmemory' (1h ago)
117
+ Working dir: clean
118
+ ```
119
+
120
+ ### Fields
121
+
122
+ | Field | Source | Notes |
123
+ |-------|--------|-------|
124
+ | Branch | `git branch --show-current` | Current branch name |
125
+ | Remote | `git remote get-url origin` | First remote name + URL. `(none)` if no remotes |
126
+ | Sync | `git rev-list --left-right --count HEAD...@{upstream}` | Ahead/behind upstream. Omitted if no remote. `(no tracking branch)` if remote exists but no tracking |
127
+ | Last commit | `git log -1 --format='%s (%cr)'` | Subject + relative time |
128
+ | Working dir | `git status --porcelain` | `clean` if empty, otherwise count by category |
129
+
130
+ ### Error Handling
131
+
132
+ If any git query fails (e.g., empty repo with no commits), the Repository section gracefully degrades — show what's available, skip what's not.
133
+
134
+ ## Auto-Push Wiring
135
+
136
+ Mutations that auto-commit (`add`, `remove`, `upgrade`) gain auto-push:
137
+
138
+ ```
139
+ mutation happens
140
+ -> if auto_commit:
141
+ git add -A
142
+ git commit -m "..."
143
+ -> if auto_push:
144
+ git push (best-effort, warn on failure)
145
+ ```
146
+
147
+ ### Warning Messages
148
+
149
+ - No remote: `Warning: auto_push enabled but no remote configured. Run: atk git remote add origin <url>`
150
+ - Push failed: `Warning: auto-push failed: <git error message>`
151
+
152
+ ## Exit Codes
153
+
154
+ `atk git` uses git's own exit codes (passed through). No new ATK exit codes needed.
155
+
156
+ For auto-push failures within mutations, the mutation still exits 0 (the commit succeeded).
@@ -96,6 +96,7 @@ schema_version: "2026-01-22"
96
96
 
97
97
  config:
98
98
  auto_commit: true # Commit after mutations (default: true)
99
+ auto_push: false # Push after auto-commit (default: false)
99
100
 
100
101
  plugins:
101
102
  - name: "OpenMemory" # Display name (user-friendly)
@@ -165,6 +166,8 @@ Display names (`name` field) have no restrictions—they are for human readabili
165
166
 
166
167
  If `auto_commit: false`, user must manually commit changes.
167
168
 
169
+ If `auto_push: true` and `auto_commit: true` and a remote is configured, ATK pushes after each auto-commit. Push failures warn but do not fail the mutation. If no remote is configured, auto-push silently no-ops with a warning.
170
+
168
171
  ## User Customizations
169
172
 
170
173
  Users customize plugins via the `custom/` directory inside each plugin.
@@ -10,11 +10,25 @@ from enum import Enum
10
10
  from pathlib import Path
11
11
 
12
12
  import atk.registry as registry_mod
13
- from atk.git import add_gitignore_exemption, git_add, git_commit, git_ls_remote, write_atk_ref
13
+ from atk.git import (
14
+ add_gitignore_exemption,
15
+ git_add,
16
+ git_commit,
17
+ git_ls_remote,
18
+ git_push,
19
+ write_atk_ref,
20
+ )
14
21
  from atk.git_source import fetch_git_plugin, normalize_git_url
15
22
  from atk.home import validate_atk_home
16
23
  from atk.lifecycle import LifecycleCommandNotDefinedError, run_lifecycle_command
17
- from atk.manifest_schema import PluginEntry, SourceInfo, SourceType, load_manifest, save_manifest
24
+ from atk.manifest_schema import (
25
+ ConfigSection,
26
+ PluginEntry,
27
+ SourceInfo,
28
+ SourceType,
29
+ load_manifest,
30
+ save_manifest,
31
+ )
18
32
  from atk.plugin import load_plugin_schema
19
33
  from atk.plugin_schema import PluginMaturity, PluginSchema
20
34
  from atk.sanitize import sanitize_directory_name
@@ -299,11 +313,13 @@ def _finalize_add(
299
313
  if source.ref:
300
314
  write_atk_ref(target_dir, source.ref)
301
315
 
302
- auto_commit = _update_manifest(atk_home, schema.name, directory, source=source)
316
+ config = _update_manifest(atk_home, schema.name, directory, source=source)
303
317
 
304
- if auto_commit:
318
+ if config.auto_commit:
305
319
  git_add(atk_home)
306
320
  git_commit(atk_home, f"Add plugin '{schema.name}'")
321
+ if config.auto_push:
322
+ git_push(atk_home)
307
323
 
308
324
  return directory
309
325
  except Exception:
@@ -338,7 +354,9 @@ def _cleanup_failed_add(atk_home: Path, target_dir: Path, directory: str, alread
338
354
  pass
339
355
 
340
356
 
341
- def _update_manifest(atk_home: Path, plugin_name: str, directory: str, source: SourceInfo) -> bool:
357
+ def _update_manifest(
358
+ atk_home: Path, plugin_name: str, directory: str, source: SourceInfo,
359
+ ) -> ConfigSection:
342
360
  """Update manifest.yaml with new plugin entry.
343
361
 
344
362
  Args:
@@ -348,7 +366,7 @@ def _update_manifest(atk_home: Path, plugin_name: str, directory: str, source: S
348
366
  source: Source metadata (type, ref, url).
349
367
 
350
368
  Returns:
351
- True if auto_commit is enabled in config, False otherwise.
369
+ The manifest's config section.
352
370
  """
353
371
  manifest = load_manifest(atk_home)
354
372
 
@@ -358,7 +376,7 @@ def _update_manifest(atk_home: Path, plugin_name: str, directory: str, source: S
358
376
  # Write back
359
377
  save_manifest(manifest, atk_home)
360
378
 
361
- return manifest.config.auto_commit
379
+ return manifest.config
362
380
 
363
381
 
364
382
 
@@ -13,6 +13,7 @@ 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
15
  from atk.commands.lifecycle import run_lifecycle_cli, run_restart_single_cli, run_uninstall_cli
16
+ from atk.commands.plug import plug_plugin, unplug_plugin
16
17
  from atk.commands.preconditions import (
17
18
  assert_plugin_or_all,
18
19
  require_git,
@@ -21,10 +22,9 @@ from atk.commands.preconditions import (
21
22
  require_ready_home,
22
23
  stdin_prompt,
23
24
  )
24
- from atk.commands.plug import plug_plugin, unplug_plugin
25
25
  from atk.commands.run import run_plugin_script
26
26
  from atk.commands.search import filter_registry_plugins, print_search_table
27
- from atk.commands.status import print_status_table
27
+ from atk.commands.status import print_repo_status, print_status_table
28
28
  from atk.commands.upgrade import upgrade_all_plugins, upgrade_single_plugin
29
29
  from atk.errors import handle_cli_error
30
30
  from atk.git_source import GitPluginNotFoundError, GitSourceError
@@ -726,9 +726,10 @@ def status(
726
726
 
727
727
  if not results:
728
728
  cli_logger.dim("No plugins installed.")
729
- raise typer.Exit(exit_codes.SUCCESS)
729
+ else:
730
+ print_status_table(results)
730
731
 
731
- print_status_table(results)
732
+ print_repo_status(atk_home)
732
733
  raise typer.Exit(exit_codes.SUCCESS)
733
734
 
734
735
 
@@ -815,6 +816,33 @@ def run(
815
816
  run_plugin_script(plugin_dir, script, ctx.args)
816
817
 
817
818
 
819
+ @app.command(
820
+ name="git",
821
+ context_settings={"allow_extra_args": True, "ignore_unknown_options": True},
822
+ )
823
+ def git_proxy(ctx: typer.Context) -> None:
824
+ """Run git commands in ATK Home.
825
+
826
+ Thin proxy that passes all arguments to git executed in the ATK Home
827
+ directory. Useful for managing remotes, pushing, pulling, and inspecting
828
+ the repository without navigating to it.
829
+
830
+ Examples:
831
+ atk git remote add origin <url>
832
+ atk git push
833
+ atk git log --oneline -5
834
+ """
835
+ import subprocess
836
+
837
+ atk_home = require_ready_home()
838
+
839
+ result = subprocess.run(
840
+ ["git", *ctx.args],
841
+ cwd=atk_home,
842
+ )
843
+ raise typer.Exit(result.returncode)
844
+
845
+
818
846
  def _show_update_notice() -> None:
819
847
  """Show update notice if a newer version is available on PyPI.
820
848
 
@@ -1,8 +1,19 @@
1
1
  """Status table rendering for the `atk status` command."""
2
2
 
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
3
7
  from rich.console import Console
4
8
  from rich.table import Table
5
9
 
10
+ from atk.git import (
11
+ git_ahead_behind,
12
+ git_get_branch,
13
+ git_get_remote_url,
14
+ git_last_commit_info,
15
+ git_working_dir_status,
16
+ )
6
17
  from atk.lifecycle import PluginStatus, PluginStatusResult, PortStatus
7
18
  from atk.plugin_schema import PluginMaturity
8
19
 
@@ -121,3 +132,47 @@ def print_status_table(results: list[PluginStatusResult]) -> None:
121
132
  "[dim]Note: Port checks verify if something is listening, not that it's the plugin.[/dim]"
122
133
  )
123
134
 
135
+
136
+ def print_repo_status(atk_home: Path) -> None:
137
+ """Print git repository status section after the plugin table."""
138
+ console.print()
139
+ console.print("[bold]Repository:[/bold]")
140
+
141
+ # Branch
142
+ branch = git_get_branch(atk_home)
143
+ branch_str = branch if branch else "(detached)"
144
+ console.print(f" Branch: {branch_str}")
145
+
146
+ # Remote
147
+ remote_info = git_get_remote_url(atk_home)
148
+ if remote_info:
149
+ remote_name, remote_url = remote_info
150
+ console.print(f" Remote: {remote_name} -> {remote_url}")
151
+ else:
152
+ console.print(" Remote: [dim](none)[/dim]")
153
+
154
+ # Sync (only if remote exists)
155
+ if remote_info:
156
+ ab = git_ahead_behind(atk_home)
157
+ if ab:
158
+ console.print(f" Sync: {ab.ahead} ahead, {ab.behind} behind")
159
+ else:
160
+ console.print(" Sync: [dim](no tracking branch)[/dim]")
161
+
162
+ # Last commit
163
+ commit = git_last_commit_info(atk_home)
164
+ if commit:
165
+ console.print(f" Last commit: {commit.subject} ({commit.relative_time})")
166
+
167
+ # Working directory
168
+ wd = git_working_dir_status(atk_home)
169
+ if wd.is_clean:
170
+ console.print(" Working dir: [green]clean[/green]")
171
+ else:
172
+ parts = []
173
+ if wd.modified:
174
+ parts.append(f"{wd.modified} modified")
175
+ if wd.untracked:
176
+ parts.append(f"{wd.untracked} untracked")
177
+ console.print(f" Working dir: [yellow]{', '.join(parts)}[/yellow]")
178
+