
Clawhip
- 1 installs
- 923 repo stars
- Updated August 3, 2026
- yeachan-heo/clawhip
Helps with ai & agent building tasks during AI-assisted development.
About
clawhip is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- clawhip
- AI & Agent Building
- AI-coding skill
Clawhip by the numbers
- 1 all-time installs (skills.sh)
- Ranked #14,102 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/yeachan-heo/clawhip --skill clawhipAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 923 |
| Last updated | August 3, 2026 |
| Repository | yeachan-heo/clawhip ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
clawhip skill attachment surface
Intent
Use this repo as:
- executable/runtime repo
- daemon process for Discord notification routing
- attachable skill surface for OpenClaw / Clawdbot
Prerequisites
⭐ If you want to support clawhip, star this repository. The interactive repo-local installer and clawhip install can offer an optional post-install GitHub star prompt via authenticated gh api access when gh is installed and authenticated. Skip it with --skip-star-prompt or CLAWHIP_SKIP_STAR_PROMPT=1.
gh api --method PUT /user/starred/Yeachan-Heo/clawhip --silentPrimary install flow
Preferred operator instruction:
@openclaw install https://github.com/Yeachan-Heo/clawhipExpected automation flow:
1. clone repo 2. run ./install.sh 3. read this SKILL.md 4. attach skill 5. scaffold ~/.clawhip/config.toml 6. start daemon 7. run live verification presets
Runtime surface
Default daemon URL:
http://127.0.0.1:25294Core commands:
clawhip
clawhip start
clawhip status
clawhip config
clawhip send --channel <id> --message "..."
clawhip github issue-opened ...
clawhip github pr-status-changed ...
clawhip git commit ...
clawhip tmux keyword ...
clawhip tmux stale ...
clawhip tmux new -s <session> --channel <id> --keywords error,complete --shell /bin/zsh -- command
clawhip tmux watch -s <existing-session> --channel <id> --mention '<@id>' --keywords error,completeLifecycle surface
clawhip install
clawhip install --systemd
clawhip install --skip-star-prompt
clawhip update --restart
clawhip uninstall --remove-systemd --remove-config
./install.sh
./install.sh --systemd
./install.sh --skip-star-promptDiscord bot token (recommended setup)
⚠️ Create a dedicated Discord bot for clawhip notifications. Do not reuse your Clawdbot / OpenClaw bot token.
Why:
- clawhip sends high-volume notifications (commits, PRs, tmux events)
- Using the same bot token as your gateway pollutes the bot's identity
- A separate bot (e.g. "CCNotifier") keeps notifications cleanly separated from AI chat
- If clawhip restarts or crashes, it won't affect your main bot
Setup: 1. Go to Discord Developer Portal 2. Create a new application (e.g. "clawhip-notifier" or "CCNotifier") 3. Create a bot, copy the token 4. Invite the bot to your server with Send Messages permission 5. Use this token in ~/.clawhip/config.toml:
[discord]
token = "your-dedicated-clawhip-bot-token"Config scaffold expectations
Key sections:
[discord][daemon][defaults][[routes]][monitors][[monitors.git.repos]][[monitors.tmux.sessions]]
Typical preset route:
[[routes]]
event = "github.*"
filter = { repo = "clawhip" }
channel = "1480171113253175356"
mention = "<@1465264645320474637>"
format = "compact"Dynamic template opt-in
[[routes]]
event = "tmux.*"
allow_dynamic_tokens = true
template = "{session}\n{tmux_tail:issue-1456:20}\n{iso_time}"Allowed dynamic tokens:
{sh:...}{tmux_tail:session:lines}{file_tail:/path:lines}{env:NAME}{now}{iso_time}
Filesystem-offloaded memory pattern
When using clawhip as part of a broader Claw OS workflow, treat memory as an offloaded filesystem tree:
MEMORY.md= small pointer/index/current-beliefs layermemory/= detailed project/channel/daily/handoff memory- update root memory only when the map or current summary changes
Read before adopting this pattern:
docs/memory-offload-architecture.mddocs/memory-offload-guide.mddocs/examples/MEMORY.example.mdskills/memory-offload/SKILL.md
Verification surface
Use the live operational runbook:
docs/live-verification.mdscripts/live-verify-default-presets.sh
Preset verification targets:
- GitHub issue opened / commented / closed
- GitHub PR opened / status changed / merged
- git commit monitor
- tmux keyword / stale / wrapper / watch
- install / update / uninstall
Attachment summary
repo = runtime
SKILL.md = attach/install/usage contract
README.md = operational spec for agentsname: CI
on:
push:
branches: [main, dev]
pull_request:
branches: [main, dev]
env:
CARGO_TERM_COLOR: always
jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo check
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo test
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- run: cargo clippy -- -D warnings
fmt:
name: Format
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all -- --check
build:
name: Build
needs: [check, test, clippy, fmt]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo build --release
- uses: actions/upload-artifact@v4
with:
name: clawhip-linux-x86_64
path: target/release/clawhip
# This file was autogenerated by dist: https://axodotdev.github.io/cargo-dist
#
# Copyright 2022-2024, axodotdev
# SPDX-License-Identifier: MIT or Apache-2.0
#
# CI that:
#
# * checks for a Git Tag that looks like a release
# * builds artifacts with dist (archives, installers, hashes)
# * uploads those artifacts to temporary workflow zip
# * on success, uploads the artifacts to a GitHub Release
#
# Note that the GitHub Release will be created with a generated
# title/body based on your changelogs.
name: Release
permissions:
"contents": "write"
# This task will run whenever you push a git tag that looks like a version
# like "1.0.0", "v0.1.0-prerelease.1", "my-app/0.1.0", "releases/v1.0.0", etc.
# Various formats will be parsed into a VERSION and an optional PACKAGE_NAME, where
# PACKAGE_NAME must be the name of a Cargo package in your workspace, and VERSION
# must be a Cargo-style SemVer Version (must have at least major.minor.patch).
#
# If PACKAGE_NAME is specified, then the announcement will be for that
# package (erroring out if it doesn't have the given version or isn't dist-able).
#
# If PACKAGE_NAME isn't specified, then the announcement will be for all
# (dist-able) packages in the workspace with that version (this mode is
# intended for workspaces with only one dist-able package, or with all dist-able
# packages versioned/released in lockstep).
#
# If you push multiple tags at once, separate instances of this workflow will
# spin up, creating an independent announcement for each one. However, GitHub
# will hard limit this to 3 tags per commit, as it will assume more tags is a
# mistake.
#
# If there's a prerelease-style suffix to the version, then the release(s)
# will be marked as a prerelease.
on:
pull_request:
push:
tags:
- '**[0-9]+.[0-9]+.[0-9]+*'
jobs:
# Fail fast if tag/Cargo.toml/Cargo.lock/CHANGELOG are out of sync.
preflight:
runs-on: "ubuntu-22.04"
if: ${{ !github.event.pull_request }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Build clawhip (release preflight needs the binary)
run: cargo build --release
- name: Release consistency preflight
run: ./target/release/clawhip release preflight "${{ github.ref_name }}"
# Run 'dist plan' (or host) to determine what tasks we need to do
plan:
needs:
- preflight
if: ${{ always() && (needs.preflight.result == 'success' || needs.preflight.result == 'skipped') }}
runs-on: "ubuntu-22.04"
outputs:
val: ${{ steps.plan.outputs.manifest }}
tag: ${{ !github.event.pull_request && github.ref_name || '' }}
tag-flag: ${{ !github.event.pull_request && format('--tag={0}', github.ref_name) || '' }}
publishing: ${{ !github.event.pull_request }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive
- name: Install dist
# we specify bash to get pipefail; it guards against the `curl` command
# failing. otherwise `sh` won't catch that `curl` returned non-0
shell: bash
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.31.0/cargo-dist-installer.sh | sh"
- name: Cache dist
uses: actions/upload-artifact@v6
with:
name: cargo-dist-cache
path: ~/.cargo/bin/dist
# sure would be cool if github gave us proper conditionals...
# so here's a doubly-nested ternary-via-truthiness to try to provide the best possible
# functionality based on whether this is a pull_request, and whether it's from a fork.
# (PRs run on the *source* but secrets are usually on the *target* -- that's *good*
# but also really annoying to build CI around when it needs secrets to work right.)
- id: plan
run: |
dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json
echo "dist ran successfully"
cat plan-dist-manifest.json
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@v6
with:
name: artifacts-plan-dist-manifest
path: plan-dist-manifest.json
# Build and packages all the platform-specific things
build-local-artifacts:
name: build-local-artifacts (${{ join(matrix.targets, ', ') }})
# Let the initial task tell us to not run (currently very blunt)
needs:
- plan
if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }}
strategy:
fail-fast: false
# Target platforms/runners are computed by dist in create-release.
# Each member of the matrix has the following arguments:
#
# - runner: the github runner
# - dist-args: cli flags to pass to dist
# - install-dist: expression to run to install dist on the runner
#
# Typically there will be:
# - 1 "global" task that builds universal installers
# - N "local" tasks that build each platform's binaries and platform-specific installers
matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }}
runs-on: ${{ matrix.runner }}
container: ${{ matrix.container && matrix.container.image || null }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive
- name: Install Rust non-interactively if not already installed
if: ${{ matrix.container }}
run: |
if ! command -v cargo > /dev/null 2>&1; then
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
fi
- name: Install dist
run: ${{ matrix.install_dist.run }}
# Get the dist-manifest
- name: Fetch local artifacts
uses: actions/download-artifact@v7
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- name: Install dependencies
run: |
${{ matrix.packages_install }}
- name: Build artifacts
run: |
# Actually do builds and make zips and whatnot
dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json
echo "dist ran successfully"
- id: cargo-dist
name: Post-build
shell: bash
run: |
# Parse out what we just built and upload it to scratch storage
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
- name: "Upload artifacts"
uses: actions/upload-artifact@v6
with:
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
path: |
${{ steps.cargo-dist.outputs.paths }}
${{ env.BUILD_MANIFEST_NAME }}
# Build and package all the platform-agnostic(ish) things
build-global-artifacts:
needs:
- plan
- build-local-artifacts
runs-on: "ubuntu-22.04"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@v7
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
- name: Fetch local artifacts
uses: actions/download-artifact@v7
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- id: cargo-dist
shell: bash
run: |
dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json
echo "dist ran successfully"
# Parse out what we just built and upload it to scratch storage
echo "paths<<EOF" >> "$GITHUB_OUTPUT"
jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT"
echo "EOF" >> "$GITHUB_OUTPUT"
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
- name: "Upload artifacts"
uses: actions/upload-artifact@v6
with:
name: artifacts-build-global
path: |
${{ steps.cargo-dist.outputs.paths }}
${{ env.BUILD_MANIFEST_NAME }}
# Determines if we should publish/announce
host:
needs:
- plan
- build-local-artifacts
- build-global-artifacts
# Only run if we're "publishing", and only if plan, local and global didn't fail (skipped is fine)
if: ${{ always() && needs.plan.result == 'success' && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: "ubuntu-22.04"
outputs:
val: ${{ steps.host.outputs.manifest }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive
- name: Install cached dist
uses: actions/download-artifact@v7
with:
name: cargo-dist-cache
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/dist
# Fetch artifacts from scratch-storage
- name: Fetch artifacts
uses: actions/download-artifact@v7
with:
pattern: artifacts-*
path: target/distrib/
merge-multiple: true
- id: host
shell: bash
run: |
dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json
echo "artifacts uploaded and released successfully"
cat dist-manifest.json
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
- name: "Upload dist-manifest.json"
uses: actions/upload-artifact@v6
with:
# Overwrite the previous copy
name: artifacts-dist-manifest
path: dist-manifest.json
# Create a GitHub Release while uploading all files to it
- name: "Download GitHub Artifacts"
uses: actions/download-artifact@v7
with:
pattern: artifacts-*
path: artifacts
merge-multiple: true
- name: Cleanup
run: |
# Remove the granular manifests
rm -f artifacts/*-dist-manifest.json
- name: Create GitHub Release
env:
PRERELEASE_FLAG: "${{ fromJson(steps.host.outputs.manifest).announcement_is_prerelease && '--prerelease' || '' }}"
ANNOUNCEMENT_TITLE: "${{ fromJson(steps.host.outputs.manifest).announcement_title }}"
ANNOUNCEMENT_BODY: "${{ fromJson(steps.host.outputs.manifest).announcement_github_body }}"
RELEASE_COMMIT: "${{ github.sha }}"
run: |
# Write and read notes from a file to avoid quoting breaking things
echo "$ANNOUNCEMENT_BODY" > $RUNNER_TEMP/notes.txt
gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/*
# Publish to crates.io
publish-crates:
needs:
- plan
- host
if: ${{ always() && needs.host.result == 'success' && needs.plan.outputs.publishing == 'true' }}
runs-on: "ubuntu-22.04"
env:
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Verify crates publish payload
run: cargo publish --dry-run --locked --package clawhip --manifest-path Cargo.toml
- name: Publish to crates.io
run: cargo publish --locked --package clawhip --manifest-path Cargo.toml
announce:
needs:
- plan
- host
- publish-crates
# use "always() && ..." to allow us to wait for all publish jobs while
# still allowing individual publish jobs to skip themselves (for prereleases).
# "host" however must run to completion, no skipping allowed!
if: ${{ always() && needs.host.result == 'success' }}
runs-on: "ubuntu-22.04"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
submodules: recursive
/target
target/
.omc/
.omx/
.claude/worktrees/
.clawhip/state/
clawhip — AGENTS.md
Daemon-first event gateway for Discord. Routes GitHub, tmux, and custom events to channels.
Working agreements
- Rust: follow existing code style, use
anyhowfor error handling. - Config changes must be backward-compatible (old configs must still parse).
- New routes/filters: add integration tests.
- CLI subcommands: include
--helpdescriptions for all flags. - Keep the daemon lightweight — no unnecessary allocations in the hot path.
Review guidelines
- Flag breaking changes to config schema or CLI interface as P0.
- Verify all Discord API calls handle rate limits and error responses.
- Check for hardcoded tokens or secrets — flag as P0.
- TOML config parsing: ensure new fields have sensible defaults (don't break existing configs).
- Route matching: verify glob patterns are tested with edge cases.
- tmux integration: confirm session monitoring handles missing/dead sessions gracefully.
- New dependencies must be justified — prefer std library where possible.
- Daemon lifecycle: verify clean shutdown and signal handling.
- Test coverage: flag new logic paths that lack corresponding tests.
clawhip Architecture — v0.4.0
clawhip v0.4.0 ships a daemon-first event pipeline for Discord delivery, plus the clone-local install/memory surfaces that wrap it. This document describes the architecture that is present on the release/0.4.0 branch.
Release themes
- typed event model
- multi-delivery router
- extracted event sources
- renderer/sink separation
- install lifecycle polish
- filesystem memory scaffolds
High-level flow
[CLI / webhook / git / GitHub / tmux]
-> [sources]
-> [mpsc queue]
-> [dispatcher]
-> [router -> renderer -> discord sink]
-> [Discord REST delivery]Core components
Typed event model (crate::event)
The daemon accepts legacy IncomingEvent payloads at ingress, normalizes them, and converts them into typed internal events through crate::event::compat. That gives v0.4.0 a typed event model without breaking the existing CLI and HTTP surfaces.
Key event families shipped in v0.4.0:
- custom events
- git commit and branch-change events
- GitHub issue opened / commented / closed events
- GitHub pull-request status change events
- agent lifecycle events
- tmux keyword and stale-session events
Sources (crate::source)
Event production is split into dedicated sources behind the Source trait:
GitSourcepolls configured repositories for commit and branch changesGitHubSourcepolls configured repositories for issue and PR changesTmuxSourcemonitors tmux sessions for keyword hits and stale panes
All sources feed a shared Tokio mpsc queue. This replaces the earlier tighter coupling between monitors, routing, and transport.
Dispatcher (crate::dispatch)
Dispatcher is the queue consumer. For each incoming event it:
1. resolves matching deliveries with the router 2. renders content for each delivery 3. hands the rendered message to the configured sink 4. continues best-effort when one delivery fails
This is the central coordination point for the v0.4.0 pipeline.
Router (crate::router)
The router now resolves 0..N deliveries per event. In practice that means:
- multiple route rules can match the same event
- a match no longer stops at the first rule
- each resolved delivery keeps the destination target, format, template, and mention context
This is the main behavioral change behind the v0.4.0 multi-delivery architecture.
Renderer (crate::render)
Rendering is now explicit. The default renderer is responsible for formatting supported event bodies into compact, alert, inline, or raw output before transport.
That keeps message formatting out of the transport layer and makes the dispatch pipeline easier to extend and test.
Sink (crate::sink)
Transport is represented by the Sink trait. The primary shipped sink in v0.4.0 is the Discord sink, which delivers either to a Discord channel or a Discord webhook target.
The renderer/sink split is important even with a single shipped sink because it removes transport concerns from routing and event modeling.
Configuration model
The preferred Discord configuration surface in v0.4.0 is:
[providers.discord]
token = "..."
default_channel = "1234567890"Legacy [discord] configuration is still accepted and normalized on load for backward compatibility.
Routes continue to use the familiar event/filter model, with a sink field that defaults to "discord":
[[routes]]
event = "github.*"
filter = { repo = "clawhip" }
sink = "discord"
channel = "1480171113253175356"
mention = "<@1465264645320474637>"
format = "compact"Delivery semantics
v0.4.0 currently uses these delivery rules:
- per-source FIFO through the shared queue
- best-effort multi-delivery; one failed delivery does not stop the others
- no built-in retry queue
- source-level tmux keyword windowing, with dispatch remaining stateless
Operational verification
The release branch includes a live verification runbook in `docs/live-verification.md`. It covers daemon status, custom events, git events, GitHub issue/PR flows, and tmux monitoring.
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "anstream"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "anstyle-parse"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys 0.61.2",
]
[[package]]
name = "anyhow"
version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "async-trait"
version = "0.1.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "axum"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8"
dependencies = [
"axum-core",
"bytes",
"form_urlencoded",
"futures-util",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-util",
"itoa",
"matchit",
"memchr",
"mime",
"percent-encoding",
"pin-project-lite",
"serde_core",
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tower",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "axum-core"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1"
dependencies = [
"bytes",
"futures-core",
"http",
"http-body",
"http-body-util",
"mime",
"pin-project-lite",
"sync_wrapper",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af"
[[package]]
name = "bumpalo"
version = "3.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
[[package]]
name = "bytes"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "cc"
version = "1.2.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7a4d3ec6524d28a329fc53654bbadc9bdd7b0431f5d65f1a56ffb28a1ee5283"
dependencies = [
"find-msvc-tools",
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cfg_aliases"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]]
name = "clap"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
[[package]]
name = "clawhip"
version = "0.6.7"
dependencies = [
"anyhow",
"async-trait",
"axum",
"clap",
"reqwest",
"serde",
"serde_json",
"serial_test",
"tempfile",
"time",
"tokio",
"toml",
"uuid",
]
[[package]]
name = "colorchoice"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
[[package]]
name = "deranged"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
dependencies = [
"powerfmt",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "equivalent"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
[[package]]
name = "errno"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "fastrand"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a043dc74da1e37d6afe657061213aa6f425f855399a11d3463c6ecccc4dfda1f"
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
[[package]]
name = "foldhash"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
[[package]]
name = "form_urlencoded"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
[[package]]
name = "futures-channel"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d"
dependencies = [
"futures-core",
]
[[package]]
name = "futures-core"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
[[package]]
name = "futures-executor"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-task"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
[[package]]
name = "futures-util"
version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"slab",
]
[[package]]
name = "getrandom"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"r-efi 5.3.0",
"wasip2",
"wasm-bindgen",
]
[[package]]
name = "getrandom"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
dependencies = [
"cfg-if",
"libc",
"r-efi 6.0.0",
"wasip2",
"wasip3",
]
[[package]]
name = "hashbrown"
version = "0.15.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
dependencies = [
"foldhash",
]
[[package]]
name = "hashbrown"
version = "0.16.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "http"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a"
dependencies = [
"bytes",
"itoa",
]
[[package]]
name = "http-body"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
dependencies = [
"bytes",
"http",
]
[[package]]
name = "http-body-util"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a"
dependencies = [
"bytes",
"futures-core",
"http",
"http-body",
"pin-project-lite",
]
[[package]]
name = "httparse"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
[[package]]
name = "httpdate"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
[[package]]
name = "hyper"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca"
dependencies = [
"atomic-waker",
"bytes",
"futures-channel",
"futures-core",
"http",
"http-body",
"httparse",
"httpdate",
"itoa",
"pin-project-lite",
"smallvec",
"tokio",
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.27.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
dependencies = [
"http",
"hyper",
"hyper-util",
"rustls",
"rustls-pki-types",
"tokio",
"tokio-rustls",
"tower-service",
"webpki-roots",
]
[[package]]
name = "hyper-util"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
dependencies = [
"base64",
"bytes",
"futures-channel",
"futures-util",
"http",
"http-body",
"hyper",
"ipnet",
"libc",
"percent-encoding",
"pin-project-lite",
"socket2",
"tokio",
"tower-service",
"tracing",
]
[[package]]
name = "icu_collections"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
dependencies = [
"displaydoc",
"potential_utf",
"utf8_iter",
"yoke",
"zerofrom",
"zerovec",
]
[[package]]
name = "icu_locale_core"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
dependencies = [
"displaydoc",
"litemap",
"tinystr",
"writeable",
"zerovec",
]
[[package]]
name = "icu_normalizer"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
dependencies = [
"icu_collections",
"icu_normalizer_data",
"icu_properties",
"icu_provider",
"smallvec",
"zerovec",
]
[[package]]
name = "icu_normalizer_data"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
[[package]]
name = "icu_properties"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
dependencies = [
"icu_collections",
"icu_locale_core",
"icu_properties_data",
"icu_provider",
"zerotrie",
"zerovec",
]
[[package]]
name = "icu_properties_data"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
[[package]]
name = "icu_provider"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
dependencies = [
"displaydoc",
"icu_locale_core",
"writeable",
"yoke",
"zerofrom",
"zerotrie",
"zerovec",
]
[[package]]
name = "id-arena"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
[[package]]
name = "idna"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
dependencies = [
"idna_adapter",
"smallvec",
"utf8_iter",
]
[[package]]
name = "idna_adapter"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
dependencies = [
"icu_normalizer",
"icu_properties",
]
[[package]]
name = "indexmap"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45a8a2b9cb3e0b0c1803dbb0758ffac5de2f425b23c28f518faabd9d805342ff"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
"serde",
"serde_core",
]
[[package]]
name = "ipnet"
version = "2.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
[[package]]
name = "iri-string"
version = "0.7.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itoa"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
[[package]]
name = "js-sys"
version = "0.3.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e04e2ef80ce82e13552136fabeef8a5ed1f985a96805761cbb9a2c34e7664d9"
dependencies = [
"cfg-if",
"futures-util",
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "leb128fmt"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
[[package]]
name = "libc"
version = "0.2.184"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48f5d2a454e16a5ea0f4ced81bd44e4cfc7bd3a507b61887c99fd3538b28e4af"
[[package]]
name = "linux-raw-sys"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]]
name = "litemap"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "lru-slab"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]]
name = "matchit"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
[[package]]
name = "memchr"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mio"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1"
dependencies = [
"libc",
"wasi",
"windows-sys 0.61.2",
]
[[package]]
name = "num-conv"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
[[package]]
name = "once_cell"
version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-link",
]
[[package]]
name = "percent-encoding"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
[[package]]
name = "potential_utf"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
dependencies = [
"zerovec",
]
[[package]]
name = "powerfmt"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
name = "ppv-lite86"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
dependencies = [
"zerocopy",
]
[[package]]
name = "prettyplease"
version = "0.2.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quinn"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
dependencies = [
"bytes",
"cfg_aliases",
"pin-project-lite",
"quinn-proto",
"quinn-udp",
"rustc-hash",
"rustls",
"socket2",
"thiserror",
"tokio",
"tracing",
"web-time",
]
[[package]]
name = "quinn-proto"
version = "0.11.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098"
dependencies = [
"bytes",
"getrandom 0.3.4",
"lru-slab",
"rand",
"ring",
"rustc-hash",
"rustls",
"rustls-pki-types",
"slab",
"thiserror",
"tinyvec",
"tracing",
"web-time",
]
[[package]]
name = "quinn-udp"
version = "0.5.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
dependencies = [
"cfg_aliases",
"libc",
"once_cell",
"socket2",
"tracing",
"windows-sys 0.60.2",
]
[[package]]
name = "quote"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
dependencies = [
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
dependencies = [
"getrandom 0.3.4",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "reqwest"
version = "0.12.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
dependencies = [
"base64",
"bytes",
"futures-core",
"http",
"http-body",
"http-body-util",
"hyper",
"hyper-rustls",
"hyper-util",
"js-sys",
"log",
"percent-encoding",
"pin-project-lite",
"quinn",
"rustls",
"rustls-pki-types",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
"tokio",
"tokio-rustls",
"tower",
"tower-http",
"tower-service",
"url",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
"webpki-roots",
]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.17",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "rustc-hash"
version = "2.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe"
[[package]]
name = "rustix"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.2",
]
[[package]]
name = "rustls"
version = "0.23.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4"
dependencies = [
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
dependencies = [
"web-time",
"zeroize",
]
[[package]]
name = "rustls-webpki"
version = "0.103.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f"
[[package]]
name = "scc"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc"
dependencies = [
"sdd",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sdd"
version = "3.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca"
[[package]]
name = "semver"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "serde_path_to_error"
version = "0.1.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
dependencies = [
"itoa",
"serde",
"serde_core",
]
[[package]]
name = "serde_spanned"
version = "0.6.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
dependencies = [
"serde",
]
[[package]]
name = "serde_urlencoded"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
dependencies = [
"form_urlencoded",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serial_test"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f"
dependencies = [
"futures-executor",
"futures-util",
"log",
"once_cell",
"parking_lot",
"scc",
"serial_test_derive",
]
[[package]]
name = "serial_test_derive"
version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a7d91949b85b0d2fb687445e448b40d322b6b3e4af6b44a29b21d9a5f33e6d9"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
dependencies = [
"errno",
"libc",
]
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
[[package]]
name = "socket2"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e"
dependencies = [
"libc",
"windows-sys 0.61.2",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "sync_wrapper"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
dependencies = [
"futures-core",
]
[[package]]
name = "synstructure"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tempfile"
version = "3.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [
"fastrand",
"getrandom 0.4.2",
"once_cell",
"rustix",
"windows-sys 0.61.2",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
"num-conv",
"powerfmt",
"serde_core",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
[[package]]
name = "time-macros"
version = "0.2.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
dependencies = [
"num-conv",
"time-core",
]
[[package]]
name = "tinystr"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
dependencies = [
"displaydoc",
"zerovec",
]
[[package]]
name = "tinyvec"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bd1c4c0fc4a7ab90fc15ef6daaa3ec3b893f004f915f2392557ed23237820cd"
dependencies = [
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.61.2",
]
[[package]]
name = "tokio-macros"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-rustls"
version = "0.26.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
dependencies = [
"rustls",
"tokio",
]
[[package]]
name = "toml"
version = "0.8.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
dependencies = [
"serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"toml_write",
"winnow",
]
[[package]]
name = "toml_write"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
[[package]]
name = "tower"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
dependencies = [
"futures-core",
"futures-util",
"pin-project-lite",
"sync_wrapper",
"tokio",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-http"
version = "0.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
dependencies = [
"bitflags",
"bytes",
"futures-util",
"http",
"http-body",
"iri-string",
"pin-project-lite",
"tower",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-layer"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
[[package]]
name = "tower-service"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
"log",
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
]
[[package]]
name = "try-lock"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "unicode-xid"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
dependencies = [
"form_urlencoded",
"idna",
"percent-encoding",
"serde",
]
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
dependencies = [
"getrandom 0.4.2",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "want"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
dependencies = [
"try-lock",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasip2"
version = "1.0.2+wasi-0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasip3"
version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0551fc1bb415591e3372d0bc4780db7e587d84e2a7e79da121051c5c4b89d0b0"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-futures"
version = "0.4.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03623de6905b7206edd0a75f69f747f134b7f0a2323392d664448bf2d3c5d87e"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fbdf9a35adf44786aecd5ff89b4563a90325f9da0923236f6104e603c7e86be"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dca9693ef2bab6d4e6707234500350d8dad079eb508dca05530c85dc3a529ff2"
dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.117"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39129a682a6d2d841b6c429d0c51e5cb0ed1a03829d8b3d1e69a011e62cb3d3b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "wasm-encoder"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
dependencies = [
"leb128fmt",
"wasmparser",
]
[[package]]
name = "wasm-metadata"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
dependencies = [
"anyhow",
"indexmap",
"wasm-encoder",
"wasmparser",
]
[[package]]
name = "wasmparser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
dependencies = [
"bitflags",
"hashbrown 0.15.5",
"indexmap",
"semver",
]
[[package]]
name = "web-sys"
version = "0.3.94"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd70027e39b12f0849461e08ffc50b9cd7688d942c1c8e3c7b22273236b4dd0a"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "web-time"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki-roots"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets 0.52.6",
]
[[package]]
name = "windows-sys"
version = "0.60.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
dependencies = [
"windows-targets 0.53.5",
]
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
"windows_i686_gnullvm 0.52.6",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
[[package]]
name = "windows-targets"
version = "0.53.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3"
dependencies = [
"windows-link",
"windows_aarch64_gnullvm 0.53.1",
"windows_aarch64_msvc 0.53.1",
"windows_i686_gnu 0.53.1",
"windows_i686_gnullvm 0.53.1",
"windows_i686_msvc 0.53.1",
"windows_x86_64_gnu 0.53.1",
"windows_x86_64_gnullvm 0.53.1",
"windows_x86_64_msvc 0.53.1",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_aarch64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_i686_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnu"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
[[package]]
name = "windows_x86_64_msvc"
version = "0.53.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
[[package]]
name = "winnow"
version = "0.7.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
dependencies = [
"wit-bindgen-rust-macro",
]
[[package]]
name = "wit-bindgen-core"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
dependencies = [
"anyhow",
"heck",
"wit-parser",
]
[[package]]
name = "wit-bindgen-rust"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
dependencies = [
"anyhow",
"heck",
"indexmap",
"prettyplease",
"syn",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
]
[[package]]
name = "wit-bindgen-rust-macro"
version = "0.51.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
dependencies = [
"anyhow",
"prettyplease",
"proc-macro2",
"quote",
"syn",
"wit-bindgen-core",
"wit-bindgen-rust",
]
[[package]]
name = "wit-component"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
dependencies = [
"anyhow",
"bitflags",
"indexmap",
"log",
"serde",
"serde_derive",
"serde_json",
"wasm-encoder",
"wasm-metadata",
"wasmparser",
"wit-parser",
]
[[package]]
name = "wit-parser"
version = "0.244.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
dependencies = [
"anyhow",
"id-arena",
"indexmap",
"log",
"semver",
"serde",
"serde_derive",
"serde_json",
"unicode-xid",
"wasmparser",
]
[[package]]
name = "writeable"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
[[package]]
name = "yoke"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
dependencies = [
"stable_deref_trait",
"yoke-derive",
"zerofrom",
]
[[package]]
name = "yoke-derive"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zerocopy"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zerofrom"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
dependencies = [
"zerofrom-derive",
]
[[package]]
name = "zerofrom-derive"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
[[package]]
name = "zerotrie"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
dependencies = [
"displaydoc",
"yoke",
"zerofrom",
]
[[package]]
name = "zerovec"
version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
dependencies = [
"yoke",
"zerofrom",
"zerovec-derive",
]
[[package]]
name = "zerovec-derive"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zmij"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[package]
name = "clawhip"
version = "0.6.7"
edition = "2024"
description = "Event-to-channel notification router for Discord — bypass gateway sessions, just events in, messages out"
license = "MIT"
repository = "https://github.com/Yeachan-Heo/clawhip"
keywords = ["discord", "notifications", "tmux", "github", "devops"]
categories = ["command-line-utilities", "development-tools"]
[dependencies]
anyhow = "1"
async-trait = "0.1"
axum = "0.8"
clap = { version = "4.5", features = ["derive", "env"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.45", features = ["full"] }
time = { version = "0.3", features = ["formatting"] }
uuid = { version = "1", features = ["v4"] }
toml = "0.8"
[dev-dependencies]
serial_test = "3"
tempfile = "3.15"
# The profile that 'dist' will build with
[profile.dist]
inherits = "release"
lto = "thin"
Changelog
0.6.7 - 2026-04-12
Highlights
- fix native hook repo/worktree metadata so worktree prompt-submitted events route with canonical main-repo names instead of branch/worktree leaf names
- reconcile prompt-submit marker handling between generated native hooks and
clawhip deliver, storing prompt-submit state at the effective worktree root - align Codex hook installation with the official OpenAI contract by supporting both
~/.codex/hooks.jsonand<repo>/.codex/hooks.jsonwhile keeping the clawhip bridge in~/.clawhip - keep Claude Code provider-native hook installation global-only, with updated operator docs and regression coverage
- add regression suites for worktree metadata emission, Codex project/global hook detection, prompt-deliver marker reconciliation, and install-scope rejection
Upgrade notes
- crate version is now
0.6.7 - rerun
clawhip hooks install --provider codex --scope global --force(or--scope projectper-repo) andclawhip hooks install --provider claude-code --scope global --forceto refresh existing hook files - existing route/config schema remains compatible; no migration required
0.6.6 - 2026-04-10
Highlights
- remove the residual dispatch bypass-delivery timing flake with a deterministic test path so CI stays boring under load
- fix
clawhip tmux newfalse-negative launch failures by handing monitoring back to the daemon after successful session creation - add
clawhip release preflightand gate the release workflow on version / Cargo.lock / changelog consistency - add
clawhip explainplus route/delivery provenance output for operator debugging
Upgrade notes
- crate version is now
0.6.5 - existing config remains compatible; no migration required
Highlights
- add Discord channel binding verification so misbound repo→channel routes are caught before config writes (#198)
- new
clawhip config verify-bindings [--json]command audits every channel ID in the config against live Discord state and exits non-zero on drift - new
clawhip setup --bind REPO=CHANNEL_ID [--expect-name REPO=NAME]resolves the channel via Discord, writes a route with achannel_namehint, and refuses stale/mismatched bindings before saving - new optional
channel_namehint field on[[routes]],[defaults],[[monitors.git.repos]], and[[monitors.tmux.sessions]]— advisory only, old configs continue to load unchanged - keep release preflight guarding Cargo.toml / Cargo.lock / CHANGELOG consistency before publish (#189)
- wire the release workflow so inconsistent tags are rejected before
dist planandpublish-cratesrun
Upgrade notes
- crate version is now
0.6.6 - existing config remains compatible; no migration required
How to use
- drift audit:
clawhip config verify-bindings(text) or--jsonfor CI. Exit code is non-zero on any failed binding. - bind a repo to a Discord channel safely:
clawhip setup --bind oh-my-codex=1480171106324189335 --expect-name oh-my-codex=omx-dev. Clawhip resolves the channel live, printsbind: oh-my-codex -> 1480171106324189335 (#omx-dev), and writes[[routes]] filter = { repo = "oh-my-codex" }, channel = "…", channel_name = "omx-dev". Name mismatches and 404s abort before the write. - run
clawhip release preflightlocally in the repo root before tagging — omit the version to default to the currentCargo.tomlversion, or pass an explicit tag (clawhip release preflight v0.6.5,clawhip release preflight refs/tags/v0.6.5) - the same command runs in CI via the new
preflightjob gating the release workflow
0.6.4 - 2026-04-10
Breaking
- replace provider-specific wrapper/launcher docs with the shared provider-native Codex + Claude hook surface
- document
clawhip native hookas the generic ingress for shared hook payload verification - move public guidance to provider-native installation, git-derived repo/worktree routing identity, and additive
.clawhip/hooks/augmentation
Highlights
- add
clawhip deliverfor prompt-submit-aware prompt recovery into existing hooked tmux-backed provider sessions - validate repo-local hook setup and active Codex/Claude (including OMC/OMX wrapper) panes before retrying Enter
- record prompt-submit readiness in
.clawhip/state/prompt-submit.jsonso delivery can stop once the hook actually fires
Upgrade notes
- if you were using wrapper-specific launch flows, migrate to provider-owned hook registration plus
clawhip native hookfor local testing - the shared v1 contract now documents only
SessionStart,PreToolUse,PostToolUse,UserPromptSubmit, andStop
0.6.0 - 2026-04-09
Highlights
- replace the old bespoke OMC/OMX wrapper glue with provider-native Codex + Claude hook registration built around
clawhip native hook - preserve prompt-submit and stop lifecycle events in the provider-native flow so real interactive sessions emit the same critical lifecycle signals clawhip expects
- fix Codex native-hook installation/detection to use
~/.codex/hooks.jsonas the real hook registry surface instead of treating.codex/config.tomlas installed-state evidence - add tmux session/pane metadata to native hook payloads, including pane id, pane tty, attached state, and client count
- preserve the tmux metadata through normalization and rendering so local hook logs stay attributable to the originating tmux pane/session
Documentation and tooling
- update public docs to describe the shared provider-native hook contract instead of the older wrapper/launcher-specific hook assets
- remove the checked-in OMC/OMX wrapper helper scripts and legacy hook assets that were superseded by provider-owned hook registration
- add provider-native docs regression coverage so the new installation guidance remains consistent
Upgrade notes
- crate version is now
0.6.0 - rerun
clawhip hooks install --all --scope global --forceto install the latest global Codex/Claude hook wiring into~/.codex/hooks.jsonand~/.claude/settings.json - Codex hook installation now targets
~/.codex/hooks.json;.codex/config.tomlremains a general config surface and should not be treated as the hook registry - if you were relying on the older wrapper-specific OMC/OMX launch helpers or checked-in wrapper hook assets, migrate to provider-owned hook registration plus
clawhip native hookfor local verification
0.5.4 - 2026-04-05
Highlights
- native OMC/OMX lifecycle hooks with one-shot installer (
clawhip hooks install --omc|--omx|--all) clawhip omc "prompt"andclawhip omx launch "prompt"for guaranteed prompt delivery with TUI detection- session-init and session-stop hooks emit
session.started/session.finished/session.failedto clawhip daemon - cleaned up accidentally committed embedded worktree and local agent state from repo history
Upgrade notes
- crate version is now
0.5.4 - run
clawhip hooks install --omcto deploy OMC lifecycle hooks to~/.claude/hooks/ - run
clawhip hooks install --omxfor OMX lifecycle hooks - existing config remains compatible; no migration required
0.5.3 - 2026-04-04
Highlights
- fix
clawhip send --channelbeing overridden by route or default channel config - for
customevents, the explicit event channel now takes highest priority over route and default channels
Upgrade notes
- crate version is now
0.5.3 - existing config remains compatible; no migration required
- if you relied on a catch-all
event = "custom"route to redirect allclawhip sendtraffic to a specific channel, that route channel will now only apply when--channelis not specified
0.5.2 - 2026-04-04
Highlights
- reduced routine Discord burst noise with configurable batching for routine notifications
- allow
stale_minutes = 0to disable tmux stale detection cleanly - keep cron startup alive when persisted scheduler state is empty or invalid
- surface source failures as degraded alerts before the daemon appears healthy
- make matched route channels override source-provided channel hints consistently
- quiet invalid git monitor paths so they stop drowning out actionable failures
Upgrade notes
- crate version is now
0.5.2 - existing config remains compatible; no schema migration is required for this patch release
stale_minutes = 0is now treated as an explicit disable for tmux stale alerts
0.3.0 - 2026-03-09
Highlights
- introduced the typed internal event model used by the dispatcher pipeline
- generalized routing so one event can fan out to multiple deliveries
- extracted git, GitHub, and tmux monitoring into explicit event sources
- split rendering from transport and shipped the Discord sink on top of that boundary
- kept existing CLI and HTTP event ingress compatible while normalizing into the new architecture
Upgrade notes
- crate version is now
0.3.0 [providers.discord]is the preferred config surface; legacy[discord]remains compatible- routes may set
sink = "discord"; omitting it still defaults to Discord in this release
[Unit]
Description=clawhip daemon
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/clawhip start
Restart=always
RestartSec=5
Environment=RUST_LOG=info
[Install]
WantedBy=multi-user.target
[workspace]
members = ["cargo:."]
# Config for 'dist'
[dist]
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
cargo-dist-version = "0.31.0"
# CI backends to support
ci = "github"
# The installers to generate for each app
installers = ["shell"]
# Target platforms to build apps for (Rust target-triple syntax)
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"]
# Path that installers should place binaries in
install-path = "CARGO_HOME"
# Where to host releases
hosting = "github"
# Whether to install an updater program
install-updater = false
# Allow manually edited CI files
allow-dirty = ["ci"]
Canonical contract cleanup note
This historical audit has been superseded by the provider-native Codex + Claude hook rewrite.
Current source-of-truth docs:
docs/native-event-contract.md— routing, metadata, and additive augmentation guidancedocs/event-contract-v1.md— frozen shared-event contractdocs/live-verification.md— verification workflow and regression evidence expectations
When updating this area, prefer documenting the shared provider-native surface rather than older wrapper- or bridge-specific integration paths.
Effect:
tmux.keywordandgit.commitno longer collapse into the same routine batch just because they share a Discord target- batch content stays semantically coherent
- this remains backward-compatible for existing single-family burst batching
Initial canonical contract rules
For incremental adoption, route authors should prefer this vocabulary:
event/contract_eventtoolrepo_namesession_nameissue_numberpr_numberbranchchannel_hint
Legacy aliases remain supported:
reposessionchannel
Follow-up slices
Slice A: source payload parity
Optionally teach git / GitHub / tmux sources to emit the canonical names directly in payloads (repo_name, session_name) in addition to legacy keys.
Slice B: explicit delivery contract type
Promote a documented delivery identity object around:
- canonical event kind
- sink
- target
- format
- mention
- template
- dynamic-token policy
Slice C: renderer contract fixtures
Add snapshot-style tests for one canonical example per event family:
session.*workspace.*git.*github.*tmux.*- batched CI / routine summaries
Files tied to this pass
src/events.rssrc/router.rssrc/dispatch.rsdocs/canonical-contract-cleanup.md
Event Contract v1
This document freezes clawhip's v1 shared provider-native hook contract for Codex and Claude.
Status
- Schema version:
1 - Stability: stable
- Compatibility policy: v1 allows additive metadata only. The shared event set and base-field
meanings are frozen.
Shared event family
clawhip v1 supports exactly these five provider-native events:
SessionStartPreToolUsePostToolUseUserPromptSubmitStop
Provider-specific extras are out of scope for v1.
Supported ingress
The public local ingress for shared provider-native payloads is:
clawhip native hook --provider codex --file payload.json
clawhip native hook --provider claude --file payload.jsonFrozen base fields
After clawhip normalizes a provider payload, these base routing fields are the v1 contract:
| Field | Required | Notes |
|---|---|---|
provider | yes | codex or claude for the shared v1 surface. |
event | yes | One of the 5 shared event names above. |
directory | when known | Provider working directory at hook time. |
worktree_path | when known | Worktree/repo path used for routing and context. |
repo_name | when known | Repository identity for stable routing. |
repo_path | when known | Canonical git repo root; authoritative with worktree_path for routing. |
project | when known | Optional display metadata from provider payloads; not authoritative for routing. |
session_id | no | Provider/session correlation identifier. |
branch | when known | Git branch when available. |
tool_name | tool events | Tool identifier for pre/post tool hooks. |
command | tool events | Command context when a provider supplies it. |
summary | no | Short human-readable context. |
event_timestamp | no | Timestamp preserved from provider input when available. |
Augmentation rules
.clawhip/hooks/ may only add data to the normalized base contract.
Allowed:
- extra message/context fields
- frontmatter enrichment
- recent-context attachment
Disallowed:
- removing or replacing
provider,event,directory,worktree_path,repo_name, orproject - replacing the entire payload with a custom schema
- promoting provider-specific extra events into the shared route surface
Verification expectations
v1 verification must prove:
1. Codex and Claude both normalize all 5 shared events successfully. 2. Git-derived repo_path / worktree_path survive normalization and drive routing identity. 3. Augmentation is additive and preserves the base contract. 4. Public documentation only references the provider-native surface.
Relationship to the higher-level contract note
docs/native-event-contract.md remains the routing/augmentation guide. This document is the frozen v1 source of truth for shared events, base fields, and compatibility policy.
MEMORY.md — pointer/index layer
Current beliefs
- Current priority: stabilize project work, keep memory retrieval cheap, and offload detail into filesystem shards.
- Root memory is for summaries, pointers, and write obligations only.
- Detailed logs belong in
memory/.
Quick file map
- Project status:
memory/projects/clawhip.md - Today's execution log:
memory/daily/2026-03-10.md - Channel-specific state:
memory/channels/example-channel.md - Durable rules and lessons:
memory/topics/rules.md,memory/topics/lessons.md - Full subtree guide:
memory/README.md
Read this when...
- You need current repo status -> read
memory/projects/clawhip.md - You need latest execution context -> read today's file in
memory/daily/ - You are acting in one channel/lane -> read that file in
memory/channels/ - You are changing workflow policy -> read
memory/topics/rules.md
Write obligations
- Daily progress goes to today's daily file.
- Channel-specific detail goes to that channel file.
- Durable lessons get promoted into
memory/topics/lessons.md. MEMORY.mdonly changes when the pointer map or current beliefs change.
example-channel
Purpose
Canonical memory for one conversation lane.
Keep here
- channel-specific decisions
- ongoing blockers
- who owns next action
- links/pointers to project files when needed
Keep elsewhere
- generic project state ->
memory/projects/<project>.md - daily execution transcript ->
memory/daily/YYYY-MM-DD.md - durable cross-channel rules ->
memory/topics/rules.md
2026-03-10
Summary
- Worked on the memory offload pattern.
- Extracted detailed notes into filesystem shards.
- Kept root memory limited to pointers and current beliefs.
Events
- new architecture/spec documented
- guide written for agents/operators
- examples added for pointer file and memory tree
Follow-up
- continue moving monolithic sections into canonical shards
- archive cold history when the active set grows too large
clawhip
Current state
- Canonical project shard for repo-specific status.
- Use this file for active plans, blockers, and durable project context.
Keep here
- project status
- active priorities
- blockers and follow-ups
- links to handoffs and decisions
memory/README.md — retrieval guide
File map
daily/YYYY-MM-DD.md-> chronological work logchannels/<channel>.md-> per-channel state and commitmentsprojects/<project>.md-> repo/project-specific statustopics/rules.md-> durable operating rulestopics/lessons.md-> reusable lessonshandoffs/YYYY-MM-DD-<slug>.md-> bounded handoffsarchive/YYYY-MM/-> cold history
Read by situation
- Need latest execution context -> latest
daily/file - Need canonical project state -> matching file in
projects/ - Need one lane's background -> matching file in
channels/ - Need policy or norms ->
topics/rules.md
Naming rules
- Use stable slugs for channels and projects.
- Use one active daily-file convention.
- Archive inactive time slices instead of bloating hot files.
lessons
- Repeated workflows deserve stable files.
- Daily logs and durable knowledge should not live in the same place.
- Small pointer files scale better than giant narrative memory docs.
rules
- Root
MEMORY.mdstays short. - Detailed updates belong in canonical leaf shards.
- Refactor memory when retrieval becomes noisy.
Live verification workflow for built-in presets
This document is for real operational verification, not mock-only tests.
Preconditions
- running
clawhipdaemon - real Discord bot token with access to the test channel
- real GitHub auth (
gh auth statusshould succeed) - tmux installed locally
- route filters configured for the target repo/session/channel
Recommended environment:
export CLAWHIP_REPO=Yeachan-Heo/clawhip
export CLAWHIP_CHANNEL=1480171113253175356
export CLAWHIP_DAEMON_URL=http://127.0.0.1:25294
export CLAWHIP_BOT_TOKEN='<discord-bot-token>'
export CLAWHIP_MENTION='<@1465264645320474637>'Real built-in preset checklist
GitHub issue presets
- issue opened
- issue commented
- issue closed
Operational flow:
1. Create a real issue in the target repo. 2. Wait for daemon monitor pickup or webhook delivery. 3. Confirm a real Discord message arrives in the configured test channel. 4. Add a real comment to the issue. 5. Confirm the issue-commented message arrives. 6. Close the issue. 7. Confirm the issue-closed message arrives.
GitHub PR presets
- PR opened
- PR status changed
- PR merged
Operational flow:
1. Create a temporary base branch and feature branch. 2. Push the feature branch. 3. Open a real PR against the temporary base branch. 4. Confirm the PR-opened / status-changed message arrives. 5. Merge the temporary PR. 6. Confirm the merged status message arrives. 7. Delete temporary branches if desired.
Provider-native Codex + Claude contract
- shared event set:
SessionStart,PreToolUse,PostToolUse,UserPromptSubmit,Stop - generic ingestion via
clawhip native hook --provider <codex|claude>
Operational flow:
1. Enable provider-native hooks in a real Codex or Claude Code workspace:
- Codex:
clawhip hooks install --provider codex --scope globalor--scope project(matching the official Codexhooks.jsonsearch locations) - Claude Code:
clawhip hooks install --provider claude-code --scope global
2. Pipe one representative Codex payload through the generic native ingress:
printf '%s\n' '{
"session_id": "sess-65",
"cwd": "/repo/clawhip",
"event": "SessionStart"
}' | clawhip native hook --provider codex3. Confirm clawhip accepts it and renders a stable lifecycle message with project/repo context. 4. Repeat with a representative Claude payload:
printf '%s\n' '{
"session_id": "sess-65",
"cwd": "/repo/clawhip",
"event": "SessionStart"
}' | clawhip native hook --provider claude5. Confirm both providers normalize into the same shared route family. 6. Send representative payloads for PreToolUse, PostToolUse, UserPromptSubmit, and Stop. 7. Confirm additive augmentation still preserves the base routing keys when .clawhip/hooks/ is enabled.
tmux presets
- keyword detection
- stale detection
- tmux wrapper registration path
Operational flow:
1. Launch a real Codex or Claude session with provider-native hooks enabled. 2. Verify the pane is actually alive before trusting any agent.started message. 3. Confirm routed delivery in Discord. 4. Print a configured keyword (error, FAILED, PR created, etc) only when intentionally testing keyword behavior. 5. Leave the session idle beyond the stale threshold only when intentionally testing stale behavior. 6. Inspect clawhip tmux list to confirm exactly which watch registrations exist. 7. If alert text disagrees with pane reality, treat it as monitor noise and debug registration overlap / stale math before assuming session failure.
Helper script
A helper script is included:
scripts/live-verify-default-presets.sh <mode>Available modes:
issue-openedissue-commentissue-closedpr-openedpr-mergedtmux-keywordtmux-staletmux-wrapper
The script is intentionally conservative: it prints the live workflow and fetches recent Discord messages, but it does not silently mutate production resources without operator intent.
Verified live run already completed
On March 8, 2026, a real validation was run for the GitHub issue-opened monitor path:
- real issue created on
Yeachan-Heo/clawhip - daemon monitor emitted
github.issue-opened - real Discord delivery observed with route-level mention prepended
- issue closed after verification
On March 11, 2026, a real validation was run for the custom send path:
- local daemon health/status returned ok on
http://127.0.0.1:25294 cargo run -q -- send --message "🧪 clawhip live verification (...)"exited successfully- guild-wide search confirmed actual Discord delivery by the
clawhipwebhook bot - delivery landed in
#ops(1477003109564678174), confirming the configured wildcard webhook route was active
Filesystem-Offloaded Memory Architecture
This document defines the Claw OS-style memory pattern that clawhip recommends for filesystem-backed project memory: keep MEMORY.md small and high-signal, and offload detailed memory into structured filesystem documents.
Goal
Use the filesystem as the durable memory substrate while keeping the root memory surface fast to load, easy to maintain, and safe for repeated agent use.
In this pattern:
MEMORY.mdis the hot pointer/index layermemory/holds the detailed memory shards- agents read the minimum set of files needed for the current task
- agents write detailed updates to leaf files, not back into a monolith
- memory refactoring/offloading is ongoing maintenance, not a one-time cleanup
This is the memory model that fits clawhip's broader direction as an OS-like runtime: small control surfaces, explicit routing, and durable state outside the hot path.
Design principles
1. Keep the hot layer small. MEMORY.md should stay short enough to scan quickly. 2. Shard by stable retrieval paths. Organize memory by entity, domain, or time instead of one narrative file. 3. Separate index from detail. Index files answer where to read and write; leaf files hold the detail. 4. Prefer append at the edge. Daily logs and entity files absorb detail so the root stays curated. 5. Refactor memory continuously. When a section grows noisy, split it into a dedicated file and leave a pointer behind. 6. Protect private or sensitive state. Not every shard should be loaded in every context.
Layer model
Layer 1: MEMORY.md (hot pointer layer)
MEMORY.md should answer only high-value questions such as:
- what is currently true
- which files matter right now
- where a new update should be written
- what an agent must read before acting
Recommended contents:
- current beliefs / active focus
- quick file map
- scenario-based read guide
- write obligations
- recent refactors / moved files
Avoid putting long transcripts, raw logs, or exhaustive histories here.
Layer 2: subtree indexes (routing layer)
Subtree index files live under memory/ and narrow retrieval further.
Examples:
memory/README.mdmemory/channels/README.mdmemory/projects/README.mdmemory/agents/README.md
Their job is to answer:
- which shard is canonical for a category
- naming conventions
- lookup rules and aliases
- which files are active vs archived
Layer 3: leaf memory files (detail layer)
Leaf files hold the durable detail.
Common shard types:
- daily logs — chronological activity and handoff notes
- channel memory — one file per channel or conversation lane
- project memory — repo-specific state, plans, blockers, decisions
- agent memory — preferences, roles, working patterns, handoff expectations
- topic memory — rules, lessons, ops, people, research, decisions
- archive files — older daily or project material moved out of the hot set
Recommended directory layout
A practical default layout:
MEMORY.md
memory/
README.md
daily/
YYYY-MM-DD.md
channels/
README.md
<channel-slug>.md
projects/
README.md
<project-slug>.md
agents/
README.md
<agent-slug>.md
topics/
README.md
rules.md
lessons.md
ops.md
people.md
decisions/
YYYY-MM-DD-<slug>.md
handoffs/
YYYY-MM-DD-<slug>.md
archive/
YYYY-MM/
YYYY-MM-DD.md
registry/
channel-registry.json
project-registry.jsonNotes:
- teams may use
memory/daily/YYYY-MM-DD.mdormemory/YYYY-MM-DD.md; standardize on one active convention - use registries only where alias/ID lookup is genuinely useful
- avoid creating dozens of tiny folders before retrieval rules are clear
Read path
Recommended read order:
1. open MEMORY.md 2. follow the scenario-based pointer to the relevant subtree index or leaf file 3. read only the files needed for the task 4. avoid bulk-loading the whole memory tree unless explicitly required
Example:
Need current repo status?
-> MEMORY.md
-> memory/projects/clawhip.md
-> latest daily file if recent execution context mattersWrite path
Recommended write order:
1. decide the canonical target file 2. write detail into the leaf file 3. update MEMORY.md only if the pointer map or current beliefs changed 4. archive or split a file when it becomes noisy
Example event-driven routing:
- new execution log -> today's daily file
- channel-specific decision -> that channel file
- durable workflow rule ->
memory/topics/rules.md - reusable lesson ->
memory/topics/lessons.md - long section extracted from root memory -> dedicated shard + short pointer in
MEMORY.md
Offload/refactor rules
Offload content out of MEMORY.md when any of these are true:
- a section becomes mostly historical detail
- the content belongs to one stable entity or topic
- the content is needed only in specific workflows
- the root file is getting slow or noisy to scan
- the content is append-heavy and better suited to a log
When offloading:
1. create the destination file 2. move or summarize the detailed content there 3. replace the old root section with a one-line pointer and current takeaway 4. add or update a subtree index if the new area will grow
Clawhip-as-OS fit
clawhip already models the world as routed events, normalized contracts, and explicit sinks. The offloaded memory pattern applies the same operating idea to project state:
MEMORY.mdbehaves like a control-plane index- filesystem shards behave like durable state partitions
- agent workflows route reads/writes to the right partition
- archival keeps the hot surface operationally cheap
That makes memory a first-class operating pattern instead of an accidental giant note.
Non-goals
This pattern does not require clawhip to become a database, vector store, or embedded note service.
It is a documentation and workflow architecture for filesystem-backed memory that agents and operators can adopt around clawhip.
Related docs
- Filesystem-Offloaded Memory Guide
- Example pointer file
- Example memory subtree index
- Memory-offload skill guide
- Installable workflow skill
Filesystem-Offloaded Memory Guide
This guide shows how agents and operators should use the offloaded memory pattern in practice.
For the architecture/spec, see Filesystem-Offloaded Memory Architecture.
Operating rule
Treat MEMORY.md as the fast pointer layer, not the place where all detail accumulates.
A good default policy is:
- read
MEMORY.mdfirst - jump to the smallest relevant shard
- write detail into leaf files
- update root pointers only when the map or current beliefs change
Bootstrap with clawhip
clawhip now ships a first runtime vertical slice for this pattern:
# initialize a scaffold in the current repo
clawhip memory init --project clawhip --channel discord-alerts --agent codex
# pin a specific daily shard name when backfilling or scripting
clawhip memory init --project clawhip --date 2026-03-10
# inspect the scaffold and list missing recommended paths
clawhip memory status --project clawhip --channel discord-alerts --agent codexWhat clawhip memory init bootstraps:
MEMORY.mdmemory/README.mdmemory/daily/YYYY-MM-DD.mdmemory/projects/<project>.mdmemory/topics/rules.mdmemory/topics/lessons.md- optional
memory/channels/<channel>.md - optional
memory/agents/<agent>.md
The command leaves existing files untouched by default and only overwrites scaffold files when you pass --force.
What goes where
Put in MEMORY.md
- active focus
- short current-state summary
- mandatory read paths for common situations
- write obligations
- links/pointers to canonical files
- recently moved or split sections
Put in memory/ leaf files
- detailed notes
- chronological logs
- channel-specific context
- project-specific state
- lessons, decisions, and operating rules
- handoff detail
- raw or semi-raw material that is too large for the hot layer
Practical agent workflow
Before acting
1. Read MEMORY.md. 2. Follow the scenario pointer for the current task. 3. Load only the relevant project/channel/topic/daily shard. 4. If no canonical target exists, create one in the correct subtree.
While working
- append execution detail to the leaf shard that owns it
- keep root updates short and intentional
- if you discover a repeated retrieval path, add it to an index
- if a shard starts mixing unrelated topics, split it
After working
- write detailed outcome to the canonical shard
- update
MEMORY.mdwith only the new current belief or pointer change - move stale time-based material to
archive/when needed
Recommended write-routing rules
Use rules like these:
| If the update is about... | Write to... |
|---|---|
| what happened today | memory/daily/YYYY-MM-DD.md |
| one Discord/Slack/channel lane | memory/channels/<channel>.md |
| one project/repo | memory/projects/<project>.md |
| one agent/operator profile | memory/agents/<agent>.md |
| reusable lessons | memory/topics/lessons.md |
| durable policies/rules | memory/topics/rules.md |
| one handoff | memory/handoffs/YYYY-MM-DD-<slug>.md |
| older inactive history | memory/archive/... |
Migration: monolithic MEMORY.md -> offloaded memory
A safe migration path:
1. Freeze the role of MEMORY.md
Rewrite the file so it becomes:
- current beliefs
- file map
- scenario-based read guide
- write obligations
Do not keep adding detailed narrative after this step.
2. Identify high-growth sections
Typical sections to extract first:
- daily logs
- per-project sections
- per-channel sections
- long decision histories
- raw handoff dumps
- reusable rules/lessons hidden inside narrative blocks
3. Create the first shards
Start with the highest-leverage set:
memory/README.md
memory/daily/
memory/projects/
memory/channels/
memory/topics/
memory/archive/You do not need every subtree on day one.
4. Move detail, leave pointers
For each extracted section:
- move the detailed content into the new shard
- replace it in
MEMORY.mdwith: - a short summary
- the canonical file path
- when to read it
5. Add write obligations
Make the system self-maintaining by stating rules such as:
- daily activity must go to today's daily file
- channel-specific context must go to the channel file
- durable lessons must be lifted into
topics/lessons.md - root memory must only hold summaries and pointers
6. Archive aggressively
Once a daily or project shard is no longer hot:
- compress it into a monthly archive bucket, or
- leave a short status summary and move the history out
Refactor triggers
Refactor memory when:
MEMORY.mdstops being skimmable- the same topic keeps expanding in the root file
- agents repeatedly read too much irrelevant context
- a file serves more than one clear owner
- retrieval depends on remembering ad hoc prose instead of stable paths
Example starter set
Concrete example files in this repo:
- docs/examples/MEMORY.example.md
- docs/examples/memory/README.example.md
- docs/examples/memory/channels/example-channel.md
- docs/examples/memory/daily/2026-03-10.md
- skills/memory-offload/SKILL.md
- docs/examples/memory/projects/clawhip.md
- docs/examples/memory/topics/rules.md
- docs/examples/memory/topics/lessons.md
Cautions
- Do not turn the new tree into a second monolith.
- Do not create shards without clear read/write ownership.
- Do not expose sensitive memory in shared or automatically loaded files.
- Do not keep parallel daily-file conventions forever; pick one and normalize.
- Do not copy private production memory into public examples; abstract the pattern.
Quick checklist
- Is
MEMORY.mdshort and high-signal? - Does every common workflow have a canonical file?
- Are daily logs separated from durable rules/lessons?
- Are archive rules clear?
- Can an agent tell where to write without guessing?
Provider-native Codex + Claude Hook Contract
Frozen shared-event reference: see docs/event-contract-v1.md.
This document is the higher-level routing, metadata, and augmentation guide.
clawhip now treats Codex and Claude as the source of truth for hook registration and scope. clawhip's job is to ingest provider-native hook payloads, normalize them into a stable routing contract, and handle delivery.
Goal
clawhip should remain the single routing and formatting layer for provider-native operational events. Providers should fire hooks; clawhip should normalize, route, and render them.
Shared v1 hook surface
v1 intentionally supports only the five events shared by Codex and Claude:
SessionStartPreToolUsePostToolUseUserPromptSubmitStop
Provider-specific extra events stay out of the shared route surface until clawhip adopts them explicitly.
Preferred ingress
Use the generic provider-native thin client:
clawhip native hook --provider codex --file payload.json
clawhip native hook --provider claude --file payload.json
cat payload.json | clawhip native hook --provider codexThis keeps local verification, fixture testing, and provider-side forwarding on one public surface.
Stable base routing fields
When the provider payload and git repo/worktree context make them available, clawhip preserves these base fields for routing:
providereventsession_iddirectoryrepo_pathworktree_pathrepo_namebranchtool_namecommandsummaryevent_timestamp
Notes
repo_pathandworktree_pathare the authoritative routing identity.repo_nameis convenience metadata only; it must not be the sole collision breaker across repos.- Inputs outside a git repo/worktree normalize to an explicit
non_gitoutcome and are dropped
before route evaluation or delivery.
directoryandworktree_pathare base context, not optional decorations.- Tool-specific metadata is additive; it should not replace core routing fields.
Augmentation model
.clawhip/hooks/ can enrich the base payload, but only additively.
Allowed augmentation patterns:
- frontmatter or summary enrichment
- recent-context snippets
- provider-specific metadata copies that preserve the shared base fields
Disallowed augmentation patterns:
- removing
provider,event,directory,worktree_path,repo_name, orproject - replacing the base payload with a custom schema
- turning provider-specific extra events into shared-route keys without an explicit clawhip
contract update
Routing guidance
Prefer filters on structured metadata such as:
providereventworktree_pathrepo_pathrepo_namebranchtool_name
Use repo_name / project only as secondary convenience filters after the path-based routing keys.
Avoid routing on rendered message text.
Recommended route shape:
[[routes]]
event = "native.*"
filter = { provider = "codex", repo_path = "*/clawhip" }
channel = "1480171113253175356"
format = "compact"Formatting guidance
Default clawhip formatting should stay low-noise:
- compact: one-line lifecycle/status summary plus key metadata
- inline: dense room-safe summary
- alert: same payload with urgency framing
- raw: debug output for contract validation
Migration note
Provider-native global configuration is now the supported setup path.
1. Codex or Claude owns hook registration through the canonical global shared-surface install 2. clawhip ingests the provider payload through clawhip native hook 3. clawhip derives routing identity from git repo/worktree context and loads only additive augmenters 4. clawhip owns channel routing, mentions, formatting, and delivery
Legacy repo-local generated hook config/state is no longer supported; clawhip hooks install --scope project now warns and directs users to rerun the global install path without generating repo-local state.
#!/usr/bin/env bash
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
GITHUB_REPO="Yeachan-Heo/clawhip"
INSTALLER_URL="${CLAWHIP_INSTALLER_URL:-https://github.com/${GITHUB_REPO}/releases/latest/download/clawhip-installer.sh}"
CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}"
export CARGO_HOME
SYSTEMD=0
SKIP_STAR_PROMPT=0
usage() {
cat <<'EOF'
Usage: ./install.sh [--systemd] [--skip-star-prompt]
Options:
--systemd Install and start the bundled systemd service.
--skip-star-prompt Disable the optional post-install GitHub star prompt.
-h, --help Show this help text.
Environment:
CLAWHIP_SKIP_STAR_PROMPT=1
Disable the optional post-install GitHub star prompt.
EOF
}
parse_args() {
for arg in "$@"; do
case "$arg" in
--systemd) SYSTEMD=1 ;;
--skip-star-prompt) SKIP_STAR_PROMPT=1 ;;
-h|--help)
usage
exit 0
;;
*)
echo "unknown arg: $arg" >&2
usage >&2
exit 1
;;
esac
done
}
log() {
echo "[clawhip] $*"
}
is_truthy() {
case "${1:-}" in
1|true|TRUE|yes|YES|on|ON) return 0 ;;
*) return 1 ;;
esac
}
star_prompt_disabled() {
is_truthy "${CLAWHIP_SKIP_STAR_PROMPT:-}" || is_truthy "${SKIP_STAR_PROMPT:-}"
}
is_interactive_install() {
[[ -t 0 && -t 1 ]]
}
can_use_github_cli_for_star() {
command -v gh >/dev/null 2>&1 && gh auth status &>/dev/null
}
star_repo_with_gh() {
gh api --method PUT "/user/starred/${GITHUB_REPO}" --silent &>/dev/null
}
prompt_to_star_repo() {
local response
printf '[clawhip] Would you like to star %s on GitHub with gh? [y/N]: ' "$GITHUB_REPO"
read -r response || return 0
case "$response" in
[yY]|[yY][eE][sS])
if star_repo_with_gh; then
log "thanks for starring ${GITHUB_REPO}"
else
log "unable to star ${GITHUB_REPO} with gh; continuing without it"
fi
;;
*)
log "skipping GitHub star step"
;;
esac
}
maybe_prompt_to_star_repo() {
if star_prompt_disabled; then
log "skipping GitHub star prompt (--skip-star-prompt or CLAWHIP_SKIP_STAR_PROMPT)"
return 0
fi
if ! is_interactive_install; then
return 0
fi
if ! can_use_github_cli_for_star; then
return 0
fi
log "optional: star ${GITHUB_REPO} on GitHub to support the project"
prompt_to_star_repo
}
install_prebuilt_binary() {
if ! command -v curl >/dev/null 2>&1; then
log "curl is not installed; skipping prebuilt binary download"
return 1
fi
mkdir -p "$CARGO_HOME/bin"
log "attempting prebuilt binary install from ${INSTALLER_URL}"
local installer
installer="$(mktemp)"
if ! curl --proto '=https' --tlsv1.2 -LsSf "$INSTALLER_URL" -o "$installer"; then
log "no downloadable release installer found; falling back to cargo install"
rm -f "$installer"
return 1
fi
if sh "$installer"; then
rm -f "$installer"
return 0
else
local status=$?
log "prebuilt installer failed with status ${status}; falling back to cargo install"
rm -f "$installer"
return 1
fi
}
install_from_source() {
if ! command -v cargo >/dev/null 2>&1; then
cat >&2 <<'MSG'
[clawhip] A prebuilt binary was not available and Cargo is not installed.
[clawhip] Install Rust with rustup, then rerun this installer:
[clawhip] curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
[clawhip] source "$HOME/.cargo/env"
MSG
exit 1
fi
log "building from source with cargo install --path . --force"
cd "$REPO_ROOT"
cargo install --path . --force
}
sync_plugins() {
local source_dir="$REPO_ROOT/plugins"
local target_dir="$HOME/.clawhip/plugins"
if [[ ! -d "$source_dir" ]]; then
return 0
fi
rm -rf "$target_dir"
mkdir -p "$(dirname "$target_dir")"
cp -R "$source_dir" "$target_dir"
log "synced plugins to $target_dir"
}
installed_binary_path() {
if [[ -x "$CARGO_HOME/bin/clawhip" ]]; then
printf '%s\n' "$CARGO_HOME/bin/clawhip"
return 0
fi
if command -v clawhip >/dev/null 2>&1; then
command -v clawhip
return 0
fi
return 1
}
setup_quick_start() {
local binary_path
binary_path="$(installed_binary_path)" || return 0
local config_path="$HOME/.clawhip/config.toml"
if [[ -f "$config_path" ]]; then
log "existing config found at $config_path; skipping quick-start scaffold"
return 0
fi
local webhook_url="${CLAWHIP_WEBHOOK_URL:-}"
if [[ -z "${webhook_url// }" && -t 0 ]]; then
printf '[clawhip] Discord webhook URL (recommended quick start; press Enter to skip): '
read -r webhook_url || true
fi
if [[ -n "${webhook_url// }" ]]; then
log "scaffolding webhook quick-start config"
"$binary_path" setup --webhook "$webhook_url"
log "webhook config scaffolded at $config_path"
else
log "recommended quick start: clawhip setup --webhook 'https://discord.com/api/webhooks/...'"
log "bot-token mode is still supported via ~/.clawhip/config.toml"
fi
}
install_systemd_binary() {
local binary_path
binary_path="$(installed_binary_path)" || {
log "unable to find installed clawhip binary for systemd setup"
exit 1
}
log "installing $binary_path to /usr/local/bin/clawhip for systemd"
sudo install -m 755 "$binary_path" /usr/local/bin/clawhip
}
main() {
parse_args "$@"
log "install flow: prebuilt binary -> cargo fallback -> SKILL attach -> config scaffold -> optional post-install GitHub star prompt -> verification"
log "repo root: $REPO_ROOT"
if install_prebuilt_binary; then
log "prebuilt binary installed successfully"
else
install_from_source
fi
mkdir -p "$HOME/.clawhip"
log "ensured config dir $HOME/.clawhip"
sync_plugins
log "next: read SKILL.md and attach the skill surface"
setup_quick_start
if [[ "$SYSTEMD" == "1" ]]; then
install_systemd_binary
sudo cp deploy/clawhip.service /etc/systemd/system/clawhip.service
sudo systemctl daemon-reload
sudo systemctl enable --now clawhip
log "systemd unit installed and started"
fi
maybe_prompt_to_star_repo
log "recommended verification: scripts/live-verify-default-presets.sh <mode>"
log "install complete"
}
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
main "$@"
fi
#!/usr/bin/env bash
set -euo pipefail
repo_root=$(git rev-parse --show-toplevel)
hooks_dir="$repo_root/.git/hooks"
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
install -m 0755 "$script_dir/post-commit.sh" "$hooks_dir/post-commit"
install -m 0755 "$script_dir/post-checkout.sh" "$hooks_dir/post-checkout"
echo "Installed clawhip example git hooks into $hooks_dir"
echo "Optional: export CLAWHIP_CHANNEL=<discord-channel-id> inside your shell or hook wrapper."
#!/usr/bin/env bash
set -euo pipefail
old_ref=${1:-}
new_ref=${2:-}
checkout_type=${3:-0}
if [[ "$checkout_type" != "1" ]]; then
exit 0
fi
CHANNEL_ARGS=()
if [[ -n "${CLAWHIP_CHANNEL:-}" ]]; then
CHANNEL_ARGS=(--channel "$CLAWHIP_CHANNEL")
fi
repo=$(basename "$(git rev-parse --show-toplevel)")
old_branch=$(git name-rev --name-only --refs='refs/heads/*' "$old_ref" 2>/dev/null || echo "$old_ref")
new_branch=$(git name-rev --name-only --refs='refs/heads/*' "$new_ref" 2>/dev/null || echo "$new_ref")
exec clawhip git branch-changed \
--repo "$repo" \
--old-branch "$old_branch" \
--new-branch "$new_branch" \
"${CHANNEL_ARGS[@]}"
#!/usr/bin/env bash
set -euo pipefail
CHANNEL_ARGS=()
if [[ -n "${CLAWHIP_CHANNEL:-}" ]]; then
CHANNEL_ARGS=(--channel "$CLAWHIP_CHANNEL")
fi
repo=$(basename "$(git rev-parse --show-toplevel)")
branch=$(git rev-parse --abbrev-ref HEAD)
commit=$(git rev-parse HEAD)
summary=$(git log -1 --pretty=%s)
exec clawhip git commit \
--repo "$repo" \
--branch "$branch" \
--commit "$commit" \
--summary "$summary" \
"${CHANNEL_ARGS[@]}"
Legacy bridge note
This directory is no longer the public integration surface.
Use the provider-native Codex or Claude hook configuration plus the generic local ingress:
clawhip native hook --provider codex --file payload.json
clawhip native hook --provider claude --file payload.jsonUse .clawhip/hooks/ only for additive augmentation. Routing identity now comes from git repo/worktree discovery, not repo-local clawhip metadata files.
#!/usr/bin/env bash
set -euo pipefail
session=${1:?usage: notify-keyword.sh <session> <keyword> <line> [channel]}
keyword=${2:?usage: notify-keyword.sh <session> <keyword> <line> [channel]}
line=${3:?usage: notify-keyword.sh <session> <keyword> <line> [channel]}
channel=${4:-${CLAWHIP_CHANNEL:-}}
CHANNEL_ARGS=()
if [[ -n "$channel" ]]; then
CHANNEL_ARGS=(--channel "$channel")
fi
exec clawhip tmux keyword \
--session "$session" \
--keyword "$keyword" \
--line "$line" \
"${CHANNEL_ARGS[@]}"
#!/usr/bin/env bash
set -euo pipefail
usage() {
echo "usage: scan-keywords.sh --session <session> --keywords <comma,list> [--channel <id>]" >&2
exit 1
}
session=""
keywords=""
channel="${CLAWHIP_CHANNEL:-}"
while [[ $# -gt 0 ]]; do
case "$1" in
--session) session="$2"; shift 2 ;;
--keywords) keywords="$2"; shift 2 ;;
--channel) channel="$2"; shift 2 ;;
*) usage ;;
esac
done
[[ -n "$session" && -n "$keywords" ]] || usage
state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/clawhip/tmux-keywords"
mkdir -p "$state_dir"
patterns=(${keywords//,/ })
while IFS='|' read -r pane_id pane_name; do
[[ -n "$pane_id" ]] || continue
pane_output=$(tmux capture-pane -p -t "$pane_id" -S -200)
state_file="$state_dir/${session}_${pane_id//[%:]/_}.txt"
previous_output=""
if [[ -f "$state_file" ]]; then
previous_output=$(cat "$state_file")
fi
printf '%s' "$pane_output" > "$state_file"
while IFS= read -r line; do
[[ "$previous_output" == *"$line"* ]] && continue
for keyword in "${patterns[@]}"; do
if [[ "$line" == *"$keyword"* ]]; then
args=(clawhip tmux keyword --session "$session" --keyword "$keyword" --line "$line")
if [[ -n "$channel" ]]; then args+=(--channel "$channel"); fi
"${args[@]}"
fi
done
done <<< "$pane_output"
done < <(tmux list-panes -t "$session" -F '#{pane_id}|#{window_index}.#{pane_index}')
#!/usr/bin/env bash
set -euo pipefail
usage() {
echo "usage: stale-check.sh --session <session> --stale-minutes <n> [--channel <id>]" >&2
exit 1
}
session=""
stale_minutes=""
channel="${CLAWHIP_CHANNEL:-}"
while [[ $# -gt 0 ]]; do
case "$1" in
--session) session="$2"; shift 2 ;;
--stale-minutes) stale_minutes="$2"; shift 2 ;;
--channel) channel="$2"; shift 2 ;;
*) usage ;;
esac
done
[[ -n "$session" && -n "$stale_minutes" ]] || usage
state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/clawhip/tmux-stale"
mkdir -p "$state_dir"
now=$(date +%s)
threshold=$((stale_minutes * 60))
while IFS='|' read -r pane_id pane_name; do
[[ -n "$pane_id" ]] || continue
pane_output=$(tmux capture-pane -p -t "$pane_id" -S -200)
last_line=$(printf '%s\n' "$pane_output" | awk 'NF { line=$0 } END { print line }')
hash=$(printf '%s' "$pane_output" | sha1sum | awk '{print $1}')
state_file="$state_dir/${session}_${pane_id//[%:]/_}.state"
prev_hash=""
changed_at=$now
notified_at=0
if [[ -f "$state_file" ]]; then
source "$state_file"
fi
if [[ "$hash" != "$prev_hash" ]]; then
changed_at=$now
notified_at=0
elif (( now - changed_at >= threshold )) && (( notified_at == 0 || now - notified_at >= threshold )); then
args=(clawhip tmux stale --session "$session" --pane "$pane_name" --minutes "$stale_minutes" --last-line "${last_line:-<no output>}")
if [[ -n "$channel" ]]; then args+=(--channel "$channel"); fi
"${args[@]}"
notified_at=$now
fi
cat > "$state_file" <<STATE
prev_hash=$hash
changed_at=$changed_at
notified_at=$notified_at
STATE
done < <(tmux list-panes -t "$session" -F '#{pane_id}|#{window_index}.#{pane_index}')
MIT License
Copyright (c) 2026 Yeachan Heo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.