Read PR description, comments, commits, and changed files.
UnderstandIdentified and reviewed linked issues.
UnderstandAnalyzed PR diff and relevant code files.
UnderstandChecked CHANGELOG, README, and test files.
VerifyPosted detailed comment on PR in user's tone.
EditYou are given one or more GitHub PR URLs: https://github.com/badlogic/pi-mono/pull/762
For each PR URL, do the following in order:
packages/*/CHANGELOG.md files. Report whether an entry exists. If missing, state that a changelog entry is required before merge and that you will add it if the user decides to merge. Follow the changelog format rules in AGENTS.md. Verify:
### Breaking Changes, ### Added, ### Fixed, etc.)Fixed foo ([#123](https://github.com/badlogic/pi-mono/pull/123) by [@user](https://github.com/user))### Breaking Changes, not just ### FixedOutput format per PR: PR: Changelog:
If no issues are found, say so under Bad and Ugly.
The user wants me to review a GitHub PR. Let me start by reading the PR details.
>gh pr view 762 --repo badlogic/pi-mono --json title,body,comments,commits,files,author,labels,state,baseRefName,headRefName>gh pr diff 762 --repo badlogic/pi-monoLet me now read the linked issue #697 and the current main branch files to understand the context better.
>gh issue view 697 --repo badlogic/pi-mono --json title,body,comments,labels,stateLet me also check the CHANGELOG and README to verify they were updated correctly, and look at the test file to make sure I understand the implementation fully.
Now let me check the current CHANGELOG and README on main, and verify the test file:
Let me check the coding-agent docs to see if they need modification, and also verify the full context of the changes to understand the implementation.
Let me check the docs directory to see if any documentation updates are needed:
>ls -la /Users/badlogic/workspaces/pi-mono/packages/coding-agent/docs/>grep -r "apiKey\|models.json" /Users/badlogic/workspaces/pi-mono/packages/coding-agent/docs/ 2>/dev/null | head -50Let me also verify the implementation is correct by looking at the model-registry.ts diff more closely and check if there are any security concerns with the shell command execution.
The implementation looks correct:
! are executed via execSyncLet me also check the test for completeness and verify the CHANGELOG entry format is correct.
>grep -n "apiKey" /Users/badlogic/workspaces/pi-mono/packages/coding-agent/docs/sdk.md | head -20Good, there's no mention of apiKey in the SDK docs that needs updating. The feature addition is in the README which was already updated by the PR. Let me now compile my review.
Looking at the PR:
Good things:
! prefix approach is consistent with how other tools (like codecompanion in vim) handle thisPotential concerns:
execSync call uses stdio: ["ignore", "pipe", "ignore"] which ignores stderr - this is correct for silently handling errorsChangelog entry issue: The current entry is:
- API keys in `models.json` can now be retrieved via shell command using `!` prefix (e.g., `"apiKey": "!security find-generic-password -ws 'anthropic'"` for macOS Keychain) ([#697](https://github.com/badlogic/pi-mono/issues/697))Per AGENTS.md, external contributions should include PR link and author:
- API keys in `models.json` can now be retrieved via shell command using `!` prefix (e.g., `"apiKey": "!security find-generic-password -ws 'anthropic'"` for macOS Keychain) ([#762](https://github.com/badlogic/pi-mono/pull/762) by [@cv](https://github.com/cv))The PR is also missing the pkg:coding-agent label. I should verify the implementation handles edge cases properly: trailing newlines are trimmed, timeouts and missing commands both return undefined gracefully, and pipes work as shown in the tests. The implementation appears solid and ready for review.
PR: https://github.com/badlogic/pi-mono/pull/762
packages/coding-agent/CHANGELOG.md under ### Added- API keys in `models.json` can now be retrieved via shell command using `!` prefix (...) ([#762](https://github.com/badlogic/pi-mono/pull/762) by [@cv](https://github.com/cv))pkg:coding-agent labelresolveApiKeyConfig()undefined, allowing auth to fall back to other methods! prefix approach matches conventions from other tools (vim codecompanion, etc.)models.json).package-lock.json changes (removing peer: true from several deps) are unrelated lock file drift, likely from merging with main. This is benign.packages/coding-agent/src/core/model-registry.ts: Extended resolveApiKeyConfig() to detect ! prefix and execute the remainder as a shell command via execSync, returning trimmed stdout. Includes 10s timeout and graceful error handling.packages/coding-agent/test/model-registry.test.ts: Added 9 new tests for API key resolution covering all execution paths.packages/coding-agent/README.md: Updated "API key resolution" section to document the three formats: !command, env var name, and literal value.packages/coding-agent/CHANGELOG.md: Added changelog entry (needs format fix).model-registry.test.ts:
apiKey with ! prefix executes command and uses stdoutapiKey with ! prefix trims whitespace from command outputapiKey with ! prefix handles multiline output (uses trimmed result)apiKey with ! prefix returns undefined on command failureapiKey with ! prefix returns undefined on nonexistent commandapiKey with ! prefix returns undefined on empty outputapiKey as environment variable name resolves to env valueapiKey as literal value is used directly when not an env varapiKey command can use shell features like pipespackages/coding-agent/README.md: Updated appropriately with the new feature.packages/coding-agent/docs/*.md: No updates needed. The sdk.md mentions models.json but only in the context of file paths and model discovery, not API key resolution specifics.absolutely no bad shit? like, invoking bash on every api key get? error handling?
The user is pushing back on my review, asking if there are really no issues. Let me think more critically about this:
resolveApiKeyConfig is called. If it's called on every API request, that could be a performance issue ancontinue
The user is pushing back and wants me to think more critically. Let me re-examine:
Invoking bash on every API key get - I need to check when resolveApiKeyConfig is called. Is it cached? Or called on every LLM request?
Error handling - The current code silently swallows errors. Is that really the right behavior? What if the user misconfigured their command?
Let me look at the code flow more carefully.
>grep -n "resolveApiKeyConfig\|getApiKey\|customProviderApiKeys" /Users/badlogic/workspaces/pi-mono/packages/coding-agent/src/core/model-registry.tsNow I see the real issue. The resolveApiKeyConfig is called via the fallbackResolver which is invoked from getApiKey(). This means:
Every time getApiKey() is called for a custom provider with a !command apiKey, the shell command gets executed. This could be:
No caching - The resolved API key is never cached. The command runs every single time.
Error handling is silent - If the user's command fails (typo, credential manager locked, etc.), they get no feedback. The auth just silently falls back and probably fails later with a confusing "no API key" error.
Let me trace how often getApiKey is called:
>grep -rn "getApiKey\|getApiKeyForProvider" /Users/badlogic/workspaces/pi-mono/packages/coding-agent/src/ --include="*.ts" | grep -v "test" | head -30>grep -rn "fallbackResolver" /Users/badlogic/workspaces/pi-mono/packages/coding-agent/src/ --include="*.ts"Now I see the real problems:
Called on every LLM request: getApiKey is called in agent-session.ts at lines 633, 1049, 1090, 1120, 1259, 1447, 2076. That's EVERY TIME the model is used - for every message, every retry, every compaction summarization.
Called during startup/model discovery: Lines in sdk.ts (369, 383, 392) and model-resolver.ts (342) call it during model selection.
No caching: The resolveApiKeyConfig function runs execSync every single time. For a security find-generic-password command, this means:
Silent error handling: If the command fails, it returns undefined silently. The user won't know their command is broken until they get a confusing "authentication failed" error.
This is a significant performance and UX issue. The command should be:
Let me also check if there's any existing caching mechanism.
You're right. I missed significant issues:
Updated Review - Bad:
No caching - command executes on EVERY API key lookup:
getApiKey() is called on every LLM request (lines 633, 1049, 1090, 1120, 1259, 1447, 2076 in agent-session.ts)!security find-generic-password command spawns a new shell process each timeSilent error handling is a UX problem:
undefined silently!command in models.json is the problemThe fix needed:
customProviderApiKeys map already exists but stores the config not the resolved valueExample of what happens now with a typo:
{ "apiKey": "!security find-generic-pasword -ws 'anthropic'" } // typo: paswordUser sees: "Error: No API key for provider my-provider" with no hint about the failed command.
Ugly:
ok, leave a comment on the pr in my tone
The user wants me to leave a comment on the PR in their tone. Based on the AGENTS.md, the tone should be:
Let me craft a comment that points out the issues directly but kindly.
>gh pr comment 762 --repo badlogic/pi-mono --body "Thanks for the PR! The feature is useful, but there's a performance issue that needs addressing before merge:
**No caching - command executes on every API key lookup**
\`getApiKey()\` is called on every LLM request (not just once at startup). With a \`!command\` apiKey, this means spawning a shell process multiple times per user message: initial request, retries, compaction checks, etc. For a long session, this adds up.
The \`customProviderApiKeys\` map stores the raw config string, not the resolved value. The fix would be to cache the resolved key after first successful execution.
**Silent error handling**
If the command fails (typo, keychain locked, 1Password CLI not logged in), it returns \`undefined\` silently. User then sees a confusing \"no API key\" error with no indication their \`!command\` is broken.
Suggestion: log a warning on command failure so users know what went wrong.
---
Also, minor changelog format issue: external contributions should link to the PR with author attribution:
\`\`\`
([#762](https://github.com/badlogic/pi-mono/pull/762) by [@cv](https://github.com/cv))
\`\`\`"Comment posted. The user asked me to leave a comment in their tone, which I did. The comment is direct, technical, points out the specific issues with code references, and provides actionable suggestions for fixing the problems.
- API keys in `models.json` can now be retrieved via shell command using `!` prefix (e.g., `"apiKey": "!security find-generic-password -ws 'anthropic'"` for macOS Keychain) ([#697](https://github.com/badlogic/pi-mono/issues/697))- API keys in `models.json` can now be retrieved via shell command using `!` prefix (e.g., `"apiKey": "!security find-generic-password -ws 'anthropic'"` for macOS Keychain) ([#762](https://github.com/badlogic/pi-mono/pull/762) by [@cv](https://github.com/cv))- API keys in `models.json` can now be retrieved via shell command using `!` prefix (...) ([#762](https://github.com/badlogic/pi-mono/pull/762) by [@cv](https://github.com/cv)){ "apiKey": "!security find-generic-pasword -ws 'anthropic'" } // typo: pasword