@compozy/skeeper 0.1.0 → 0.2.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 +183 -211
- package/package.json +16 -16
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<div align="center">
|
|
2
|
-
<
|
|
3
|
-
|
|
2
|
+
<p>
|
|
3
|
+
<img src="docs/assets/skeeper-readme-hero.png" alt="skeeper hero showing AI specs syncing into a sidecar Git repository" width="100%">
|
|
4
|
+
</p>
|
|
4
5
|
<p>
|
|
5
6
|
<a href="https://github.com/compozy/skeeper/actions/workflows/ci.yml">
|
|
6
7
|
<img src="https://github.com/compozy/skeeper/actions/workflows/ci.yml/badge.svg" alt="CI">
|
|
@@ -8,9 +9,6 @@
|
|
|
8
9
|
<a href="https://pkg.go.dev/github.com/compozy/skeeper">
|
|
9
10
|
<img src="https://pkg.go.dev/badge/github.com/compozy/skeeper.svg" alt="Go Reference">
|
|
10
11
|
</a>
|
|
11
|
-
<a href="https://goreportcard.com/report/github.com/compozy/skeeper">
|
|
12
|
-
<img src="https://goreportcard.com/badge/github.com/compozy/skeeper" alt="Go Report Card">
|
|
13
|
-
</a>
|
|
14
12
|
<a href="LICENSE">
|
|
15
13
|
<img src="https://img.shields.io/badge/License-MIT-blue.svg" alt="License: MIT">
|
|
16
14
|
</a>
|
|
@@ -20,331 +18,305 @@
|
|
|
20
18
|
</p>
|
|
21
19
|
</div>
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
## ✨ Highlights
|
|
21
|
+
Spec docs drift from code, or they bloat every PR. Skeeper picks neither.
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
- **Shared sidecars without collisions.** `directory` namespaces each source repo inside one sidecar remote, including both stored paths and pushed branches.
|
|
29
|
-
- **Edit specs where they belong.** Spec files stay next to the code they describe. `skeeper` mirrors them into `.skeeper/` for you.
|
|
30
|
-
- **A post-commit hook that never breaks your commit.** 750 ms foreground budget; on failure, the sync queues locally and retries on the next manual `skeeper sync`.
|
|
31
|
-
- **Branch-aware mirroring.** Sidecar branches track main-tree branches, so feature work and `main` stay isolated.
|
|
32
|
-
- **Fresh-clone hydration.** `skeeper hydrate` restores matched specs into a new clone so teammates start with full context.
|
|
33
|
-
- **Glob-based pattern matching.** Doublestar globs (`**/SPEC.md`, `docs/specs/**`, `.claude/plans/**`) — match specs the way you actually organize them.
|
|
34
|
-
- **Shells out to `git` and `gh`.** Reuses your existing GitHub auth. Every operation is debuggable with the same Git commands you already know.
|
|
35
|
-
- **Single static binary, zero runtime deps.** Linux, macOS, Windows on amd64/arm64. CGO disabled.
|
|
23
|
+
It mirrors `SPEC.md`, ADRs, RFCs, and AI plan files into a sidecar Git repository and commits a tiny `skeeper.lock` to your main repo that pins every commit to exact sidecar commits. PR diffs stay focused on code, spec history stays auditable, and nothing silently drifts because the managed Git hooks fail the commit if the sidecar state cannot be proven.
|
|
36
24
|
|
|
37
|
-
##
|
|
25
|
+
## ✨ Highlights
|
|
38
26
|
|
|
39
|
-
|
|
27
|
+
- **Lockfile-backed reliability.** `skeeper.lock` records sidecar URL, source branch, namespace branch, sidecar commit, per-namespace digest, file count, and byte count.
|
|
28
|
+
- **Strict managed hooks.** The managed `pre-commit` and `pre-merge-commit` hooks sync staged content, push the sidecar, write and stage `skeeper.lock`, and fail closed. The managed `pre-push` hook verifies the lock against the sidecar remote.
|
|
29
|
+
- **Specs stay local to their code.** Edit `SPEC.md`, `docs/specs/**`, `.claude/plans/**`, ADRs, RFCs, or custom globs where they naturally belong.
|
|
30
|
+
- **Shared sidecars without collisions.** Namespaces isolate stored paths and sidecar branches inside one sidecar remote.
|
|
31
|
+
- **Branch-aware history.** Namespace branches use `<namespace>/__branches__/<source-branch>`.
|
|
32
|
+
- **Fresh-clone hydration.** `skeeper hydrate` restores files from the locked sidecar commits, not a best-effort latest branch.
|
|
33
|
+
- **Agent-friendly commands.** `status`, `sync`, `verify`, `fsck`, `hooks check`, `repair status`, `pattern`, `adopt`, and `untrack` all support deterministic output where needed.
|
|
34
|
+
- **Skill for AI agents.** A bundled skill at [`.agents/skills/skeeper/SKILL.md`](.agents/skills/skeeper/SKILL.md) teaches coding agents the strict-sync workflow, namespaces, and recovery commands.
|
|
40
35
|
|
|
41
|
-
|
|
42
|
-
brew tap compozy/compozy
|
|
43
|
-
brew install --cask skeeper
|
|
44
|
-
```
|
|
36
|
+
## 🎯 Who Is This For
|
|
45
37
|
|
|
46
|
-
|
|
38
|
+
- Teams using AI coding agents that produce `SPEC.md`, PRD, TechSpec, and plan markdown next to code.
|
|
39
|
+
- Engineering organizations running ADRs, RFCs, and design docs in-repo without making every PR a docs+code review.
|
|
40
|
+
- Solo developers who want full spec history (`git log`, `git blame`, branches, PRs) without polluting their main repository's diff.
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
npm install -g @compozy/skeeper
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
#### Go
|
|
42
|
+
## 📦 Installation
|
|
53
43
|
|
|
54
44
|
```bash
|
|
55
45
|
go install github.com/compozy/skeeper/cmd/skeeper@latest
|
|
56
46
|
```
|
|
57
47
|
|
|
58
|
-
|
|
48
|
+
Other release channels are available through GitHub Releases, Homebrew, NPM, and the distroless Docker image.
|
|
59
49
|
|
|
60
|
-
|
|
61
|
-
git clone git@github.com:compozy/skeeper.git
|
|
62
|
-
cd skeeper && make verify && go build -o bin/skeeper ./cmd/skeeper
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
#### Docker
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
git clone git@github.com:compozy/skeeper.git
|
|
69
|
-
cd skeeper && make docker-build # builds skeeper:dev (distroless, nonroot)
|
|
70
|
-
docker run --rm -v "$PWD:/workspace" -w /workspace skeeper:dev status
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
#### Prerequisites
|
|
50
|
+
Prerequisites:
|
|
74
51
|
|
|
75
52
|
- `git` on `PATH`
|
|
76
|
-
- `gh`
|
|
53
|
+
- `gh` only when `skeeper init` creates a new GitHub sidecar repo
|
|
77
54
|
|
|
78
55
|
## 🔄 How It Works
|
|
79
56
|
|
|
80
|
-
Spec files live
|
|
81
|
-
|
|
82
|
-
On every `git commit`, the managed post-commit hook runs `skeeper sync --hook` with a 750 ms foreground budget. `skeeper` matches files against your patterns, copies them into `.skeeper/`, commits with a reference to the main commit SHA, and pushes to the sidecar remote.
|
|
83
|
-
|
|
84
|
-
When `directory` is configured, files are stored under that namespace in the sidecar and branches are pushed as `<directory>/__branches__/<source-branch>`. For example, `directory: skeeper` on source branch `main` stores `src/auth/SPEC.md` as `skeeper/src/auth/SPEC.md` and pushes sidecar branch `skeeper/__branches__/main`.
|
|
57
|
+
Spec files live in the main worktree but are ignored by the main repository through a managed `.gitignore` block. The sidecar repository stores mirrored files under `<namespace>/<path>` and pushes them to `<namespace>/__branches__/<source-branch>`.
|
|
85
58
|
|
|
86
|
-
|
|
59
|
+
On commit, the managed `pre-commit` block runs last. On automatic merge commits, the managed `pre-merge-commit` block runs the same strict sync path because Git does not run `pre-commit` for merge commits. Both hooks build a plan from the staged index plus explicitly owned ignored/untracked spec paths, fetch and rebase sidecar branches, mirror content into `.skeeper/`, commit and push the sidecar, write `skeeper.lock`, and stage that lock before Git creates the main commit.
|
|
87
60
|
|
|
88
61
|
```mermaid
|
|
89
|
-
flowchart
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
62
|
+
flowchart TD
|
|
63
|
+
Start([👤 git commit]):::user --> UserHook[🪝 Existing user hook content]:::user
|
|
64
|
+
UserHook --> Block
|
|
65
|
+
|
|
66
|
+
subgraph Block [📦 Skeeper pre-commit block]
|
|
67
|
+
direction TB
|
|
68
|
+
S1[🧮 Reconcile staged specs<br/>+ ownership] --> S2[🔄 Fetch & rebase<br/>sidecar branch]
|
|
69
|
+
S2 --> S3[🪞 Mirror namespace files<br/>into .skeeper/]
|
|
70
|
+
S3 --> S4[📤 Commit & push sidecar]
|
|
71
|
+
S4 --> S5[🔒 Write & stage<br/>skeeper.lock]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
Block --> Commit[✅ Main commit proceeds]:::ok
|
|
75
|
+
Commit --> Push([🚀 git push]):::user
|
|
76
|
+
Push --> Verify[🔍 Skeeper pre-push verify]:::skeeper
|
|
77
|
+
Verify --> Done([🎉 Sidecar verified]):::ok
|
|
78
|
+
|
|
79
|
+
classDef user fill:#dbeafe,stroke:#1d4ed8,color:#0c1e3e
|
|
80
|
+
classDef skeeper fill:#fef3c7,stroke:#b45309,color:#3b2c00
|
|
81
|
+
classDef ok fill:#dcfce7,stroke:#15803d,color:#052e16
|
|
82
|
+
class S1,S2,S3,S4,S5 skeeper
|
|
99
83
|
```
|
|
100
84
|
|
|
85
|
+
If sync fails, the commit fails. This is intentional: a committed main change should not silently drift from the sidecar. The audited bypass is `SKEEPER_SKIP=1`; it records `.git/skeeper/bypass.json`, prints a warning, and `pre-push`, `status`, `fsck`, and `verify` continue to surface stale-lock diagnostics until `skeeper sync` repairs the state. `git commit --no-verify` is unsupported because Git skips all hook code and cannot record an audit trail.
|
|
86
|
+
|
|
101
87
|
## ⚙️ Configuration
|
|
102
88
|
|
|
103
|
-
`skeeper init` writes `.skeeper.yml` at the
|
|
89
|
+
`skeeper init` writes `.skeeper.yml` at the repository root. Commit it.
|
|
104
90
|
|
|
105
91
|
```yaml
|
|
106
|
-
# Required: sidecar repository URL
|
|
107
92
|
sidecar: git@github.com:user/myproject-specs.git
|
|
108
93
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
94
|
+
namespaces:
|
|
95
|
+
- name: project
|
|
96
|
+
patterns:
|
|
97
|
+
- "**/SPEC.md"
|
|
98
|
+
- "docs/specs/**"
|
|
99
|
+
- ".claude/plans/**"
|
|
100
|
+
- "**/*.spec.md"
|
|
101
|
+
exclude:
|
|
102
|
+
- "docs/specs/private/**"
|
|
118
103
|
|
|
119
|
-
# Optional: install one-liner shown to teammates after `skeeper hydrate`
|
|
120
104
|
bootstrap: brew tap compozy/compozy && brew install --cask skeeper
|
|
121
105
|
```
|
|
122
106
|
|
|
123
|
-
|
|
107
|
+
Advanced operational defaults are optional:
|
|
124
108
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
109
|
+
```yaml
|
|
110
|
+
settings:
|
|
111
|
+
guardrails:
|
|
112
|
+
max_files: 100
|
|
113
|
+
max_bytes: 10485760
|
|
114
|
+
hooks:
|
|
115
|
+
pre_push_timeout: 30s
|
|
116
|
+
allow_skip_env: SKEEPER_SKIP
|
|
117
|
+
|
|
118
|
+
namespaces:
|
|
119
|
+
- name: generated
|
|
120
|
+
patterns:
|
|
121
|
+
- "generated/specs/**"
|
|
122
|
+
respect_gitignore: false
|
|
123
|
+
```
|
|
135
124
|
|
|
136
|
-
|
|
125
|
+
Rules:
|
|
137
126
|
|
|
138
|
-
|
|
127
|
+
- Unknown keys are rejected.
|
|
128
|
+
- Every namespace needs a `name` and at least one glob in `patterns`.
|
|
129
|
+
- `exclude` is the only public exclusion mechanism. Negative globs in `patterns` are rejected.
|
|
130
|
+
- Ownership must be unique. If two namespaces own the same file, the plan fails and asks for an `exclude` fix.
|
|
131
|
+
- `respect_gitignore: false` bypasses root `.gitignore`, nested `.gitignore`, `.git/info/exclude`, and global excludes for that namespace. `.git/` and `.skeeper/` are always excluded.
|
|
139
132
|
|
|
140
|
-
|
|
141
|
-
go install github.com/compozy/skeeper/cmd/skeeper@latest
|
|
142
|
-
```
|
|
133
|
+
Local-only state lives under `.git/skeeper/`:
|
|
143
134
|
|
|
144
|
-
|
|
135
|
+
| File | Purpose |
|
|
136
|
+
| ------------------ | ---------------------------------------------- |
|
|
137
|
+
| `transaction.json` | Current resumable mutating operation and phase |
|
|
138
|
+
| `bypass.json` | Latest audited strict-hook bypass |
|
|
145
139
|
|
|
146
|
-
|
|
140
|
+
## 🚀 Quick Start
|
|
147
141
|
|
|
148
142
|
```bash
|
|
149
143
|
skeeper init
|
|
150
144
|
```
|
|
151
145
|
|
|
152
|
-
Interactive
|
|
146
|
+
Interactive init asks for the sidecar mode, repository name or URL, namespace, bootstrap command, and optional extra context globs. With flags:
|
|
153
147
|
|
|
154
148
|
```bash
|
|
155
149
|
skeeper init \
|
|
156
150
|
--sidecar-name myproject-specs \
|
|
157
151
|
--visibility private \
|
|
158
|
-
--
|
|
152
|
+
--namespace project \
|
|
159
153
|
--patterns "**/SPEC.md" \
|
|
160
|
-
--patterns "
|
|
154
|
+
--patterns "docs/specs/**"
|
|
161
155
|
```
|
|
162
156
|
|
|
163
|
-
|
|
157
|
+
Use an existing shared sidecar:
|
|
164
158
|
|
|
165
159
|
```bash
|
|
166
160
|
skeeper init \
|
|
167
161
|
--sidecar git@github.com:user/shared-specs.git \
|
|
168
|
-
--
|
|
162
|
+
--namespace project \
|
|
169
163
|
--patterns "**/SPEC.md"
|
|
170
164
|
```
|
|
171
165
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
### 3. Edit specs and commit normally
|
|
166
|
+
Then edit specs and commit normally:
|
|
175
167
|
|
|
176
168
|
```bash
|
|
177
169
|
$EDITOR src/auth/SPEC.md
|
|
178
|
-
git add .
|
|
170
|
+
git add src/auth/service.go src/auth/SPEC.md
|
|
179
171
|
git commit -m "auth: design OAuth provider flow"
|
|
180
172
|
```
|
|
181
173
|
|
|
182
|
-
The
|
|
174
|
+
The `pre-commit` and `pre-merge-commit` hooks mirror specs and stage `skeeper.lock`. If a hook stages a new lock, review it and include it in the commit.
|
|
183
175
|
|
|
184
|
-
|
|
176
|
+
## 🛟 Failed Sync Recovery
|
|
177
|
+
|
|
178
|
+
Inspect local repair state:
|
|
185
179
|
|
|
186
180
|
```bash
|
|
187
|
-
skeeper status
|
|
188
|
-
skeeper log src/auth/SPEC.md # sidecar Git history for one file
|
|
181
|
+
skeeper repair status
|
|
189
182
|
```
|
|
190
183
|
|
|
191
|
-
|
|
184
|
+
Resume the recorded operation when network/auth/sidecar contention has been fixed:
|
|
192
185
|
|
|
193
186
|
```bash
|
|
194
|
-
|
|
195
|
-
cd myproject
|
|
196
|
-
skeeper hydrate
|
|
187
|
+
skeeper repair resume
|
|
197
188
|
```
|
|
198
189
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
### 6. Recover from a failed sync
|
|
202
|
-
|
|
203
|
-
If the hook ever queued work (network blip, push rejection):
|
|
190
|
+
Abort only before the main index has been mutated:
|
|
204
191
|
|
|
205
192
|
```bash
|
|
206
|
-
skeeper
|
|
207
|
-
skeeper sync --pull # rebase the sidecar branch first — useful when teammates pushed
|
|
193
|
+
skeeper repair abort
|
|
208
194
|
```
|
|
209
195
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
The post-commit hook is a _managed block_ in `.git/hooks/post-commit`, installed idempotently. It runs `skeeper sync --hook` with a 750 ms foreground budget so your `git commit` stays snappy even on a slow network.
|
|
213
|
-
|
|
214
|
-
On the success path, `skeeper` matches files with doublestar globs, copies them into `.skeeper/`, then runs `git add`, `git commit`, and `git push` against the sidecar remote. With `directory`, the copy destination is `.skeeper/<directory>/<path>` and the push target is `<directory>/__branches__/<source-branch>`. Sidecar commits reference the main-repo SHA so you can correlate spec changes back to the code change that triggered them.
|
|
215
|
-
|
|
216
|
-
On the failure path — timeout, auth failure, network failure, or push rejection — `skeeper` writes a retry record to `.git/skeeper/queue.json`, appends to `.git/skeeper/sync.log`, prints a one-line note, and the hook exits 0. The next `skeeper sync` drains the queue before running a normal sync. Use `skeeper sync --pull` when a teammate pushed sidecar updates between your commits; it fetches and rebases before pushing.
|
|
196
|
+
Run a fresh repair sync when a bypass or stale lock is reported:
|
|
217
197
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
198
|
+
```bash
|
|
199
|
+
skeeper sync
|
|
200
|
+
skeeper verify
|
|
201
|
+
```
|
|
222
202
|
|
|
223
203
|
## 📖 CLI Reference
|
|
224
204
|
|
|
225
|
-
<details>
|
|
226
|
-
<summary><code>skeeper init</code> — Create and connect a sidecar specs repository</summary>
|
|
227
|
-
|
|
228
205
|
```bash
|
|
229
206
|
skeeper init [flags]
|
|
207
|
+
skeeper sync [--dry-run] [--json] [--commit --message <msg>] [--force]
|
|
208
|
+
skeeper adopt <path-or-glob>... [--dry-run] [--json] [--force] [--commit --message <msg>]
|
|
209
|
+
skeeper untrack <path-or-glob>... [--dry-run] [--json] [--force] [--commit --message <msg>]
|
|
210
|
+
skeeper pattern test <glob> [--namespace <name>] [--json]
|
|
211
|
+
skeeper pattern add <glob> [--namespace <name>] [--exclude <glob>]... [--adopt-existing] [--dry-run] [--json] [--force] [--commit --message <msg>]
|
|
212
|
+
skeeper hydrate
|
|
213
|
+
skeeper status [--json]
|
|
214
|
+
skeeper log <path> [--latest]
|
|
215
|
+
skeeper fsck [--json] [--source-branch <branch>]
|
|
216
|
+
skeeper verify [--json] [--source-branch <branch>]
|
|
217
|
+
skeeper hooks install [--json]
|
|
218
|
+
skeeper hooks check [--json]
|
|
219
|
+
skeeper merge-driver [--json]
|
|
220
|
+
skeeper repair status|resume|abort [--json]
|
|
221
|
+
skeeper version
|
|
230
222
|
```
|
|
231
223
|
|
|
232
|
-
|
|
233
|
-
| ---------------- | --------- | ----------------------------------------------------- |
|
|
234
|
-
| `--sidecar` | | Existing sidecar repository URL |
|
|
235
|
-
| `--sidecar-name` | | GitHub sidecar repository name or `OWNER/REPO` |
|
|
236
|
-
| `--visibility` | `private` | GitHub visibility: `private`, `public`, or `internal` |
|
|
237
|
-
| `--directory` | repo slug | Sidecar directory namespace for this source repo |
|
|
238
|
-
| `--no-directory` | `false` | Omit namespace and use legacy root behavior |
|
|
239
|
-
| `--bootstrap` | | Optional install command stored in `.skeeper.yml` |
|
|
240
|
-
| `--patterns` | | Spec glob pattern; repeat for multiple patterns |
|
|
224
|
+
Command notes:
|
|
241
225
|
|
|
242
|
-
|
|
226
|
+
- `sync` uses working-tree content and stages `skeeper.lock`. Hook mode uses staged index content.
|
|
227
|
+
- `adopt` and `untrack` push sidecar coverage before removing main-index tracking.
|
|
228
|
+
- `pattern add --adopt-existing` updates `.skeeper.yml`, updates the managed `.gitignore` block, then runs the same adoption transaction.
|
|
229
|
+
- `verify` checks `skeeper.lock` against the sidecar remote and does not require hooks.
|
|
230
|
+
- `fsck` compares current working-tree specs against locked sidecar content and does not mutate files or refs.
|
|
231
|
+
- `hydrate` restores from locked sidecar commits by default.
|
|
232
|
+
- `log --latest` fetches the namespace branch and reads its latest history instead of the locked commit.
|
|
233
|
+
- `hooks install` removes legacy Skeeper post-commit blocks, installs strict pre-commit/pre-merge-commit/pre-push blocks, writes `.gitattributes`, and configures the `skeeper.lock` merge driver.
|
|
243
234
|
|
|
244
|
-
|
|
235
|
+
## 🤖 CI Action
|
|
245
236
|
|
|
246
|
-
|
|
247
|
-
<summary><code>skeeper hydrate</code> — Restore spec files from the sidecar repository</summary>
|
|
237
|
+
Use the same-repository Action to verify `skeeper.lock` in CI:
|
|
248
238
|
|
|
249
|
-
```
|
|
250
|
-
skeeper
|
|
239
|
+
```yaml
|
|
240
|
+
name: skeeper
|
|
241
|
+
|
|
242
|
+
on:
|
|
243
|
+
pull_request:
|
|
244
|
+
push:
|
|
245
|
+
branches: [main]
|
|
246
|
+
|
|
247
|
+
jobs:
|
|
248
|
+
verify:
|
|
249
|
+
runs-on: ubuntu-latest
|
|
250
|
+
steps:
|
|
251
|
+
- uses: actions/checkout@v4
|
|
252
|
+
with:
|
|
253
|
+
fetch-depth: 0
|
|
254
|
+
- uses: compozy/skeeper@v0.1.1
|
|
255
|
+
with:
|
|
256
|
+
args: |
|
|
257
|
+
verify
|
|
258
|
+
--json
|
|
259
|
+
ssh-private-key: ${{ secrets.SKEEPER_SSH_PRIVATE_KEY }}
|
|
251
260
|
```
|
|
252
261
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
</details>
|
|
256
|
-
|
|
257
|
-
<details>
|
|
258
|
-
<summary><code>skeeper sync</code> — Mirror spec files into the sidecar repository</summary>
|
|
259
|
-
|
|
260
|
-
```bash
|
|
261
|
-
skeeper sync [flags]
|
|
262
|
-
```
|
|
262
|
+
Credential precedence:
|
|
263
263
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
| `--hook` | `false` | Run in post-commit hook mode: 750 ms foreground budget, always exits 0 |
|
|
264
|
+
1. `ssh-private-key` writes a temp key and sets `GIT_SSH_COMMAND`.
|
|
265
|
+
2. `token` configures HTTPS GitHub credentials.
|
|
266
|
+
3. Existing runner Git/SSH credentials are used when neither input is provided.
|
|
268
267
|
|
|
269
|
-
|
|
268
|
+
Secrets are masked before configuration. The wrapper downloads the released Skeeper binary for the action ref/tag and delegates verification to the CLI.
|
|
270
269
|
|
|
271
|
-
|
|
270
|
+
## 🩺 Troubleshooting
|
|
272
271
|
|
|
273
|
-
|
|
274
|
-
<summary><code>skeeper status</code> — Show sidecar sync status</summary>
|
|
272
|
+
**`SKEEPER_SKIP=1` was used**
|
|
275
273
|
|
|
276
|
-
|
|
277
|
-
skeeper status
|
|
278
|
-
```
|
|
274
|
+
Run `skeeper status`, then `skeeper sync`, then `skeeper verify`. The bypass journal remains visible until sync clears it.
|
|
279
275
|
|
|
280
|
-
|
|
276
|
+
**Sidecar push was rejected**
|
|
281
277
|
|
|
282
|
-
|
|
278
|
+
Run `skeeper repair status`. If the failure happened before main-index mutation, fix network/auth or sidecar contention and run `skeeper repair resume`. If the main index was already mutated, inspect the listed files manually.
|
|
283
279
|
|
|
284
|
-
|
|
285
|
-
<summary><code>skeeper log <path></code> — Show sidecar history for a spec file</summary>
|
|
280
|
+
**`skeeper.lock` conflicts during merge**
|
|
286
281
|
|
|
287
|
-
|
|
288
|
-
skeeper log <path>
|
|
289
|
-
```
|
|
282
|
+
Run `skeeper hooks install` to ensure the merge driver is configured, then rerun the merge. Manual editing of scalar sidecar SHAs is unsupported; regenerate the lock through `skeeper merge-driver` or `skeeper sync`.
|
|
290
283
|
|
|
291
|
-
|
|
284
|
+
**`verify` reports a lock mismatch**
|
|
292
285
|
|
|
293
|
-
|
|
286
|
+
The main commit and sidecar remote disagree. Run `skeeper sync`, include the updated `skeeper.lock`, and rerun `skeeper verify`.
|
|
294
287
|
|
|
295
|
-
|
|
296
|
-
<summary><code>skeeper version</code> — Print build metadata</summary>
|
|
288
|
+
**A namespace overlaps another namespace**
|
|
297
289
|
|
|
298
|
-
|
|
299
|
-
skeeper version
|
|
300
|
-
```
|
|
290
|
+
Move shared files into exactly one namespace by adding `exclude:` entries. Skeeper does not use order-based precedence.
|
|
301
291
|
|
|
302
|
-
|
|
292
|
+
## 🚫 When Skeeper Is the Wrong Tool
|
|
303
293
|
|
|
304
|
-
|
|
294
|
+
- Repositories where specs already belong in the main diff and reviewers explicitly want them inline.
|
|
295
|
+
- Teams that need PR review on the spec content itself before merge — Skeeper mirrors after the main commit succeeds, by design.
|
|
296
|
+
- Repositories without a stable sidecar Git host: Skeeper fails the commit when the sidecar is unreachable (the audited `SKEEPER_SKIP=1` bypass exists, but it is not a substitute for a working remote).
|
|
297
|
+
- Storing build artifacts, generated code, or large binaries. Default guardrails cap mutating plans at 100 files and 10 MiB on purpose.
|
|
305
298
|
|
|
306
299
|
## 🛠️ Development
|
|
307
300
|
|
|
308
301
|
```bash
|
|
309
|
-
mise install
|
|
302
|
+
mise install
|
|
310
303
|
bun install
|
|
311
304
|
make hooks-install
|
|
312
|
-
make verify
|
|
305
|
+
make verify
|
|
313
306
|
```
|
|
314
307
|
|
|
315
308
|
Common targets:
|
|
316
309
|
|
|
317
310
|
```bash
|
|
318
|
-
make fmt
|
|
319
|
-
make lint
|
|
320
|
-
make test
|
|
321
|
-
make build
|
|
322
|
-
make cover
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
Releases are prepared through release pull requests and published with [GoReleaser Pro](.goreleaser.yml). A push to `main` creates or updates a release PR with `pr-release`; the release PR runs a GoReleaser dry run, and merging the release commit publishes GitHub release artifacts, the Homebrew cask, and the NPM package.
|
|
326
|
-
|
|
327
|
-
Release publishing requires these GitHub Actions secrets:
|
|
328
|
-
|
|
329
|
-
| Secret | Purpose |
|
|
330
|
-
| ---------------- | ------------------------------------------------------------- |
|
|
331
|
-
| `RELEASE_TOKEN` | Create/update release PRs, push release tags, update Homebrew |
|
|
332
|
-
| `GORELEASER_KEY` | Run GoReleaser Pro |
|
|
333
|
-
| `NPM_TOKEN` | Publish `@compozy/skeeper` and authenticate npm in release CI |
|
|
334
|
-
|
|
335
|
-
Release notes are generated by `pr-release`. Add pending human-authored notes under `.release-notes/`; the release PR writes the current release body to `RELEASE_BODY.md` and prepends it to `RELEASE_NOTES.md`. The production workflow passes `RELEASE_BODY.md` to GoReleaser with the Skeeper release header and footer templates.
|
|
336
|
-
|
|
337
|
-
Local release checks:
|
|
338
|
-
|
|
339
|
-
```bash
|
|
340
|
-
make release-snapshot # requires GoReleaser Pro in PATH, or GORELEASER_KEY for the installer
|
|
311
|
+
make fmt
|
|
312
|
+
make lint
|
|
313
|
+
make test
|
|
314
|
+
make build
|
|
315
|
+
make cover
|
|
316
|
+
make release-snapshot
|
|
341
317
|
```
|
|
342
318
|
|
|
343
|
-
Contributor guidance, commit conventions, and
|
|
344
|
-
|
|
345
|
-
## 🤝 Contributing
|
|
346
|
-
|
|
347
|
-
Contributions are welcome. Open an issue to discuss larger changes, or send a pull request for fixes and small improvements. All commits follow [Conventional Commits](https://www.conventionalcommits.org/) (`build`, `chore`, `ci`, `docs`, `feat`, `fix`, `perf`, `refactor`, `test`), enforced by `commitlint`.
|
|
319
|
+
Contributor guidance, commit conventions, and agent instructions live in [`CLAUDE.md`](CLAUDE.md) and [`AGENTS.md`](AGENTS.md).
|
|
348
320
|
|
|
349
321
|
## 📄 License
|
|
350
322
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@compozy/skeeper",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"description": "Sidecar Git versioning for spec artifacts",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"postinstall": "node install.js",
|
|
@@ -27,63 +27,63 @@
|
|
|
27
27
|
},
|
|
28
28
|
"archives": {
|
|
29
29
|
"darwin-arm64": {
|
|
30
|
-
"name": "skeeper_0.
|
|
31
|
-
"url": "https://github.com/compozy/skeeper/releases/download/v0.
|
|
30
|
+
"name": "skeeper_0.2.0_darwin_arm64.tar.gz",
|
|
31
|
+
"url": "https://github.com/compozy/skeeper/releases/download/v0.2.0/skeeper_0.2.0_darwin_arm64.tar.gz",
|
|
32
32
|
"bins": [
|
|
33
33
|
"skeeper"
|
|
34
34
|
],
|
|
35
35
|
"format": "tar.gz",
|
|
36
36
|
"checksum": {
|
|
37
37
|
"algorithm": "sha256",
|
|
38
|
-
"digest": "
|
|
38
|
+
"digest": "0b989d71f5cf6dc929bcf18d9f86fbe827ba74231b38b98fe268a299ba56d7da"
|
|
39
39
|
}
|
|
40
40
|
},
|
|
41
41
|
"darwin-x64": {
|
|
42
|
-
"name": "skeeper_0.
|
|
43
|
-
"url": "https://github.com/compozy/skeeper/releases/download/v0.
|
|
42
|
+
"name": "skeeper_0.2.0_darwin_x86_64.tar.gz",
|
|
43
|
+
"url": "https://github.com/compozy/skeeper/releases/download/v0.2.0/skeeper_0.2.0_darwin_x86_64.tar.gz",
|
|
44
44
|
"bins": [
|
|
45
45
|
"skeeper"
|
|
46
46
|
],
|
|
47
47
|
"format": "tar.gz",
|
|
48
48
|
"checksum": {
|
|
49
49
|
"algorithm": "sha256",
|
|
50
|
-
"digest": "
|
|
50
|
+
"digest": "fced98b16ffae5f72d843dfe532b989ee4f4c6df59973167f38a8e68fde1385c"
|
|
51
51
|
}
|
|
52
52
|
},
|
|
53
53
|
"linux-arm64": {
|
|
54
|
-
"name": "skeeper_0.
|
|
55
|
-
"url": "https://github.com/compozy/skeeper/releases/download/v0.
|
|
54
|
+
"name": "skeeper_0.2.0_linux_arm64.tar.gz",
|
|
55
|
+
"url": "https://github.com/compozy/skeeper/releases/download/v0.2.0/skeeper_0.2.0_linux_arm64.tar.gz",
|
|
56
56
|
"bins": [
|
|
57
57
|
"skeeper"
|
|
58
58
|
],
|
|
59
59
|
"format": "tar.gz",
|
|
60
60
|
"checksum": {
|
|
61
61
|
"algorithm": "sha256",
|
|
62
|
-
"digest": "
|
|
62
|
+
"digest": "04ab4382e4a01cb7a9165c055b8cf3b9675d03fbebe056ff67e2b04450c02a54"
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
"linux-x64": {
|
|
66
|
-
"name": "skeeper_0.
|
|
67
|
-
"url": "https://github.com/compozy/skeeper/releases/download/v0.
|
|
66
|
+
"name": "skeeper_0.2.0_linux_x86_64.tar.gz",
|
|
67
|
+
"url": "https://github.com/compozy/skeeper/releases/download/v0.2.0/skeeper_0.2.0_linux_x86_64.tar.gz",
|
|
68
68
|
"bins": [
|
|
69
69
|
"skeeper"
|
|
70
70
|
],
|
|
71
71
|
"format": "tar.gz",
|
|
72
72
|
"checksum": {
|
|
73
73
|
"algorithm": "sha256",
|
|
74
|
-
"digest": "
|
|
74
|
+
"digest": "f1e0426e926fc654fccf23a6744cebc9313bf43cd5cc14576adef6cdf5612583"
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
77
|
"win32-x64": {
|
|
78
|
-
"name": "skeeper_0.
|
|
79
|
-
"url": "https://github.com/compozy/skeeper/releases/download/v0.
|
|
78
|
+
"name": "skeeper_0.2.0_windows_x86_64.zip",
|
|
79
|
+
"url": "https://github.com/compozy/skeeper/releases/download/v0.2.0/skeeper_0.2.0_windows_x86_64.zip",
|
|
80
80
|
"bins": [
|
|
81
81
|
"skeeper.exe"
|
|
82
82
|
],
|
|
83
83
|
"format": "zip",
|
|
84
84
|
"checksum": {
|
|
85
85
|
"algorithm": "sha256",
|
|
86
|
-
"digest": "
|
|
86
|
+
"digest": "7dc1569a187eecaccc5e79e5f69be4da95acf050a6b58816bb790a38303e8733"
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|