v0.6.0 - Copilot CLI adapter
Stroq 0.6.0 adds a fourth agent: GitHub Copilot CLI.
Copilot CLI support
stroq init --agent copilotwrites.github/hooks/stroq.json(or~/.copilot/hooks/stroq.jsonwith--user, honouringCOPILOT_HOME;--dry-runpreviews) and registersstroq hook copilot pre/stroq hook copilot postonpreToolUseandpostToolUse. The file is Stroq's own — Copilot loads every*.jsonin the hooks directory independently — so no sibling file is ever touched; astroq.jsonStroq did not write is replaced with a printed notice. The engine, rules, policy, provenance, secret-egress guard and hash-chained audit are shared with the Claude Code, Cursor and Codex adapters unchanged.askis a real prompt. Copilot'spreToolUseaccepts a top-level{"permissionDecision": "allow" | "deny" | "ask", "permissionDecisionReason"}, so a policyask(git reset --hard, an external push, an unknownnpx) prompts the user in the interactive CLI:Stroq asks before this action (<rule>): …. The cloud coding agent turnsaskintodeny.- Fail-closed the way Copilot honours it. An internal error, unparsable stdin, a failed stdin read or a missing/unknown phase on
preexits with code 2, the reason on stderr and an empty stdout — Copilot denies on exit 2 regardless of stdout.postand low-impactpretools answer errors with silence. A Copilot hook timeout always fails open, so the installedtimeoutSecis Copilot's own 30 s and the README recommends a global install. - Native tools are mapped:
bash/powershelland the aliasesshell,sh,zsh,exec_command,local_shell,run_command→ Bash;view→ Read;create→ Write;edit/str_replace_editor→ Edit (command: "view"→ Read; the editor sub-command is never treated as a shell command);apply_patch→ Write through its header lines;web_fetch/web_search;grep/rg/glob;ask_user/taskpass through; any other name is an MCP callmcp__copilot__<tool>(Copilot's hooks never report the server). - Every argument spelling is read, and only Stroq's own lists are judged.
toolArgsas an object or a JSON string; commands fromcommand/cmd/input/script/raw; file paths frompath/file_path/rawand URLs fromurl/uri/href/raw, each distinct candidate judged and the worst decision wins; a caller-suppliedurls/file_pathslist is dropped rather than trusted; every fan-out is bounded (copilot-too-many-targetsbeyond 64 files or URLs); a high-impact call whosetoolArgswas non-empty but yielded no command, path or URL is denied ascopilot-unreadable-input. - Self-protection.
.github/hooks/*,.github/copilot/settings(.local).json,.copilot/hooks/*,.copilot/settings.jsonand.copilot/config.jsonareconfig.selffor every agent, so a tainted session cannot rewrite Stroq's hook file or switch hooks off withdisableAllHooks. stroq doctorgains acopilot hooksline (a hook file withoutversion: 1, which Copilot drops, is reported not installed); a runnable demo (examples/demo/run-copilot-demo.sh) asserts every decision, including the realask, and runs in CI.- Documented limits: the secret guard scans a
web_fetchcall'surlandpromptonly;find .github -name stroq.json -deleteis not self-tampering; hooks may not fire in some subagents and never from plugins; a timeout fails open; the wire format is inferred from GitHub's reference and third-party examples, fixtures are hand-written; Windows untested.
Also
- The Codex adapter shares its guard ordering, per-target fan-out, direct-deny audit entry and result reading with the Copilot adapter through two new modules (
adapters/pre-decision.ts,adapters/tool-result.ts); its behaviour is unchanged and its tests are the acceptance check.
Verification
- 1062 tests across 66 files (up from 875 / 62); CI on Node 22 and 24 runs all four demos and the attack suite (
12 scenarios: 8 blocked, 4 asked, 0 passed through). - The adapter went through per-task reviews, a whole-branch security review and two fix rounds, each probing the real engine with adversarial payloads. Found and closed before merge: a
version-less hook file reported as installed;web_fetchlosing its URL for any shape but{url: string};shell/shspellings bypassing the shell rule set; a caller-suppliedurls/file_pathslist shadowing the real target; an unbounded URL fan-out that could run past Copilot's fail-open timeout.
Install: npm install -g @stroq/cli then stroq init (Claude Code), stroq init --agent cursor, stroq init --agent codex or stroq init --agent copilot; or npx @stroq/cli init.