Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
51ce14a
feat(slice-23): add Frontline config schema and PreToolUse handlers
neomatrix369 Aug 15, 2026
c4bbf62
Merge pull request #74 from neomatrix369/slice/23-config-handler-scripts
neomatrix369 Aug 15, 2026
12117b7
feat(slice-24): add tripwire setup-agent-hooks install path
neomatrix369 Aug 15, 2026
d4fa08d
docs(slice-24): record PR #75 in gate evidence
neomatrix369 Aug 15, 2026
8e52d74
Merge pull request #75 from neomatrix369/slice/24-setup-agent-hooks
neomatrix369 Aug 15, 2026
8c5ca7d
feat(slice-25): prove live enforce smoke after setup-agent-hooks
neomatrix369 Aug 15, 2026
d910b1c
docs(slice-25): record PR #76 in gate evidence
neomatrix369 Aug 15, 2026
fd30eda
Merge pull request #76 from neomatrix369/slice/25-live-enforce-smoke
neomatrix369 Aug 15, 2026
2674d05
docs(slice-26): publish dual-output contract for /tw-* skills
neomatrix369 Aug 15, 2026
055e752
Merge pull request #77 from neomatrix369/slice/26-api-output-contract
neomatrix369 Aug 15, 2026
14a2a25
docs(slice-26): record PR #77 in gate evidence
neomatrix369 Aug 15, 2026
b3e9c12
fix(slice-26): lower setupAgentHooks eslint complexity under 10
neomatrix369 Aug 15, 2026
32b5308
Merge pull request #78 from neomatrix369/slice/26-api-output-contract
neomatrix369 Aug 15, 2026
4c0b7b6
feat(slice-27): add /tw-enable and /tw-disable config toggles
neomatrix369 Aug 15, 2026
3cca65e
docs(slice-27): record PR #80 in gate evidence
neomatrix369 Aug 15, 2026
a0d4d58
Merge pull request #80 from neomatrix369/slice/27-tw-enable-disable
neomatrix369 Aug 15, 2026
ab129f1
feat(slice-28): add /tw-verify dual-output status reporting
neomatrix369 Aug 15, 2026
f6280f5
Merge pull request #81 from neomatrix369/slice/28-tw-verify
neomatrix369 Aug 15, 2026
4fa2875
feat(slice-29): add /tw-scan submit skill with dual force syntax
neomatrix369 Aug 15, 2026
5f7dadc
Merge pull request #82 from neomatrix369/slice/29-tw-scan
neomatrix369 Aug 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .claude/skills/tw-disable/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
name: tw-disable
description: Disable Tripwire PreToolUse enforcement by setting enable=false in ~/.tripwire/config.json
disable-model-invocation: true
---

# /tw-disable

Toggle **only** the `enable` flag in `~/.tripwire/config.json` to `false`.
Do not call Tripwire scan, guard, or other APIs — config write only.
Preserve `scan_validity_days` and any other keys.

Full bypass at the PreToolUse enforcement layer. Manual `/tw-verify` and
`/tw-scan` remain usable when disabled (they do not no-op solely because
`enable` is false).

## Steps

1. Resolve config path: `$TRIPWIRE_CONFIG` if set, else `~/.tripwire/config.json`.
2. Run from the tripwire repo (or installed package):

```bash
uv run python -c "from guard.control_skills import disable_enforcement; print(disable_enforcement())"
```

3. Confirm the printed JSON has `"enable": false` and other keys unchanged.
4. Tell the operator: PreToolUse enforcement is **off** (approve-all bypass).
Verify and scan skills remain available.
25 changes: 25 additions & 0 deletions .claude/skills/tw-enable/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
name: tw-enable
description: Enable Tripwire PreToolUse enforcement by setting enable=true in ~/.tripwire/config.json
disable-model-invocation: true
---

# /tw-enable

Toggle **only** the `enable` flag in `~/.tripwire/config.json` to `true`.
Do not call Tripwire scan, guard, or other APIs — config write only.
Preserve `scan_validity_days` and any other keys.

## Steps

1. Resolve config path: `$TRIPWIRE_CONFIG` if set, else `~/.tripwire/config.json`.
2. Run from the tripwire repo (or installed package):

```bash
uv run python -c "from guard.control_skills import enable_enforcement; print(enable_enforcement())"
```

3. Confirm the printed JSON has `"enable": true` and other keys unchanged.
4. Tell the operator: PreToolUse enforcement is **on** (unscanned/RED blocked).

`/tw-verify` and `/tw-scan` are unaffected by this flag — they remain usable either way.
60 changes: 60 additions & 0 deletions .claude/skills/tw-scan/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
name: tw-scan
description: Submit one or more skill/MCP/tool names to tripwire scan (supports --force / force)
disable-model-invocation: true
---

# /tw-scan

Submit resolved artifact paths to the existing `tripwire scan` API and return
confirmation with introspected identifiers (`batch_id`, `scan_run_ids`).

Same multi-name resolution rules as `/tw-verify`. Works whether enforcement is
enabled or disabled. Output contract:
[frontline-output-contract.md](../../../docs/user-guide/frontline-output-contract.md).

## Force syntax

Both forms resubmit even over a valid non-stale result:

- `/tw-scan name --force`
- `/tw-scan name force`

## Steps

1. Accept space- or comma-separated names, optionally with `--force` or bare `force`.
2. **Resolve** each name to a filesystem path (Claude Code visibility). Unresolved
names are reported as not-found and are **not** submitted.
3. Call `guard.scan.scan_artifacts` (or the equivalent one-liner):

```bash
uv run python -c "
from guard.scan import parse_scan_args, scan_artifacts
from guard.verify import ResolvedArtifact

tokens = ['name-a', 'name-b'] # plus '--force' or 'force' when requested
names, force = parse_scan_args(tokens)

def resolve(name: str):
# Agent fills ResolvedArtifact(...) or returns None when not found.
raise SystemExit('wire resolve from Claude visibility')

def submit(paths, *, force):
# Production: invoke existing tripwire scan / runScan for these paths.
# Return only introspected fields: batch_id, scan_run_ids, failed_targets.
raise SystemExit('wire submit to tripwire scan API')

result = scan_artifacts(names, resolve=resolve, submit=submit, force=force)
print(result.to_markdown())
print(result.to_machine())
"
```

4. Show the operator **both** the Markdown confirmation and the machine JSON.
Echo `batch_id` and `scan_run_ids` from the scan API — do not invent fields.

## Notes

- Submit path is the existing `tripwire scan` / `cli/src/orchestrator.js` `runScan`
stdout shape (slice 26 introspection).
- `/tw-scan` does not toggle the enable flag; use `/tw-enable` / `/tw-disable`.
65 changes: 65 additions & 0 deletions .claude/skills/tw-verify/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
name: tw-verify
description: Report Tripwire scan status for one or more skill/MCP/tool names (dual Markdown+JSON output)
disable-model-invocation: true
---

# /tw-verify

Report scan status for one or more names in **one pass** (do not stop at the
first issue). Output must follow the Frontline dual-audience contract:
[frontline-output-contract.md](../../../docs/user-guide/frontline-output-contract.md)
— human Markdown table **and** machine JSON with the same per-artifact facts.

Works whether enforcement is enabled or disabled (`/tw-enable` / `/tw-disable`).

## Steps

1. Accept space- or comma-separated names from the user.
2. **Resolve** each name to a filesystem path using Claude Code visibility into
installed skills / MCP config / tools. If there is no match, still include
that name in the report as `not-found` with a useful human message (do not
abort the whole run).
3. For each resolved name, call:

```bash
uv run python -c "
from datetime import UTC, datetime
from guard.verify import ResolvedArtifact, StatusRecord, verify_artifacts

# Replace resolve/fetch_status with real Supabase lookups in production wiring.
# For a dry smoke with fixtures, inject StatusRecord values as below.

def resolve(name: str):
# Agent fills ResolvedArtifact(name=..., artifact_type=..., resolved_path=...)
# or returns None when not found.
raise SystemExit('wire resolve from Claude visibility')

def fetch_status(resolved):
# Query Supabase items.heatmap_status + latest scan_runs (see guard_hook.py).
raise SystemExit('wire fetch_status from Supabase')

result = verify_artifacts(
['name-a', 'name-b'],
resolve=resolve,
fetch_status=fetch_status,
)
print(result.to_markdown())
print(result.to_machine())
"
```

Prefer importing and calling `guard.verify.verify_artifacts` from a short
helper once resolve + Supabase fetch are wired; keep the dual-output helpers
shared for `/tw-scan` and `/tw-self-check`.

4. Show the operator **both** the Markdown table and the JSON `artifacts` array.
5. RED rows must include **Will be blocked when Tripwire is enabled**.
6. Unscanned rows must offer `/tw-scan <name>` for that artifact.
7. If the operator accepts a scan offer, invoke `/tw-scan` for those names.

## Notes

- Status comes from Supabase (`items.heatmap_status` + scan timestamps / in-flight
runs), not from a synchronous `tripwire` status CLI.
- Staleness uses `scan_validity_days` from `~/.tripwire/config.json` (default 14).
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@
.code-review-graph/

# Local agent / MCP config (project rules under .cursor/rules are tracked)
.claude/
.claude/*
!.claude/skills/
.claude/skills/*
!.claude/skills/tw-*/
!.claude/skills/tw-*/**
.cursor/*
!.cursor/rules/
!.cursor/rules/**
Expand Down
27 changes: 27 additions & 0 deletions cli/bin/tripwire.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ensureSchema } from '../src/ensureSchema.js';
import { loadEnv } from '../src/loadEnv.js';
import { runScan } from '../src/orchestrator.js';
import { runRoute } from '../src/router.js';
import { setupAgentHooks } from '../src/setupAgentHooks.js';

loadEnv();

Expand Down Expand Up @@ -73,4 +74,30 @@ program
}
});

program
.command('setup-agent-hooks')
.description(
'Install PreToolUse handlers under ~/.tripwire/hooks (chmod 700), write default config if absent, register Claude Code PreToolUse',
)
.option('--home <dir>', 'HOME root for ~/.tripwire (tests / alternate install)')
.option('--claude-settings <path>', 'Claude Code settings.json path (default: ~/.claude/settings.json)')
.action(async (opts) => {
try {
const result = await setupAgentHooks({
homeDir: opts.home,
claudeSettingsPath: opts.claudeSettings,
});
console.log(JSON.stringify({
ok: true,
hooksDir: result.hooksDir,
configPath: result.configPath,
claudeSettingsPath: result.claudeSettingsPath,
preToolUseSh: result.preToolUseSh,
}));
} catch (err) {
console.error(err.message || err);
process.exitCode = 1;
}
});

program.parseAsync(process.argv);
Loading
Loading