gardusig-cli 0.1.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.
Files changed (164) hide show
  1. gardusig_cli-0.1.0/LICENSE +21 -0
  2. gardusig_cli-0.1.0/PKG-INFO +274 -0
  3. gardusig_cli-0.1.0/README.md +238 -0
  4. gardusig_cli-0.1.0/cli/__init__.py +3 -0
  5. gardusig_cli-0.1.0/cli/__main__.py +4 -0
  6. gardusig_cli-0.1.0/cli/cli.py +71 -0
  7. gardusig_cli-0.1.0/cli/commands/__init__.py +1 -0
  8. gardusig_cli-0.1.0/cli/commands/backup.py +49 -0
  9. gardusig_cli-0.1.0/cli/commands/bookmarks.py +21 -0
  10. gardusig_cli-0.1.0/cli/commands/chrome.py +77 -0
  11. gardusig_cli-0.1.0/cli/commands/contest.py +120 -0
  12. gardusig_cli-0.1.0/cli/commands/docker.py +386 -0
  13. gardusig_cli-0.1.0/cli/commands/drive.py +223 -0
  14. gardusig_cli-0.1.0/cli/commands/gh.py +419 -0
  15. gardusig_cli-0.1.0/cli/commands/git.py +790 -0
  16. gardusig_cli-0.1.0/cli/commands/links.py +81 -0
  17. gardusig_cli-0.1.0/cli/commands/notion.py +215 -0
  18. gardusig_cli-0.1.0/cli/commands/publish.py +63 -0
  19. gardusig_cli-0.1.0/cli/commands/restore.py +8 -0
  20. gardusig_cli-0.1.0/cli/integration/__init__.py +5 -0
  21. gardusig_cli-0.1.0/cli/integration/cli_api_checks.py +531 -0
  22. gardusig_cli-0.1.0/cli/integration/contest_integration.py +156 -0
  23. gardusig_cli-0.1.0/cli/integration/docker_integration.py +346 -0
  24. gardusig_cli-0.1.0/cli/integration/docker_mocks.py +125 -0
  25. gardusig_cli-0.1.0/cli/integration/git_mocks.py +59 -0
  26. gardusig_cli-0.1.0/cli/integration/integration_coverage.py +329 -0
  27. gardusig_cli-0.1.0/cli/integration/public_commands.py +82 -0
  28. gardusig_cli-0.1.0/cli/integration/public_endpoints.py +1151 -0
  29. gardusig_cli-0.1.0/cli/integration/tag_zip_integration.py +449 -0
  30. gardusig_cli-0.1.0/cli/integration/workflow_integration.py +804 -0
  31. gardusig_cli-0.1.0/cli/integration/workspaces.py +83 -0
  32. gardusig_cli-0.1.0/cli/internal/__init__.py +1 -0
  33. gardusig_cli-0.1.0/cli/internal/read/__init__.py +9 -0
  34. gardusig_cli-0.1.0/cli/internal/read/git.py +47 -0
  35. gardusig_cli-0.1.0/cli/internal/read/safety.py +99 -0
  36. gardusig_cli-0.1.0/cli/internal/write/__init__.py +13 -0
  37. gardusig_cli-0.1.0/cli/internal/write/gate.py +66 -0
  38. gardusig_cli-0.1.0/cli/internal/write/git.py +38 -0
  39. gardusig_cli-0.1.0/cli/models/__init__.py +1 -0
  40. gardusig_cli-0.1.0/cli/models/backup.py +10 -0
  41. gardusig_cli-0.1.0/cli/models/bookmark.py +14 -0
  42. gardusig_cli-0.1.0/cli/models/repository.py +9 -0
  43. gardusig_cli-0.1.0/cli/models/task.py +68 -0
  44. gardusig_cli-0.1.0/cli/providers/__init__.py +1 -0
  45. gardusig_cli-0.1.0/cli/providers/base.py +23 -0
  46. gardusig_cli-0.1.0/cli/providers/chrome.py +9 -0
  47. gardusig_cli-0.1.0/cli/providers/drive_client.py +56 -0
  48. gardusig_cli-0.1.0/cli/providers/gh.py +47 -0
  49. gardusig_cli-0.1.0/cli/providers/github.py +13 -0
  50. gardusig_cli-0.1.0/cli/providers/google_drive.py +31 -0
  51. gardusig_cli-0.1.0/cli/providers/icloud_drive.py +17 -0
  52. gardusig_cli-0.1.0/cli/providers/notion.py +244 -0
  53. gardusig_cli-0.1.0/cli/providers/onedrive.py +31 -0
  54. gardusig_cli-0.1.0/cli/providers/proton_drive.py +31 -0
  55. gardusig_cli-0.1.0/cli/services/__init__.py +3 -0
  56. gardusig_cli-0.1.0/cli/services/backup_repository.py +181 -0
  57. gardusig_cli-0.1.0/cli/services/backup_zip.py +65 -0
  58. gardusig_cli-0.1.0/cli/services/bookmark_sync.py +9 -0
  59. gardusig_cli-0.1.0/cli/services/contest_docker.py +193 -0
  60. gardusig_cli-0.1.0/cli/services/contest_runner.py +274 -0
  61. gardusig_cli-0.1.0/cli/services/contest_serde.py +30 -0
  62. gardusig_cli-0.1.0/cli/services/docker_runtime.py +332 -0
  63. gardusig_cli-0.1.0/cli/services/drive_sync.py +108 -0
  64. gardusig_cli-0.1.0/cli/services/gh_sequence.py +66 -0
  65. gardusig_cli-0.1.0/cli/services/gh_service.py +344 -0
  66. gardusig_cli-0.1.0/cli/services/git_archive.py +5 -0
  67. gardusig_cli-0.1.0/cli/services/git_review.py +34 -0
  68. gardusig_cli-0.1.0/cli/services/git_shortcuts.py +610 -0
  69. gardusig_cli-0.1.0/cli/services/notion_markdown.py +173 -0
  70. gardusig_cli-0.1.0/cli/services/notion_pairs.py +232 -0
  71. gardusig_cli-0.1.0/cli/services/notion_sync.py +220 -0
  72. gardusig_cli-0.1.0/cli/services/pypi_publish.py +80 -0
  73. gardusig_cli-0.1.0/cli/services/replica_deploy.py +128 -0
  74. gardusig_cli-0.1.0/cli/utils/__init__.py +12 -0
  75. gardusig_cli-0.1.0/cli/utils/catalog.py +179 -0
  76. gardusig_cli-0.1.0/cli/utils/config.py +316 -0
  77. gardusig_cli-0.1.0/cli/utils/confirm.py +18 -0
  78. gardusig_cli-0.1.0/cli/utils/external_client.py +143 -0
  79. gardusig_cli-0.1.0/cli/utils/fs.py +13 -0
  80. gardusig_cli-0.1.0/cli/utils/hashing.py +12 -0
  81. gardusig_cli-0.1.0/cli/utils/http.py +10 -0
  82. gardusig_cli-0.1.0/cli/utils/logger.py +15 -0
  83. gardusig_cli-0.1.0/cli/utils/process.py +62 -0
  84. gardusig_cli-0.1.0/cli/utils/quick_defaults.py +40 -0
  85. gardusig_cli-0.1.0/cli/utils/retry.py +30 -0
  86. gardusig_cli-0.1.0/cli/utils/yaml.py +22 -0
  87. gardusig_cli-0.1.0/cli/utils/zip.py +18 -0
  88. gardusig_cli-0.1.0/cli/workflows/__init__.py +1 -0
  89. gardusig_cli-0.1.0/cli/workflows/backup.py +1 -0
  90. gardusig_cli-0.1.0/cli/workflows/restore.py +1 -0
  91. gardusig_cli-0.1.0/gardusig_cli.egg-info/PKG-INFO +274 -0
  92. gardusig_cli-0.1.0/gardusig_cli.egg-info/SOURCES.txt +162 -0
  93. gardusig_cli-0.1.0/gardusig_cli.egg-info/dependency_links.txt +1 -0
  94. gardusig_cli-0.1.0/gardusig_cli.egg-info/entry_points.txt +2 -0
  95. gardusig_cli-0.1.0/gardusig_cli.egg-info/requires.txt +13 -0
  96. gardusig_cli-0.1.0/gardusig_cli.egg-info/top_level.txt +1 -0
  97. gardusig_cli-0.1.0/pyproject.toml +80 -0
  98. gardusig_cli-0.1.0/setup.cfg +4 -0
  99. gardusig_cli-0.1.0/tests/test_api_workspaces.py +24 -0
  100. gardusig_cli-0.1.0/tests/test_backup_encrypt_config.py +28 -0
  101. gardusig_cli-0.1.0/tests/test_backup_repository.py +123 -0
  102. gardusig_cli-0.1.0/tests/test_backup_zip.py +51 -0
  103. gardusig_cli-0.1.0/tests/test_bookmark_scripts.py +149 -0
  104. gardusig_cli-0.1.0/tests/test_bootstrap_structure.py +172 -0
  105. gardusig_cli-0.1.0/tests/test_chrome_commands.py +76 -0
  106. gardusig_cli-0.1.0/tests/test_chrome_docker_integration.py +62 -0
  107. gardusig_cli-0.1.0/tests/test_cli_api_integration.py +75 -0
  108. gardusig_cli-0.1.0/tests/test_cli_endpoints.py +399 -0
  109. gardusig_cli-0.1.0/tests/test_cli_help.py +56 -0
  110. gardusig_cli-0.1.0/tests/test_contest_cli.py +59 -0
  111. gardusig_cli-0.1.0/tests/test_contest_docker_integration.py +66 -0
  112. gardusig_cli-0.1.0/tests/test_contest_integration_checks.py +27 -0
  113. gardusig_cli-0.1.0/tests/test_contest_runner.py +195 -0
  114. gardusig_cli-0.1.0/tests/test_contest_validate_integration.py +59 -0
  115. gardusig_cli-0.1.0/tests/test_coverage_unit_gate.py +47 -0
  116. gardusig_cli-0.1.0/tests/test_docker_commands.py +521 -0
  117. gardusig_cli-0.1.0/tests/test_docker_integration.py +87 -0
  118. gardusig_cli-0.1.0/tests/test_docker_integration_checks.py +27 -0
  119. gardusig_cli-0.1.0/tests/test_docker_integration_live.py +80 -0
  120. gardusig_cli-0.1.0/tests/test_docker_runtime.py +265 -0
  121. gardusig_cli-0.1.0/tests/test_drive_commands.py +129 -0
  122. gardusig_cli-0.1.0/tests/test_drive_docker_integration.py +45 -0
  123. gardusig_cli-0.1.0/tests/test_drive_internal.py +33 -0
  124. gardusig_cli-0.1.0/tests/test_drive_sync.py +66 -0
  125. gardusig_cli-0.1.0/tests/test_external_api_integration.py +250 -0
  126. gardusig_cli-0.1.0/tests/test_external_client.py +88 -0
  127. gardusig_cli-0.1.0/tests/test_gh_commands.py +134 -0
  128. gardusig_cli-0.1.0/tests/test_gh_docker_integration.py +38 -0
  129. gardusig_cli-0.1.0/tests/test_gh_labels_manifest.py +41 -0
  130. gardusig_cli-0.1.0/tests/test_gh_provider.py +41 -0
  131. gardusig_cli-0.1.0/tests/test_gh_sequence.py +35 -0
  132. gardusig_cli-0.1.0/tests/test_gh_service.py +192 -0
  133. gardusig_cli-0.1.0/tests/test_git_commands.py +335 -0
  134. gardusig_cli-0.1.0/tests/test_git_docker_harness.py +47 -0
  135. gardusig_cli-0.1.0/tests/test_git_mocks.py +36 -0
  136. gardusig_cli-0.1.0/tests/test_git_push_shortcut.py +105 -0
  137. gardusig_cli-0.1.0/tests/test_git_safety.py +47 -0
  138. gardusig_cli-0.1.0/tests/test_git_scripts.py +35 -0
  139. gardusig_cli-0.1.0/tests/test_git_shortcuts_service.py +627 -0
  140. gardusig_cli-0.1.0/tests/test_git_tag_zip.py +272 -0
  141. gardusig_cli-0.1.0/tests/test_git_workflow.py +96 -0
  142. gardusig_cli-0.1.0/tests/test_http_timeout.py +30 -0
  143. gardusig_cli-0.1.0/tests/test_integration_coverage_gate.py +82 -0
  144. gardusig_cli-0.1.0/tests/test_internal_read_write.py +73 -0
  145. gardusig_cli-0.1.0/tests/test_links_command.py +26 -0
  146. gardusig_cli-0.1.0/tests/test_notion_commands.py +171 -0
  147. gardusig_cli-0.1.0/tests/test_notion_docker_integration.py +131 -0
  148. gardusig_cli-0.1.0/tests/test_notion_internal.py +85 -0
  149. gardusig_cli-0.1.0/tests/test_notion_markdown.py +21 -0
  150. gardusig_cli-0.1.0/tests/test_notion_pairs.py +165 -0
  151. gardusig_cli-0.1.0/tests/test_notion_sync.py +186 -0
  152. gardusig_cli-0.1.0/tests/test_public_commands_integration.py +69 -0
  153. gardusig_cli-0.1.0/tests/test_public_endpoints_integration.py +73 -0
  154. gardusig_cli-0.1.0/tests/test_publish_commands.py +38 -0
  155. gardusig_cli-0.1.0/tests/test_pypi_packaging.py +28 -0
  156. gardusig_cli-0.1.0/tests/test_pypi_publish.py +53 -0
  157. gardusig_cli-0.1.0/tests/test_quick_defaults.py +40 -0
  158. gardusig_cli-0.1.0/tests/test_replica_deploy.py +39 -0
  159. gardusig_cli-0.1.0/tests/test_scripts_layout.py +25 -0
  160. gardusig_cli-0.1.0/tests/test_stubs_review_main.py +132 -0
  161. gardusig_cli-0.1.0/tests/test_tag_zip_integration.py +48 -0
  162. gardusig_cli-0.1.0/tests/test_unit_coverage.py +167 -0
  163. gardusig_cli-0.1.0/tests/test_utils_helpers.py +227 -0
  164. gardusig_cli-0.1.0/tests/test_workflow_integration.py +48 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gustavo Gardusi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,274 @@
1
+ Metadata-Version: 2.4
2
+ Name: gardusig-cli
3
+ Version: 0.1.0
4
+ Summary: macOS CLI for git shortcuts, backups, and sync workflows
5
+ Author: gardusig
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/gardusig/cli
8
+ Project-URL: Repository, https://github.com/gardusig/cli
9
+ Project-URL: Issues, https://github.com/gardusig/cli/issues
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: MacOS
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Version Control :: Git
19
+ Classifier: Topic :: Utilities
20
+ Requires-Python: >=3.12
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: typer>=0.12
24
+ Requires-Dist: pydantic>=2.0
25
+ Requires-Dist: pydantic-settings>=2.0
26
+ Requires-Dist: PyYAML>=6.0
27
+ Requires-Dist: rich>=13.0
28
+ Requires-Dist: httpx>=0.27
29
+ Provides-Extra: dev
30
+ Requires-Dist: build>=1.0; extra == "dev"
31
+ Requires-Dist: pytest>=8.0; extra == "dev"
32
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
33
+ Requires-Dist: pytest-timeout>=2.3; extra == "dev"
34
+ Requires-Dist: twine>=5.0; extra == "dev"
35
+ Dynamic: license-file
36
+
37
+ # cli
38
+
39
+ macOS CLI: **`cli git`** · **`cli drive`** · **`cli chrome`** · **`cli notion`**.
40
+
41
+ ## Naming
42
+
43
+ | Context | Identifier |
44
+ | --- | --- |
45
+ | **GitHub repo** | [gardusig/cli](https://github.com/gardusig/cli) (clone path is usually `cli/`) |
46
+ | **PyPI package** | `gardusig-cli` — `pip install gardusig-cli` |
47
+ | **Console command** | `cli` (unchanged after PyPI install) |
48
+ | **Python import** | `cli` |
49
+
50
+ The repo stays **`cli`**; only the published distribution name on PyPI is **`gardusig-cli`** (`cli` is taken on PyPI).
51
+
52
+ ## Requirements
53
+
54
+ | Tool | Needed for |
55
+ | --- | --- |
56
+ | **macOS** | Primary target for local use |
57
+ | **Python 3.12+** | Local install (`bootstrap.sh` creates a venv) |
58
+ | **[Homebrew](https://brew.sh/)** | Recommended way to install Python and git on macOS |
59
+ | **git** | `cli git` (run from inside a repository) |
60
+ | **zip** | Encrypted tag archives (`cli drive ingest` on `encrypted: true` repos) |
61
+ | **[Docker Desktop](https://www.docker.com/products/docker-desktop/)** | Verification — `./scripts/test-unit.sh` and `./scripts/test-integration.sh` in Docker |
62
+
63
+ Install Python and git with Homebrew:
64
+
65
+ ```bash
66
+ brew install python@3.12 git
67
+ ```
68
+
69
+ Optional: `gh` (GitHub CLI) for [cursor-skills](https://github.com/gardusig/cursor-skills) workflows — not required by cli itself.
70
+
71
+ ## Configuration (global)
72
+
73
+ Cli reads **`config/config.yaml`** in the clone, or **`~/.config/cli/`** after install. Override the directory with `CLI_CONFIG_DIR`.
74
+
75
+ Copy the bundled `config/` tree and edit paths for your machine before daily use:
76
+
77
+ | Setting | Config key | Purpose |
78
+ | --- | --- | --- |
79
+ | **Git repositories** | `backup.repositories[].path` | Repos for `cli drive ingest` / `drive status` |
80
+ | **Tag zip folder** | `backup.tags_dir` | Local store (default: iCloud `git-tags/`) — source for `drive upload` |
81
+ | **Cloud upload roots** | `drives.yaml` → `google` / `onedrive` / `proton` | Remote folder names per provider |
82
+ | **Notion task root** | `notion.task_root` | Private `header/` + `body/` task files |
83
+ | **Notion pairs manifest** | `notion.pairs_file` | `config/notion/tasks.pairs.json` in this repo |
84
+ | **Notion database** | `notion.database_id` | Existing board ID + `NOTION_TOKEN` env |
85
+ | **Backup zip password** | `BACKUP_ZIP_PASSWORD` env | Encrypted repos (`encrypted: true` in `backup.repositories`) |
86
+ | **Chrome bookmarks file** | `chrome.bookmarks_file` | HTML backup (`chrome bookmarks ingest`) |
87
+ | **Chrome downloads** | `chrome.downloads_dir` | Folder polled when ingesting from Chrome |
88
+
89
+ Example `config.yaml`:
90
+
91
+ ```yaml
92
+ backup:
93
+ tags_dir: ~/Library/Mobile Documents/com~apple~CloudDocs/git-tags
94
+ repositories:
95
+ - path: ~/git-local/cli
96
+ - path: ~/git-local/my-other-repo
97
+
98
+ notion:
99
+ database_id: your-notion-database-id
100
+ task_root: ~/git-local/private/configured/tasks
101
+ pairs_file: config/notion/tasks.pairs.json
102
+
103
+ chrome:
104
+ profile: Default
105
+ bookmarks_file: ~/git-local/private/bookmarks/bookmarks.html
106
+ downloads_dir: ~/Downloads
107
+ ```
108
+
109
+ Test fixtures (not for production) live under `tests/fixtures/notion/tasks` and `tests/fixtures/bookmarks.html`.
110
+
111
+ Cloud providers: `config/drives.yaml`. Notion token: **`export NOTION_TOKEN=...`** (never commit).
112
+
113
+ Environment overrides (optional): `CLI_BOOKMARKS_FILE`, `CLI_DOWNLOADS_DIR`, `CLI_CONFIG_DIR`, `NOTION_TOKEN`.
114
+
115
+ ## Install (macOS)
116
+
117
+ **Global install (recommended):** `cli` works in any terminal after one setup.
118
+
119
+ ```bash
120
+ ./scripts/install.sh
121
+ # open a new terminal OR: source ~/.zprofile
122
+ cli --version
123
+ cli git --help
124
+ ```
125
+
126
+ **Dev install (current shell only):** venv + runtime deps for working in this repo.
127
+
128
+ ```bash
129
+ ./scripts/bootstrap.sh
130
+ source .venv/bin/activate
131
+ python -m cli --help
132
+ ```
133
+
134
+ Verification always runs in Docker — same image locally and in CI. Do not run `pytest` on the host; use `./scripts/test-unit.sh` and `./scripts/test-integration.sh` instead.
135
+
136
+ ## Common git commands
137
+
138
+ Run from inside a repository (`cd` into the repo first).
139
+
140
+ | Task | Command |
141
+ | --- | --- |
142
+ | **Sync main** (before/after work) | `cli git reset --yes` (`--main-only` to skip branch prune) |
143
+ | **Start issue** (align main + branch) | `cli git start issue-9-slug --yes` |
144
+ | **During work** (add + commit + push) | `cli git push --yes` (on `main`, starts random branch first) |
145
+ | Branch in place (no align) | `cli git start [name] --no-prep` |
146
+ | Commit only | `cli git commit` |
147
+ | Sync feature branch | `cli git pull` |
148
+ | Delete merged branch | `cli git branch-delete BRANCH --yes` |
149
+ | Clear all branches (keep `main`) | `cli git branch-clear --yes` |
150
+ | Tag on main (default: today) | `cli git tag` · `cli git tag list` · `cli git tag push` |
151
+ | Zip one tag (cwd repo) | `cli git zip` · `cli git zip TAG` |
152
+
153
+ Short alias: `cli g push --yes` == `cli git push --yes`.
154
+
155
+ Shell wrappers: `scripts/git/` (e.g. `./scripts/git/review.sh`). See [docs/git.md](docs/git.md).
156
+
157
+ **Safety:** destructive actions (reset, clean, delete, push) require `--yes` or an interactive confirmation. Default `cli git start` aligns main then branches; pass `--no-prep` to branch from the current state.
158
+
159
+ ## Drive (`cli drive`)
160
+
161
+ Local hub: **iCloud** `git-tags/{repo}/{repo}-{tag}.zip` (via `backup.tags_dir`). Cloud: append-only upload to Google Drive, OneDrive, Proton.
162
+
163
+ | Task | Command |
164
+ | --- | --- |
165
+ | **Status** (git tags vs local zips) | `cli drive status` |
166
+ | **Ingest** (zip all tags → local store) | `cli drive ingest` (all repos in config) or `cli drive ingest PATH` |
167
+ | List local zips | `cli drive list` · `cli drive list PATH` |
168
+ | Delete local zip | `cli drive delete PATH TAG --yes` |
169
+ | **Upload** to cloud | `cli drive upload` · `cli drive upload google` · `onedrive` · `proton` |
170
+ | **Sync** (ingest all + upload all) | `cli drive sync` |
171
+
172
+ Typical end-of-day:
173
+
174
+ ```bash
175
+ cli git tag --yes && cli git zip # single repo (cwd)
176
+ cli drive upload # push missing zips to cloud
177
+ ```
178
+
179
+ Multi-repo:
180
+
181
+ ```bash
182
+ cli drive sync # ingest all repos + upload all clouds
183
+ # or step by step:
184
+ cli drive ingest
185
+ cli drive status
186
+ cli drive upload
187
+ ```
188
+
189
+ `git zip` is the quick path for the current repo; `drive ingest` iterates configured repositories (or one `PATH`). See [docs/drive.md](docs/drive.md) · [issue #4](https://github.com/gardusig/cli/issues/4).
190
+
191
+ Shell wrappers: `scripts/drive/` (`status.sh`, `ingest.sh`, `upload.sh`, `sync.sh`).
192
+
193
+ ## Chrome (`cli chrome`)
194
+
195
+ Browser integrations; **bookmarks** is the first subcommand. Path: **`chrome.bookmarks_file`** in config.
196
+
197
+ | Direction | Command |
198
+ | --- | --- |
199
+ | **Chrome → local** | `cli chrome bookmarks ingest` |
200
+ | **Local → Chrome** | `cli chrome bookmarks deploy` |
201
+
202
+ ```bash
203
+ cli chrome bookmarks ingest # Chrome → local HTML file
204
+ cli chrome bookmarks deploy # local file → Chrome
205
+ ```
206
+
207
+ Shell wrappers: `./scripts/chrome/ingest.sh` · `./scripts/chrome/deploy.sh` (deprecated: `import.sh` / `export.sh`).
208
+
209
+ See [docs/bookmarks.md](docs/bookmarks.md) · epic [#24](https://github.com/gardusig/cli/issues/24) (shell scripts: [#1](https://github.com/gardusig/cli/issues/1)).
210
+
211
+ ## Notion (`cli notion`)
212
+
213
+ Local tasks: **`notion.task_root`** (private header/body) + **`notion.pairs_file`** (`config/notion/tasks.pairs.json`). Auth: **`NOTION_TOKEN`** + `notion.database_id`.
214
+
215
+ | Command | Purpose |
216
+ | --- | --- |
217
+ | `cli notion pairs build` | Scan header/ + body/ → `tasks.pairs.json` |
218
+ | `cli notion ingest` | Notion → local pairs |
219
+ | `cli notion deploy --yes` | Local pairs → Notion (archives board first by default) |
220
+ | `cli notion sync --yes` | Ingest from Notion, then deploy local tasks |
221
+ | `cli notion cleanup --yes` | Archive all database pages |
222
+
223
+ See [docs/notion.md](docs/notion.md) · epic [#2](https://github.com/gardusig/cli/issues/2) · children [#20](https://github.com/gardusig/cli/issues/20)–[#23](https://github.com/gardusig/cli/issues/23).
224
+
225
+ ## Docker
226
+
227
+ Local Docker monitor and cleanup (requires `docker` on PATH; no container start):
228
+
229
+ | Task | Command |
230
+ | --- | --- |
231
+ | **Dashboard** (CPU, memory, storage) | `cli docker top` |
232
+ | **By domain** | `cli docker stats --by cpu` / `memory` / `storage` |
233
+ | **Storage lists** | `cli docker images` · `cli docker containers` · `cli docker df` |
234
+ | **Stop running** | `cli docker stop --yes` |
235
+ | **Delete containers** | `cli docker container-delete --yes` |
236
+ | **Prune images** | `cli docker image-delete --yes` (`--all-images` for all unused) |
237
+ | **Full reset** | `cli docker reset --yes` |
238
+ | Targeted cleanup | `cli docker clean containers --yes` · `clean images` · `clean all` |
239
+
240
+ Shell wrappers live in `scripts/docker/` (e.g. `./scripts/docker/reset.sh --yes`).
241
+
242
+ Destructive commands use the write gate; pass `--yes` in scripts.
243
+
244
+ ## Verify (Docker)
245
+
246
+ Requires [Docker Desktop](https://www.docker.com/products/docker-desktop/) on macOS (or Docker Engine on Linux). The `cli:dev` Linux image is the only supported test environment:
247
+
248
+ ```bash
249
+ ./scripts/docker/build-image.sh # build once (or auto-build on first test run)
250
+ ./scripts/test-unit.sh # unit tests (≥80% coverage)
251
+ ./scripts/test-integration.sh # full pytest + smoke + live docker
252
+ ./scripts/docker/shell.sh # onboard: interactive shell in container
253
+ ```
254
+
255
+ See [docs/docker.md](docs/docker.md).
256
+
257
+ ## Docs
258
+
259
+ - [Setup](docs/setup.md)
260
+ - [Git commands](docs/git.md)
261
+ - [GitHub (`cli gh`)](docs/gh.md)
262
+ - [Drive (local + cloud)](docs/drive.md)
263
+ - [Chrome](docs/bookmarks.md) · [Notion](docs/notion.md)
264
+ - [Docker integration](docs/docker.md)
265
+ - [Configuration](docs/configuration.md)
266
+ - [Architecture](docs/architecture.md)
267
+
268
+ ## Related
269
+
270
+ - [cursor-skills](https://github.com/gardusig/cursor-skills) — `@gh-*` AI workflows for issues/PRs
271
+ - Cloud drive epic: [cli #4](https://github.com/gardusig/cli/issues/4)
272
+ - Bootstrap spec: [cli #3](https://github.com/gardusig/cli/issues/3)
273
+ - Chrome: [cli #24](https://github.com/gardusig/cli/issues/24) · bookmarks scripts [#1](https://github.com/gardusig/cli/issues/1)
274
+ - Docker integration: [cli #9](https://github.com/gardusig/cli/issues/9)
@@ -0,0 +1,238 @@
1
+ # cli
2
+
3
+ macOS CLI: **`cli git`** · **`cli drive`** · **`cli chrome`** · **`cli notion`**.
4
+
5
+ ## Naming
6
+
7
+ | Context | Identifier |
8
+ | --- | --- |
9
+ | **GitHub repo** | [gardusig/cli](https://github.com/gardusig/cli) (clone path is usually `cli/`) |
10
+ | **PyPI package** | `gardusig-cli` — `pip install gardusig-cli` |
11
+ | **Console command** | `cli` (unchanged after PyPI install) |
12
+ | **Python import** | `cli` |
13
+
14
+ The repo stays **`cli`**; only the published distribution name on PyPI is **`gardusig-cli`** (`cli` is taken on PyPI).
15
+
16
+ ## Requirements
17
+
18
+ | Tool | Needed for |
19
+ | --- | --- |
20
+ | **macOS** | Primary target for local use |
21
+ | **Python 3.12+** | Local install (`bootstrap.sh` creates a venv) |
22
+ | **[Homebrew](https://brew.sh/)** | Recommended way to install Python and git on macOS |
23
+ | **git** | `cli git` (run from inside a repository) |
24
+ | **zip** | Encrypted tag archives (`cli drive ingest` on `encrypted: true` repos) |
25
+ | **[Docker Desktop](https://www.docker.com/products/docker-desktop/)** | Verification — `./scripts/test-unit.sh` and `./scripts/test-integration.sh` in Docker |
26
+
27
+ Install Python and git with Homebrew:
28
+
29
+ ```bash
30
+ brew install python@3.12 git
31
+ ```
32
+
33
+ Optional: `gh` (GitHub CLI) for [cursor-skills](https://github.com/gardusig/cursor-skills) workflows — not required by cli itself.
34
+
35
+ ## Configuration (global)
36
+
37
+ Cli reads **`config/config.yaml`** in the clone, or **`~/.config/cli/`** after install. Override the directory with `CLI_CONFIG_DIR`.
38
+
39
+ Copy the bundled `config/` tree and edit paths for your machine before daily use:
40
+
41
+ | Setting | Config key | Purpose |
42
+ | --- | --- | --- |
43
+ | **Git repositories** | `backup.repositories[].path` | Repos for `cli drive ingest` / `drive status` |
44
+ | **Tag zip folder** | `backup.tags_dir` | Local store (default: iCloud `git-tags/`) — source for `drive upload` |
45
+ | **Cloud upload roots** | `drives.yaml` → `google` / `onedrive` / `proton` | Remote folder names per provider |
46
+ | **Notion task root** | `notion.task_root` | Private `header/` + `body/` task files |
47
+ | **Notion pairs manifest** | `notion.pairs_file` | `config/notion/tasks.pairs.json` in this repo |
48
+ | **Notion database** | `notion.database_id` | Existing board ID + `NOTION_TOKEN` env |
49
+ | **Backup zip password** | `BACKUP_ZIP_PASSWORD` env | Encrypted repos (`encrypted: true` in `backup.repositories`) |
50
+ | **Chrome bookmarks file** | `chrome.bookmarks_file` | HTML backup (`chrome bookmarks ingest`) |
51
+ | **Chrome downloads** | `chrome.downloads_dir` | Folder polled when ingesting from Chrome |
52
+
53
+ Example `config.yaml`:
54
+
55
+ ```yaml
56
+ backup:
57
+ tags_dir: ~/Library/Mobile Documents/com~apple~CloudDocs/git-tags
58
+ repositories:
59
+ - path: ~/git-local/cli
60
+ - path: ~/git-local/my-other-repo
61
+
62
+ notion:
63
+ database_id: your-notion-database-id
64
+ task_root: ~/git-local/private/configured/tasks
65
+ pairs_file: config/notion/tasks.pairs.json
66
+
67
+ chrome:
68
+ profile: Default
69
+ bookmarks_file: ~/git-local/private/bookmarks/bookmarks.html
70
+ downloads_dir: ~/Downloads
71
+ ```
72
+
73
+ Test fixtures (not for production) live under `tests/fixtures/notion/tasks` and `tests/fixtures/bookmarks.html`.
74
+
75
+ Cloud providers: `config/drives.yaml`. Notion token: **`export NOTION_TOKEN=...`** (never commit).
76
+
77
+ Environment overrides (optional): `CLI_BOOKMARKS_FILE`, `CLI_DOWNLOADS_DIR`, `CLI_CONFIG_DIR`, `NOTION_TOKEN`.
78
+
79
+ ## Install (macOS)
80
+
81
+ **Global install (recommended):** `cli` works in any terminal after one setup.
82
+
83
+ ```bash
84
+ ./scripts/install.sh
85
+ # open a new terminal OR: source ~/.zprofile
86
+ cli --version
87
+ cli git --help
88
+ ```
89
+
90
+ **Dev install (current shell only):** venv + runtime deps for working in this repo.
91
+
92
+ ```bash
93
+ ./scripts/bootstrap.sh
94
+ source .venv/bin/activate
95
+ python -m cli --help
96
+ ```
97
+
98
+ Verification always runs in Docker — same image locally and in CI. Do not run `pytest` on the host; use `./scripts/test-unit.sh` and `./scripts/test-integration.sh` instead.
99
+
100
+ ## Common git commands
101
+
102
+ Run from inside a repository (`cd` into the repo first).
103
+
104
+ | Task | Command |
105
+ | --- | --- |
106
+ | **Sync main** (before/after work) | `cli git reset --yes` (`--main-only` to skip branch prune) |
107
+ | **Start issue** (align main + branch) | `cli git start issue-9-slug --yes` |
108
+ | **During work** (add + commit + push) | `cli git push --yes` (on `main`, starts random branch first) |
109
+ | Branch in place (no align) | `cli git start [name] --no-prep` |
110
+ | Commit only | `cli git commit` |
111
+ | Sync feature branch | `cli git pull` |
112
+ | Delete merged branch | `cli git branch-delete BRANCH --yes` |
113
+ | Clear all branches (keep `main`) | `cli git branch-clear --yes` |
114
+ | Tag on main (default: today) | `cli git tag` · `cli git tag list` · `cli git tag push` |
115
+ | Zip one tag (cwd repo) | `cli git zip` · `cli git zip TAG` |
116
+
117
+ Short alias: `cli g push --yes` == `cli git push --yes`.
118
+
119
+ Shell wrappers: `scripts/git/` (e.g. `./scripts/git/review.sh`). See [docs/git.md](docs/git.md).
120
+
121
+ **Safety:** destructive actions (reset, clean, delete, push) require `--yes` or an interactive confirmation. Default `cli git start` aligns main then branches; pass `--no-prep` to branch from the current state.
122
+
123
+ ## Drive (`cli drive`)
124
+
125
+ Local hub: **iCloud** `git-tags/{repo}/{repo}-{tag}.zip` (via `backup.tags_dir`). Cloud: append-only upload to Google Drive, OneDrive, Proton.
126
+
127
+ | Task | Command |
128
+ | --- | --- |
129
+ | **Status** (git tags vs local zips) | `cli drive status` |
130
+ | **Ingest** (zip all tags → local store) | `cli drive ingest` (all repos in config) or `cli drive ingest PATH` |
131
+ | List local zips | `cli drive list` · `cli drive list PATH` |
132
+ | Delete local zip | `cli drive delete PATH TAG --yes` |
133
+ | **Upload** to cloud | `cli drive upload` · `cli drive upload google` · `onedrive` · `proton` |
134
+ | **Sync** (ingest all + upload all) | `cli drive sync` |
135
+
136
+ Typical end-of-day:
137
+
138
+ ```bash
139
+ cli git tag --yes && cli git zip # single repo (cwd)
140
+ cli drive upload # push missing zips to cloud
141
+ ```
142
+
143
+ Multi-repo:
144
+
145
+ ```bash
146
+ cli drive sync # ingest all repos + upload all clouds
147
+ # or step by step:
148
+ cli drive ingest
149
+ cli drive status
150
+ cli drive upload
151
+ ```
152
+
153
+ `git zip` is the quick path for the current repo; `drive ingest` iterates configured repositories (or one `PATH`). See [docs/drive.md](docs/drive.md) · [issue #4](https://github.com/gardusig/cli/issues/4).
154
+
155
+ Shell wrappers: `scripts/drive/` (`status.sh`, `ingest.sh`, `upload.sh`, `sync.sh`).
156
+
157
+ ## Chrome (`cli chrome`)
158
+
159
+ Browser integrations; **bookmarks** is the first subcommand. Path: **`chrome.bookmarks_file`** in config.
160
+
161
+ | Direction | Command |
162
+ | --- | --- |
163
+ | **Chrome → local** | `cli chrome bookmarks ingest` |
164
+ | **Local → Chrome** | `cli chrome bookmarks deploy` |
165
+
166
+ ```bash
167
+ cli chrome bookmarks ingest # Chrome → local HTML file
168
+ cli chrome bookmarks deploy # local file → Chrome
169
+ ```
170
+
171
+ Shell wrappers: `./scripts/chrome/ingest.sh` · `./scripts/chrome/deploy.sh` (deprecated: `import.sh` / `export.sh`).
172
+
173
+ See [docs/bookmarks.md](docs/bookmarks.md) · epic [#24](https://github.com/gardusig/cli/issues/24) (shell scripts: [#1](https://github.com/gardusig/cli/issues/1)).
174
+
175
+ ## Notion (`cli notion`)
176
+
177
+ Local tasks: **`notion.task_root`** (private header/body) + **`notion.pairs_file`** (`config/notion/tasks.pairs.json`). Auth: **`NOTION_TOKEN`** + `notion.database_id`.
178
+
179
+ | Command | Purpose |
180
+ | --- | --- |
181
+ | `cli notion pairs build` | Scan header/ + body/ → `tasks.pairs.json` |
182
+ | `cli notion ingest` | Notion → local pairs |
183
+ | `cli notion deploy --yes` | Local pairs → Notion (archives board first by default) |
184
+ | `cli notion sync --yes` | Ingest from Notion, then deploy local tasks |
185
+ | `cli notion cleanup --yes` | Archive all database pages |
186
+
187
+ See [docs/notion.md](docs/notion.md) · epic [#2](https://github.com/gardusig/cli/issues/2) · children [#20](https://github.com/gardusig/cli/issues/20)–[#23](https://github.com/gardusig/cli/issues/23).
188
+
189
+ ## Docker
190
+
191
+ Local Docker monitor and cleanup (requires `docker` on PATH; no container start):
192
+
193
+ | Task | Command |
194
+ | --- | --- |
195
+ | **Dashboard** (CPU, memory, storage) | `cli docker top` |
196
+ | **By domain** | `cli docker stats --by cpu` / `memory` / `storage` |
197
+ | **Storage lists** | `cli docker images` · `cli docker containers` · `cli docker df` |
198
+ | **Stop running** | `cli docker stop --yes` |
199
+ | **Delete containers** | `cli docker container-delete --yes` |
200
+ | **Prune images** | `cli docker image-delete --yes` (`--all-images` for all unused) |
201
+ | **Full reset** | `cli docker reset --yes` |
202
+ | Targeted cleanup | `cli docker clean containers --yes` · `clean images` · `clean all` |
203
+
204
+ Shell wrappers live in `scripts/docker/` (e.g. `./scripts/docker/reset.sh --yes`).
205
+
206
+ Destructive commands use the write gate; pass `--yes` in scripts.
207
+
208
+ ## Verify (Docker)
209
+
210
+ Requires [Docker Desktop](https://www.docker.com/products/docker-desktop/) on macOS (or Docker Engine on Linux). The `cli:dev` Linux image is the only supported test environment:
211
+
212
+ ```bash
213
+ ./scripts/docker/build-image.sh # build once (or auto-build on first test run)
214
+ ./scripts/test-unit.sh # unit tests (≥80% coverage)
215
+ ./scripts/test-integration.sh # full pytest + smoke + live docker
216
+ ./scripts/docker/shell.sh # onboard: interactive shell in container
217
+ ```
218
+
219
+ See [docs/docker.md](docs/docker.md).
220
+
221
+ ## Docs
222
+
223
+ - [Setup](docs/setup.md)
224
+ - [Git commands](docs/git.md)
225
+ - [GitHub (`cli gh`)](docs/gh.md)
226
+ - [Drive (local + cloud)](docs/drive.md)
227
+ - [Chrome](docs/bookmarks.md) · [Notion](docs/notion.md)
228
+ - [Docker integration](docs/docker.md)
229
+ - [Configuration](docs/configuration.md)
230
+ - [Architecture](docs/architecture.md)
231
+
232
+ ## Related
233
+
234
+ - [cursor-skills](https://github.com/gardusig/cursor-skills) — `@gh-*` AI workflows for issues/PRs
235
+ - Cloud drive epic: [cli #4](https://github.com/gardusig/cli/issues/4)
236
+ - Bootstrap spec: [cli #3](https://github.com/gardusig/cli/issues/3)
237
+ - Chrome: [cli #24](https://github.com/gardusig/cli/issues/24) · bookmarks scripts [#1](https://github.com/gardusig/cli/issues/1)
238
+ - Docker integration: [cli #9](https://github.com/gardusig/cli/issues/9)
@@ -0,0 +1,3 @@
1
+ """cli: git shortcuts and backup/sync workflows."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,4 @@
1
+ from cli.cli import run
2
+
3
+ if __name__ == "__main__":
4
+ run()
@@ -0,0 +1,71 @@
1
+ from __future__ import annotations
2
+
3
+ import typer
4
+
5
+ from cli import __version__
6
+ from cli.commands.backup import backup_app
7
+ from cli.commands.bookmarks import bookmarks_app
8
+ from cli.commands.chrome import chrome_app
9
+ from cli.commands.contest import contest_app
10
+ from cli.commands.docker import docker_app
11
+ from cli.commands.drive import drive_app
12
+ from cli.commands.gh import gh_app
13
+ from cli.commands.git import git_app
14
+ from cli.commands.links import links_app
15
+ from cli.commands.notion import notion_app
16
+ from cli.commands.publish import publish_app
17
+ from cli.commands.restore import restore_app
18
+ from cli.utils.logger import setup_logging
19
+
20
+ app = typer.Typer(
21
+ name="cli",
22
+ help="Git shortcuts and drive (tag zips) for macOS. Run `cli links` for docs and scripts.",
23
+ no_args_is_help=True,
24
+ )
25
+
26
+ app.add_typer(links_app, name="links")
27
+ app.add_typer(git_app, name="git")
28
+ app.add_typer(gh_app, name="gh")
29
+ app.add_typer(backup_app, name="backup", hidden=True)
30
+ app.add_typer(restore_app, name="restore")
31
+ app.add_typer(drive_app, name="drive")
32
+ app.add_typer(notion_app, name="notion")
33
+ app.add_typer(chrome_app, name="chrome")
34
+ app.add_typer(bookmarks_app, name="bookmarks", hidden=True)
35
+ app.add_typer(docker_app, name="docker")
36
+ app.add_typer(contest_app, name="contest")
37
+ app.add_typer(publish_app, name="publish")
38
+
39
+
40
+ @app.callback(invoke_without_command=True)
41
+ def main(
42
+ ctx: typer.Context,
43
+ version: bool = typer.Option(False, "--version", "-V", help="Show version and exit."),
44
+ verbose: bool = typer.Option(False, "--verbose", "-v"),
45
+ ) -> None:
46
+ setup_logging(verbose=verbose)
47
+ if version:
48
+ typer.echo(__version__)
49
+ raise typer.Exit()
50
+ if ctx.invoked_subcommand is None and not version:
51
+ typer.echo(ctx.get_help())
52
+ typer.echo("\nFull index: cli links | docs/README.md")
53
+ raise typer.Exit()
54
+ ctx.obj = {"verbose": verbose}
55
+
56
+
57
+ def run() -> None:
58
+ """CLI entrypoint — surfaces ExternalCallError as a clean user message."""
59
+ from cli.utils.config import load_local_env
60
+ from cli.utils.external_client import ExternalCallError
61
+
62
+ load_local_env()
63
+ try:
64
+ app()
65
+ except ExternalCallError as exc:
66
+ typer.echo(exc.user_message, err=True)
67
+ raise typer.Exit(1) from exc
68
+
69
+
70
+ # Short alias: cli g <cmd> == cli git <cmd>
71
+ app.add_typer(git_app, name="g", hidden=True)
@@ -0,0 +1 @@
1
+ """CLI command modules."""
@@ -0,0 +1,49 @@
1
+ """Hidden aliases for legacy `cli backup` → use `cli drive` instead."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import typer
6
+
7
+ from cli.commands.drive import delete_cmd, ingest_cmd, list_cmd, status_cmd
8
+
9
+ backup_app = typer.Typer(
10
+ help="(deprecated) use cli drive",
11
+ no_args_is_help=True,
12
+ hidden=True,
13
+ )
14
+ repository_app = typer.Typer(help="(deprecated)", hidden=True)
15
+
16
+
17
+ @backup_app.command("status")
18
+ def backup_status_alias() -> None:
19
+ """Deprecated: use `cli drive status`."""
20
+ status_cmd()
21
+
22
+
23
+ @repository_app.command("sync")
24
+ def repository_sync_alias(
25
+ path: str | None = typer.Argument(None),
26
+ ) -> None:
27
+ """Deprecated: use `cli drive ingest`."""
28
+ ingest_cmd(path)
29
+
30
+
31
+ @repository_app.command("list")
32
+ def repository_list_alias(
33
+ path: str | None = typer.Argument(None),
34
+ ) -> None:
35
+ """Deprecated: use `cli drive list`."""
36
+ list_cmd(path)
37
+
38
+
39
+ @repository_app.command("delete")
40
+ def repository_delete_alias(
41
+ path: str = typer.Argument(...),
42
+ tag: str = typer.Argument(...),
43
+ yes: bool = typer.Option(False, "--yes", "-y"),
44
+ ) -> None:
45
+ """Deprecated: use `cli drive delete`."""
46
+ delete_cmd(path, tag, yes=yes)
47
+
48
+
49
+ backup_app.add_typer(repository_app, name="repository")