feat: @cireilclaw/plugin-github — GitHub App integration plugin#19
Conversation
|
Linter diff in the way? Review this PR in Change Stack to focus on meaningful changes and expand context only when needed. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughA new GitHub plugin package is introduced with REST API helpers, GitHub App authentication, typed API models, and tools for issues, pull requests, comments, repositories, and content. Workspace scripts and several package versions were bumped, and an example GitHub config TOML was added. ChangesGitHub Plugin Implementation
Workspace Configuration and Release Updates
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@config/plugins/github.toml.example`:
- Around line 7-8: The template incorrectly says privateKey may be a file path
though the code treats it as PEM content; update the implementation so auth
accepts either a PEM string or a filesystem path: in the config parsing (the
privateKey setting) and in the auth flow where createPrivateKey(...) is called,
detect whether privateKey looks like a PEM (e.g., starts with "-----BEGIN") and
otherwise attempt to read the file at that path (synchronously or async) and use
its contents as the PEM before calling createPrivateKey; ensure errors are
handled with clear messages so a path that cannot be read fails fast.
In `@packages/plugin-github/src/auth.ts`:
- Around line 21-33: The JWT is being signed with crypto.sign(undefined, ...)
which is incorrect for RS256; update the call in the function that builds the
JWT (the code using header/payload, b64url, createPrivateKey and sign) to call
crypto.sign with the explicit digest algorithm 'sha256' as the first argument,
ensure you sign the UTF-8 bytes of signingInput using the key from
createPrivateKey(pem), and continue returning the token as
`${signingInput}.${signature.toString("base64url")}` (ensuring the signature is
produced by the 'sha256' digest to match the JWT header's RS256).
In `@packages/plugin-github/src/config.ts`:
- Around line 6-33: The installationId parsing in inner (function inner and the
ConfigSchema/installationId handling) currently uses Number.parseInt which
accepts partial numbers and can yield NaN; update the code that builds the
returned installationId so that when parsed.installationId is a string you first
validate it matches a full unsigned integer (e.g. /^\d+$/) or otherwise parse to
a number and verify Number.isInteger(value) && value > 0, and if validation
fails throw a ToolError indicating an invalid installationId in plugin config;
keep the existing numeric branch unchanged.
In `@packages/plugin-github/src/content.ts`:
- Around line 33-40: In github-read-file (in
packages/plugin-github/src/content.ts) guard before decoding: verify
data.encoding === "base64" and that data.content is non-null/ non-empty; if
those checks fail, throw a ToolError with a clear message (referencing the path)
or perform a second request to the GitHub raw endpoint using Accept:
application/vnd.github.v3.raw to obtain the file bytes, and only call
Buffer.from(data.content, "base64") when the checks pass; update any error
messages accordingly and reference the existing ToolError type and the
data.encoding/data.content fields.
In `@packages/plugin-github/src/pulls.ts`:
- Around line 132-146: The githubListPrFiles tool (defined as githubListPrFiles
and using ghParse) currently fetches the PR files endpoint only once and thus
truncates results for PRs with >30 files; update execute in githubListPrFiles to
paginate: call the /repos/{owner}/{repo}/pulls/{number}/files endpoint
repeatedly with page and per_page (use per_page=100) or follow the Link header
until no next page, accumulate GHPrFile[] results into a single items array, and
return the combined list; ensure parsing still uses listPrFilesSchema for inputs
and reuse ghParse for each request while handling response headers to detect
termination.
In `@packages/plugin-github/src/repos.ts`:
- Around line 11-24: The current execute function calls
ghParse<GHInstallationRepos> once for "/installation/repositories" and only maps
result.repositories, which misses paginated pages; update execute to perform
paginated fetching (follow GitHub's Link header or use page/per_page params) by
repeatedly calling ghParse for subsequent pages until no next page, accumulate
all repositories into a single array, then map that accumulated array to the
existing repo shape (referencing execute, ghParse, GHInstallationRepos, and the
"/installation/repositories" endpoint) and return { repos, success: true } after
all pages are fetched.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 70adcf54-0166-42f3-862a-0418ed23dddb
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (18)
config/plugins/github.toml.examplepackage.jsonpackages/brave-search/package.jsonpackages/openweather/package.jsonpackages/plugin-github/package.jsonpackages/plugin-github/src/api.tspackages/plugin-github/src/auth.tspackages/plugin-github/src/comments.tspackages/plugin-github/src/config.tspackages/plugin-github/src/content.tspackages/plugin-github/src/index.tspackages/plugin-github/src/issues.tspackages/plugin-github/src/pulls.tspackages/plugin-github/src/repos.tspackages/plugin-github/src/types.tspackages/plugin-github/tsconfig.jsonpackages/plugin-github/tsdown.config.tspackages/template/package.json
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/plugin-github/src/api.ts (1)
33-53: 🧹 Nitpick | 🔵 Trivial | 💤 Low value
ghParsedoesn't supportextraHeaders, limiting flexibility.The
ghfunction acceptsextraHeadersto allow callers to override theAcceptheader (as used incontent.tsfor raw file fallback), butghParsedoesn't expose this parameter. This forces callers needing custom headers to useghdirectly and handle response parsing manually.Consider adding the parameter for consistency:
♻️ Suggested change
async function ghParse<TData>( ctx: PluginToolContext, method: string, path: string, body?: unknown, + extraHeaders?: Record<string, string>, ): Promise<TData> { - const response = await gh(ctx, method, path, body); + const response = await gh(ctx, method, path, body, extraHeaders);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/plugin-github/src/api.ts` around lines 33 - 53, ghParse currently lacks the optional extraHeaders parameter supported by gh, so callers cannot override headers (e.g., Accept) without using gh directly; update the ghParse function signature to accept extraHeaders?: Record<string, string> (or the same type used by gh) and pass it through to the gh(ctx, method, path, body, extraHeaders) call, keeping existing error handling and return behavior intact (including the 204 empty object case) so callers like content.ts can supply custom headers without reimplementing parsing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/plugin-github/src/auth.ts`:
- Around line 20-33: The PEM-detection in resolvePrivateKey is brittle because
it fails when the input has leading/trailing whitespace; update
resolvePrivateKey to trim the input first (e.g., const trimmed =
keyOrPath.trim()), check trimmed.startsWith("-----BEGIN "), and if it is a PEM
return the trimmed string; if not, attempt to readFileSync using the trimmed
value and preserve the existing ToolError behavior (including the captured error
message) so paths with accidental whitespace are handled correctly; keep
references to resolvePrivateKey and ToolError for locating the change.
In `@packages/plugin-github/src/content.ts`:
- Around line 39-64: Before calling gh for the raw fallback, enforce a byte-size
cap by comparing data.size to a defined MAX_RAW_FALLBACK_BYTES constant and
throw a ToolError if data.size exceeds that cap so you never fetch very large
files; after a successful rawResponse, keep returning size: data.size (not
rawContent.length) to preserve GitHub's byte-size semantics and avoid using
character length; update the logic around gh, rawResponse, rawContent and
ToolError to bail early on oversized data and to set the returned size from
data.size.
---
Outside diff comments:
In `@packages/plugin-github/src/api.ts`:
- Around line 33-53: ghParse currently lacks the optional extraHeaders parameter
supported by gh, so callers cannot override headers (e.g., Accept) without using
gh directly; update the ghParse function signature to accept extraHeaders?:
Record<string, string> (or the same type used by gh) and pass it through to the
gh(ctx, method, path, body, extraHeaders) call, keeping existing error handling
and return behavior intact (including the 204 empty object case) so callers like
content.ts can supply custom headers without reimplementing parsing.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: f7d76fcd-51b7-49c5-aba3-91ad2350601d
📒 Files selected for processing (6)
packages/plugin-github/src/api.tspackages/plugin-github/src/auth.tspackages/plugin-github/src/config.tspackages/plugin-github/src/content.tspackages/plugin-github/src/pulls.tspackages/plugin-github/src/repos.ts
Add package.json, tsconfig.json, and tsdown.config.ts for new @cireilclaw/plugin-github package. Follows same build setup as existing plugins (tsdown ESM, workspace SDK dep).
- auth.ts: GitHub App JWT generation (RS256) + installation token cache - api.ts: gh/ghParse HTTP helpers wrapping ctx.net.fetch - config.ts: valibot schema for appId/privateKey/installationId - types.ts: typed interfaces for GitHub REST API responses
- github-create-issue: file a new issue with labels/assignees - github-read-issue: detailed issue info - github-list-issues: filterable issue list (state, labels, assignee) - github-update-issue: modify title, body, state, labels - github-search-issues: full-text search across issues - github-add-issue-comment: post a comment - github-list-issue-comments: read existing comments
- github-read-pr / github-list-prs / github-list-pr-files - github-list-repos / github-read-repo - github-read-file / github-list-contents / github-search-code
Entry point registers all 15 tools under the 'github' plugin name. Example config documents appId/privateKey/installationId fields.
@cireilclaw/plugin-brave-search 0.2.2 -> 0.2.3 @cireilclaw/plugin-openweather 0.1.2 -> 0.1.3 @cireilclaw/plugin-template 0.1.1 -> 0.1.2
Publishes all @cireilclaw/* packages to npm via pnpm publish -r, filtering out the un-scoped cireilclaw-runtime.
…56 digest - resolvePrivateKey: detect PEM content vs file path via -----BEGIN prefix - sign with 'sha256' digest to match JWT RS256 algorithm header
parseInstallationId rejects non-numeric strings and NaN, moved before inner to fix no-use-before-define.
…allback - Verify data.encoding === 'base64' and content non-empty before decode - Fall back to raw endpoint with Accept: application/vnd.github.v3.raw - Add extraHeaders param to gh() for custom Accept headers
Fetches all pages of /pulls/{number}/files for PRs with >30 files.
Use shared parseLinkNext from api.ts, follow Link headers to fetch all pages of /installation/repositories.
resolvePrivateKey trims input to handle accidental whitespace around the PEM string or file path.
- MAX_RAW_FALLBACK_BYTES prevents fetching very large files - Return data.size (GitHub byte count) instead of rawContent.length
ghParse now accepts optional extraHeaders and forwards them to gh, so callers can supply custom Accept headers.
951b600 to
fab6fd6
Compare
Adds
@cireilclaw/plugin-github, a full-featured GitHub App integration plugin with 15 tools across 5 domains.Tools:
github-create-issue,github-read-issue,github-list-issues,github-update-issue,github-search-issuesgithub-add-issue-comment,github-list-issue-commentsgithub-read-pr,github-list-prs,github-list-pr-filesgithub-list-repos,github-read-repogithub-read-file,github-list-contents,github-search-codeAuth: GitHub App JWT → installation token flow via
node:crypto, cached for 1-hour expiry.Other changes:
@cireilclaw/plugin-brave-search(0.2.2 → 0.2.3),@cireilclaw/plugin-openweather(0.1.2 → 0.1.3),@cireilclaw/plugin-template(0.1.1 → 0.1.2)publish:allscript publishes all@cireilclaw/*packages, excluding runtimeSecurity Considerations — additions & clarifications
Private key input trimming
Extra headers passthrough
Raw fallback size cap
JWT/token handling
URL handling & pagination
Potential risks / reviewer attention items
File-read error exposure
Forwarded headers surface
Minimal runtime shape validation
Confirmed mitigations already present