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
2 changes: 1 addition & 1 deletion docs/knowledge/repository-knowledge.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"source": "src/adapter.rs",
"subject": "http://aegis.gastown.local/ontology/code/tambo/src%2Fadapter.rs",
"summary": "The Adapter trait separates desired version, installation, version read-back and functional verification. Built-in adapters integrate the selected stack tools; package adapters may bind verification to PATH executables.",
"source_sha256": "88ad05b97e3a7f3a0760482c567d49034da4148abcfc1aba48488fd01aa51386",
"source_sha256": "ae8f7906c950a5b7f51a9373034323b501b7694c5c6b953d02c39962f76966e7",
"properties": {
"interfaceKind": "library",
"publicName": "caboodle::adapter::Adapter"
Expand Down
138 changes: 89 additions & 49 deletions hook-bundles/yupana.json
Original file line number Diff line number Diff line change
@@ -1,51 +1,91 @@
{
"schema": "st.hook-bundle/1",
"name": "yupana",
"version": "0.10.5",
"owner": "caboodle",
"roles": [
"*"
],
"hooks": [
{
"event": "PostToolUse",
"matcher": ".*",
"command": "out=$(yupana hook post-edit) || exit 0; printf %s \"$out\"",
"harnesses": [
"claude"
]
},
{
"event": "PostToolUse",
"matcher": "Bash",
"command": "yupana hook post-bash || exit 0",
"harnesses": [
"claude"
],
"evidence": {
"command": "python3 -c 'PATHS=['\"'\"'~/.local/state/yupana/metrics.jsonl'\"'\"']\nFIELD='\"'\"'ts'\"'\"'\nWANT=('\"'\"'action_outcome'\"'\"',)\nimport sys,os,json,glob,datetime as d\nbest=None\nfor p in PATHS:\n for path in glob.glob(os.path.expanduser(p)):\n try:\n f=open(path,'\"'\"'rb'\"'\"'); f.seek(0,2); f.seek(max(0,f.tell()-262144)); lines=f.read().splitlines(); f.close()\n except OSError: continue\n for l in reversed(lines):\n try: r=json.loads(l)\n except Exception: continue\n if WANT and r.get('\"'\"'kind'\"'\"') not in WANT and r.get('\"'\"'event_type'\"'\"') not in WANT: continue\n v=r.get(FIELD)\n if isinstance(v,(int,float)): t=int(v)\n elif isinstance(v,str): t=int(d.datetime.fromisoformat(v[:19]).replace(tzinfo=d.timezone.utc).timestamp())\n else: continue\n best=t if best is None else max(best,t); break\nif best is None: sys.exit(1)\nprint(best)\n'",
"max_age_seconds": 900
}
},
{
"event": "PostToolUseFailure",
"matcher": "Bash",
"command": "yupana hook post-bash || exit 0",
"harnesses": [
"claude"
],
"evidence": {
"command": "python3 -c 'PATHS=['\"'\"'~/.local/state/yupana/metrics.jsonl'\"'\"']\nFIELD='\"'\"'ts'\"'\"'\nWANT=('\"'\"'action_outcome'\"'\"',)\nimport sys,os,json,glob,datetime as d\nbest=None\nfor p in PATHS:\n for path in glob.glob(os.path.expanduser(p)):\n try:\n f=open(path,'\"'\"'rb'\"'\"'); f.seek(0,2); f.seek(max(0,f.tell()-262144)); lines=f.read().splitlines(); f.close()\n except OSError: continue\n for l in reversed(lines):\n try: r=json.loads(l)\n except Exception: continue\n if WANT and r.get('\"'\"'kind'\"'\"') not in WANT and r.get('\"'\"'event_type'\"'\"') not in WANT: continue\n v=r.get(FIELD)\n if isinstance(v,(int,float)): t=int(v)\n elif isinstance(v,str): t=int(d.datetime.fromisoformat(v[:19]).replace(tzinfo=d.timezone.utc).timestamp())\n else: continue\n best=t if best is None else max(best,t); break\nif best is None: sys.exit(1)\nprint(best)\n'",
"max_age_seconds": 900
}
},
{
"event": "PreToolUse",
"matcher": "apply_patch",
"command": "yupana hook pre-edit",
"harnesses": [
"codex"
]
}
]
"hooks": [
{
"command": "out=$(YUPANA_PROJECTION_HTTP_TIMEOUT_SECS=5 YUPANA_PROJECTION_TOTAL_BUDGET_SECS=8 yupana hook session-start) || exit 0; printf %s \"$out\"",
"event": "SessionStart",
"evidence": {
"command": "python3 -c 'PATHS=['\"'\"'~/.local/state/yupana/metrics.jsonl'\"'\"']\nFIELD='\"'\"'ts'\"'\"'\nWANT=('\"'\"'session_start_invoked'\"'\"',)\nimport sys,os,json,glob,datetime as d\nbest=None\nfor p in PATHS:\n for path in glob.glob(os.path.expanduser(p)):\n try:\n f=open(path,'\"'\"'rb'\"'\"'); f.seek(0,2); f.seek(max(0,f.tell()-262144)); lines=f.read().splitlines(); f.close()\n except OSError: continue\n for l in reversed(lines):\n try: r=json.loads(l)\n except Exception: continue\n if WANT and r.get('\"'\"'kind'\"'\"') not in WANT and r.get('\"'\"'event_type'\"'\"') not in WANT: continue\n v=r.get(FIELD)\n if isinstance(v,(int,float)): t=int(v)\n elif isinstance(v,str): t=int(d.datetime.fromisoformat(v[:19]).replace(tzinfo=d.timezone.utc).timestamp())\n else: continue\n best=t if best is None else max(best,t); break\nif best is None: sys.exit(1)\nprint(best)\n'",
"max_age_seconds": 86400
},
"harnesses": [
"claude",
"codex",
"opencode"
],
"timeout": 10
},
{
"command": "out=$(YUPANA_PROJECTION_HTTP_TIMEOUT_SECS=5 YUPANA_PROJECTION_TOTAL_BUDGET_SECS=20 yupana hook pre-edit) || exit 0; printf %s \"$out\"",
"event": "PreToolUse",
"evidence": {
"command": "python3 -c 'PATHS=['\"'\"'~/.local/state/yupana/metrics.jsonl'\"'\"']\nFIELD='\"'\"'ts'\"'\"'\nWANT=('\"'\"'guard'\"'\"',)\nimport sys,os,json,glob,datetime as d\nbest=None\nfor p in PATHS:\n for path in glob.glob(os.path.expanduser(p)):\n try:\n f=open(path,'\"'\"'rb'\"'\"'); f.seek(0,2); f.seek(max(0,f.tell()-262144)); lines=f.read().splitlines(); f.close()\n except OSError: continue\n for l in reversed(lines):\n try: r=json.loads(l)\n except Exception: continue\n if WANT and r.get('\"'\"'kind'\"'\"') not in WANT and r.get('\"'\"'event_type'\"'\"') not in WANT: continue\n v=r.get(FIELD)\n if isinstance(v,(int,float)): t=int(v)\n elif isinstance(v,str): t=int(d.datetime.fromisoformat(v[:19]).replace(tzinfo=d.timezone.utc).timestamp())\n else: continue\n best=t if best is None else max(best,t); break\nif best is None: sys.exit(1)\nprint(best)\n'",
"max_age_seconds": 900
},
"harnesses": [
"claude",
"opencode"
],
"matcher": "Edit|Write|MultiEdit",
"timeout": 30
},
{
"command": "out=$(yupana hook post-edit) || exit 0; printf %s \"$out\"",
"event": "PostToolUse",
"harnesses": [
"claude"
],
"matcher": ".*"
},
{
"command": "yupana hook post-bash || exit 0",
"event": "PostToolUse",
"evidence": {
"command": "python3 -c 'PATHS=['\"'\"'~/.local/state/yupana/metrics.jsonl'\"'\"']\nFIELD='\"'\"'ts'\"'\"'\nWANT=('\"'\"'action_outcome'\"'\"',)\nimport sys,os,json,glob,datetime as d\nbest=None\nfor p in PATHS:\n for path in glob.glob(os.path.expanduser(p)):\n try:\n f=open(path,'\"'\"'rb'\"'\"'); f.seek(0,2); f.seek(max(0,f.tell()-262144)); lines=f.read().splitlines(); f.close()\n except OSError: continue\n for l in reversed(lines):\n try: r=json.loads(l)\n except Exception: continue\n if WANT and r.get('\"'\"'kind'\"'\"') not in WANT and r.get('\"'\"'event_type'\"'\"') not in WANT: continue\n v=r.get(FIELD)\n if isinstance(v,(int,float)): t=int(v)\n elif isinstance(v,str): t=int(d.datetime.fromisoformat(v[:19]).replace(tzinfo=d.timezone.utc).timestamp())\n else: continue\n best=t if best is None else max(best,t); break\nif best is None: sys.exit(1)\nprint(best)\n'",
"max_age_seconds": 900
},
"harnesses": [
"claude"
],
"matcher": "Bash"
},
{
"command": "yupana hook post-bash || exit 0",
"event": "PostToolUseFailure",
"evidence": {
"command": "python3 -c 'PATHS=['\"'\"'~/.local/state/yupana/metrics.jsonl'\"'\"']\nFIELD='\"'\"'ts'\"'\"'\nWANT=('\"'\"'action_outcome'\"'\"',)\nimport sys,os,json,glob,datetime as d\nbest=None\nfor p in PATHS:\n for path in glob.glob(os.path.expanduser(p)):\n try:\n f=open(path,'\"'\"'rb'\"'\"'); f.seek(0,2); f.seek(max(0,f.tell()-262144)); lines=f.read().splitlines(); f.close()\n except OSError: continue\n for l in reversed(lines):\n try: r=json.loads(l)\n except Exception: continue\n if WANT and r.get('\"'\"'kind'\"'\"') not in WANT and r.get('\"'\"'event_type'\"'\"') not in WANT: continue\n v=r.get(FIELD)\n if isinstance(v,(int,float)): t=int(v)\n elif isinstance(v,str): t=int(d.datetime.fromisoformat(v[:19]).replace(tzinfo=d.timezone.utc).timestamp())\n else: continue\n best=t if best is None else max(best,t); break\nif best is None: sys.exit(1)\nprint(best)\n'",
"max_age_seconds": 900
},
"harnesses": [
"claude"
],
"matcher": "Bash"
},
{
"command": "out=$(yupana hook pre-edit) || exit 0; printf %s \"$out\"",
"event": "PreToolUse",
"evidence": {
"command": "python3 -c 'PATHS=['\"'\"'~/.local/state/yupana/metrics.jsonl'\"'\"']\nFIELD='\"'\"'ts'\"'\"'\nWANT=('\"'\"'guard'\"'\"',)\nimport sys,os,json,glob,datetime as d\nbest=None\nfor p in PATHS:\n for path in glob.glob(os.path.expanduser(p)):\n try:\n f=open(path,'\"'\"'rb'\"'\"'); f.seek(0,2); f.seek(max(0,f.tell()-262144)); lines=f.read().splitlines(); f.close()\n except OSError: continue\n for l in reversed(lines):\n try: r=json.loads(l)\n except Exception: continue\n if WANT and r.get('\"'\"'kind'\"'\"') not in WANT and r.get('\"'\"'event_type'\"'\"') not in WANT: continue\n v=r.get(FIELD)\n if isinstance(v,(int,float)): t=int(v)\n elif isinstance(v,str): t=int(d.datetime.fromisoformat(v[:19]).replace(tzinfo=d.timezone.utc).timestamp())\n else: continue\n best=t if best is None else max(best,t); break\nif best is None: sys.exit(1)\nprint(best)\n'",
"max_age_seconds": 900
},
"harnesses": [
"codex"
],
"matcher": "apply_patch"
},
{
"command": "yupana hook post-read || exit 0",
"event": "PostToolUse",
"harnesses": [
"claude"
],
"matcher": "Read|Bash"
}
],
"name": "yupana",
"owner": "yupana",
"roles": [
"*"
],
"schema": "st.hook-bundle/1",
"version": "0.15.4"
}
10 changes: 10 additions & 0 deletions hook-bundles/yupana.origin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"repository": "https://github.com/intigraph/yupana",
"commit": "564281c0edc4ca9e9aefe5531d1af6017403ada7",
"tag": "v0.15.4",
"path": "hooks/yupana.bundle.json",
"substitution": {
"{{VERSION}}": "0.15.4"
},
"sha256": "650098cc77a3520f23632b9716a5ca19f237dd2184ea1f024311b719cb776b23"
}
5 changes: 4 additions & 1 deletion src/hook_bundles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -670,5 +670,8 @@ fn codex_edit_guard_targets_native_patches_once() {
.collect();
assert_eq!(edits.len(), 1);
assert_eq!(edits[0]["matcher"], "apply_patch");
assert_eq!(edits[0]["command"], "yupana hook pre-edit");
assert_eq!(
edits[0]["command"],
"out=$(yupana hook pre-edit) || exit 0; printf %s \"$out\""
);
}
7 changes: 4 additions & 3 deletions tests/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,9 +207,10 @@ exit 2
// applied: verify asserts the bundles, and tests about other steps must
// not trip on them. A test about registration clears this first.
let mut seeded = String::new();
for entry in fs::read_dir(Path::new(env!("CARGO_MANIFEST_DIR")).join("hook-bundles")).unwrap() {
let bundle: serde_json::Value =
serde_json::from_str(&fs::read_to_string(entry.unwrap().path()).unwrap()).unwrap();
// Use the runtime's bundle inventory: provenance sidecars in the same
// directory are not hook bundles and must not enter the fake registry.
for (_, raw) in caboodle::hook_bundles::BUNDLES {
let bundle: serde_json::Value = serde_json::from_str(raw).unwrap();
seeded.push_str(&format!(
"{} {}\n",
bundle["name"].as_str().unwrap(),
Expand Down
45 changes: 45 additions & 0 deletions tests/test_yupana_bundle_origin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
"""Pinned whole-bundle refresh; host registration is a separate delivery step."""
import hashlib
import json
from pathlib import Path
import unittest

ROOT = Path(__file__).resolve().parents[1]


class YupanaBundleTests(unittest.TestCase):
def test_release_bytes_match_reviewed_origin(self):
raw = (ROOT / 'hook-bundles/yupana.json').read_bytes()
origin = json.loads((ROOT / 'hook-bundles/yupana.origin.json').read_text())
self.assertEqual(hashlib.sha256(raw).hexdigest(), origin['sha256'])
self.assertEqual(origin['tag'], 'v0.15.4')
bundle = json.loads(raw)
self.assertEqual((bundle['name'], bundle['version'], bundle['owner']),
('yupana', '0.15.4', 'yupana'))

def test_refresh_keeps_failure_guard_and_existing_advisory_routes(self):
hooks = json.loads((ROOT / 'hook-bundles/yupana.json').read_text())['hooks']
for event, matcher, command in [
('PostToolUse', '.*', 'yupana hook post-edit'),
('PostToolUse', 'Bash', 'yupana hook post-bash'),
('PostToolUseFailure', 'Bash', 'yupana hook post-bash'),
('PreToolUse', 'apply_patch', 'yupana hook pre-edit'),
]:
found = [h for h in hooks if h['event'] == event and h.get('matcher') == matcher]
self.assertEqual(len(found), 1)
self.assertIn(command, found[0]['command'])
self.assertFalse(any('codex_notify' in h for h in hooks))

def test_read_legend_is_opt_in_and_has_no_codex_enrollment(self):
bundle = json.loads((ROOT / 'hook-bundles/yupana.json').read_text())
reads = [h for h in bundle['hooks'] if 'hook post-read' in h['command']]
self.assertEqual(len(reads), 1)
self.assertEqual(reads[0]['harnesses'], ['claude'])
self.assertEqual(reads[0]['matcher'], 'Read|Bash')
self.assertNotIn('YUPANA_READ_LEGEND=', json.dumps(bundle))
self.assertNotIn('DP_LEGEND=', json.dumps(bundle))
self.assertNotIn('codex_notify', bundle)


if __name__ == '__main__':
unittest.main()
Loading