@codedealer/pi-sync 0.0.0-stage → 1.1.0

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.
package/README.md CHANGED
@@ -1,3 +1,135 @@
1
- # Temporary Holding Version
1
+ # pi-sync
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A small Node.js CLI for applying explicitly declared Pi user-level resources from a setup repository. This repository contains the public CLI only; personal setup files belong in a separate private setup repository.
4
+
5
+ ## Requirements
6
+
7
+ - Node.js 18 or newer
8
+
9
+ ## Public installation and private-repository bootstrap
10
+
11
+ The npm package publishes the CLI and its first-party `pi-sync` skill only. Its explicit `files` allowlist excludes setup repositories, credentials, scratch planning files, and test fixtures. Install globally at user level where your npm/pnpm prefix is writable (configure a user-writable npm prefix if needed; do not use sudo), or use a one-shot runner:
12
+
13
+ ```sh
14
+ npm install --global @codedealer/pi-sync
15
+ # or: pnpm add --global @codedealer/pi-sync
16
+ # fallback without a global install:
17
+ pnpm dlx @codedealer/pi-sync bootstrap --repo-url https://github.com/OWNER/PRIVATE-SETUP.git
18
+ # optionally select a user-writable prerequisite install directory:
19
+ pnpm dlx @codedealer/pi-sync bootstrap --repo-url https://github.com/OWNER/PRIVATE-SETUP.git --bin-dir "$HOME/.local/bin"
20
+ # or:
21
+ npx --yes @codedealer/pi-sync bootstrap --repo-url https://github.com/OWNER/PRIVATE-SETUP.git
22
+ ```
23
+
24
+ Bootstrap clones into `~/.pi-sync/setup` by default (override with `--repo-dir PATH`) and applies to `PI_CODING_AGENT_DIR` or `~/.pi/agent` (override with `--agent-dir PATH`). It first tests Git access, then uses an authenticated GitHub CLI session when available. If Git access is unavailable and GitHub CLI is missing, bootstrap installs the official GitHub CLI release into `~/.local/bin` (override with `--bin-dir PATH`) when a supported platform artifact and writable destination are available, then pauses for authentication. If installation is unsupported or fails, it reports the exact missing tool and a no-sudo user action. The authentication handoff includes a `commandArgs` array for `gh auth login --hostname github.com`; when Pi Sync installed the tool locally, it points directly to that executable. After authenticating, rerun the returned resume arguments. Pi Sync never asks for, prints, or stores credentials. Once fetched, bootstrap validates the manifest and lockfile and installs every declared `requiredTools` prerequisite before applying anything; unsupported names are rejected and failed required tools stop setup. It declares the bundled skill as `{ "type": "skill", "source": "resources/skills/pi-sync", "target": "skills/pi-sync" }`, previews normal manifest-driven apply, and installs only through the ordinary conflict-safe apply flow. Existing declared or local content at a colliding path is reported and left untouched. Bootstrap does not commit or push repository changes.
25
+
26
+ ## Setup repository format (schema version 1)
27
+
28
+ Place `pi-sync.json` and `pi-sync.lock.json` at the root of the setup repository. Schema version 1 supports authored Pi resources — skill directories, extensions (single files or directories), prompts, agents, and roles — plus explicitly declared npm/GitHub Pi packages. Package pins are kept in the lockfile and ordinary apply never discovers newer releases.
29
+
30
+ ```json
31
+ {
32
+ "schemaVersion": 1,
33
+ "resources": [
34
+ {
35
+ "type": "skill",
36
+ "source": "resources/skills/example",
37
+ "target": "skills/example"
38
+ }
39
+ ],
40
+ "settings": [
41
+ {
42
+ "target": "settings.json",
43
+ "fields": [
44
+ { "path": "/theme", "value": "dark" },
45
+ { "path": "/ui/compactMode", "value": true }
46
+ ]
47
+ }
48
+ ],
49
+ "dependencies": [
50
+ { "type": "npm", "name": "example-package" },
51
+ { "type": "github", "repository": "OWNER/pi-extension" }
52
+ ],
53
+ "policy": { "minimumReleaseAgeDays": 7 },
54
+ "requiredTools": ["gh"]
55
+ }
56
+ ```
57
+
58
+ `requiredTools` is optional and contains only tool names from Pi Sync's supported installer catalog; it is not a place for shell commands, URLs, or arbitrary installer metadata. The initial catalog supports GitHub CLI (`gh`) from official GitHub CLI release artifacts on Linux/macOS (x64/arm64) and Windows x64. Each complete release archive is checked against a code-pinned SHA-256 digest from GitHub CLI's official [`gh_2.74.2_checksums.txt`](https://github.com/cli/cli/releases/download/v2.74.2/gh_2.74.2_checksums.txt) before extraction, writing, or execution. HTTPS alone is not treated as artifact integrity. The project does not independently verify a vendor signature; changing the version or digests requires reviewed source changes. Bootstrap checks command availability and reports detected versions. Missing catalogued tools are installed into `~/.local/bin` by default (or a user-selected `--bin-dir`), without sudo or persistent PATH/configuration changes. Unsupported platform/architecture, missing pin, digest mismatch, download, execution, or write failures stop bootstrap with the exact tool, reason, and user action; no required tool is silently skipped. WSL, containers, and remote Linux environments use the Linux artifact and user-local path. On Windows, use the selected bin directory in the current user's PATH yourself if you want to invoke the installed CLI outside Pi Sync. The default Node-only Pi Sync runtime itself does not depend on these external tools. Deterministic host-injected platform fixtures validate asset selection but do not claim native behavior. A dedicated CI workflow runs the intercepted-download install, execution, temporary user-path, and PATH-discovery smoke test on native macOS and Windows runners. Linux adapter tests run on the host test environment; they do not represent executed WSL, container, or remote environments, which remain unverified as deployment environments.
59
+
60
+ The policy is repository-owned; `minimumReleaseAgeDays` defaults to seven and may not be set below seven. Machines cannot override it. The corresponding lockfile starts empty until `pi-sync update` resolves mature releases:
61
+
62
+ ```json
63
+ {
64
+ "schemaVersion": 1,
65
+ "packages": [
66
+ { "type": "npm", "name": "example-package", "version": "1.2.3", "releasedAt": "2025-01-01T00:00:00.000Z" },
67
+ { "type": "github", "repository": "OWNER/pi-extension", "commit": "0123456789abcdef0123456789abcdef01234567", "tag": "v1.2.3", "releasedAt": "2025-01-01T00:00:00.000Z" }
68
+ ]
69
+ }
70
+ ```
71
+
72
+ The npm version is an exact published version; GitHub records the resolved immutable commit SHA plus tag/timestamp review metadata. Missing or incomplete timestamps, tags, or commit SHAs fail closed. Pi Sync pins only top-level package references. Pi controls transitive dependency resolution; Pi Sync does not provide a transitive lock or reproducibility guarantee.
73
+
74
+ Resources use per-type roots, and every source and target must stay inside its declared root:
75
+
76
+ | type | source | target | shape |
77
+ | --- | --- | --- | --- |
78
+ | `skill` | `resources/skills/NAME` | `skills/NAME` | directory |
79
+ | `extension` | `resources/extensions/NAME` | `extensions/NAME` | single file or directory |
80
+ | `prompt` | `resources/prompts/NAME.md` | `prompts/NAME.md` | single `.md` file |
81
+ | `agent` | `resources/agents/NAME.md` | `agents/NAME.md` | single `.md` file |
82
+ | `role` | `resources/roles/NAME.md` | `roles/NAME.md` | single `.md` file |
83
+
84
+ For non-skill types the source and target must use the same name. Symbolic links are rejected, paths use relative POSIX syntax, and no path may traverse outside its root. `node_modules` directories are never imported or managed; only authored content and portable extension configuration belong in the setup repository. Package-installed code is never imported. The optional schema-v1 `settings` list supports explicitly selected JSON Pointer fields in `settings.json`, `models.json`, `keybindings.json`, or `mcp.json`; each entry has a `target` and `fields` array of `{ "path": "/nested/key", "value": ... }`. Keep paths narrow and never declare literal credentials or machine-local values; Pi Sync rejects credential-like field names in managed setting values as a defense-in-depth check, with one exception: a credential-like key may hold a strict `$NAME` or `${NAME}` environment reference (which Pi interpolates at request time), so the reference is synced while the actual value stays configured per machine. This is not a general secret scanner. Unselected keys and values are preserved. A pre-existing selected setting that differs from its declared value is reported as a collision on first apply rather than overwritten. Unsupported schema fields, resource types, malformed dependency declarations, and lockfile package entries that do not exactly match manifest dependencies are rejected before the local installation is changed.
85
+
86
+ Apply stores its last-applied content baseline and recoverable backups under `<agent-dir>/.pi-sync/`, outside the setup repository. The baseline enables file-level three-way resource reconciliation and per-field settings reconciliation: repository-only changes are applied independently, local-only changes are preserved and reported as `local-divergence`, and overlapping changes are reported as conflicts without overwriting the conflicted item. Non-conflicting files and settings fields can still be reconciled in the same apply. Apply operations are serialized with a local lock; if a lock's owner process has exited, apply fails closed and asks the user to remove `.pi-sync/apply.lock` only after confirming that no apply process is running. Local state and backups are not part of the setup repository.
87
+
88
+ ## Commands
89
+
90
+ Run from the setup repository, or select it explicitly:
91
+
92
+ ```sh
93
+ pi-sync status [--repo PATH] [--agent-dir PATH]
94
+ pi-sync plan [--repo PATH] [--agent-dir PATH]
95
+ pi-sync apply [--repo PATH] [--agent-dir PATH] [--approve]
96
+ pi-sync add --type skill --source skills/NAME [--repo PATH] [--agent-dir PATH]
97
+ pi-sync add --type extension --source extensions/NAME [--repo PATH] [--agent-dir PATH]
98
+ pi-sync add --type prompt --source prompts/NAME.md [--repo PATH] [--agent-dir PATH]
99
+ pi-sync add --type agent --source agents/NAME.md [--repo PATH] [--agent-dir PATH]
100
+ pi-sync add --type role --source roles/NAME.md [--repo PATH] [--agent-dir PATH]
101
+ pi-sync add --type dependency --source npm:PACKAGE [--repo PATH]
102
+ pi-sync add --type dependency --source github:OWNER/REPOSITORY [--repo PATH]
103
+ pi-sync remove --type skill --target skills/NAME [--repo PATH] [--agent-dir PATH]
104
+ pi-sync remove --type extension --target extensions/NAME [--repo PATH] [--agent-dir PATH]
105
+ pi-sync remove --type prompt --target prompts/NAME.md [--repo PATH] [--agent-dir PATH]
106
+ pi-sync remove --type agent --target agents/NAME.md [--repo PATH] [--agent-dir PATH]
107
+ pi-sync remove --type role --target roles/NAME.md [--repo PATH] [--agent-dir PATH]
108
+ pi-sync remove --type dependency --source npm:PACKAGE [--repo PATH] [--agent-dir PATH] [--uninstall]
109
+ pi-sync remove --type dependency --source github:OWNER/REPOSITORY [--repo PATH] [--agent-dir PATH] [--uninstall]
110
+ pi-sync update [--repo PATH]
111
+ pi-sync update --apply [--repo PATH] [--agent-dir PATH] [--approve]
112
+ pi-sync update --apply --push [--repo PATH] [--agent-dir PATH] [--approve]
113
+ pi-sync push [--repo PATH]
114
+ pi-sync push --publish [--repo PATH]
115
+ pi-sync prune [--repo PATH] [--agent-dir PATH]
116
+ pi-sync prune --confirm [--repo PATH] [--agent-dir PATH]
117
+ ```
118
+
119
+ The target defaults to `PI_CODING_AGENT_DIR`, then `~/.pi/agent`. Each command emits JSON. `status` and `plan` report `install`, `update`, `unchanged`, `local-divergence`, `conflict`, or `collision` actions. Apply installs absent resources, reconciles tracked changes, and leaves unlisted resources and settings untouched. Unknown pre-existing resource paths and settings collisions are never overwritten.
120
+
121
+ `add` supports explicitly importing one resource of each type from the local Pi agent directory: a skill directory from `skills/NAME`, an extension file or directory from `extensions/NAME`, or a single `.md` file from `prompts/NAME.md`, `agents/NAME.md`, or `roles/NAME.md`. It copies only the selected file or directory to the matching `resources/<type-root>/` path and appends the corresponding manifest resource. `add --type dependency` declares only the selected npm package or GitHub repository; it does not edit pins or install packages. Run `update` to preview mature exact pins, then `update --apply` to explicitly apply eligible updates. Managed package updates must be performed through `pi-sync update`; ordinary `apply` uses lockfile pins without registry/GitHub update lookups. First-time installs require interactive confirmation or explicit `--approve` for non-interactive use. Known credential-store paths and likely secret material stop resource import and are reported for review; the scanner is best-effort and does not guarantee arbitrary content is secret-free.
122
+
123
+ Do not use the external `pi update --extensions` command for managed packages. It is unsupported for managed packages and may move them beyond their recorded pins; Pi Sync cannot intercept or prevent it. Use `pi-sync update` (and `--apply` after reviewing the plan) to update managed packages under the repository's release-age policy.
124
+
125
+ `remove` is the explicit way to remove a managed resource or dependency from the repository. Resource removal takes the resource `--type` and the manifest `--target` (for example `skills/NAME` or `prompts/NAME.md`); dependency removal takes `--type dependency` and `--source npm:PACKAGE` or `github:OWNER/REPOSITORY`. Removal is non-destructive by design: it validates the entry and repository, then deletes only the manifest entry and repository content (or the manifest dependency and its exact lockfile pin). It never modifies the local Pi installation and never queues or schedules anything for deletion. A removed resource stays installed indefinitely and is reported as `stale` by `status`, `plan`, and `apply` until it is explicitly pruned or re-declared in the manifest; local removal still happens only through the explicit `pi-sync prune --confirm` flow. A removed dependency stays installed locally by default. Passing `--uninstall` additionally runs `pi uninstall` for the removed pin (for example `npm:PACKAGE@1.2.3`) and, on success, clears that package's local Pi Sync pin bookkeeping; a failed uninstall is reported with exit code `1` without rolling back or altering the repository change. Publishing a removal uses the ordinary explicit push flow, and push accepts commits that remove previously managed paths.
126
+
127
+ ## Agent API and first-party skill
128
+
129
+ Every command emits JSON with `apiVersion: 1` and an `ok` boolean. Conflict responses include structured `conflicts`, `actions`, and/or `staleResources`; errors retain a readable `error` string and include `errorCode`, `details`, and `exitCode`. Exit `0` means the operation completed, `1` means an operational conflict, required first-install approval, package-manager failure, untrustworthy update metadata, a failed explicit `remove --uninstall`, or unsafe stale-resource operation, and `2` means invalid input or repository/manifest validation failed. In particular, apply leaves conflicted items untouched and returns `1`; inspect the JSON and re-plan rather than guessing or overwriting.
130
+
131
+ The agent workflow is documented in the first-party skill at `resources/skills/pi-sync/SKILL.md`, and that artifact is included in the published CLI package. Its manifest entry is `{ "type": "skill", "source": "resources/skills/pi-sync", "target": "skills/pi-sync" }`. The CLI apply installs declared Pi packages from exact lockfile pins and reconciles managed resources; it does not install undeclared bundled content.
132
+
133
+ Push is opt-in. `pi-sync push` previews the full patch for `pi-sync.json` (including policy), `pi-sync.lock.json`, and only resources explicitly registered by the manifest. It excludes unrelated staged/unstaged files, ignored files, and unregistered resources. Review the preview before `pi-sync push --publish`; the publishing flow re-displays the commit preview before creating an ordinary commit and fast-forward push. Git hooks are skipped for the Pi Sync commit so hooks cannot add paths outside the managed allowlist. Ahead commits are validated against the setup manifest as of that commit, the manifest before the commit, and the current managed set, so commits that add, edit, or remove managed paths — including a committed removal of a resource — are publishable. It stops on genuinely unrelated commits ahead of upstream or any remote advancement/divergence and never force-pushes. A rejected push leaves the managed commit local. `update --apply --push` is explicit authorization to publish after the approved package update and follows the same managed-only checks.
134
+
135
+ Resources previously applied by Pi Sync but removed from the manifest are reported as `stale` in status, plan, and apply output. Ordinary apply never removes them. The first-class way to reach this state is `pi-sync remove`, which keeps the resource installed until an explicit `pi-sync prune --confirm`. `prune` previews eligible stale resources without changing files; `prune --confirm` removes only stale resources that still exactly match their saved last-applied baseline. Modified stale resources are retained and reported. Removed resources are backed up under `.pi-sync/backups/`, and unrelated local resources are not touched.
package/package.json CHANGED
@@ -1,6 +1,23 @@
1
1
  {
2
2
  "name": "@codedealer/pi-sync",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.1.0",
4
+ "description": "A manifest-driven CLI for applying explicitly managed Pi resources.",
5
+ "type": "module",
6
+ "bin": {
7
+ "pi-sync": "src/cli.js"
8
+ },
9
+ "files": [
10
+ "README.md",
11
+ "src/cli.js",
12
+ "src/packages.js",
13
+ "src/prerequisites.js",
14
+ "resources/skills/pi-sync/SKILL.md"
15
+ ],
16
+ "scripts": {
17
+ "test": "node --test"
18
+ },
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
22
+ "license": "MIT"
23
+ }
@@ -0,0 +1,146 @@
1
+ ---
2
+ name: pi-sync
3
+ description: Inspect and safely operate the explicitly managed Pi setup using the pi-sync CLI.
4
+ ---
5
+
6
+ # Pi Sync agent workflow
7
+
8
+ Use `pi-sync` as the only interface for inspecting and changing resources managed by Pi Sync. Do not edit `pi-sync.json` or `pi-sync.lock.json` directly, copy managed resources into place behind Pi Sync, or install a managed dependency through another tool. Pi Sync supports authored Pi resources (skill directories, extension files or directories, and single `.md` prompt, agent, and role files), selected settings fields, and exact top-level npm/GitHub package pins. Push is opt-in and separate from apply/update unless the user explicitly authorizes it.
9
+
10
+ ## Concepts and boundaries
11
+
12
+ - The **setup repository** is the user's private repository and source of truth for explicitly managed resources. The public `pi-sync` CLI package does not contain a user's setup.
13
+ - The **local Pi installation** defaults to `~/.pi/agent`; `PI_CODING_AGENT_DIR` or `--agent-dir PATH` selects another directory.
14
+ - Only entries declared in the manifest are managed. Unlisted resources, credentials, sessions, caches, and other local-only state must remain local.
15
+ - The CLI's `.pi-sync/state.json` and backups are local bookkeeping under the agent directory, not repository content.
16
+ - Never add or copy auth files, API keys, OAuth tokens, sessions, caches, or other credentials into the setup repository. Secret detection is best effort, not a guarantee. A private repository is not a secret store.
17
+
18
+ ## Inspect before changing
19
+
20
+ Use the explicit repository path where possible:
21
+
22
+ ```sh
23
+ pi-sync status --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
24
+ pi-sync plan --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
25
+ ```
26
+
27
+ Omit `--agent-dir` to use `PI_CODING_AGENT_DIR`, then the default `~/.pi/agent`. `status` and `plan` are read-only. Review `actions`, `conflicts`, and `staleResources` in the JSON before taking an action. A stale resource remains installed; ordinary apply does not prune it.
28
+
29
+ ## Apply when requested
30
+
31
+ Run apply only when the user asks to reconcile the setup repository into the local installation:
32
+
33
+ ```sh
34
+ pi-sync apply --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
35
+ ```
36
+
37
+ Apply follows the reviewed repository state, preserves unlisted content and local-only divergence, and never overwrites a reported collision or conflicted item. A non-zero result requires stopping to explain the structured conflict or error; do not resolve conflicts by guessing, overwriting, or editing managed files around the CLI. Review a new plan after the user resolves the issue.
38
+
39
+ ## Add only a specifically selected local resource
40
+
41
+ When the user explicitly asks to add a named local resource, review its contents for private data and use the CLI with the matching type:
42
+
43
+ ```sh
44
+ pi-sync add --type skill --source skills/NAME --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
45
+ pi-sync add --type extension --source extensions/NAME --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
46
+ pi-sync add --type prompt --source prompts/NAME.md --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
47
+ pi-sync add --type agent --source agents/NAME.md --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
48
+ pi-sync add --type role --source roles/NAME.md --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
49
+ ```
50
+
51
+ `add` imports only the selected file or directory to the matching `resources/<type-root>/` path (skills are directories, extensions may be a single file or directory, and prompts/agents/roles must be single `.md` files directly under their root) and registers it in the manifest. It does not import unrelated files, and `node_modules` or package-installed code is never imported. Review any `reviewRequired` findings and remove suspected secret content before retrying; never bypass the check. A collision, invalid path, or malformed repository stops the operation. Do not install an added resource separately; apply it through Pi Sync when requested.
52
+
53
+ The first-party skill is shipped at `resources/skills/pi-sync/SKILL.md`. Fresh-machine setup uses the CLI bootstrap command, which safely declares it in the fetched setup repository as:
54
+
55
+ ```json
56
+ { "type": "skill", "source": "resources/skills/pi-sync", "target": "skills/pi-sync" }
57
+ ```
58
+
59
+ Bootstrap previews the manifest-driven apply before applying; it does not install this skill through a separate copy path or overwrite a collision. To start or resume setup, run `pi-sync bootstrap --repo-url https://github.com/OWNER/PRIVATE-SETUP.git` (optionally `--repo-dir PATH`, `--agent-dir PATH`, and `--bin-dir PATH`). The setup manifest may list tools in `requiredTools`, but only catalogued names are accepted; never add shell commands or arbitrary installer metadata. Bootstrap checks versions, installs supported missing tools into a user-writable directory without sudo, and stops with an actionable structured handoff if a required tool cannot be installed. Before extracting or executing GitHub CLI, it verifies the complete official release archive against the reviewed, code-pinned SHA-256 digest; a mismatch fails closed. When GitHub CLI is needed for private-repository access, bootstrap installs the official release if supported, then pauses for authentication. Follow the JSON handoff and use its `commandArgs` for `gh auth login --hostname github.com` (the executable may be in the local install directory), then rerun the returned command arguments. Bootstrap never prints or stores credentials and never commits or pushes the private setup repository.
60
+
61
+ ## Add and update managed packages only when asked
62
+
63
+ Add only a specifically requested dependency using a package identity, not a mutable version or branch:
64
+
65
+ ```sh
66
+ pi-sync add --type dependency --source npm:PACKAGE --repo /path/to/private-setup
67
+ pi-sync add --type dependency --source github:OWNER/REPOSITORY --repo /path/to/private-setup
68
+ ```
69
+
70
+ Then inspect the release-age-gated update plan and exact proposed lock entries:
71
+
72
+ ```sh
73
+ pi-sync update --repo /path/to/private-setup
74
+ ```
75
+
76
+ The setup repository's `pi-sync.json` owns `policy.minimumReleaseAgeDays` (at least seven days; defaults to seven). Machine arguments and local settings cannot lower it. npm pins are exact published versions; GitHub pins are immutable commit SHAs with release tag and timestamp retained for review. Missing or untrustworthy release timestamps/SHAs stop update discovery closed. Preview does not modify pins or install packages. Only apply an explicitly approved update:
77
+
78
+ ```sh
79
+ pi-sync update --apply --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
80
+ ```
81
+
82
+ Ordinary `apply` never queries newer registry/GitHub releases. It installs only lockfile pins; an initial third-party install requires an interactive confirmation or explicit `--approve` for non-interactive operation. For example:
83
+
84
+ ```sh
85
+ pi-sync apply --approve --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
86
+ ```
87
+
88
+ Pi Sync coordinates exact top-level pins through Pi's package manager. Pi currently owns transitive dependency resolution; Pi Sync does not provide or claim a reproducible transitive tree. Managed package updates must be performed through `pi-sync update --apply` under the repository's age policy. Do not run `pi update --extensions` when it could update managed packages: that command is unsupported for managed packages, may move them beyond their recorded exact pins, and Pi Sync cannot intercept or prevent it. When a user requests or discusses updating managed packages, explain/remind them to use Pi Sync instead. Do not claim that external Pi updates preserve pins or are constrained by Pi Sync, or claim/assume transitive dependencies are locked.
89
+
90
+ ## Remove only an explicitly requested resource or dependency
91
+
92
+ Removal is the inverse of add. Only when the user explicitly asks to remove a named resource or dependency, use the matching type:
93
+
94
+ ```sh
95
+ pi-sync remove --type skill --target skills/NAME --repo /path/to/private-setup
96
+ pi-sync remove --type extension --target extensions/NAME --repo /path/to/private-setup
97
+ pi-sync remove --type prompt --target prompts/NAME.md --repo /path/to/private-setup
98
+ pi-sync remove --type agent --target agents/NAME.md --repo /path/to/private-setup
99
+ pi-sync remove --type role --target roles/NAME.md --repo /path/to/private-setup
100
+ pi-sync remove --type dependency --source npm:PACKAGE --repo /path/to/private-setup
101
+ pi-sync remove --type dependency --source github:OWNER/REPOSITORY --repo /path/to/private-setup
102
+ ```
103
+
104
+ Removal is non-destructive by design: it deletes only the manifest entry and the repository content (or the manifest dependency and its exact lockfile pin). It never modifies the local Pi installation and never queues or schedules anything for deletion. A removed resource stays installed indefinitely and is reported as `stale` by `status`, `plan`, and `apply` until it is explicitly pruned or re-declared in the manifest. Local removal of a removed resource happens only through the existing explicit `pi-sync prune --confirm` flow, which keeps its baseline-match and backup protections. A removed dependency stays installed locally by default; only an explicit `pi-sync remove --type dependency --source ... --uninstall` uninstalls it by running `pi uninstall` for the removed pin, and an uninstall failure is reported (exit `1`) without rolling back or altering the repository change. `remove` validates the entry and the repository before changing anything, uses the atomic manifest write pattern, and exits `2` on invalid input or repository validation failure. Publish a removal with the normal explicit push flow (`pi-sync push` preview, then `--publish`), which accepts commits that remove previously managed paths.
105
+
106
+ ## Push only when explicitly authorized
107
+
108
+ Never infer permission to publish from a request to inspect, set up, apply, add, or update Pi Sync. First preview the exact managed-only commit contents:
109
+
110
+ ```sh
111
+ pi-sync push --repo /path/to/private-setup
112
+ ```
113
+
114
+ Review the listed paths and full patch with the user. The preview includes only `pi-sync.json` (including repository policy), `pi-sync.lock.json`, and files under resources explicitly registered by the manifest. Unrelated staged/unstaged files, ignored files, and unregistered resources are excluded. The publishing command prints the same managed commit preview to stderr immediately before committing/pushing. If the user explicitly asks to publish, run:
115
+
116
+ ```sh
117
+ pi-sync push --publish --repo /path/to/private-setup
118
+ ```
119
+
120
+ This creates an ordinary commit and uses a normal fast-forward push; force-push is never used. Git hooks are skipped for this Pi Sync commit so they cannot stage content outside the managed allowlist. Ahead commits are validated against the setup manifest as of that commit, the manifest before the commit, and the current managed set, so commits that add, edit, or remove managed paths — including a committed removal of a resource — are publishable. Pi Sync stops when local commits contain genuinely unrelated paths or when the upstream has advanced/diverged. Reconcile such branches manually and review a fresh preview; do not reset, rebase, merge, or force-push on the user's behalf. A rejected push preserves the local managed commit and changes for a later safe retry. `update --apply --push` is also an explicit publication request after approved package updates; the same full managed patch preview is shown immediately before publication. Preview with `pi-sync push` first whenever possible so the resulting managed commit paths are reviewed before that command.
121
+
122
+ ## Prune only on an explicit request
123
+
124
+ Pruning is destructive and is never part of ordinary apply. Do not infer permission to prune from a request to inspect, set up, or apply Pi Sync. Only when the user explicitly authorizes pruning, first show the preview:
125
+
126
+ ```sh
127
+ pi-sync prune --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
128
+ ```
129
+
130
+ Explain the listed targets and any retained modified/overlapping resources. If the user confirms the specific prune, then run:
131
+
132
+ ```sh
133
+ pi-sync prune --confirm --repo /path/to/private-setup --agent-dir "$PI_CODING_AGENT_DIR"
134
+ ```
135
+
136
+ Pi Sync removes only previously managed stale resources that still match their saved baseline, and backs them up. Modified or overlapping resources are retained for manual resolution. Never delete them by another means to work around that protection.
137
+
138
+ ## JSON and exit status
139
+
140
+ Every response is JSON with `apiVersion: 1`. Successful operations have `ok: true`. Conflicts and blocked operations have `ok: false`; inspect structured `conflicts`, `actions`, `staleResources`, `reviewRequired`, or `details` instead of parsing prose. Error responses include `errorCode`, a readable `error`, `details`, and `exitCode`.
141
+
142
+ - Exit `0`: operation succeeded (including a read-only plan that reports eligible actions and a completed `remove`).
143
+ - Exit `1`: an operational conflict, required first-install approval, package-manager failure, untrustworthy update metadata, a failed explicit `remove --uninstall`, or a stale item that cannot safely be pruned. Stop and explain the structured result; do not override it.
144
+ - Exit `2`: invalid input, unsupported operation, or repository/manifest validation failure. Correct the input only with the user's authorization, then re-plan.
145
+
146
+ `apply` can complete independent non-conflicting resource actions while leaving conflicted items untouched; its exit status remains `1` when any conflict was reported. Bootstrap returns structured authentication and prerequisite-install handoffs; follow them without exposing credentials, invoking sudo, or installing outside the user-writable destination. `update` returns a JSON preview by default and changes lockfile pins or installs packages only with `--apply`. `update --apply --push` requires explicit update approval plus explicit publication authorization and then follows the same managed-only fast-forward push rules. `--approve` is solely for explicit first-time package-install approval; it does not lower release-age policy or resolve conflicts. Push previews and results use the versioned JSON API. Pi Sync pins only top-level package versions/revisions; transitive reproducibility is not guaranteed.