pi-sync-cli 0.1.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -216,3 +216,6 @@ __marimo__/
216
216
 
217
217
  # Streamlit
218
218
  .streamlit/secrets.toml
219
+
220
+ # pi background task artifacts
221
+ .pi/tasks/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pi-sync-cli
3
- Version: 0.1.0
3
+ Version: 0.3.0
4
4
  Summary: Sync pi agent config and extensions between hosts over rsync
5
5
  Project-URL: Repository, https://github.com/say4n/pi-sync
6
6
  Project-URL: Issues, https://github.com/say4n/pi-sync/issues
@@ -62,6 +62,8 @@ Host-local state is deliberately never touched: `sessions/`, `npm/`,
62
62
  | `--delete` | mirror `extensions/` exactly (deletes extras on the destination) |
63
63
  | `--dry-run` | report changes, copy nothing |
64
64
  | `-x, --exclude PATTERN` | skip matching files (repeatable) |
65
+ | `--install` | install pi on hosts that lack it, without prompting |
66
+ | `--uninstall` | remove pi from the host instead of syncing (config is kept) |
65
67
  | `--local-dir` | default `$PI_CODING_AGENT_DIR` or `~/.pi/agent` |
66
68
  | `--remote-dir` | default `~/.pi/agent` |
67
69
  | `-v` / `--verbose` | print each rsync command and its output |
@@ -69,6 +71,74 @@ Host-local state is deliberately never touched: `sessions/`, `npm/`,
69
71
  Multiple hosts are accepted: `pi-sync a b c`. Exits non-zero if any host is
70
72
  unreachable or any transfer fails.
71
73
 
74
+ ## Host preflight
75
+
76
+ Each host gets one ssh probe that reports reachability and pi's location in the
77
+ same round trip.
78
+
79
+ If pi is missing, pi-sync hands the terminal to pi's own installer
80
+ (`curl -fsSL https://pi.dev/install.sh | sh`), which keeps full control: its
81
+ prompts (its install/uninstall/do-nothing menu, a Node.js install, a sudo
82
+ password) work normally, and the sync continues once it exits. pi-sync does not
83
+ add a confirmation of its own, because the installer already asks. Without a
84
+ terminal it runs unattended under `--install`, stays quiet otherwise, and
85
+ `--dry-run` never installs anything.
86
+
87
+ The installer's exit status is not treated as proof: its "do nothing" choice
88
+ exits 0, so the host is re-probed afterwards and reported honestly.
89
+
90
+ **A host without pi is skipped** — copying into a host that has never run pi is
91
+ not useful and usually fails anyway, since there is no agent directory to copy
92
+ into. That covers an installer run that installed nothing, a failed install, and
93
+ a non-interactive run without `--install`; a skipped host makes the run exit
94
+ non-zero. After a successful install pi-sync creates the agent directory,
95
+ because rsync will not create intermediate directories on its own.
96
+
97
+ The probe checks `command -v pi` plus the usual install locations
98
+ (`~/.local/bin`, `~/.pi/bin`, `~/.pi/agent/bin`, linuxbrew, homebrew,
99
+ `/usr/local/bin`), because a non-interactive ssh session does not source the
100
+ host's shell init — on a linuxbrew host `command -v pi` alone misses it.
101
+
102
+ ## Uninstalling
103
+
104
+ `--uninstall` removes pi from the host instead of syncing. It runs
105
+ `npm uninstall -g --prefix <prefix> @earendil-works/pi-coding-agent`, deriving the
106
+ prefix from where pi actually lives (as the official installer does), then
107
+ re-probes to confirm the binary is really gone — npm can exit 0 having removed
108
+ nothing. `~/.pi/agent` is deliberately left untouched, since that is your config
109
+ rather than the CLI.
110
+
111
+ This exists because the official installer can only uninstall through its
112
+ interactive menu: its unattended mode always installs or reinstalls. If the
113
+ uninstall fails, that is usually a managed install
114
+ (`PI_EXPERIMENTAL=1`, under `<agent dir>/install`) — run
115
+ `curl -fsSL https://pi.dev/install.sh | sh` on the host and choose `u`.
116
+
117
+ ## Shell completions
118
+
119
+ Host arguments complete from `~/.ssh/config`, following `Include` directives and
120
+ skipping wildcard entries:
121
+
122
+ ```bash
123
+ # bash
124
+ _PI_SYNC_COMPLETE=bash_source pi-sync > ~/.pi-sync-complete.bash
125
+ echo 'source ~/.pi-sync-complete.bash' >> ~/.bashrc
126
+
127
+ # zsh
128
+ _PI_SYNC_COMPLETE=zsh_source pi-sync > ~/.pi-sync-complete.zsh
129
+ echo 'source ~/.pi-sync-complete.zsh' >> ~/.zshrc
130
+
131
+ # fish (config.fish)
132
+ _PI_SYNC_COMPLETE=fish_source pi-sync | source
133
+ ```
134
+
135
+ Writing the script out (rather than `eval "$(_PI_SYNC_COMPLETE=bash_source pi-sync)"`
136
+ on every shell start) keeps shell startup fast, since the eval form runs the
137
+ binary each time. PowerShell works the same way via `powershell_source`.
138
+
139
+ Completions are read from the config at completion time, so new hosts appear
140
+ without regenerating anything.
141
+
72
142
  ## Caveats
73
143
 
74
144
  - `settings.json` is machine-written by pi (`lastChangelogVersion` bumps, UI
@@ -0,0 +1,145 @@
1
+ # pi-sync
2
+
3
+ Sync pi agent config between hosts over rsync, using your existing ssh config
4
+ for routing (so `~/.ssh/config` aliases just work).
5
+
6
+ ```bash
7
+ pi-sync tinfoil # push config + extensions
8
+ pi-sync --config laptop # only models.json and settings.json
9
+ pi-sync --pull --all tinfoil # fetch the host's config back
10
+ pi-sync --dry-run --all a b # preview against two hosts
11
+ ```
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ # the PyPI package is pi-sync-cli; it installs the `pi-sync` command
17
+ pipx install pi-sync-cli
18
+ pipx install git+ssh://git@github.com/say4n/pi-sync # from source (needs access)
19
+ ```
20
+
21
+ Requires Python 3.10+. `uv tool install` works in place of `pipx install`.
22
+
23
+ ## What syncs
24
+
25
+ Only the declarative parts of the agent dir:
26
+
27
+ | Group | Files |
28
+ | --- | --- |
29
+ | `--config` | `models.json`, `settings.json` |
30
+ | `--extensions` | `extensions/` |
31
+ | `--auth` | `auth.json` — secrets, opt-in, warns on push |
32
+
33
+ `--all` is `--config` + `--extensions` (also the default when no flag is given).
34
+
35
+ Host-local state is deliberately never touched: `sessions/`, `npm/`,
36
+ `models-store.json` (regenerated from the pi.dev catalog), `ayu/`, `bin/`,
37
+ `trust.json`.
38
+
39
+ ## Flags
40
+
41
+ | Flag | Effect |
42
+ | --- | --- |
43
+ | `--all` / `--config` / `--extensions` / `--auth` | what to sync |
44
+ | `--pull` | host → local instead of local → host |
45
+ | `--delete` | mirror `extensions/` exactly (deletes extras on the destination) |
46
+ | `--dry-run` | report changes, copy nothing |
47
+ | `-x, --exclude PATTERN` | skip matching files (repeatable) |
48
+ | `--install` | install pi on hosts that lack it, without prompting |
49
+ | `--uninstall` | remove pi from the host instead of syncing (config is kept) |
50
+ | `--local-dir` | default `$PI_CODING_AGENT_DIR` or `~/.pi/agent` |
51
+ | `--remote-dir` | default `~/.pi/agent` |
52
+ | `-v` / `--verbose` | print each rsync command and its output |
53
+
54
+ Multiple hosts are accepted: `pi-sync a b c`. Exits non-zero if any host is
55
+ unreachable or any transfer fails.
56
+
57
+ ## Host preflight
58
+
59
+ Each host gets one ssh probe that reports reachability and pi's location in the
60
+ same round trip.
61
+
62
+ If pi is missing, pi-sync hands the terminal to pi's own installer
63
+ (`curl -fsSL https://pi.dev/install.sh | sh`), which keeps full control: its
64
+ prompts (its install/uninstall/do-nothing menu, a Node.js install, a sudo
65
+ password) work normally, and the sync continues once it exits. pi-sync does not
66
+ add a confirmation of its own, because the installer already asks. Without a
67
+ terminal it runs unattended under `--install`, stays quiet otherwise, and
68
+ `--dry-run` never installs anything.
69
+
70
+ The installer's exit status is not treated as proof: its "do nothing" choice
71
+ exits 0, so the host is re-probed afterwards and reported honestly.
72
+
73
+ **A host without pi is skipped** — copying into a host that has never run pi is
74
+ not useful and usually fails anyway, since there is no agent directory to copy
75
+ into. That covers an installer run that installed nothing, a failed install, and
76
+ a non-interactive run without `--install`; a skipped host makes the run exit
77
+ non-zero. After a successful install pi-sync creates the agent directory,
78
+ because rsync will not create intermediate directories on its own.
79
+
80
+ The probe checks `command -v pi` plus the usual install locations
81
+ (`~/.local/bin`, `~/.pi/bin`, `~/.pi/agent/bin`, linuxbrew, homebrew,
82
+ `/usr/local/bin`), because a non-interactive ssh session does not source the
83
+ host's shell init — on a linuxbrew host `command -v pi` alone misses it.
84
+
85
+ ## Uninstalling
86
+
87
+ `--uninstall` removes pi from the host instead of syncing. It runs
88
+ `npm uninstall -g --prefix <prefix> @earendil-works/pi-coding-agent`, deriving the
89
+ prefix from where pi actually lives (as the official installer does), then
90
+ re-probes to confirm the binary is really gone — npm can exit 0 having removed
91
+ nothing. `~/.pi/agent` is deliberately left untouched, since that is your config
92
+ rather than the CLI.
93
+
94
+ This exists because the official installer can only uninstall through its
95
+ interactive menu: its unattended mode always installs or reinstalls. If the
96
+ uninstall fails, that is usually a managed install
97
+ (`PI_EXPERIMENTAL=1`, under `<agent dir>/install`) — run
98
+ `curl -fsSL https://pi.dev/install.sh | sh` on the host and choose `u`.
99
+
100
+ ## Shell completions
101
+
102
+ Host arguments complete from `~/.ssh/config`, following `Include` directives and
103
+ skipping wildcard entries:
104
+
105
+ ```bash
106
+ # bash
107
+ _PI_SYNC_COMPLETE=bash_source pi-sync > ~/.pi-sync-complete.bash
108
+ echo 'source ~/.pi-sync-complete.bash' >> ~/.bashrc
109
+
110
+ # zsh
111
+ _PI_SYNC_COMPLETE=zsh_source pi-sync > ~/.pi-sync-complete.zsh
112
+ echo 'source ~/.pi-sync-complete.zsh' >> ~/.zshrc
113
+
114
+ # fish (config.fish)
115
+ _PI_SYNC_COMPLETE=fish_source pi-sync | source
116
+ ```
117
+
118
+ Writing the script out (rather than `eval "$(_PI_SYNC_COMPLETE=bash_source pi-sync)"`
119
+ on every shell start) keeps shell startup fast, since the eval form runs the
120
+ binary each time. PowerShell works the same way via `powershell_source`.
121
+
122
+ Completions are read from the config at completion time, so new hosts appear
123
+ without regenerating anything.
124
+
125
+ ## Caveats
126
+
127
+ - `settings.json` is machine-written by pi (`lastChangelogVersion` bumps, UI
128
+ toggles), so two hosts pushing it will overwrite each other's local
129
+ preferences. Sync it when you change `packages`, not reflexively.
130
+ - Extensions that write runtime files inside their own directory (logs,
131
+ checkpoints) get those files synced too, and each host's copy is overwritten by
132
+ whichever side pushed last — exclude them with `-x '*/logs/*'`.
133
+ - Extension versions are whatever each host has installed; pin them in
134
+ `settings.json` (`npm:pi-lens@1.2.3`) if you need hosts identical.
135
+ - `--auth` copies API keys in the clear. Prefer `OPENCODE_API_KEY` (and friends)
136
+ in the environment where you can.
137
+ - Remote paths go through the host's shell, so `~` expands there as usual.
138
+
139
+ ## Development
140
+
141
+ ```bash
142
+ uv sync
143
+ uv run pytest
144
+ uv run pi-sync --help
145
+ ```
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pi-sync-cli"
3
- version = "0.1.0"
3
+ version = "0.3.0"
4
4
  description = "Sync pi agent config and extensions between hosts over rsync"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -0,0 +1,5 @@
1
+ {
2
+ "venvPath": ".",
3
+ "venv": ".venv",
4
+ "include": ["src", "tests"]
5
+ }