Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .chock/bin/claude_code.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by agentseam 0.3.1 -- bundle("claude_code"). Do not hand-edit, except the
# Generated by agentseam 0.3.2 -- bundle("claude_code"). Do not hand-edit, except the
# HANDLER block below (between "agentseam handler >>>" and "<<< agentseam handler"),
# which is exactly what this file leaves for you to fill in.
#
Expand All @@ -23,7 +23,7 @@
import warnings as _warnings

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.1)
# contract (agentseam 0.3.2)

"""Canonical event vocabulary, normalized envelope, and decision type."""

Expand Down
4 changes: 2 additions & 2 deletions .chock/bin/codex_cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by agentseam 0.3.1 -- bundle("codex_cli"). Do not hand-edit, except the
# Generated by agentseam 0.3.2 -- bundle("codex_cli"). Do not hand-edit, except the
# HANDLER block below (between "agentseam handler >>>" and "<<< agentseam handler"),
# which is exactly what this file leaves for you to fill in.
#
Expand All @@ -22,7 +22,7 @@
import warnings as _warnings

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.1)
# contract (agentseam 0.3.2)

"""Canonical event vocabulary, normalized envelope, and decision type."""

Expand Down
4 changes: 2 additions & 2 deletions .chock/bin/cursor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by agentseam 0.3.1 -- bundle("cursor"). Do not hand-edit, except the
# Generated by agentseam 0.3.2 -- bundle("cursor"). Do not hand-edit, except the
# HANDLER block below (between "agentseam handler >>>" and "<<< agentseam handler"),
# which is exactly what this file leaves for you to fill in.
#
Expand All @@ -22,7 +22,7 @@
import warnings as _warnings

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.1)
# contract (agentseam 0.3.2)

"""Canonical event vocabulary, normalized envelope, and decision type."""

Expand Down
4 changes: 2 additions & 2 deletions .chock/bin/devin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by agentseam 0.3.1 -- bundle("devin"). Do not hand-edit, except the
# Generated by agentseam 0.3.2 -- bundle("devin"). Do not hand-edit, except the
# HANDLER block below (between "agentseam handler >>>" and "<<< agentseam handler"),
# which is exactly what this file leaves for you to fill in.
#
Expand All @@ -22,7 +22,7 @@
import warnings as _warnings

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.1)
# contract (agentseam 0.3.2)

"""Canonical event vocabulary, normalized envelope, and decision type."""

Expand Down
4 changes: 2 additions & 2 deletions .chock/bin/gemini_cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by agentseam 0.3.1 -- bundle("gemini_cli"). Do not hand-edit, except the
# Generated by agentseam 0.3.2 -- bundle("gemini_cli"). Do not hand-edit, except the
# HANDLER block below (between "agentseam handler >>>" and "<<< agentseam handler"),
# which is exactly what this file leaves for you to fill in.
#
Expand All @@ -22,7 +22,7 @@
import warnings as _warnings

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.1)
# contract (agentseam 0.3.2)

"""Canonical event vocabulary, normalized envelope, and decision type."""

Expand Down
4 changes: 2 additions & 2 deletions .chock/bin/grok.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by agentseam 0.3.1 -- bundle("grok"). Do not hand-edit, except the
# Generated by agentseam 0.3.2 -- bundle("grok"). Do not hand-edit, except the
# HANDLER block below (between "agentseam handler >>>" and "<<< agentseam handler"),
# which is exactly what this file leaves for you to fill in.
#
Expand All @@ -22,7 +22,7 @@
import warnings as _warnings

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.1)
# contract (agentseam 0.3.2)

"""Canonical event vocabulary, normalized envelope, and decision type."""

Expand Down
4 changes: 2 additions & 2 deletions .chock/bin/tabnine.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by agentseam 0.3.1 -- bundle("tabnine"). Do not hand-edit, except the
# Generated by agentseam 0.3.2 -- bundle("tabnine"). Do not hand-edit, except the
# HANDLER block below (between "agentseam handler >>>" and "<<< agentseam handler"),
# which is exactly what this file leaves for you to fill in.
#
Expand All @@ -22,7 +22,7 @@
import warnings as _warnings

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.1)
# contract (agentseam 0.3.2)

"""Canonical event vocabulary, normalized envelope, and decision type."""

Expand Down
4 changes: 2 additions & 2 deletions .chock/bin/vscode_copilot.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by agentseam 0.3.1 -- bundle("vscode_copilot"). Do not hand-edit, except the
# Generated by agentseam 0.3.2 -- bundle("vscode_copilot"). Do not hand-edit, except the
# HANDLER block below (between "agentseam handler >>>" and "<<< agentseam handler"),
# which is exactly what this file leaves for you to fill in.
#
Expand All @@ -22,7 +22,7 @@
import warnings as _warnings

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.1)
# contract (agentseam 0.3.2)

"""Canonical event vocabulary, normalized envelope, and decision type."""

Expand Down
4 changes: 2 additions & 2 deletions .chock/bin/windsurf.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated by agentseam 0.3.1 -- bundle("windsurf"). Do not hand-edit, except the
# Generated by agentseam 0.3.2 -- bundle("windsurf"). Do not hand-edit, except the
# HANDLER block below (between "agentseam handler >>>" and "<<< agentseam handler"),
# which is exactly what this file leaves for you to fill in.
#
Expand All @@ -22,7 +22,7 @@
import warnings as _warnings

# ------------------------------------------------------------------------------
# contract (agentseam 0.3.1)
# contract (agentseam 0.3.2)

"""Canonical event vocabulary, normalized envelope, and decision type."""

Expand Down
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# Chock changelog

## Unreleased

- **`devin` plugin format**: `chock plugin build --format devin` packages a policy as a native
Devin plugin (`.devin-plugin/plugin.json` + `skills/<id>/SKILL.md` + a root-level `hooks.json`,
not the nested `hooks/hooks.json` every other format uses). Same guard, same adapter,
byte-identical to every other format -- only the envelope differs, and the hook command reaches
its own bundled copies via a shell expansion of `$DEVIN_PLUGIN_ROOT`, the environment variable
the vendor documents hook commands receive (agentseam records no `${...}` plugin-root token for
Devin, unlike Codex or Cursor -- that expansion is chock's own inference, not a vendor-recorded
token). Unlike every other hook format, the package claims no enforcement tier: the vendor's own
docs call plugin hooks "currently best effort and fail open ... so don't rely on them for
crucial guardrails yet," for local Devin sessions (the CLI and Devin Desktop) only, and the
posture text says so instead of claiming a block.
- **`chock marketplace build --tree devin`**: Devin has no marketplace index file -- `devin
plugins install` instead reads a repo's root `.devin-plugin/plugin.json` as a meta-plugin whose
`optionalPlugins` point `git-subdir` entries at each built plugin, so `--tree devin` writes that
root manifest in place of an index (a new `--url` is required; chock never reads `git remote`
for it). `chock-market.lock` and `PLUGINS.md` cover the devin tree the same way they cover every
other tree.
- **Pinned `agentseam==0.3.2`**, which records Devin's native plugin layout; the vendored runtime
goldens moved with it (version stamp only, no handler change).

## 0.9.2 — `sync` no longer leaves a vendor's hook config pointing at a runtime it just deleted

- **Fixed: narrowing `supported_agents` on 0.9.1 left dangling hook entries behind (#151).**
Expand Down
71 changes: 35 additions & 36 deletions docs/cli-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,13 @@ The one "make it so" verb (`uv sync` semantics): recompiles every enabled policy
`.chock/compiled/`, reinstalls the git-hook dispatchers, policy wrappers and the in-agent hooks of the agents
`supported_agents` names, uninstalls chock's entries from any vendor it no longer names (deleting a config file that
held only chock's) before pruning that vendor's runtime, regenerates `INDEX.md` and the `AGENTS.md` pointer, refreshes
the registry, and rewrites `chock.lock`. Run it after editing a policy, toggling config by hand, or bumping the engine
version. A failed recompile never removes the existing compiled tree — the build is staged and swapped in only on
success — and a lockfile-write failure fails the command. An adopter-edited dispatcher is backed up to
`<event>.chock-backup` before being regenerated; custom steps belong in `<event>.d/`.

- `--check` — write nothing; exit non-zero listing every compiled artifact that no longer
matches its manifest. This is the CI drift gate.
- `--ci` — additionally write the GitHub Actions workflow that runs every compiled `ci-gate` on pull requests. Idempotent;
refuses to overwrite a workflow it did not write. Until this runs, `ci-gate` output is compiled but not enforced.
the registry, and rewrites `chock.lock`. Run after editing a policy, toggling config, or bumping the engine version. A
failed recompile never removes the existing compiled tree — staged and swapped in only on success — and a
lockfile-write failure fails the command. An adopter-edited dispatcher is backed up to `<event>.chock-backup` before
being regenerated; custom steps belong in `<event>.d/`.

- `--check` — write nothing; exit non-zero listing every compiled artifact that no longer matches its manifest. The CI drift gate.
- `--ci` — additionally write the GitHub Actions workflow running every compiled `ci-gate` on pull requests. Idempotent; refuses to overwrite a workflow it did not write. Until this runs, `ci-gate` output is compiled but not enforced.
- `--skills` — additionally refresh the bundled authoring skills in `.agents/skills/`.
- `--skip-hooks` — compile and refresh bookkeeping without touching `.git/hooks`.

Expand All @@ -103,8 +101,7 @@ success — and a lockfile-write failure fails the command. An adopter-edited di
chock check [--repo .] [--only validate,verify,evals,matrix,mechanisms,index,conflicts,baseline] [--mode MODE] [--event EVENT] [--base REF]
```

Runs every truth check, read-only — `check` never regenerates what it measures (that is
`sync`'s job):
Runs every truth check, read-only — `check` never regenerates what it measures (that is `sync`'s job):

| Target | What it proves |
| :--- | :--- |
Expand Down Expand Up @@ -197,36 +194,41 @@ diffs the result to catch a stale registry. See [Registry & Lockfile](registry-a
### `plugin build` — package policies as installable plugins

```bash
chock plugin build [--repo .] [--policies-dir base] [--format agent-plugins|claude|copilot|cursor|codex|all] [--out-dir DIST] [--policy ID ...] [--out PATH] [--check]
chock plugin build [--repo .] [--policies-dir base] [--format agent-plugins|claude|copilot|cursor|codex|devin|all] [--out-dir DIST] [--policy ID ...] [--out PATH] [--check]
```

Renders each policy as a plugin. The default `agent-plugins` format writes an
[Agent Plugins 1.0.0](https://agent-plugins.org) package into each policy folder — additive,
`manifest.yaml` stays the source of truth, and a packaged policy is `advisory` wherever it is
read: v1 defines no enforcement semantics, so packaging changes no value in `coverage.json`.

The four hook formats ship a byte-identical guard and adapter; only the envelope differs.
The five hook formats ship a byte-identical guard and adapter; only the envelope differs.
`claude` (`.claude-plugin/`) is read natively by Claude Code, Copilot CLI, VS Code and Grok
Build; `copilot` is the Agent Plugins 1.0 layout under `com.github.copilot/hooks/`; `cursor`
(`.cursor-plugin/`, `beforeShellExecution`) and `codex` (`.codex-plugin/`, `PreToolUse`) each
reach a hook engine no other package can, failing **open** when `python3` is absent. They
require `--out-dir` (or `--out`); in-place output is refused so a policy folder is never
mistaken for a published plugin. `--policies-dir` packages a published directory; `--check`
judges without writing. `--policy ID` (repeatable; manifest `id` or directory name, else a
named error) narrows the build and skips `--out-dir` stale-package cleanup. `--out PATH`
(exactly one `--policy`) writes straight to `PATH` instead of `<out-dir>/<format>/<id>/`.
reach a hook engine no other package can, failing **open** when `python3` is absent. `devin`
(`.devin-plugin/plugin.json` + `hooks.json`, `PreToolUse`) is best-effort by the vendor's own
design, fail-open, not enforced. They require `--out-dir` (or `--out`); in-place output is
refused so a policy folder is never mistaken for a published plugin. `--policies-dir` packages
a published directory; `--check` judges without writing. `--policy ID` (repeatable; manifest
`id` or directory name, else a named error) narrows the build and skips `--out-dir`
stale-package cleanup. `--out PATH` (exactly one `--policy`) writes straight to `PATH` instead
of `<out-dir>/<format>/<id>/`.

### `marketplace build` — index a built plugin tree

```bash
chock marketplace build [--dist .] [--name chock] [--tree claude|cursor|codex] [--check]
chock marketplace build [--dist .] [--name chock] [--tree claude|cursor|codex|devin] [--url URL] [--check]
```

Scans `<dist>/<tree>/*/` and writes that vendor's index: claude →
`.claude-plugin/marketplace.json` + `.github/plugin/marketplace.json` (Copilot CLI's
path), cursor → `.cursor-plugin/marketplace.json`, codex → the legacy `.claude-plugin/`
shape Codex reads from git marketplaces. Entries derive from built manifests, never
hand-listed; an empty tree exits 2; `--check` reports drift without writing.
shape Codex reads from git marketplaces. `devin` has no index format: it writes a root
`.devin-plugin/plugin.json` meta-plugin whose `optionalPlugins` point `git-subdir` entries at
each built plugin, and needs `--url` (the marketplace repo's own git URL — never guessed from
`git remote`). Entries derive from built manifests, never hand-listed; an empty tree exits 2;
`--check` reports drift without writing.

### `gateway run` -- the MCP gateway proxy

Expand All @@ -251,20 +253,17 @@ claims print under **NOT verified**. `require` is the CI-side gate: present, val
chock compliance report [--repo .] [--framework owasp_asi] [--json]
```

Lists the framework controls and which installed policies claim to cover them. Each
control's state is `covered`, `partial`, or `uncovered` (per-claim `coverage` on a policy
is `partial` or `full`). The command fails closed with exit 2 on a missing `--repo`, an
unknown framework nothing claims, or an unknown subcommand. In a repo that has been
synced, a claim also requires the policy's compiled output to exist — a declared control
whose compiled mechanism was deleted is not counted.

Builtin frameworks (one per data file in `src/chock/authoring/data/`, each enumerated
from its publisher's primary source): `owasp_asi` (ASI01–10), `mitre_atlas` (170
techniques, from the official machine-readable dataset), `nist_ai_rmf` (the 72 AI RMF 1.0
subcategories), `eu_ai_act` (a curated set of technical-obligation articles). A policy
claims controls in its manifest's `compliance:` block, keyed by framework name — unknown
framework names still validate, so private frameworks work with `--json` and your own
control list.
Lists the framework controls and which installed policies claim to cover them. Each control's state is `covered`,
`partial`, or `uncovered` (per-claim `coverage` on a policy is `partial` or `full`). The command fails closed with
exit 2 on a missing `--repo`, an unknown framework nothing claims, or an unknown subcommand. In a repo that has been
synced, a claim also requires the policy's compiled output to exist — a declared control whose compiled mechanism
was deleted is not counted.

Builtin frameworks (one per data file in `src/chock/authoring/data/`, each enumerated from its publisher's primary
source): `owasp_asi` (ASI01–10), `mitre_atlas` (170 techniques, from the official machine-readable dataset),
`nist_ai_rmf` (the 72 AI RMF 1.0 subcategories), `eu_ai_act` (a curated set of technical-obligation articles). A
policy claims controls in its manifest's `compliance:` block, keyed by framework name — unknown framework names
still validate, so private frameworks work with `--json` and your own control list.

## Pre-launch aliases

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ dependencies = [
# It never ships to an adopter repo -- the vendored PreToolUse/SessionStart runners
# stay self-contained stdlib-only files, unaffected by this dependency. Pinned exact
# per plan/spine-a/contract.md: the wave boundary is the published PyPI artifact.
"agentseam==0.3.1",
"agentseam==0.3.2",
]

[project.optional-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion requirements/brand-assets.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pyyaml>=6.0
jsonschema>=4.18,<5
referencing>=0.35,<0.38
agentseam==0.3.1
agentseam==0.3.2
cairosvg==2.9.0
6 changes: 3 additions & 3 deletions requirements/brand-assets.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
#
# pip-compile --generate-hashes --output-file=requirements/brand-assets.txt --strip-extras requirements/brand-assets.in
#
agentseam==0.3.1 \
--hash=sha256:4e5b249162204f0bd90c60e2c67a8417c999881a786d4d18a4e85dca570c6564 \
--hash=sha256:db7099395724122989ef8fbf1115d09da2cb0194fff9ce2a3ebe979793fc5271
agentseam==0.3.2 \
--hash=sha256:63616579111f24db9045113a3ef123f6cbb399333c2affb65f8bbb6956d2fb10 \
--hash=sha256:869448a47184dc523491932557f4d87243a6fd7e05d5c51913804013395e643c
# via -r requirements/brand-assets.in
attrs==26.1.0 \
--hash=sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309 \
Expand Down
11 changes: 9 additions & 2 deletions src/chock/compile/emitters/in_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,16 @@ def hook_entry(command: str, *, matcher: str | None = None) -> dict[str, Any]:


def hooks_map_file(vendor: str, command: str) -> dict[str, Any]:
"""A claude-plugin-format hooks file under `vendor`'s own pre-tool event spelling."""
"""A hooks file under `vendor`'s own pre-tool event spelling.

Wrapped in a top-level `hooks` key (the claude-plugin format) unless `vendor`'s own
hook_entry is bare -- Devin's native `hooks.json` at the plugin root is the event map
itself, with no wrapper, unlike the nested `hooks/hooks.json` every other format here
shares.
"""
matcher = vendors.shell_matcher(vendor)
return {"hooks": {vendors.pre_tool_event(vendor): [hook_entry(command, matcher=matcher)]}}
event_map = {vendors.pre_tool_event(vendor): [hook_entry(command, matcher=matcher)]}
return event_map if vendors.hook_entry_bare(vendor) else {"hooks": event_map}


def cursor_entry(command: str) -> dict[str, Any]:
Expand Down
8 changes: 6 additions & 2 deletions src/chock/plugin/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@
from chock.plugin.codex import build_codex_plugin, codex_plugin_differences
from chock.plugin.copilot import build_copilot_plugin, copilot_plugin_differences
from chock.plugin.cursor import build_cursor_plugin, cursor_plugin_differences
from chock.plugin.devin import build_devin_plugin, devin_plugin_differences
from chock.scaffold.recompile import discover_policy_dirs

FORMATS = ("agent-plugins", "claude", "copilot", "cursor", "codex")
FORMATS = ("agent-plugins", "claude", "copilot", "cursor", "codex", "devin")

HOOK_FORMATS = frozenset({"claude", "copilot", "cursor", "codex"})
HOOK_FORMATS = frozenset({"claude", "copilot", "cursor", "codex", "devin"})

HOOK_EMITTERS = {
"claude": (claude_plugin_differences, build_claude_plugin),
"copilot": (copilot_plugin_differences, build_copilot_plugin),
"cursor": (cursor_plugin_differences, build_cursor_plugin),
"codex": (codex_plugin_differences, build_codex_plugin),
"devin": (devin_plugin_differences, build_devin_plugin),
}


Expand Down Expand Up @@ -216,5 +218,7 @@ def main(argv: list[str] | None = None) -> int:
print(" .cursor-plugin/plugin.json + hooks/ (beforeShellExecution) per guard policy")
if "codex" in formats:
print(" .codex-plugin/plugin.json + hooks/ (PreToolUse) per guard policy")
if "devin" in formats:
print(" .devin-plugin/plugin.json + hooks.json (PreToolUse, best-effort) per guard policy")
print(" Skills are advisory in any client. Repo-level enforcement still needs `chock sync`.")
return 0
3 changes: 3 additions & 0 deletions src/chock/plugin/data/stores/devin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"owned_subtrees": ["scripts", "hooks.json"]
}
Loading
Loading