Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
8a53f92
test(skills-embed): add failing gate for the configure-time embed step
s0undt3ch Sep 16, 2026
d4af6d2
Revert "test(skills-embed): add failing gate for the configure-time e…
s0undt3ch Sep 16, 2026
6583676
test(skills-embed): extend selfcontainedcheck gate with embedded_skil…
s0undt3ch Sep 16, 2026
1f98194
feat(cmake): embed skills/ and hooks/ into a generated header at conf…
s0undt3ch Sep 16, 2026
ae4b10e
docs(cmake): flag the *.md-only glob's coupling to today's tree
s0undt3ch Sep 16, 2026
c153c4a
test(skills-install): add gate for ripwire skills install extraction …
s0undt3ch Sep 16, 2026
93ffcd0
build(gate-manifest): update gate count and add pargates weight for s…
s0undt3ch Sep 16, 2026
a23db02
fix(skills-install gate): honor RIPWIRE_BIN env var for binary override
s0undt3ch Sep 16, 2026
770dac3
feat(skills-install): store extraction, link-safe symlinking, claude-…
s0undt3ch Sep 16, 2026
a2c5e4e
fix(gateexitcheck): pin skillsinstallcheck.sh in FAILFAST
s0undt3ch Sep 17, 2026
c6ae0af
fix(skills-install): use error_code std::filesystem overloads, no throws
s0undt3ch Sep 17, 2026
a25cd6c
test(skills-install): add failing gate arms for manifest v2 and link-…
s0undt3ch Sep 17, 2026
db37fa6
fix(skills-install gate): arm 5 guard against empty manifest-tracked …
s0undt3ch Sep 17, 2026
2b53f0d
feat(skills-install): manifest v2 read/write and link-safe pruning
s0undt3ch Sep 17, 2026
ff8db78
fix(skillsinstallcheck): make arm 5 actually exercise prune's link-sa…
s0undt3ch Sep 17, 2026
661b3c4
test(skills-install): add failing gate arms for per-agent modes, --al…
s0undt3ch Sep 17, 2026
958c173
fix(skillsinstallcheck arms 6-8): correct sandbox pattern to match ar…
s0undt3ch Sep 17, 2026
736d9dd
feat(skills-install): per-agent modes, --all detection, --contributor…
s0undt3ch Sep 17, 2026
f9f2b5a
fix(skills-install): --all propagates per-agent/hook failures; correc…
s0undt3ch Sep 17, 2026
3a0da3e
test(skills-install): add failing gate arm for --hook merge preservation
s0undt3ch Sep 17, 2026
8e46d0a
feat(skills-install): --hook merge via jq, preserving pre-existing en…
s0undt3ch Sep 17, 2026
bc9bfc8
fix(skills-install): correct runCommandCapture comment, dedup shell-q…
s0undt3ch Sep 18, 2026
3192627
test(wrap): update section 7 for the collapsed single-line skills-ins…
s0undt3ch Sep 18, 2026
3b9bd02
feat(wrap): collapse the skills-install probe to one unconditional line
s0undt3ch Sep 18, 2026
3d41b9d
fix(skills-install): extracted hook scripts must be executable (0755)
s0undt3ch Sep 18, 2026
a9873a3
doc(skills-install): note the immutable-store mode caveat inline
s0undt3ch Sep 18, 2026
8e2184d
test(doctor): add failing gate for shim recognition and skills staleness
s0undt3ch Sep 18, 2026
e9a2aa3
feat(doctor): recognise mise/aqua shims by location, report skills st…
s0undt3ch Sep 18, 2026
0c66f89
fix(doctor): add a repair hint to the shim-managed byte-mismatch row
s0undt3ch Sep 18, 2026
abc12e5
refactor(skills-install): skills/install.sh becomes a thin wrapper ov…
s0undt3ch Sep 18, 2026
2131c11
fix(skills-install): refuse a bare positional destination instead of …
s0undt3ch Sep 18, 2026
d1682b2
fix(skills-install): support explicit DEST_PATH, refuse ambiguous pos…
s0undt3ch Sep 18, 2026
6e11765
fix(skills-install): write kStoreKey into the manifest, not the binar…
s0undt3ch Sep 18, 2026
40397e8
test: sandbox CLAUDE_CONFIG_DIR against ambient leaks in install/hook…
s0undt3ch Sep 18, 2026
52cc5c5
test: update wrap-truth gates for the collapsed skills-install comman…
s0undt3ch Sep 18, 2026
f208b4e
test: pin dangling/foreign-symlink repair and manifest-outcome contra…
s0undt3ch Sep 18, 2026
ffa7e93
fix(skills-install): repair dangling/foreign symlinks, record link ou…
s0undt3ch Sep 18, 2026
cc0605c
fix(wrap): shell-escape the skills-install recipe path (C3)
s0undt3ch Sep 18, 2026
73629c8
fix(doctor): shim-aware binary-path row, legend covers managed/stale …
s0undt3ch Sep 18, 2026
b77da0b
fix(codexdoctor): repair-command hint names the embedded verb, not th…
s0undt3ch Sep 18, 2026
e397308
test: read the hook matcher from skillsinstall.h, not a script line t…
s0undt3ch Sep 18, 2026
be93084
test: assert the deleted $prefix/share/ripwire staging stays deleted …
s0undt3ch Sep 18, 2026
c0f2b92
docs: INSTALL.md names ripwire skills install, not the removed skills…
s0undt3ch Sep 18, 2026
d5b746e
fix(skills-install): refuse an unknown --flag by name instead of trea…
s0undt3ch Sep 18, 2026
b85c728
fix(skills-install-wrapper): warn on the cwd-dependent PATH fallback …
s0undt3ch Sep 18, 2026
e65ca9b
test(routehookcheck): scope V4's Codex check to doctor rows, not the …
s0undt3ch Sep 18, 2026
bfe49a3
fix(codexdoctor): manifest-parity hint names --force, the actual repair
s0undt3ch Sep 18, 2026
a999c90
test: centralize agent-home env-var sandboxing in one sourced helper
s0undt3ch Sep 18, 2026
ad1b350
fix(test): reorder+propagate agent-home sandboxing (C1,C2,C3,I0,M1,M7)
s0undt3ch Sep 18, 2026
38aff0d
fix(test): neutralise CLAUDE_CONFIG_DIR and RIPWIRE_DATA_HOME in inst…
s0undt3ch Sep 19, 2026
942630f
fix(test): update stale quote/recipe-shape assertions and per-arm tem…
s0undt3ch Sep 19, 2026
9eef0fc
fix(test): restore OPENCLAW_STATE_DIR unset, trim comment that broke …
s0undt3ch Sep 19, 2026
45a5362
fix(test): clear ambient CLAUDE_CONFIG_DIR/RIPWIRE_DATA_HOME in agent…
s0undt3ch Sep 19, 2026
2226a42
fix(skills-install): count and report foreign-entry skips instead of …
s0undt3ch Sep 19, 2026
e91ada5
fix(codexdoctor): name what's actually blocking instead of a hint tha…
s0undt3ch Sep 19, 2026
4d6f800
fix(test): mise/aqua arms discriminate the shim's own path; drop stal…
s0undt3ch Sep 19, 2026
6ecbdb0
fix(test): doctorstalecheck.sh positive assertions + sentinel enforce…
s0undt3ch Sep 19, 2026
c947e75
fix(docs,install): correct INSTALL.md staging claim; gate the hooks h…
s0undt3ch Sep 19, 2026
66cc464
docs: fix-wave-2 report
s0undt3ch Sep 19, 2026
bb74a72
fix(install): don't let set -eu hard-abort on a failed skills activation
s0undt3ch Sep 19, 2026
b1db7cc
test: point four gate reads at .ripwire-manifest-v2, not the deleted …
s0undt3ch Sep 19, 2026
3bc97ce
fix(codexdoctor): non-throwing directory iteration in shim layout res…
s0undt3ch Sep 19, 2026
a7337a0
fix(skillsinstall): route raw POSIX calls through rw::os, register th…
s0undt3ch Sep 19, 2026
97bf8ae
fix(skillsinstall): finish the release-installer gates CI caught (dan…
s0undt3ch Sep 19, 2026
0005234
fix(test): skillsinstallcheck's idempotent-store arm never actually c…
s0undt3ch Sep 19, 2026
2af7449
fix(skillsinstall): address review round — store integrity, no /tmp f…
s0undt3ch Sep 20, 2026
e9410e8
fix(embed): make hooks/ embed step recursive (review item 13)
s0undt3ch Sep 20, 2026
4d738b8
fix(skills-install): usage text — bare `skills`, install --help, --he…
s0undt3ch Sep 20, 2026
f9b5a88
fix(doctor): mise/aqua resolvers match REAL install layouts (review i…
s0undt3ch Sep 20, 2026
d712e96
fix(skillsinstall): 0755 directory creation, jq PATH pinning + stale …
s0undt3ch Sep 20, 2026
11add07
fix(doctor): shimBinaryCheck no longer claims same_file="0" it never …
s0undt3ch Sep 20, 2026
13eda03
fix(skillsinstall): dedupe FdGuard into rw::pathguard::OwnedFd, RAII-…
s0undt3ch Sep 20, 2026
92ba110
docs(changelog): add [Unreleased] entry for embedded skills/hooks + r…
s0undt3ch Sep 20, 2026
1bf77f3
docs(changelog): rewrite the [Unreleased] entry to cover the PR's ful…
s0undt3ch Sep 20, 2026
6de7c8e
fix(test): hooksembedrecursivecheck.sh, shimselfunverifiedcheck.sh — …
s0undt3ch Sep 20, 2026
828cd05
fix(test): close two false-greens binoverridecheck.sh found in the ne…
s0undt3ch Sep 20, 2026
e341f75
chore(restack): reconcile gate count and printf-parity manifest with …
s0undt3ch Sep 20, 2026
46182e4
chore(restack): reconcile gate count (651) and printf-parity manifest…
s0undt3ch Sep 21, 2026
d86b795
fix(test): adopt main's clean-env.sh/statcompat.sh, retire this branc…
s0undt3ch Sep 21, 2026
f73050c
fix(os): close os::symlink's POSIX/Windows declaration parity gap; fi…
s0undt3ch Sep 21, 2026
ad909cb
fix(skillsinstall): convert std::filesystem::path to narrow string be…
s0undt3ch Sep 21, 2026
5d7b886
docs(install): fix stale staged-file wording for the embedded-skills …
s0undt3ch Sep 22, 2026
320a7e8
fix(skillsinstall): close a misdirected-subcommand gap and two CWEs f…
s0undt3ch Sep 22, 2026
53f2c4e
test(hermes): assert a planted ripwire-prefixed user file survives in…
s0undt3ch Sep 22, 2026
f93d5f0
fix(skillsinstall): never trust a symlink or regular file planted at …
s0undt3ch Sep 25, 2026
5511121
fix(skillsinstall): find ripwire beside skills/ in a release archive,…
s0undt3ch Sep 25, 2026
c747539
chore(quality): re-ack the stale duplication finding, reconcile print…
s0undt3ch Sep 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/pargates-shard-weights.json
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
"docmentioncheck.sh": 0.9,
"docscommandscheck.sh": 1.4,
"doctorcheck.sh": 61.0,
"doctorstalecheck.sh": 3.0,
"donelegendcheck.sh": 10.8,
"droppedpositivecheck.sh": 42.7,
"duprowcheck.sh": 1.5,
Expand Down Expand Up @@ -226,6 +227,7 @@
"headsnapcachecheck.sh": 19.9,
"historyoraclecheck.sh": 6.9,
"hookcheck.sh": 78.0,
"hooksembedrecursivecheck.sh": 8.0,
"hostilecheck.sh": 6.2,
"hotspotsincecheck.sh": 8.6,
"htmlcolorcheck.sh": 12.4,
Expand Down Expand Up @@ -502,6 +504,7 @@
"shadowcheck.sh": 11.2,
"shapingflagcheck.sh": 193.7,
"shellgateindexcheck.sh": 1.2,
"shimselfunverifiedcheck.sh": 1.8,
"showcasecapturecheck.sh": 20.9,
"sibliftcheck.sh": 0.5,
"sigredactcheck.sh": 0.9,
Expand All @@ -517,6 +520,7 @@
"skillroutingjudgedcheck.sh": 0.2,
"skillscan.sh": 0.8,
"skillscanreadcheck.sh": 0.4,
"skillsinstallcheck.sh": 2.2,
"skilltruthcheck.sh": 5.4,
"skippedcheck.sh": 0.9,
"skipreasoncheck.sh": 1.6,
Expand Down
70 changes: 53 additions & 17 deletions .ripwire_quality_acks

Large diffs are not rendered by default.

36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,42 @@ those methods; ripwire does not capture them). Every floor is stated and pinned,

## [0.6.2] — 2026-09-21

### Added — skills and hooks are embedded in the binary; `ripwire skills install` replaces the checkout-symlink installer

`skills/` and `hooks/` are baked into the `ripwire` binary at configure time (CMake globs both directories,
recursively, into a generated content-addressed header), replacing the two previously separate mechanisms that
staged them: `skills/install.sh`'s checkout-symlink installer, and the curl installer's `$prefix/share/ripwire/
{skills,hooks}` copy step. Activation is one command, `ripwire skills install
[--claude|--codex|--codex-legacy|--hermes|--openclaw|--all|DEST_PATH]`, which extracts the embedded set into a
versioned, content-addressed store under `$RIPWIRE_DATA_HOME` (every directory it creates forced to `0755`
regardless of the process umask, contents verified byte-for-byte against the binary's own embedded copy before an
extraction is ever trusted) and symlinks from there into the target agent's own skill-discovery directory,
including `skills/hermes/ripwire-*/`'s Hermes-native set. A dangling or foreign-but-live symlink at the
destination is repaired; a real file or a planted symlink is refused even under `--force`; a stale entry no
longer current is pruned only when it is ours to prune (manifest-tracked, or resolving under our own store) —
never a user's own symlink that merely shares the `ripwire-*` naming. `--all` installs into every detected agent
at once; `--contributor` also installs repo-maintainer-only skills; `--hook` registers each agent's
PreToolUse/SessionStart nudge and, for Codex and Claude, the UserPromptSubmit prompt router and the
substitution-meter disclosure banner, merged into the agent's own config via a PATH-resolved `jq`. The manifest
and any hook config are written atomically (temp file + rename), and record what actually linked, never what was
merely attempted. `--doctor` reports install provenance — `not_installed=`/`stale=` against the binary's own
embedded version — and recognises a mise/aqua version-manager shim by its real on-disk install layout (an
arbitrarily nested archive extraction, not a hardcoded shape) instead of ever calling a shim-managed install
STALE by mtime; an unresolvable self path is disclosed as `self_unverified=`, never silently claimed as a byte
match. `skills/install.sh` is now a thin wrapper over the same binary path. Bare `ripwire skills` and `ripwire
skills install --help`/`-h` now print usage instead of mapping `skills/` as a crawl root or refusing as an
unknown flag. Windows is out of scope for this round (blocked on the separate native port, #44). Gates:
`test/skillsinstallcheck.sh` (new, 21 arms), `test/skillinstallcheck.sh`, `test/hermesinstallcheck.sh`,
`test/hooksembedrecursivecheck.sh` (new), `test/shimselfunverifiedcheck.sh` (new), `test/doctorstalecheck.sh`,
`test/codexinstallhonestycheck.sh`, `test/selfcontainedcheck.sh`'s embed cross-check. (#225)

### Fixed — `ripwire wrap`'s printed `skills install` recipe line could execute shell metacharacters in a resolved binary path

The recipe line names this process's own resolved executable path verbatim; a path containing `$(...)` or a
backtick (a version-manager shim resolved through an unusual install layout) was pasted, and would run, as live
shell. The path is now single-quote-escaped (`rw::shSingleQuote`) before printing, so it is always inert text to
paste, never executable. Gate: `test/wrapverbscheck.sh`.

### Added — Microsoft's `cl.exe` builds the tree, so both Windows front ends compile and both gate

The native Windows port (#44) built with clang-cl only; `cl.exe` stopped at the GCC/Clang language extensions
Expand Down
59 changes: 59 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,65 @@ foreach(_ripwire_query_name IN LISTS _ripwire_query_names)
endforeach()
file(APPEND "${_ripwire_embedded_queries}" "}};\n\ninline constexpr std::string_view queryFor( std::string_view sub ) noexcept\n{\n for( const EmbeddedQuery& query : kEmbeddedQueries )\n {\n if( query.sub == sub )\n return query.source;\n }\n return {};\n}\n\n} // namespace rw::embedded_queries\n")

# ---- configure-time skills/hooks embedding (same model as _ripwire_embedded_queries above) ----
# skills/*/SKILL.md (+ companion .md files) and hooks/*.sh remain the editable source of truth.
# CMake regenerates this constexpr table whenever any of those files change; the subcommand in
# src/skillsinstall.h reads only the binary's read-only data at runtime — no filesystem dependency
# on skills/ or hooks/ existing on disk (redhat-et/ripwire#225).
set(_ripwire_embedded_skills "${_ripwire_generated_dir}/embedded_skills.h")
# *.md only: every file under skills/<name>/ is currently markdown; a non-.md companion file added
# later (a script, JSON, image) needs this glob widened too, or test/selfcontainedcheck.sh's
# byte-exact cross-check (which walks every file under skills/*/, no extension filter) goes red.
file(GLOB_RECURSE _ripwire_skill_files CONFIGURE_DEPENDS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/skills" "${CMAKE_CURRENT_SOURCE_DIR}/skills/*.md")
# Only files inside a skills/<name>/ subdirectory are an embeddable skill — top-level files such as
# skills/CONSOLIDATION.md are repo-maintainer notes, not a skill payload, and must not be embedded.
list(FILTER _ripwire_skill_files EXCLUDE REGEX "^[^/]+$")
file(GLOB_RECURSE _ripwire_hook_files CONFIGURE_DEPENDS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/hooks" "${CMAKE_CURRENT_SOURCE_DIR}/hooks/*.sh")
list(SORT _ripwire_skill_files) # deterministic iteration order — G4/determinism contract
list(SORT _ripwire_hook_files)

set(_ripwire_embed_hash_input "")
string(ASCII 30 _ripwire_embed_rs) # ASCII record separator — CMake has no \x escape, so build the byte via string(ASCII ...)
file(WRITE "${_ripwire_embedded_skills}" "#pragma once\n\n#include <array>\n#include <cstddef>\n#include <string_view>\n\nnamespace rw::embedded_skills\n{\n\nstruct EmbeddedFile\n{\n std::string_view relativePath;\n std::string_view bytes;\n};\n\n")

foreach(_ripwire_group skill hook)
if(_ripwire_group STREQUAL "skill")
set(_ripwire_group_files ${_ripwire_skill_files})
set(_ripwire_group_root "${CMAKE_CURRENT_SOURCE_DIR}/skills")
set(_ripwire_group_prefix "skills")
set(_ripwire_group_array "kSkillFiles")
set(_ripwire_group_count_name "kSkillFileCount")
else()
set(_ripwire_group_files ${_ripwire_hook_files})
set(_ripwire_group_root "${CMAKE_CURRENT_SOURCE_DIR}/hooks")
set(_ripwire_group_prefix "hooks")
set(_ripwire_group_array "kHookFiles")
set(_ripwire_group_count_name "kHookFileCount")
endif()
list(LENGTH _ripwire_group_files _ripwire_group_count)
file(APPEND "${_ripwire_embedded_skills}" "inline constexpr std::size_t ${_ripwire_group_count_name} = ${_ripwire_group_count};\ninline constexpr std::array<EmbeddedFile, ${_ripwire_group_count_name}> ${_ripwire_group_array} = {{\n")
set(_ripwire_embed_index 0)
foreach(_ripwire_rel IN LISTS _ripwire_group_files)
set(_ripwire_abs "${_ripwire_group_root}/${_ripwire_rel}")
set(_ripwire_stored_rel "${_ripwire_group_prefix}/${_ripwire_rel}")
file(READ "${_ripwire_abs}" _ripwire_file_content)
string(APPEND _ripwire_embed_hash_input "${_ripwire_stored_rel}${_ripwire_embed_rs}${_ripwire_file_content}${_ripwire_embed_rs}")
set(_ripwire_delim "CTXE_${_ripwire_group}_${_ripwire_embed_index}")
string(FIND "${_ripwire_file_content}" ")${_ripwire_delim}\"" _ripwire_embed_collision)
if(NOT _ripwire_embed_collision EQUAL -1)
message(FATAL_ERROR "embedded file ${_ripwire_abs} collides with its generated raw-string delimiter")
endif()
file(APPEND "${_ripwire_embedded_skills}" " { \"${_ripwire_stored_rel}\", R\"${_ripwire_delim}(${_ripwire_file_content})${_ripwire_delim}\" },\n")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_ripwire_abs}")
math(EXPR _ripwire_embed_index "${_ripwire_embed_index} + 1")
endforeach()
file(APPEND "${_ripwire_embedded_skills}" "}};\n\n")
endforeach()

string(SHA256 _ripwire_embed_full_hash "${_ripwire_embed_hash_input}")
string(SUBSTRING "${_ripwire_embed_full_hash}" 0 8 _ripwire_embed_hash8)
file(APPEND "${_ripwire_embedded_skills}" "inline constexpr std::string_view kVersion = \"${PROJECT_VERSION}\";\ninline constexpr std::string_view kHash8 = \"${_ripwire_embed_hash8}\";\ninline constexpr std::string_view kStoreKey = \"${PROJECT_VERSION}-${_ripwire_embed_hash8}\";\n\n} // namespace rw::embedded_skills\n")

# ---- generated version.h ----
# The version string lives in ONE place: the project(ripwire VERSION ...) call above. --version reads
# this generated header; test/versioncheck.sh diffs its output against CMakeLists.txt's own VERSION so
Expand Down
40 changes: 22 additions & 18 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ The installer:
- downloads the latest [GitHub Release](https://github.com/redhat-et/ripwire/releases) for your OS and CPU
and verifies its SHA-256;
- installs the binary to `~/.local/bin/ripwire`, with no sudo;
- stages the agent skills and hooks under `~/.local/share/ripwire/`;
- activates the skills for each agent it finds on the machine (Claude Code, Codex, and Hermes with
- activates the skills and hooks embedded in the binary (nothing is staged on disk) for each agent it
finds on the machine (Claude Code, Codex, and Hermes with
[initial support](#skills)) and prints one line per agent;
- never registers hooks and never edits your shell profile.

Expand All @@ -40,7 +40,7 @@ reports x86-64; the installer sends it to a native arm64 shell, where the arm64
| `RIPWIRE_INSTALL_PREFIX` | Install under this prefix instead of `~/.local`; the binary goes in `<prefix>/bin`. |
| `RIPWIRE_INSTALL_YES=1` | Skip the confirmation prompt. |
| `RIPWIRE_SKIP_CPU_CHECK=1` | Skip the x86-64-v3 CPU check. The binary is still test-run before it is installed. |
| `RIPWIRE_NO_ACTIVATE=1` | Stage the skills without activating them. |
| `RIPWIRE_NO_ACTIVATE=1` | Skip activation; run `ripwire skills install --all` later. |

Then try it in a repository:

Expand All @@ -66,25 +66,29 @@ first command to build for that machine's own CPU, or use `./install.sh` below,

To put a source build on your `PATH`, run `./install.sh` from the checkout. It builds a Release binary tuned
for this machine's CPU in `build-install/` and installs it under `RIPWIRE_INSTALL_PREFIX`, or under
`brew --prefix` when Homebrew is present, or else under `~/.local`. Like the prebuilt installer, it stages
skills and hooks under `<prefix>/share/ripwire/` and activates the skills for Claude Code and Codex when it
finds them. `RIPWIRE_NO_ACTIVATE=1` stages only; `RIPWIRE_ACTIVATE_CODEX=1` also registers the Codex hooks.
`brew --prefix` when Homebrew is present, or else under `~/.local`. Unlike the prebuilt installer, this route
still stages `skills/` and `hooks/` as separate files under `<prefix>/share/ripwire/`, and activates Claude
Code and Codex (when it finds them) by running that staged `skills/install.sh`. `RIPWIRE_NO_ACTIVATE=1`
stages the files without activating either agent; `RIPWIRE_ACTIVATE_CODEX=1` also registers the Codex hooks.
The prebuilt release installer (`scripts/install.sh`) is the one with skills and hooks embedded in the
`ripwire` binary itself — nothing staged — and its own `RIPWIRE_NO_ACTIVATE=1` skips activation outright
(there is nothing to stage).
Comment thread
coderabbitai[bot] marked this conversation as resolved.

## Connect your coding agent

### Skills

Skills tell an agent *when* to reach for which ripwire verb. The installers activate them for the agents they
detect. To activate them yourself, run the staged skills installer (replace `~/.local` with your prefix if
you changed it):
detect. To activate them yourself (or re-activate after an upgrade), run `ripwire skills install` — replace
`ripwire` with the absolute path to your binary if it is not on `PATH`:

| Agent | Command | Links into |
| --- | --- | --- |
| Claude Code | `bash ~/.local/share/ripwire/skills/install.sh` | `${CLAUDE_CONFIG_DIR:-~/.claude}/skills` |
| Codex | `bash ~/.local/share/ripwire/skills/install.sh --codex` | `${AGENTS_HOME:-~/.agents}/skills` |
| Hermes (initial support) | `bash ~/.local/share/ripwire/skills/install.sh --hermes` | `${HERMES_HOME:-~/.hermes}/skills` |
| openclaw (initial support) | `bash ~/.local/share/ripwire/skills/install.sh --openclaw` | `~/.agents/skills` |
| Anything else | `bash ~/.local/share/ripwire/skills/install.sh <dir>` | `<dir>` |
| Claude Code | `ripwire skills install --claude` | `${CLAUDE_CONFIG_DIR:-~/.claude}/skills` |
| Codex | `ripwire skills install --codex` | `${AGENTS_HOME:-~/.agents}/skills` |
| Hermes (initial support) | `ripwire skills install --hermes` | `${HERMES_HOME:-~/.hermes}/skills` |
| openclaw (initial support) | `ripwire skills install --openclaw` | `~/.agents/skills` |
| Anything else | `ripwire skills install <dir>` | `<dir>` |

Hermes and openclaw support is initial. CI checks what the installers write on disk. For Hermes, a contributor
also ran the installer and the MCP registration against a real Hermes install when support landed
Expand All @@ -100,11 +104,11 @@ itself.

### Advisory hooks (optional)

`bash ~/.local/share/ripwire/skills/install.sh --hook` registers ripwire's advisory hooks for Claude Code in
`ripwire skills install --claude --hook` registers ripwire's advisory hooks for Claude Code in
its `settings.json`: a SessionStart primer that adds ripwire's when-to-use guidance, a UserPromptSubmit router
that suggests one ready-to-run command when it is confident, and a PreToolUse hook that suggests nothing and
only records, on your machine, which tool calls ripwire could have answered. Adding `--codex --hook` does the
same for Codex in `~/.codex/hooks.json`; open `/hooks` in Codex to review and trust them. The hooks never
only records, on your machine, which tool calls ripwire could have answered. `ripwire skills install --codex
--hook` does the same for Codex in `~/.codex/hooks.json`; open `/hooks` in Codex to review and trust them. The hooks never
block a tool call, they need `jq`, and they are registered only when you pass `--hook`.

### MCP server (optional)
Expand All @@ -120,8 +124,8 @@ claude mcp add ripwire -- ripwire --mcp
## Check and upgrade

`ripwire . --doctor` checks the install: the binary, the skills and the hooks. To upgrade, run the same
install command again (pin a release with `RIPWIRE_VERSION`). It replaces the binary and the staged skills
and refreshes their links.
install command again (pin a release with `RIPWIRE_VERSION`). It replaces the binary and re-links the
skills and hooks from the new binary's own embedded store.

## Uninstall

Expand Down
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -962,9 +962,10 @@ ripwire --version
`degraded="1"` and may report as failing even when `ripwire` is on `Path` (its PATH lookup is a known gap).
The cache lives in `%LOCALAPPDATA%\Temp\ripwire-<uid>`.
- **Agent skills** (Claude Code, Codex): from Git Bash, in the unzipped folder, `bash skills/install.sh` (Claude Code)
or `bash skills/install.sh --codex`. Or copy them by hand in PowerShell:
`New-Item -ItemType Directory -Force "$HOME\.claude\skills" | Out-Null; Copy-Item -Recurse -Force "$bin\skills\ripwire-*" "$HOME\.claude\skills\"`
(Codex reads `$HOME\.agents\skills`). `--hook` is untested on Windows, and `scripts/install.sh` (the curl installer) does not run there.
or `bash skills/install.sh --codex`. Without Developer Mode / `SeCreateSymbolicLinkPrivilege`, `ripwire.exe` has no
symlink support and copies each skill's files instead of linking them — a later reinstall still refreshes them, it
just will not notice a store update on its own the way a symlink would. `--hook` is untested on Windows, and
`scripts/install.sh` (the curl installer) does not run there.
</details>

**Building it yourself needs CMake 3.24+ and a C++23 compiler, and nothing else installed first** —
Expand Down Expand Up @@ -1942,9 +1943,9 @@ wrong, and it has. These are the results that say so, all in-tree, all published
### In the tests

<details>
<summary><b>648 gate scripts</b>, five contracts no unit test can hold, and the house rule: write the gate before the code it measures</summary> <!-- gatecount -->
<summary><b>651 gate scripts</b>, five contracts no unit test can hold, and the house rule: write the gate before the code it measures</summary> <!-- gatecount -->

`test/regression.sh` names **648 gate scripts** and is the authoritative list; <!-- gatecount -->
`test/regression.sh` names **651 gate scripts** and is the authoritative list; <!-- gatecount -->
`python3 test/pargates.py . ./build/ripwire -j 6` runs the same set in parallel. On top of them sit the
contracts that do not fit a unit test: two runs byte-identical, warm output identical to cold, output
that pipes clean through `xmllint --noout`, a sanitizer build with `-fno-sanitize-recover=all`, and a
Expand Down
Loading
Loading