[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-80483":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":9,"language":10,"languages":9,"totalLinesOfCode":9,"stars":11,"forks":12,"watchers":13,"openIssues":14,"contributorsCount":15,"subscribersCount":15,"size":15,"stars1d":14,"stars7d":14,"stars30d":16,"stars90d":15,"forks30d":15,"starsTrendScore":17,"compositeScore":18,"rankGlobal":9,"rankLanguage":9,"license":19,"archived":20,"fork":20,"defaultBranch":21,"hasWiki":20,"hasPages":20,"topics":22,"createdAt":9,"pushedAt":9,"updatedAt":23,"readmeContent":24,"aiSummary":25,"trendingCount":15,"starSnapshotCount":15,"syncStatus":26,"lastSyncTime":27,"discoverSource":28},80483,"skeeper","compozy\u002Fskeeper","compozy","Version your spec artifacts — PRDs, tech specs, ADRs, AI plans — in a sidecar Git repository, without polluting your main PRs.",null,"Go",72,6,68,1,0,4,3,2.54,"MIT License",false,"main",[],"2026-06-12 02:04:03","\u003Cdiv align=\"center\">\n  \u003Cp>\n    \u003Cimg src=\"docs\u002Fassets\u002Fskeeper-readme-hero.png\" alt=\"skeeper hero showing AI specs syncing into a sidecar Git repository\" width=\"100%\">\n  \u003C\u002Fp>\n  \u003Cp>\n    \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fcompozy\u002Fskeeper\u002Factions\u002Fworkflows\u002Fci.yml\">\n      \u003Cimg src=\"https:\u002F\u002Fgithub.com\u002Fcompozy\u002Fskeeper\u002Factions\u002Fworkflows\u002Fci.yml\u002Fbadge.svg\" alt=\"CI\">\n    \u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002Fcompozy\u002Fskeeper\">\n      \u003Cimg src=\"https:\u002F\u002Fpkg.go.dev\u002Fbadge\u002Fgithub.com\u002Fcompozy\u002Fskeeper.svg\" alt=\"Go Reference\">\n    \u003C\u002Fa>\n    \u003Ca href=\"LICENSE\">\n      \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FLicense-MIT-blue.svg\" alt=\"License: MIT\">\n    \u003C\u002Fa>\n    \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fcompozy\u002Fskeeper\u002Freleases\">\n      \u003Cimg src=\"https:\u002F\u002Fimg.shields.io\u002Fgithub\u002Fv\u002Frelease\u002Fcompozy\u002Fskeeper?include_prereleases\" alt=\"Release\">\n    \u003C\u002Fa>\n  \u003C\u002Fp>\n\u003C\u002Fdiv>\n\nSpec docs drift from code, or they bloat every PR. Skeeper picks neither.\n\nIt 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.\n\n## ✨ Highlights\n\n- **Lockfile-backed reliability.** `skeeper.lock` records sidecar URL, source branch, namespace branch, sidecar commit, per-namespace digest, file count, and byte count.\n- **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.\n- **Specs stay local to their code.** Edit `SPEC.md`, `docs\u002Fspecs\u002F**`, `.claude\u002Fplans\u002F**`, ADRs, RFCs, or custom globs where they naturally belong.\n- **Shared sidecars without collisions.** Namespaces isolate stored paths and sidecar branches inside one sidecar remote.\n- **Branch-aware history.** Namespace branches use `\u003Cnamespace>\u002F__branches__\u002F\u003Csource-branch>`.\n- **Git-like spec sync.** `skeeper pull` brings remote docs in, applies safe remote deletes, `skeeper push` publishes local docs and safe local deletes, and `skeeper sync` runs pull then push.\n- **Safe by default.** Push rejects sidecar branches whose remote tip does not match the local `skeeper.lock`; destructive pruning without a known base still requires `--prune`.\n- **Fresh-clone restore.** `skeeper restore --all` restores files from the exact sidecar commits recorded in `skeeper.lock`.\n- **Small command surface.** Daily use is `status`, `pull`, `push`, `sync`, `diff`, `reconcile`, `restore`, `track`, `untrack`, `repair`, `log`, and `version`; Git hook plumbing lives behind hidden `skeeper internal` commands.\n- **Skill for AI agents.** A bundled skill at [`skills\u002Fskeeper\u002FSKILL.md`](skills\u002Fskeeper\u002FSKILL.md) teaches coding agents the strict-sync workflow, namespaces, and recovery commands.\n\n## 🎯 Who Is This For\n\n- Teams using AI coding agents that produce `SPEC.md`, PRD, TechSpec, and plan markdown next to code.\n- Engineering organizations running ADRs, RFCs, and design docs in-repo without making every PR a docs+code review.\n- Solo developers who want full spec history (`git log`, `git blame`, branches, PRs) without polluting their main repository's diff.\n\n## 📦 Installation\n\n#### Homebrew\n\n```bash\nbrew install compozy\u002Fcompozy\u002Fskeeper\n```\n\n#### NPM\n\n```bash\nnpm install -g @compozy\u002Fskeeper\n```\n\n#### Go\n\n```bash\ngo install github.com\u002Fcompozy\u002Fskeeper\u002Fcmd\u002Fskeeper@latest\n```\n\n#### GitHub Releases\n\nDownload the archive for your OS and architecture from [GitHub Releases](https:\u002F\u002Fgithub.com\u002Fcompozy\u002Fskeeper\u002Freleases), then place the `skeeper` binary on your `PATH`.\n\n#### From Source\n\n```bash\ngit clone git@github.com:compozy\u002Fskeeper.git\ncd skeeper\nmake verify\ngo build -o bin\u002Fskeeper .\u002Fcmd\u002Fskeeper\n```\n\n#### Docker\n\n```bash\ngit clone git@github.com:compozy\u002Fskeeper.git\ncd skeeper\nmake docker-build\ndocker run --rm -v \"$PWD:\u002Fworkspace\" -w \u002Fworkspace skeeper:dev status\n```\n\nPrerequisites:\n\n- `git` on `PATH`\n- `gh` only when `skeeper init` creates a new GitHub sidecar repo; existing sidecars can be reused with `--sidecar`\n\n## 🔄 How It Works\n\nSpec 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 `\u003Cnamespace>\u002F\u003Cpath>` and pushes them to `\u003Cnamespace>\u002F__branches__\u002F\u003Csource-branch>`.\n\nOn 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\u002Funtracked spec paths, fetch sidecar refs, verify the sidecar branch still matches `skeeper.lock`, mirror content into `.skeeper\u002F`, commit and push the sidecar, write `skeeper.lock`, and stage that lock before Git creates the main commit.\n\n```mermaid\nflowchart TD\n    Start([👤 git commit]):::user --> UserHook[🪝 Existing user hook content]:::user\n    UserHook --> Block\n\n    subgraph Block [📦 Skeeper pre-commit block]\n        direction TB\n        S1[🧮 Reconcile staged specs\u003Cbr\u002F>+ ownership] --> S2[🔄 Fetch &amp; verify\u003Cbr\u002F>sidecar base]\n        S2 --> S3[🪞 Mirror namespace files\u003Cbr\u002F>into .skeeper\u002F]\n        S3 --> S4[📤 Commit &amp; push sidecar]\n        S4 --> S5[🔒 Write &amp; stage\u003Cbr\u002F>skeeper.lock]\n    end\n\n    Block --> Commit[✅ Main commit proceeds]:::ok\n    Commit --> Push([🚀 git push]):::user\n    Push --> Verify[🔍 Skeeper pre-push status check]:::skeeper\n    Verify --> Done([🎉 Sidecar checked]):::ok\n\n    classDef user fill:#dbeafe,stroke:#1d4ed8,color:#0c1e3e\n    classDef skeeper fill:#fef3c7,stroke:#b45309,color:#3b2c00\n    classDef ok fill:#dcfce7,stroke:#15803d,color:#052e16\n    class S1,S2,S3,S4,S5 skeeper\n```\n\nIf 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\u002Fskeeper\u002Fbypass.json`, prints a warning, and `status --check`, `repair`, and the managed `pre-push` hook continue to surface stale-lock diagnostics until `skeeper sync` or `skeeper repair` repairs the state. `git commit --no-verify` is unsupported because Git skips all hook code and cannot record an audit trail.\n\n## ⚙️ Configuration\n\n`skeeper init` writes `.skeeper.yml` at the repository root. Commit it.\n\n```yaml\nsidecar: git@github.com:user\u002Fmyproject-specs.git\n\nnamespaces:\n  - name: project\n    patterns:\n      - \"**\u002FSPEC.md\"\n      - \"docs\u002Fspecs\u002F**\"\n      - \".claude\u002Fplans\u002F**\"\n      - \"**\u002F*.spec.md\"\n    exclude:\n      - \"docs\u002Fspecs\u002Fprivate\u002F**\"\n```\n\nAdvanced operational defaults are optional:\n\n```yaml\nsettings:\n  guardrails:\n    max_files: 100\n    max_bytes: 10485760\n  hooks:\n    pre_push_timeout: 30s\n    allow_skip_env: SKEEPER_SKIP\n\nnamespaces:\n  - name: generated\n    patterns:\n      - \"generated\u002Fspecs\u002F**\"\n    respect_gitignore: false\n```\n\nRules:\n\n- Unknown keys are rejected.\n- Every namespace needs a `name` and at least one glob in `patterns`.\n- `exclude` is the only public exclusion mechanism. Negative globs in `patterns` are rejected.\n- Ownership must be unique. If two namespaces own the same file, the plan fails and asks for an `exclude` fix.\n- `respect_gitignore: false` bypasses root `.gitignore`, nested `.gitignore`, `.git\u002Finfo\u002Fexclude`, and global excludes for that namespace. `.git\u002F` and `.skeeper\u002F` are always excluded.\n\nLocal-only state lives under `.git\u002Fskeeper\u002F`:\n\n| File               | Purpose                                        |\n| ------------------ | ---------------------------------------------- |\n| `transaction.json` | Current resumable mutating operation and phase |\n| `bypass.json`      | Latest audited strict-hook bypass              |\n| `hydration.json`   | Last locked sidecar blobs hydrated locally     |\n| `rescue\u002F`          | Local files moved aside before prune\u002Foverwrite |\n\n## 🚀 Quick Start\n\n```bash\nskeeper init\n```\n\nInteractive init asks for the sidecar mode, repository name or URL, namespace, bootstrap command, and optional extra context globs. With flags:\n\n```bash\nskeeper init \\\n  --sidecar-name myproject-specs \\\n  --visibility private \\\n  --namespace project \\\n  --track \"**\u002FSPEC.md\" \\\n  --track \"docs\u002Fspecs\u002F**\"\n```\n\nUse an existing shared sidecar:\n\n```bash\nskeeper init \\\n  --sidecar git@github.com:user\u002Fshared-specs.git \\\n  --namespace project \\\n  --track \"**\u002FSPEC.md\"\n```\n\nThen edit specs and commit normally:\n\n```bash\n$EDITOR src\u002Fauth\u002FSPEC.md\ngit add src\u002Fauth\u002Fservice.go src\u002Fauth\u002FSPEC.md\ngit commit -m \"auth: design OAuth provider flow\"\n```\n\nThe `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.\n\n## 🛟 Failed Sync Recovery\n\nStart with status. It prints the health summary and the next action:\n\n```bash\nskeeper status --paths\n```\n\nUse repair as the single recovery door for broken local state, stale bypasses, hook drift, missing sidecar objects, and interrupted transactions:\n\n```bash\nskeeper repair\nskeeper status --check\n```\n\nWhen two clones have different docs and both sides should be preserved, use the union workflow:\n\n```bash\nskeeper sync\ngit add skeeper.lock\ngit commit -m \"skeeper: sync docs\"\ngit push\n```\n\n## 📖 CLI Reference\n\nThe public surface is intentionally small. `status` tells you what is wrong and what to run next; `repair` is the only public recovery door; Git hook and merge-driver plumbing runs through hidden `skeeper internal` commands.\n\n\u003Cdetails>\n\u003Csummary>\u003Ccode>skeeper init\u003C\u002Fcode> — Create or connect a sidecar repository\u003C\u002Fsummary>\n\n```bash\nskeeper init [flags]\n```\n\nRun `init` once per main repository. Without flags in an interactive terminal, it opens the guided setup. With flags, it can create a GitHub sidecar or connect an existing remote. `init` installs hooks and merge-driver wiring.\n\n| Flag             | Default   | Description                                       |\n| ---------------- | --------- | ------------------------------------------------- |\n| `--sidecar`      |           | Existing sidecar repository URL                   |\n| `--sidecar-name` |           | GitHub sidecar repository name or `OWNER\u002FREPO`    |\n| `--visibility`   | `private` | GitHub repository visibility                      |\n| `--namespace`    |           | Sidecar namespace for this project                |\n| `--track`        |           | Managed spec glob; repeat for multiple globs      |\n| `--patterns`     |           | Compatibility spelling for managed spec globs     |\n| `--bootstrap`    |           | Optional install command stored in `.skeeper.yml` |\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>\u003Ccode>skeeper status\u003C\u002Fcode> — Inspect sync health and next action\u003C\u002Fsummary>\n\n```bash\nskeeper status [--json] [--check] [--paths]\n```\n\nUse `status` before guessing. It reports sidecar URL, current branch, lock state, hook health, namespace drift counts, bypass state, active transactions, diagnostics, and a next-action line. `--check` exits non-zero when Skeeper needs action, making it the CI health check. `--paths` includes per-path drift classes such as `local_only`, `local_deleted`, `remote_deleted`, `local_modified`, and `both_modified_conflict`.\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>\u003Ccode>skeeper pull\u003C\u002Fcode>, \u003Ccode>push\u003C\u002Fcode>, and \u003Ccode>sync\u003C\u002Fcode> — Git-like spec convergence\u003C\u002Fsummary>\n\n```bash\nskeeper pull [--json] [--no-git]\nskeeper push [--dry-run] [--json] [--commit --message \u003Cmsg>] [--force] [--prune]\nskeeper sync [--dry-run] [--json] [--commit --message \u003Cmsg>] [--force] [--prune]\n```\n\nUse `pull` to fetch sidecar refs, materialize remote docs into the working tree, and apply remote deletes when the local file still matches the last hydrated base. It fast-forwards the main repo unless `--no-git` is set.\n\nUse `push` to publish local managed docs and safe local deletes, write `skeeper.lock`, and stage the lockfile. It rejects sidecar branches whose remote tip does not match the local lock; run `skeeper pull` or `skeeper sync` first.\n\nUse `sync` for the common two-clone flow. It runs a sidecar pull, then a push, so disjoint additions converge and deletions propagate when they can be proven against the hydration base.\n\n`--prune` is explicit and destructive: it deletes remote-only sidecar files that are absent locally even when they do not have a trusted local deletion base.\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>\u003Ccode>skeeper diff\u003C\u002Fcode> and \u003Ccode>reconcile\u003C\u002Fcode> — Inspect and resolve drift\u003C\u002Fsummary>\n\n```bash\nskeeper diff [--json] [--namespace \u003Cname>] [--class \u003Cpath-class>...]\nskeeper reconcile [--dry-run] [--json] [--adopt-local|--prune-local|--merge|--ours|--theirs]\n```\n\nUse `diff` to inspect the lock\u002Fworktree\u002Fbase comparison without mutating files. Use `reconcile` when Skeeper blocks on ambiguous local-vs-sidecar drift: `--ours` publishes the local side, `--theirs` applies the sidecar side with rescue where local data would be lost, `--merge` writes conflict markers for both-modified files, `--adopt-local` publishes local-only changes, and `--prune-local` moves local-only files into rescue storage.\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>\u003Ccode>skeeper restore\u003C\u002Fcode> — Restore local files from locked sidecar state\u003C\u002Fsummary>\n\n```bash\nskeeper restore \u003Cpath...> [--dry-run] [--json]\nskeeper restore --all [--dry-run] [--json]\n```\n\nUse `restore \u003Cpath>` to overwrite selected local files with the content pinned by `skeeper.lock`. Existing local content is moved into rescue storage before overwrite. Use `restore --all` after a fresh clone, bisect, or checkout when you need every locked managed file materialized locally. Use `pull` when you want the latest remote sidecar tip instead of the locked state.\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>\u003Ccode>skeeper track\u003C\u002Fcode> and \u003Ccode>untrack\u003C\u002Fcode> — Change managed coverage\u003C\u002Fsummary>\n\n```bash\nskeeper track \u003Cglob> [--namespace \u003Cname>] [--exclude \u003Cglob>]... [--sync] [--dry-run] [--json] [--force] [--commit --message \u003Cmsg>]\nskeeper untrack \u003Cpath-or-glob>... [--dry-run] [--json] [--force] [--commit --message \u003Cmsg>]\n```\n\nUse `track` to add a managed glob to `.skeeper.yml` and the managed `.gitignore` block. Add `--sync` when matching files already exist and should be published into the sidecar immediately.\n\nUse `untrack` when a managed path should stop being tracked in the main repository after the sidecar has the content.\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>\u003Ccode>skeeper repair\u003C\u002Fcode> — Diagnose and repair local Skeeper state\u003C\u002Fsummary>\n\n```bash\nskeeper repair [--check] [--json]\n```\n\n`repair` handles hook drift, strict-hook bypasses, interrupted transactions, missing local sidecar objects, and rescue reporting. It applies safe repairs automatically and stops on ambiguous overwrite\u002Fdelete decisions. Use `repair --check` for read-only diagnosis.\n\n\u003C\u002Fdetails>\n\n\u003Cdetails>\n\u003Csummary>\u003Ccode>skeeper log\u003C\u002Fcode>, \u003Ccode>version\u003C\u002Fcode>, and \u003Ccode>completion\u003C\u002Fcode> — Utility commands\u003C\u002Fsummary>\n\n```bash\nskeeper log \u003Cpath> [--latest] [--source-branch \u003Cbranch>]\nskeeper version\nskeeper completion \u003Cbash|fish|powershell|zsh>\n```\n\n`log` shows sidecar history for one managed spec path. By default it reads the locked commit; use `--latest` to fetch and inspect the latest namespace branch instead.\n\n`version` prints build version, commit, and build date.\n\n`completion` is provided by Cobra and generates shell completion scripts.\n\n\u003C\u002Fdetails>\n\n## 🤖 CI Action\n\nUse the same-repository Action to check Skeeper health in CI:\n\n```yaml\nname: skeeper\n\non:\n  pull_request:\n  push:\n    branches: [main]\n\njobs:\n  check:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n        with:\n          fetch-depth: 0\n      - uses: compozy\u002Fskeeper@v0.2.1\n        with:\n          args: |\n            status\n            --check\n            --json\n          ssh-private-key: ${{ secrets.SKEEPER_SSH_PRIVATE_KEY }}\n```\n\nCredential precedence:\n\n1. `ssh-private-key` writes a temp key and sets `GIT_SSH_COMMAND`.\n2. `token` configures HTTPS GitHub credentials.\n3. Existing runner Git\u002FSSH credentials are used when neither input is provided.\n\nSecrets are masked before configuration. The wrapper downloads the released Skeeper binary for the action ref\u002Ftag and delegates the status check to the CLI.\n\n## 🩺 Troubleshooting\n\n**`SKEEPER_SKIP=1` was used**\n\nRun `skeeper status`, then `skeeper sync`, then `skeeper status --check`. The bypass journal remains visible until sync clears it.\n\n**Sidecar push was rejected**\n\nRun `skeeper repair --check`. If the failure is safe to repair automatically, run `skeeper repair` after fixing network\u002Fauth or sidecar contention. If the report names an ambiguous overwrite\u002Fdelete decision, inspect the listed files manually and use `skeeper sync` after resolving it.\n\n**`skeeper.lock` conflicts during merge**\n\nRun `skeeper repair` to ensure hooks and merge-driver wiring are configured, then rerun the merge. Manual editing of scalar sidecar SHAs is unsupported; regenerate the lock through `skeeper sync`.\n\n**`skeeper pull` or `skeeper restore` is blocked by local managed files**\n\nRun `skeeper status --paths` or `skeeper diff` to inspect exact paths. Use `skeeper sync` when local-only docs or local deletes should be published. Use `skeeper reconcile --ours` or `--theirs` for conflicts. Use `skeeper push --prune` only when the local set is intentionally authoritative and remote-only docs without a trusted local deletion base should be pruned.\n\n**`status --check` reports a lock mismatch**\n\nThe main commit and sidecar remote disagree. Run `skeeper sync`, include the updated `skeeper.lock`, and rerun `skeeper status --check`.\n\n**A namespace overlaps another namespace**\n\nMove shared files into exactly one namespace by adding `exclude:` entries. Skeeper does not use order-based precedence.\n\n## 🚫 When Skeeper Is the Wrong Tool\n\n- Repositories where specs already belong in the main diff and reviewers explicitly want them inline.\n- Teams that need PR review on the spec content itself before merge — Skeeper mirrors after the main commit succeeds, by design.\n- 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).\n- Storing build artifacts, generated code, or large binaries. Default guardrails cap mutating plans at 100 files and 10 MiB on purpose.\n\n## 🛠️ Development\n\n```bash\nmise install\nbun install\nmake hooks-install\nmake verify\n```\n\nCommon targets:\n\n```bash\nmake fmt\nmake lint\nmake test\nmake build\nmake cover\nmake release-snapshot\n```\n\nContributor guidance, commit conventions, and agent instructions live in [`CLAUDE.md`](CLAUDE.md) and [`AGENTS.md`](AGENTS.md).\n\n## 📄 License\n\n[MIT](LICENSE)\n","skeeper 是一个用于版本化管理项目文档（如 PRD、技术规范、ADRs 和 AI 计划）的工具，它将这些文档存储在一个侧边 Git 仓库中，避免了主仓库中的代码提交受到污染。其核心功能包括通过 `skeeper.lock` 文件确保文档与代码同步的一致性，以及严格的预提交和合并钩子来保证只有当侧边仓库的状态可验证时才允许提交。此外，skeeper 支持分支感知的历史记录管理和类似 Git 的命令行操作，使得文档的本地编辑和远程同步变得简单且安全。此工具特别适合需要维护大量项目文档并与代码保持紧密关联的开发团队使用，在提高文档管理效率的同时保证了代码审查流程的专注度。",2,"2026-06-11 04:00:56","CREATED_QUERY"]