Compare commits
5 Commits
49ecc68a86
...
0120bd8b1b
| Author | SHA1 | Date | |
|---|---|---|---|
| 0120bd8b1b | |||
| b783102338 | |||
| dd8452d38b | |||
| 291bb9dbf3 | |||
| 77f6f6a76c |
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"last_dolt_commit": "qqlkjgnbnf7iatesc1ummn7e86llmv0o",
|
||||
"last_dolt_commit": "toe8dna9743n523thnefrp1ddnu8blag",
|
||||
"last_event_id": 0,
|
||||
"timestamp": "2026-03-02T03:18:05.069295Z",
|
||||
"timestamp": "2026-03-02T03:34:56.2779025Z",
|
||||
"counts": {
|
||||
"issues": 10,
|
||||
"events": 16,
|
||||
"events": 22,
|
||||
"comments": 0,
|
||||
"dependencies": 14,
|
||||
"labels": 0,
|
||||
|
||||
@@ -14,3 +14,9 @@
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-01T22:04:02Z","event_type":"closed","id":14,"issue_id":"rook-z5s","new_value":"{\"status\":\"closed\"}","old_value":"{\"id\":\"rook-z5s\",\"title\":\"Core agent loop: asyncio, Claude API, tool dispatch\",\"description\":\"Main agent turn loop. Claude Haiku 4.5 primary, Sonnet 4.6 fallback. Tool call dispatch. Asyncio event loop with background daemon thread support.\",\"status\":\"in_progress\",\"priority\":2,\"issue_type\":\"task\",\"assignee\":\"Ed_\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-02T02:14:30Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-02T03:00:12Z\"}"}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-01T22:14:30Z","event_type":"claimed","id":15,"issue_id":"rook-dn4","new_value":"{\"assignee\":\"Ed_\",\"status\":\"in_progress\"}","old_value":"{\"id\":\"rook-dn4\",\"title\":\"Policy engine: allowlists, confirm gates, backup-before-edit\",\"description\":\"Core policy module. Allowlists per capability (file, shell, git). confirm_spawn() gate for risky ops. backup_before_edit() helper. Approved working dirs enforcement.\",\"status\":\"open\",\"priority\":2,\"issue_type\":\"task\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-02T02:14:29Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-02T02:14:29Z\"}"}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-01T22:18:04Z","event_type":"closed","id":16,"issue_id":"rook-dn4","new_value":"{\"status\":\"closed\"}","old_value":"{\"id\":\"rook-dn4\",\"title\":\"Policy engine: allowlists, confirm gates, backup-before-edit\",\"description\":\"Core policy module. Allowlists per capability (file, shell, git). confirm_spawn() gate for risky ops. backup_before_edit() helper. Approved working dirs enforcement.\",\"status\":\"in_progress\",\"priority\":2,\"issue_type\":\"task\",\"assignee\":\"Ed_\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-02T02:14:29Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-02T03:14:31Z\"}"}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-01T22:26:20Z","event_type":"claimed","id":17,"issue_id":"rook-6zs","new_value":"{\"assignee\":\"Ed_\",\"status\":\"in_progress\"}","old_value":"{\"id\":\"rook-6zs\",\"title\":\"Shell capability: safe subprocess execution\",\"description\":\"Run shell commands with policy checks. Allowlist of approved dirs and commands. confirm_spawn() gate. Log all subprocess calls with timestamps.\",\"status\":\"open\",\"priority\":2,\"issue_type\":\"task\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-02T02:14:31Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-02T02:14:31Z\"}"}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-01T22:28:26Z","event_type":"closed","id":18,"issue_id":"rook-6zs","new_value":"{\"status\":\"closed\"}","old_value":"{\"id\":\"rook-6zs\",\"title\":\"Shell capability: safe subprocess execution\",\"description\":\"Run shell commands with policy checks. Allowlist of approved dirs and commands. confirm_spawn() gate. Log all subprocess calls with timestamps.\",\"status\":\"in_progress\",\"priority\":2,\"issue_type\":\"task\",\"assignee\":\"Ed_\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-02T02:14:31Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-02T03:26:21Z\"}"}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-01T22:28:35Z","event_type":"claimed","id":19,"issue_id":"rook-43b","new_value":"{\"assignee\":\"Ed_\",\"status\":\"in_progress\"}","old_value":"{\"id\":\"rook-43b\",\"title\":\"File capability: read/write/edit with backup\",\"description\":\"File operations with backup-before-edit. Allowlist for working dirs. No destructive deletes without confirmation. Type-hinted API.\",\"status\":\"open\",\"priority\":2,\"issue_type\":\"task\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-02T02:14:31Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-02T02:14:31Z\"}"}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-01T22:31:21Z","event_type":"closed","id":20,"issue_id":"rook-43b","new_value":"{\"status\":\"closed\"}","old_value":"{\"id\":\"rook-43b\",\"title\":\"File capability: read/write/edit with backup\",\"description\":\"File operations with backup-before-edit. Allowlist for working dirs. No destructive deletes without confirmation. Type-hinted API.\",\"status\":\"in_progress\",\"priority\":2,\"issue_type\":\"task\",\"assignee\":\"Ed_\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-02T02:14:31Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-02T03:28:35Z\"}"}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-01T22:31:25Z","event_type":"claimed","id":21,"issue_id":"rook-wka","new_value":"{\"assignee\":\"Ed_\",\"status\":\"in_progress\"}","old_value":"{\"id\":\"rook-wka\",\"title\":\"Git capability: git ops with policy allowlist\",\"description\":\"Git operations (status, diff, add, commit, log). Allowlist gates on push/reset/force ops. Confirmation required for destructive git commands.\",\"status\":\"open\",\"priority\":2,\"issue_type\":\"task\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-02T02:14:32Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-02T02:14:32Z\"}"}
|
||||
{"actor":"Ed_","comment":null,"created_at":"2026-03-01T22:34:56Z","event_type":"closed","id":22,"issue_id":"rook-wka","new_value":"{\"status\":\"closed\"}","old_value":"{\"id\":\"rook-wka\",\"title\":\"Git capability: git ops with policy allowlist\",\"description\":\"Git operations (status, diff, add, commit, log). Allowlist gates on push/reset/force ops. Confirmation required for destructive git commands.\",\"status\":\"in_progress\",\"priority\":2,\"issue_type\":\"task\",\"assignee\":\"Ed_\",\"owner\":\"edwardgz@gmail.com\",\"created_at\":\"2026-03-02T02:14:32Z\",\"created_by\":\"Ed_\",\"updated_at\":\"2026-03-02T03:31:26Z\"}"}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"4e573e258c203f88413b3876a48280d60400783e45ba3b7075009cafc674f565","created_at":"2026-03-02T02:14:32Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Launch CoSy as persistent subprocess via Popen. stdin/stdout pipe. Send Forth-APL expressions, read results. Backup before redefining words. Only load from personal vocab dir.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-11u","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"CoSy integration: subprocess pipe to CoSy.bat/reva.exe","updated_at":"2026-03-02T02:14:32Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"70a42353906165120ed7320f2673e1b136641edcc94bac65c291ceb56329b156","created_at":"2026-03-02T02:14:31Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"File operations with backup-before-edit. Allowlist for working dirs. No destructive deletes without confirmation. Type-hinted API.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-43b","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"File capability: read/write/edit with backup","updated_at":"2026-03-02T02:14:31Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"4c5f1fdc2ddb1c860cc8f07897a384cc32f8adbd99dd0914128a747e4b439dc3","created_at":"2026-03-02T02:14:31Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Run shell commands with policy checks. Allowlist of approved dirs and commands. confirm_spawn() gate. Log all subprocess calls with timestamps.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-6zs","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Shell capability: safe subprocess execution","updated_at":"2026-03-02T02:14:31Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":"Ed_","await_id":"","await_type":"","close_reason":"","closed_at":"2026-03-02T03:31:21Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"70a42353906165120ed7320f2673e1b136641edcc94bac65c291ceb56329b156","created_at":"2026-03-02T02:14:31Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"File operations with backup-before-edit. Allowlist for working dirs. No destructive deletes without confirmation. Type-hinted API.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-43b","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"closed","target":"","timeout_ns":0,"title":"File capability: read/write/edit with backup","updated_at":"2026-03-02T03:31:21Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":"Ed_","await_id":"","await_type":"","close_reason":"","closed_at":"2026-03-02T03:28:26Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"4c5f1fdc2ddb1c860cc8f07897a384cc32f8adbd99dd0914128a747e4b439dc3","created_at":"2026-03-02T02:14:31Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Run shell commands with policy checks. Allowlist of approved dirs and commands. confirm_spawn() gate. Log all subprocess calls with timestamps.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-6zs","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"closed","target":"","timeout_ns":0,"title":"Shell capability: safe subprocess execution","updated_at":"2026-03-02T03:28:26Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"484cebdc8e9e90f14a3dab875ebabff4591a3be96365feaa5a3c565d88de9557","created_at":"2026-03-02T02:14:30Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Telegram bot daemon thread. Poll for messages and audio notes. Pass audio through STT. Route transcribed text to agent queue via asyncio.run_coroutine_threadsafe().","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-8o3","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Telegram integration: polling, audio STT input","updated_at":"2026-03-02T02:14:30Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":"Ed_","await_id":"","await_type":"","close_reason":"","closed_at":"2026-03-02T02:38:14Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"281bb6b78c0987562762da2cafe040ff3a9e6c3b00f429a978eaf10810c336cb","created_at":"2026-03-02T02:14:29Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Initialize Python project. pyproject.toml with deps (anthropic, elevenlabs, python-telegram-bot, dearpygui). Set up src/rook/ package. Configure uv.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-bfj","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"closed","target":"","timeout_ns":0,"title":"Project scaffolding: pyproject.toml, src layout, uv setup","updated_at":"2026-03-02T02:38:14Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":"Ed_","await_id":"","await_type":"","close_reason":"","closed_at":"2026-03-02T03:18:05Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"0d909735f985b5028b9f49b5430097b81a618db45fb77ae7998ec418cfd31216","created_at":"2026-03-02T02:14:29Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Core policy module. Allowlists per capability (file, shell, git). confirm_spawn() gate for risky ops. backup_before_edit() helper. Approved working dirs enforcement.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-dn4","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"closed","target":"","timeout_ns":0,"title":"Policy engine: allowlists, confirm gates, backup-before-edit","updated_at":"2026-03-02T03:18:05Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"5b9bc779e38b599a57edb6a2374a863a62adc559b106e23e6d266e8b7a7c9079","created_at":"2026-03-02T02:14:30Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Speak agent responses. ElevenLabs TTS with Rook voice ID (scratchy/military). Google TTS fallback. Async playback so it doesn't block the agent loop.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-hi1","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"TTS output: ElevenLabs Rook voice + Google TTS fallback","updated_at":"2026-03-02T02:14:30Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"20afcd532ae263f8dbd89ebcc93dd71177c9f9f5d185dc716b9319b353fbf6b1","created_at":"2026-03-02T02:14:32Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Dear PyGui dark-theme GUI. Dockable panels for agent transcript, TTS status, CoSy output. Runs on main thread; asyncio on background daemon thread.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-iwn","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Dear PyGui GUI: ModernCoSy dockable panels","updated_at":"2026-03-02T02:14:32Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":null,"await_id":"","await_type":"","close_reason":"","closed_at":null,"closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"b1d9f7aa2ce83b5c655f8f56cb29fba8a3e73f09fa1522bd56a1db9cd9206d57","created_at":"2026-03-02T02:14:32Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Git operations (status, diff, add, commit, log). Allowlist gates on push/reset/force ops. Confirmation required for destructive git commands.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-wka","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"open","target":"","timeout_ns":0,"title":"Git capability: git ops with policy allowlist","updated_at":"2026-03-02T02:14:32Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":"Ed_","await_id":"","await_type":"","close_reason":"","closed_at":"2026-03-02T03:34:56Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"b1d9f7aa2ce83b5c655f8f56cb29fba8a3e73f09fa1522bd56a1db9cd9206d57","created_at":"2026-03-02T02:14:32Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Git operations (status, diff, add, commit, log). Allowlist gates on push/reset/force ops. Confirmation required for destructive git commands.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-wka","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"closed","target":"","timeout_ns":0,"title":"Git capability: git ops with policy allowlist","updated_at":"2026-03-02T03:34:56Z","waiters":"","wisp_type":"","work_type":""}
|
||||
{"acceptance_criteria":"","actor":"","agent_state":"","assignee":"Ed_","await_id":"","await_type":"","close_reason":"","closed_at":"2026-03-02T03:04:02Z","closed_by_session":"","compacted_at":null,"compacted_at_commit":null,"compaction_level":0,"content_hash":"911e58b2f36018c64b9bd4fdfec5dc4fffbca208deea583105ff268f501f5fbc","created_at":"2026-03-02T02:14:30Z","created_by":"Ed_","crystallizes":0,"defer_until":null,"description":"Main agent turn loop. Claude Haiku 4.5 primary, Sonnet 4.6 fallback. Tool call dispatch. Asyncio event loop with background daemon thread support.","design":"","due_at":null,"ephemeral":0,"estimated_minutes":null,"event_kind":"","external_ref":null,"hook_bead":"","id":"rook-z5s","is_template":0,"issue_type":"task","last_activity":null,"metadata":"{}","mol_type":"","notes":"","original_size":null,"owner":"edwardgz@gmail.com","payload":"","pinned":0,"priority":2,"quality_score":null,"rig":"","role_bead":"","role_type":"","sender":"","source_repo":"","source_system":"","spec_id":"","status":"closed","target":"","timeout_ns":0,"title":"Core agent loop: asyncio, Claude API, tool dispatch","updated_at":"2026-03-02T03:04:02Z","waiters":"","wisp_type":"","work_type":""}
|
||||
|
||||
BIN
logs/phase_verify.log
Normal file
BIN
logs/phase_verify.log
Normal file
Binary file not shown.
32
src/rook/files.py
Normal file
32
src/rook/files.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import os
|
||||
from rook.policy import is_approved_dir, confirm_spawn, backup_before_edit
|
||||
|
||||
|
||||
class PolicyError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def read_file(path: str) -> str:
|
||||
if not is_approved_dir(path):
|
||||
if not confirm_spawn("read file", path):
|
||||
raise PolicyError(f"Access denied: {path}")
|
||||
with open(path) as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def write_file(path: str, content: str) -> None:
|
||||
if not is_approved_dir(path):
|
||||
if not confirm_spawn("write file", path):
|
||||
raise PolicyError(f"Access denied: {path}")
|
||||
if os.path.exists(path):
|
||||
backup_before_edit(path)
|
||||
with open(path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
def delete_file(path: str) -> None:
|
||||
if not is_approved_dir(path):
|
||||
pass
|
||||
if not confirm_spawn("delete file", path):
|
||||
raise PolicyError(f"Delete denied: {path}")
|
||||
os.remove(path)
|
||||
16
src/rook/git.py
Normal file
16
src/rook/git.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import subprocess
|
||||
from rook.policy import check_allowlist, confirm_spawn
|
||||
|
||||
|
||||
class PolicyError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def run_git(args: list[str], cwd: str = '.') -> str:
|
||||
if check_allowlist('git', args[0]) is False:
|
||||
if not confirm_spawn('git ' + args[0], ' '.join(args)):
|
||||
raise PolicyError(f"git {args[0]} denied by policy")
|
||||
result = subprocess.run(['git'] + args, cwd=cwd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(result.stderr)
|
||||
return result.stdout
|
||||
21
src/rook/shell.py
Normal file
21
src/rook/shell.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import logging
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
from rook.policy import is_approved_dir, confirm_spawn
|
||||
|
||||
logger = logging.getLogger('rook.shell')
|
||||
|
||||
|
||||
class PolicyError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def run_shell(cmd: str, cwd: str = '.') -> str:
|
||||
if not is_approved_dir(cwd):
|
||||
if not confirm_spawn(f'shell: {cmd}', f'cwd={cwd}'):
|
||||
raise PolicyError(f'Shell command denied: {cmd!r} in {cwd!r}')
|
||||
logger.info('[%s] run_shell: %s', datetime.utcnow().isoformat(), cmd)
|
||||
result = subprocess.run(cmd, shell=True, cwd=cwd, capture_output=True, text=True)
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(f'Command failed (rc={result.returncode}): {result.stderr}')
|
||||
return result.stdout
|
||||
62
tests/test_files.py
Normal file
62
tests/test_files.py
Normal file
@@ -0,0 +1,62 @@
|
||||
import os
|
||||
import pytest
|
||||
from unittest.mock import patch
|
||||
|
||||
from rook.files import read_file, write_file, delete_file, PolicyError
|
||||
|
||||
|
||||
def test_read_file_returns_content(tmp_path):
|
||||
tmp_file = tmp_path / "hello.txt"
|
||||
tmp_file.write_text("hello world")
|
||||
with patch("rook.files.is_approved_dir", return_value=True):
|
||||
result = read_file(str(tmp_file))
|
||||
assert result == "hello world"
|
||||
|
||||
|
||||
def test_read_file_unapproved_raises():
|
||||
with patch("rook.files.is_approved_dir", return_value=False), \
|
||||
patch("rook.files.confirm_spawn", return_value=False):
|
||||
with pytest.raises(PolicyError):
|
||||
read_file("/etc/passwd")
|
||||
|
||||
|
||||
def test_write_file_creates_backup(tmp_path):
|
||||
tmp_file = tmp_path / "data.txt"
|
||||
tmp_file.write_text("old")
|
||||
with patch("rook.files.is_approved_dir", return_value=True):
|
||||
write_file(str(tmp_file), "new")
|
||||
bak_path = str(tmp_file) + ".bak"
|
||||
assert os.path.exists(bak_path)
|
||||
with open(bak_path) as f:
|
||||
assert f.read() == "old"
|
||||
assert tmp_file.read_text() == "new"
|
||||
|
||||
|
||||
def test_write_file_new_file_no_backup(tmp_path):
|
||||
path = str(tmp_path / "new.txt")
|
||||
with patch("rook.files.is_approved_dir", return_value=True), \
|
||||
patch("rook.files.backup_before_edit"):
|
||||
write_file(path, "content")
|
||||
assert os.path.exists(path)
|
||||
with open(path) as f:
|
||||
assert f.read() == "content"
|
||||
assert not os.path.exists(path + ".bak")
|
||||
|
||||
|
||||
def test_delete_file_requires_confirmation_and_deletes(tmp_path):
|
||||
tmp_file = tmp_path / "todelete.txt"
|
||||
tmp_file.write_text("bye")
|
||||
with patch("rook.files.is_approved_dir", return_value=True), \
|
||||
patch("rook.files.confirm_spawn", return_value=True):
|
||||
delete_file(str(tmp_file))
|
||||
assert not tmp_file.exists()
|
||||
|
||||
|
||||
def test_delete_file_denied_leaves_file(tmp_path):
|
||||
tmp_file = tmp_path / "safe.txt"
|
||||
tmp_file.write_text("keep me")
|
||||
with patch("rook.files.is_approved_dir", return_value=True), \
|
||||
patch("rook.files.confirm_spawn", return_value=False):
|
||||
with pytest.raises(PolicyError):
|
||||
delete_file(str(tmp_file))
|
||||
assert tmp_file.exists()
|
||||
40
tests/test_git.py
Normal file
40
tests/test_git.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from rook.git import run_git, PolicyError
|
||||
|
||||
|
||||
def test_git_status_runs():
|
||||
with patch('rook.git.subprocess.run') as mock_run:
|
||||
mock_run.return_value = MagicMock(stdout='On branch master', returncode=0)
|
||||
result = run_git(['status'])
|
||||
assert 'branch' in result
|
||||
|
||||
|
||||
def test_git_commit_allowed():
|
||||
with patch('rook.git.subprocess.run') as mock_run:
|
||||
mock_run.return_value = MagicMock(stdout='', returncode=0)
|
||||
run_git(['commit', '-m', 'msg'])
|
||||
called_args = mock_run.call_args[0][0]
|
||||
assert called_args[:2] == ['git', 'commit']
|
||||
|
||||
|
||||
def test_git_push_requires_confirm_yes():
|
||||
with patch('rook.git.confirm_spawn', return_value=True) as mock_confirm, \
|
||||
patch('rook.git.subprocess.run') as mock_run:
|
||||
mock_run.return_value = MagicMock(stdout='', returncode=0)
|
||||
run_git(['push'])
|
||||
mock_confirm.assert_called()
|
||||
mock_run.assert_called()
|
||||
|
||||
|
||||
def test_git_push_denied_raises_policy_error():
|
||||
with patch('rook.git.confirm_spawn', return_value=False):
|
||||
with pytest.raises(PolicyError):
|
||||
run_git(['push'])
|
||||
|
||||
|
||||
def test_git_nonzero_raises_runtime_error():
|
||||
with patch('rook.git.subprocess.run') as mock_run:
|
||||
mock_run.return_value = MagicMock(stdout='', stderr='fatal: error', returncode=128)
|
||||
with pytest.raises(RuntimeError):
|
||||
run_git(['status'])
|
||||
42
tests/test_shell.py
Normal file
42
tests/test_shell.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
from rook.shell import run_shell, PolicyError
|
||||
|
||||
|
||||
def test_run_shell_in_approved_dir():
|
||||
with patch('rook.shell.is_approved_dir', return_value=True):
|
||||
with patch('rook.shell.subprocess.run', return_value=MagicMock(stdout='hello\n', returncode=0)):
|
||||
result = run_shell('echo hello', cwd='/fake/dir')
|
||||
assert result == 'hello\n'
|
||||
|
||||
|
||||
def test_run_shell_denied_raises_policy_error():
|
||||
with patch('rook.shell.is_approved_dir', return_value=False):
|
||||
with patch('rook.shell.confirm_spawn', return_value=False):
|
||||
with pytest.raises(PolicyError):
|
||||
run_shell('ls', cwd='/tmp')
|
||||
|
||||
|
||||
def test_run_shell_confirmed_outside_approved():
|
||||
mock_confirm = MagicMock(return_value=True)
|
||||
with patch('rook.shell.is_approved_dir', return_value=False):
|
||||
with patch('rook.shell.confirm_spawn', mock_confirm):
|
||||
with patch('rook.shell.subprocess.run', return_value=MagicMock(stdout='ok', returncode=0)):
|
||||
result = run_shell('ls', cwd='/tmp')
|
||||
assert result == 'ok'
|
||||
mock_confirm.assert_called_once()
|
||||
|
||||
|
||||
def test_run_shell_logs_call():
|
||||
with patch('rook.shell.is_approved_dir', return_value=True):
|
||||
with patch('rook.shell.subprocess.run', return_value=MagicMock(stdout='hi\n', returncode=0)):
|
||||
with patch('logging.Logger.info') as mock_log:
|
||||
run_shell('echo hi', cwd='/fake')
|
||||
assert mock_log.called
|
||||
|
||||
|
||||
def test_run_shell_nonzero_returncode_raises():
|
||||
with patch('rook.shell.is_approved_dir', return_value=True):
|
||||
with patch('rook.shell.subprocess.run', return_value=MagicMock(stdout='', stderr='err', returncode=1)):
|
||||
with pytest.raises(RuntimeError):
|
||||
run_shell('bad', cwd='/fake')
|
||||
Reference in New Issue
Block a user