pekral/ai-olympus is a Composer plugin ("type": "composer-plugin"). Composer requires explicit opt-in before running any plugin — including this one — to guard against supply-chain attacks from unknown packages.
When you composer require pekral/ai-olympus:dev-master --dev, Composer may ask:
Do you trust "pekral/ai-olympus" to execute code and wish to enable it now? (yes/no)
If you answer yes, Composer writes the following entry to your project's composer.json:
{
"config": {
"allow-plugins": {
"pekral/ai-olympus": true
}
}
}This is the standard Composer plugin-trust mechanism (allow-plugins). It is project-scoped, version-controlled alongside your composer.json, and must be deliberately set to true by you. The package never modifies allow-plugins on its own behalf — you remain in full control of which plugins your project trusts.
If you prefer to give a non-interactive answer (e.g. in CI), you can pass the flag explicitly:
composer require pekral/ai-olympus:dev-master --dev --no-plugins # skip the plugin during install
composer config allow-plugins.pekral/ai-olympus true # then grant trust manuallyGranting allow-plugins: true also enables the package's Composer plugin to react to post-install-cmd and post-update-cmd events. By default the plugin does nothing on these events. The auto-install hook is activated only when you add the following opt-in to your project's composer.json:
{
"extra": {
"ai-olympus": {
"auto-install": true
}
}
}When auto-install is true, every composer install or composer update automatically runs Installer::run(['ai-olympus', 'install', '--force']) — the same installer that you would call manually, with --force and without any opt-in flags (--allow-bundled-scripts, --allow-subagent-writes, --deny-network-bash). Security implication: any package that ships a post-install-cmd / post-update-cmd hook and is trusted via allow-plugins can trigger code execution during a routine composer install. Review the extra.ai-olympus block in your composer.json before enabling auto-install, and treat it the same way you treat other Composer script hooks.
See also: Installation — Automatic Installation via Composer Plugin.
All security-sensitive installer flags are opt-in by design — the package grants no additional permissions by default.
What it does. Alongside this flag, the installer idempotently appends a narrow allow-list for this package's bundled scripts to ~/.claude/settings.json (permissions.allow):
Bash(*skills/code-review-github/scripts/load-issue.sh *)
Bash(*skills/code-review-jira/scripts/load-issue.sh *)
These two patterns pre-approve the GitHub and JIRA load-issue.sh scripts that the code-review-github and code-review-jira skills invoke, so Claude Code stops prompting for confirmation on every run.
What it does not do. It grants access only to the two specific, version-controlled scripts shipped in this package. All other entries in ~/.claude/settings.json are preserved untouched. The flag has no effect when neither HOME nor USERPROFILE is available.
Implementation reference. src/InstallerClaudeSettings.php — applyIfRequested() → ensureBundledScriptPermissions().
See also: Installation — CLI Switches.
What it does. Alongside this flag, the installer prepends two scoped permission entries to permissions.allow in the project's .claude/settings.local.json:
Edit(//<absolute-project-path>/**)
Write(//<absolute-project-path>/**)
These entries pre-allow dispatched subagents (e.g. donatello) to write files inside the project tree without requiring an interactive approval on each operation. A dispatched subagent runs non-interactively, so a write is denied at runtime unless the path is already in permissions.allow.
Why settings.local.json and not settings.json. The entries carry a machine-absolute path — they are personal and not portable. settings.local.json is git-ignored by Claude Code by default, so the absolute path never leaks into version control.
Safety guarantees. The flag is idempotent: it only adds missing entries and never removes or modifies existing ones. After writing, the installer reads the file back and validates that every required entry is present (InstallerProjectSettings::validateSubagentWritePermissions()), so a malformed file can never be produced. The package grants nothing by default — this flag is the explicit, human-owned opt-in.
Implementation reference. src/InstallerProjectSettings.php — applySubagentWritesIfRequested() → ensureSubagentWritesEnabled().
See also: docs/agents.md — Troubleshooting (subagent file writes blocked).
What it does. Alongside this flag, the installer idempotently appends ten patterns to permissions.deny in the project's .claude/settings.local.json:
Bash(curl:*) Bash(wget:*) Bash(nc:*) Bash(ncat:*) Bash(netcat:*)
Bash(telnet:*) Bash(ssh:*) Bash(scp:*) Bash(sftp:*) Bash(openssl s_client:*)
Claude Code then refuses those commands before they run. This is the vendor's own recommended shape for the problem (deny the Bash network tools, keep WebFetch(domain:…) for the fetches you actually want), and its value is precise: permission rules are enforced by Claude Code, not by the model, so a prompt injection telling an agent to curl attacker.example is stopped by the harness rather than by the agent's good behaviour. ncat / netcat / telnet / sftp are listed separately because the :* suffix enforces a word boundary — Bash(nc:*) does not match ncat. openssl is denied only through its network subcommand, because a deny rule cannot carry allow-list exceptions and a bare Bash(openssl:*) would also block openssl dgst, the checksum verification this repository's own rules require.
Scope — session-wide, project-scoped, never per agent. A permissions.deny rule applies to the whole Claude Code session: inside this project it restricts every agent and your own interactive Bash use identically. That is the trade-off the flag exists to let you accept deliberately; it is why the flag is off by default and why it writes the project-local file rather than ~/.claude/settings.json — permission rules merge across scopes and are evaluated deny → ask → allow with specificity ignored, so a deny in the user-level file would apply to every project on the machine and could not be relaxed by a project that genuinely needs curl. Your ordinary terminal outside Claude Code is unaffected. To make the policy team-wide, copy the same permissions.deny block into the committed .claude/settings.json yourself — the installer deliberately never writes a tracked file.
What it does not do — this is not an egress control. A permission rule matches the command string Claude Code is asked to run, never the process tree that command spawns. Concretely, the following all remain open:
- Child processes of any allowed command —
gh,git clone/push/fetch,composer,npm,php -r,node -e,python3 -c "import socket". This package's own scripts are in that category by design and keep working with every pattern in place (skills/code-review-bugsnag/scripts/_lib.sh,load-issue.sh,upsert-comment.sh,skills/_shared/attachments.sh,skills/_shared/assert-current-repo.shcallcurl/sshdirectly). - Wrappers that are not stripped —
bash -c 'curl …',sh -c,env curl,npx,docker exec,devbox run,mise exec,direnv exec,xargs -n1 curl(only a barexargsis stripped). - Absolute and relative paths —
Bash(curl:*)matches the stringcurl …, not/usr/bin/curl …or./curl. - Shell built-in networking —
exec 3<>/dev/tcp/host/80runs no binary, so there is nothing to match. - Tools not on the list —
socat,aria2c,httpie,rsync,ftp,openssl s_server/s_time. --dangerously-skip-permissions/bypassPermissions, which skip rule evaluation entirely.WebFetch/WebSearchstay available wherever they are granted — deliberately, per the guidance above.
The tier that would actually close 1–4 is Claude Code sandboxing, an OS-level restriction on the Bash tool's filesystem and network access that also covers child processes. This package does not configure sandboxing, and this flag only approximates it for literal, first-order network commands. Treat the gain as advisory instruction → harness-enforced refusal for the listed command strings, not as containment against a determined agent.
Safety guarantees. The flag is idempotent and additive: it only appends missing patterns, and existing permissions.allow entries and unrelated keys are preserved untouched, as is every string entry already in permissions.deny (nothing is reordered). One precise exception, stated rather than glossed over: a non-string item inside permissions.deny — a number, null, an object — is dropped when that list is rewritten, because the installer sanitises the list to strings and such an item is not a rule Claude Code could enforce in the first place. After writing, the installer reads the file back and validates that every pattern is present (InstallerProjectSettings::validateNetworkBashDenyPermissions()) — so the installer never reports the restriction as applied when it was not actually written. Unlike --allow-bundled-scripts, it has no HOME precondition that could turn it into a silent no-op.
How to undo it. There is no inverse flag. Open .claude/settings.local.json in the project, delete the ten Bash(...) strings above from the permissions.deny array (leaving any entry you added yourself), and save. Removing the whole deny key is also safe if it holds nothing else.
Implementation reference. src/InstallerProjectSettings.php — applyNetworkBashDenyIfRequested() → ensureNetworkBashDenyPermissions(), patterns in getNetworkBashDenyPermissions().
The five shipped subagents (agents/*.md) each declare a tools: allow-list and, since issue #163, a disallowedTools: entry — the two layers the Claude Code harness actually enforces. Both are pinned by tests/Installer/AgentsTest.php so an agent cannot silently gain a tool it should not have. Full detail (per-agent Bash purpose lists, the harness research behind the numbers below) lives in docs/agents.md Capability model and @rules/compound-engineering/orchestration.md Bash capability boundary; this section states only the facts a security reviewer of this package needs without opening either.
- Enforced today: the
tools:allow-list itself, and thedisallowedTools:entry every agent now carries (read-only agents loseWrite, Edit; agents with no documentation-fetch need loseWebSearch, WebFetch). - Not enforced, advisory only: every agent also carries
Bash, which subsumes both write access and outbound network access regardless of whattools:/disallowedTools:say. There is no per-agent Bash command allow-list this package ships or can ship: the agent frontmattertools:field has no syntax for a scoped command pattern (Bash(gh:*)is not expressible), andpermissions.allow/permissions.denypatterns apply session-wide, never per agent, so scoping one agent's Bash would scope every agent's (and the human's) identically. The only genuinely per-agent mechanism is ahooks: PreToolUsevalidator — runtime code this instructions-only package does not ship. One was shipped behind an opt-in--enforce-agent-bash-boundaryflag and removed again (issue #265), because it failed open in eleven separate cases and asked the user to confirm ordinary commands it could not read. - What the installer writes by default: without an opt-in flag, the installer writes no Bash restriction of any kind — not a
permissions.allow/permissions.denyentry, not a hook, nothing.--allow-bundled-scriptsand--allow-subagent-writesdo not restrict Bash either; they only pre-approve two specific scripts and pre-allowWrite/Editfor a dispatched subagent, respectively. - One partial mechanism now exists, opt-in:
--deny-network-bash(see Installer security flags above) writespermissions.denyentries for ten literal outbound-network commands, moving exactly those command strings from advisory instruction to harness-enforced refusal. It narrows the gap; it does not close it. The restriction is session-wide, not per agent (it restricts the human's own interactive Bash in this project identically), and it matches command strings rather than process trees — child processes of allowed commands, unstripped wrappers, absolute paths,/dev/tcp, and unlisted tools all remain open, as enumerated under--deny-network-bash. Everything not on that list stays exactly as advisory as before. - No mechanism makes the boundary per-agent. The per-agent half of the Bash boundary is advisory in full. The OS-level tier (Claude Code sandboxing) is not configured by this package, and it remains the only tier that would cover child processes.
- If you ever installed the removed hook,
installnow removes its entry for you. The flag never had an inverse, so a project that opted in still carries the handler in.claude/settings.local.json. It points at a subcommand the binary no longer has:ai-olympus bash-guardfalls through to the installer, printsUnknown command: bash-guard, and exits1. Claude Code treats a non-zero exit other than2as a non-blocking hook error, so the Bash call still runs — but the error is printed on every Bash call until the entry is gone. Everyinstallrun now deletes it (issue #6): each hook handler whosecommandends in one of this package's own binaries followed by thebash-guardsubcommand, and only those. Both binary names count, and the closing quote the writer put around a path holding a space is tolerated — the flag itself wroteagent-skills bash-guard(or…/agent-skills' bash-guard), becausebash-guardwas deleted five days before the rebrand renamed the binary toai-olympus, and a hand-edited file may carry either name. A handler you added yourself is left in place, and a matcher group, a hook event, and thehookskey are pruned only once that deletion empties them.installreads.claude/settings.local.jsonon every run to make this check, whether or not the file holds such a handler — so a file that is not valid JSON now ends the install withCannot parse Claude settings file <path>: Syntax error.and exit1, where the same install used to read that file only behind--allow-subagent-writes/--deny-network-bashand succeeded without it. Nothing is written unless a handler was actually removed. The removal is reported in the install summary, which also tells you to restart the session — hooks are read once, at session start, so the error keeps printing in the session that was already running. To remove it by hand instead, delete the same handler from theBashgroup underhooks.PreToolUse.
Default installation does not read or write global ~/.claude/settings.json. The --disable-co-author-attribution flag opts into setting includeCoAuthoredBy: false when absent; it preserves an existing value, including true, and all unrelated settings. The preference applies across Claude Code projects. To undo an earlier installation's preference, remove the key manually; upgrades do not infer whether an existing value was chosen by the user. Composer auto-install does not enable this flag.
| Path | Created by | Condition |
|---|---|---|
~/.claude/settings.json — sets includeCoAuthoredBy: false |
--disable-co-author-attribution (opt-in) |
HOME/USERPROFILE set; key absent — never overwrites an existing value |
~/.claude/settings.json — adds permissions.allow bundled-script entries |
--allow-bundled-scripts |
HOME/USERPROFILE set |
.claude/settings.local.json — prepends permissions.allow scoped Edit/Write entries |
--allow-subagent-writes |
always |
.claude/settings.local.json — appends permissions.deny network-command entries |
--deny-network-bash |
always |
.claude/settings.local.json — deletes hook handlers pointing at the removed bash-guard |
install (unconditional) |
read on every run (invalid JSON fails the install); rewritten only when a matching handler is present |
.claude/rules/ |
install |
always |
.claude/skills/ (and ~/.claude/skills/ with --global) |
install |
always |
.claude/agents/ |
install |
always |
.codex/rules/, .codex/agent-instructions/, .codex/agents/ |
install |
always |
.agents/skills/ (and ~/.agents/skills/ with --global) |
install |
always |
AGENTS.md |
install |
only when the project has none; never overwritten |
.claude/run/code-review-memory.sqlite (.codex/run/ under Codex) |
ai-olympus memory migrate and memory add, never install |
git ignores the path and does not track it; otherwise the command exits 2 and writes nothing. install --prune never deletes it. |
The installer never writes outside the project directory and the user's home directory, and it never modifies composer.json or any project source file.
A skill is executable instruction: an agent follows its SKILL.md and runs its scripts with the permissions of your session, including Bash. Treat skills the way you treat Composer packages.
- Install them only from this package —
composer require pekral/ai-olympusfrom Packagist, or/plugin marketplace add pekral/ai-olympusin Claude Code. ASKILL.mdcopied from anywhere else is untrusted code, even when it looks identical to one shipped here. - Pin the version and commit
composer.lock, so every machine installs the same release you reviewed (the Installation command pins a release). - Read the changes before you upgrade.
install --forcereplaces the installed rules, skills, and agents; checkCHANGELOG.mdfor the release, and when your project commits.claude/and.agents/, review their diff before committing it. - Report a modified copy that claims to come from this package through the private process below.
If you discover a security issue in this package, please report it privately so it can be addressed before public disclosure.
Contact: open a GitHub Security Advisory (preferred) or email kral.petr.88@gmail.com.
Please include a description of the issue, reproduction steps, and the potential impact. You will receive a response within a reasonable time. Public disclosure is coordinated after a fix is available.