Repository navigation
feat: scaffold V1 — configs, action metadata, pure review core #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
23 commits
Select commit
Hold shift + click to select a range
734be5f
feat: scaffold V1 — configs, action metadata, pure review core
aliasunder 5f38bc3
chore: gitignore local vault symlinks
aliasunder fcf3c77
fix: address review — Dockerfile layer split, multi-line edge tests, …
aliasunder c0fe1ff
chore: add OSS community files and pre-commit tooling
aliasunder b0fc24f
chore: pin exact dependency versions
aliasunder 5c6af9e
chore: switch planned OpenRouter client to official @openrouter/sdk
aliasunder b14ef8b
feat: add cost_summary input (default true)
aliasunder 3402971
chore: add env-var for ambient environment access
aliasunder f119470
docs: drop cross-repo reference from AGENTS.md
aliasunder ed6a355
feat: enforce module layering boundary via eslint
aliasunder ea0cff3
feat: structured JSON logger with child contexts and lazy props
aliasunder 243ea8d
docs: update logger description in AGENTS.md structure
aliasunder 0aa3030
fix: skip npm lifecycle scripts in Docker + normalize inverted findin…
aliasunder 23133a4
fix: allow type imports in second lint boundary pattern + injectable …
aliasunder 70d3145
fix: address ship-check review findings — phantom diff lines, logger …
aliasunder ac6e817
feat: randomized prompt delimiters + digest-pinned base image with De…
aliasunder d53b3a8
fix: address test-audit + bug-check findings — snap bound, logger gua…
aliasunder 2f4b7bd
style: block body for isDuplicate — multiline responses get explicit …
aliasunder 0dfa5c8
refactor: use getBooleanInput for boolean inputs instead of hand-roll…
aliasunder 3c7b128
docs: codify SDK-utils-first and block-body conventions in AGENTS.md
aliasunder db0501f
style: prettier formatting on AGENTS.md
aliasunder c40d0bd
feat: wire input collection in main.ts — getBooleanInput at the SDK b…
aliasunder de18702
chore: sweep for stale claims and unused deps after the getBooleanInp…
aliasunder File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| # CodeRabbit configuration. | ||
| # Docs: https://docs.coderabbit.ai/getting-started/yaml-configuration | ||
| reviews: | ||
| # Trim the recurring summary comment to just the useful parts. | ||
| poem: false | ||
| sequence_diagrams: false | ||
| review_status: false |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| node_modules | ||
| dist | ||
| .git | ||
| .github | ||
| fixtures | ||
| **/__tests__ | ||
| *.md | ||
| .env |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # Keeps the digest-pinned base image in the Dockerfile current — pinning | ||
| # closes the mutable-tag supply-chain hole; Dependabot restores the patch | ||
| # stream the pin would otherwise lose. | ||
| version: 2 | ||
| updates: | ||
| - package-ecosystem: docker | ||
| directory: / | ||
| schedule: | ||
| interval: weekly |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| name: CI | ||
|
|
||
| on: | ||
| pull_request: | ||
| push: | ||
| branches: [main] | ||
|
|
||
| # Least-privilege default; no job needs more than read | ||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: ci-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| checks: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | ||
| with: | ||
| persist-credentials: false | ||
|
|
||
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | ||
| with: | ||
| node-version-file: .nvmrc | ||
| cache: npm | ||
|
|
||
| - name: Install dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Format check | ||
| run: npm run prettier:check | ||
|
|
||
| - name: Lint | ||
| run: npm run lint | ||
|
|
||
| - name: Test | ||
| run: npm test | ||
|
|
||
| - name: Build | ||
| run: npm run build | ||
|
|
||
| # Catches Dockerfile drift that unit tests cannot (missing files, bad COPY paths) | ||
| - name: Docker build smoke | ||
| run: docker build -t umm-actually-ci . |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,3 +3,7 @@ dist/ | |
| .env | ||
| *.log | ||
| .DS_Store | ||
|
|
||
| # Symlinks into the private Obsidian vault (local dev only) | ||
| CLAUDE.local.md | ||
| TASKS.md | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| # Gitleaks configuration — extends the built-in default ruleset. | ||
| # Add documented, non-secret placeholders to the allowlist below if a default | ||
| # rule ever flags one (none needed today). | ||
| [extend] | ||
| useDefault = true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| npx tsc --noEmit | ||
| npx lint-staged |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 24 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| { | ||
| "semi": false | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,106 @@ | ||
| # AGENTS.md | ||
|
|
||
| Project conventions for AI-assisted development on umm-actually. | ||
|
|
||
| ## What this project is | ||
|
|
||
| A Docker-based GitHub Action that reviews pull requests with an LLM via | ||
| OpenRouter and posts one consolidated PR review with inline findings. The | ||
| review is diff-anchored but not diff-bounded: changed code is traced into | ||
| its callers, and pre-existing bugs in traced code are valid findings. | ||
|
|
||
| ## Structure | ||
|
|
||
| ```text | ||
| action.yml # action metadata — inputs/outputs, runs.using: docker | ||
| Dockerfile # multi-stage: build (tsc) → slim runtime | ||
| fixtures/ # test fixtures (event payloads, sample diff, LLM responses) | ||
| src/ | ||
| main.ts # entrypoint — collects/validates inputs; pipeline + outputs land next PR | ||
| config.ts # action inputs → validated ActionConfig | ||
| logger.ts # structured JSON logger — levels, child contexts, lazy props | ||
| github/ # GitHub I/O: event payload → PrContext (octokit wrappers planned — next PR) | ||
| openrouter/ # (planned — next PR) OpenRouter I/O: @openrouter/sdk wrapper, structured-output ladder | ||
| diff/ # pure transforms over parse-diff output | ||
| context/ # (planned — next PR) workspace reads: conventions file, changed files, related files | ||
| review/ # pure review logic: finding schema, phases, prompt, selection, comment mapping | ||
| orchestrate.ts # (planned — next PR) the pipeline — fully testable with stub clients | ||
| ``` | ||
|
|
||
| ## Module layering | ||
|
|
||
| Pure leaves (`diff/`, `review/`) → I/O clients (`github/`, `openrouter/`, | ||
| `context/`) → composition (`orchestrate.ts`). Only `main.ts` touches real | ||
| `process.env` and constructs SDK clients. A pure module importing an I/O | ||
| module is a backwards dependency and a bug — enforced by a | ||
| `@typescript-eslint/no-restricted-imports` block in `eslint.config.ts` | ||
| (type-only imports are allowed; they're erased at compile time). | ||
|
|
||
| Use the official SDKs — don't hand-roll what `@actions/core`, | ||
| `@actions/github` (octokit), `@openrouter/sdk`, or `parse-diff` already do. | ||
| Before writing any parsing or validation helper, check the SDK's utility | ||
| surface first: `@actions/core` ships `getBooleanInput` (strict YAML 1.2 | ||
| booleans), which replaced a hand-rolled boolean-string Zod transform here. | ||
| Values an SDK util already parses arrive in `RawInputs` pre-parsed from the | ||
| collection boundary (main.ts) — "validation lives in config.ts" is about | ||
| where _our_ rules live, not a reason to reimplement the platform's. Zod | ||
| validates the things that are genuinely ours: LLM structured output and | ||
| action config. | ||
|
|
||
| Types are colocated with the code that uses them — no standalone types | ||
| files. Prefer SDK-provided types over redefining shapes. | ||
|
|
||
| ## Code style | ||
|
|
||
| - Functional over OOP. Arrow functions over `function` declarations. | ||
| - Factory/closure pattern for stateful modules; single namespace export for | ||
| cohesive service surfaces (`githubClient.submitReview(…)`). | ||
| - `type` over `interface`. TypeScript strict mode. `node:` prefix for built-ins. | ||
| - Explicit return types on exports. No `any`. No `as` or `!` — use runtime | ||
| guards or schema validation to narrow. | ||
| - Immutable by default; avoid `let`. A `reduce` must return a new accumulator | ||
| each step — never mutate-and-return. When mutation is genuinely needed, | ||
| add a comment justifying it. | ||
| - Explicit names over abbreviations, everywhere — params, callbacks, locals. | ||
| - Early returns over nested `if/else`. Extract multi-clause conditionals into | ||
| named booleans. Name booleans for the affirmative state. | ||
| - Block bodies `{}` for any multiline function response — expression bodies | ||
| only for one-liners. A multi-clause boolean spanning lines gets | ||
| `{ return (...) }`; guard chains get explicit early returns, never a | ||
| chained `||`/ternary expression body. | ||
| - Named params for functions with more than two args or adjacent same-typed | ||
| args. | ||
| - Data-layer and I/O functions take `(params, logger)` — logger is required. | ||
| - `process.env` is never read via raw property access. Action inputs | ||
| (`INPUT_*`) go through `@actions/core` `getInput`/`getBooleanInput`; the | ||
| event name and payload come from `@actions/github` `context` (it reads | ||
| `GITHUB_EVENT_NAME`/`GITHUB_EVENT_PATH` and parses the JSON — don't | ||
| hand-read those); remaining ambient environment (`GITHUB_WORKSPACE`, …) | ||
| goes through the `env-var` package | ||
| (`envVar.from(env).get("NAME").required().asString()`), with the env | ||
| record injectable for tests. | ||
| - Comments explain non-obvious domain context; never restate what a | ||
| self-documenting name already says. Regex constants get doc comments. | ||
| - Relative imports use explicit `.js` extensions (ESM runtime requirement). | ||
|
|
||
| ## Test conventions | ||
|
|
||
| - Tests read as a behavioral spec: one focused `it()` per behavior, named so | ||
| a failure identifies the regression without reading the body. | ||
| - `const` per test via factory helpers; `beforeEach` only when per-test | ||
| creation is genuinely impractical. | ||
| - Exact assertions over loose matchers; assert whole values over substrings | ||
| when output is deterministic. | ||
| - Two-bar rule: a test must (1) fail when the behavior breaks and (2) pass | ||
| only because the intended behavior occurred. Guard against silent no-op, | ||
| wrong-error, and early-return passes. | ||
| - Stub SDK clients are plain objects injected through factories — no HTTP | ||
| mocking libraries. | ||
| - Fixtures live in `fixtures/` and are shared across test files. | ||
|
|
||
| ## CI conventions | ||
|
|
||
| - Pin third-party actions to full commit SHAs with a version comment. | ||
| - Top-level `permissions: contents: read`; jobs escalate individually. | ||
| - Secrets scoped to steps, never job-level env. | ||
| - `persist-credentials: false` on checkout unless a push is required. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # Changelog | ||
|
|
||
| All notable changes to this project are documented in this file. It will be | ||
| updated by the release workflow (not yet built) from | ||
| [Conventional Commit](https://www.conventionalcommits.org) messages, and each | ||
| version will correspond to a GitHub Release. | ||
|
|
||
| ## [Unreleased] | ||
|
|
||
| Initial development — no releases yet. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # Contributor Covenant Code of Conduct | ||
|
|
||
| ## Our Pledge | ||
|
|
||
| We as members, contributors, and leaders pledge to make participation in our | ||
| community a harassment-free experience for everyone, regardless of background | ||
| or identity. | ||
|
|
||
| We pledge to act and interact in ways that contribute to an open, welcoming, | ||
| diverse, inclusive, and healthy community. | ||
|
|
||
| ## Our Standards | ||
|
|
||
| Examples of behavior that contributes to a positive environment for our | ||
| community include: | ||
|
|
||
| - Demonstrating empathy and kindness toward other people | ||
| - Being respectful of differing opinions, viewpoints, and experiences | ||
| - Giving and gracefully accepting constructive feedback | ||
| - Accepting responsibility and apologizing to those affected by our mistakes, | ||
| and learning from the experience | ||
| - Focusing on what is best not just for us as individuals, but for the overall | ||
| community | ||
|
|
||
| Examples of unacceptable behavior include: | ||
|
|
||
| - Trolling, insulting or derogatory comments, and personal or political attacks | ||
| - Public or private harassment | ||
| - Publishing others' private information without their explicit permission | ||
| - Other conduct which could reasonably be considered inappropriate in a | ||
| professional setting | ||
|
|
||
| ## Enforcement Responsibilities | ||
|
|
||
| Community leaders are responsible for clarifying and enforcing our standards of | ||
| acceptable behavior and will take appropriate and fair corrective action in | ||
| response to any behavior that they deem inappropriate, threatening, offensive, | ||
| or harmful. | ||
|
|
||
| ## Scope | ||
|
|
||
| This Code of Conduct applies within all community spaces, and also applies when | ||
| an individual is officially representing the community in public spaces. | ||
|
|
||
| ## Enforcement | ||
|
|
||
| Instances of abusive, harassing, or otherwise unacceptable behavior may be | ||
| reported by [opening a GitHub issue](https://github.com/aliasunder/umm-actually/issues/new?labels=conduct) | ||
| with the label **conduct**. If you need to report privately, you can use | ||
| [GitHub's private vulnerability reporting](https://github.com/aliasunder/umm-actually/security/advisories/new) | ||
| to reach the maintainer confidentially. | ||
|
|
||
| All complaints will be reviewed and investigated promptly and fairly. | ||
|
|
||
| ## Attribution | ||
|
|
||
| This Code of Conduct is adapted from the [Contributor Covenant][homepage], | ||
| version 2.1, available at | ||
| [https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. | ||
|
|
||
| [homepage]: https://www.contributor-covenant.org | ||
| [v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # Contributing | ||
|
|
||
| Thanks for your interest in umm-actually! It's a single-purpose GitHub Action, | ||
| so the contribution loop is simple. | ||
|
|
||
| ## What this is | ||
|
|
||
| A Docker-based GitHub Action that reviews pull requests with an LLM via | ||
| [OpenRouter](https://openrouter.ai) and posts one consolidated PR review with | ||
| inline findings. TypeScript inside, Node 24, strict mode throughout. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Node.js 24 (an `.nvmrc` is provided — `nvm use`) | ||
| - Docker (for the image build check) | ||
|
|
||
| ## Build & test | ||
|
|
||
| ```bash | ||
| npm ci | ||
| npm test # vitest — the full unit suite | ||
| npm run lint # eslint (typescript-eslint strict) | ||
| npm run prettier:check | ||
| npm run build # tsc | ||
| docker build . # what CI's smoke step runs | ||
| ``` | ||
|
|
||
| All of these must pass before pushing — CI runs the same steps. | ||
|
|
||
| ## Code conventions | ||
|
|
||
| Conventions live in [AGENTS.md](./AGENTS.md) — the single source of truth. Key | ||
| points: | ||
|
|
||
| - Functional style, immutable by default; no `any`, no `as`, no `!` — runtime | ||
| guards and Zod narrowing instead | ||
| - Pure modules (`diff/`, `review/`) never import I/O modules; only `main.ts` | ||
| touches `process.env` and constructs SDK clients | ||
| - Use the official SDKs (`@actions/core`, `@actions/github`, | ||
| `@openrouter/sdk`, `parse-diff`) — don't hand-roll what they already do | ||
| - Relative imports carry explicit `.js` extensions (ESM runtime requirement) | ||
| - Tests are behavioral specs: one focused `it()` per behavior, exact | ||
| assertions, `const` per test | ||
|
|
||
| ## Pull request process | ||
|
|
||
| 1. **Branch from `main`** with a descriptive prefix (`feat/`, `fix/`, `docs/`, | ||
| `ci/`, `chore/`) | ||
| 2. **Keep PRs focused** — one logical change per PR | ||
| 3. **Run the full local check** (test, lint, format, build, docker build) | ||
| before pushing | ||
| 4. **Use a Conventional Commit PR title** (`feat: …`, `fix: …`) — PRs are | ||
| squash-merged and the title becomes the changelog entry | ||
| 5. **Required checks must pass** — the CI workflow runs format, lint, tests, | ||
| build, and a Docker build smoke step | ||
|
|
||
| ## Issues | ||
|
|
||
| - **Bug reports** and **feature requests**: open a GitHub issue | ||
| - **Security issues**: see [SECURITY.md](./SECURITY.md) — report privately, | ||
| not as a public issue | ||
|
|
||
| ## Releases | ||
|
|
||
| Releases are cut by the maintainer. Each release will publish the action's | ||
| Docker image and update [`CHANGELOG.md`](./CHANGELOG.md) from Conventional | ||
| Commit messages via a release workflow (not yet built) — which is why PR | ||
| titles matter already: on a squash merge the title becomes the changelog | ||
| entry. | ||
|
|
||
| ## License | ||
|
|
||
| [MIT](./LICENSE). By contributing, you agree your contributions are licensed | ||
| under the MIT License. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| # Base image digest-pinned (same idiom as SHA-pinned actions in ci.yml) — the | ||
| # image builds on the consumer's runner, so a mutated tag would flow straight | ||
| # into the container that holds github_token and openrouter_api_key. | ||
| # Dependabot (docker ecosystem) keeps the digest current. | ||
|
|
||
| # Production dependencies — cached independently of source changes | ||
| FROM node:24-slim@sha256:cb4e8f7c443347358b7875e717c29e27bf9befc8f5a26cf18af3c3dec80e58c5 AS prod-deps | ||
| WORKDIR /app | ||
| COPY package.json package-lock.json ./ | ||
| # --ignore-scripts: the prepare script runs husky, a devDependency absent here | ||
| RUN npm ci --omit=dev --ignore-scripts | ||
|
|
||
| # Build stage: dev dependencies + TypeScript compile | ||
| FROM node:24-slim@sha256:cb4e8f7c443347358b7875e717c29e27bf9befc8f5a26cf18af3c3dec80e58c5 AS build | ||
| WORKDIR /app | ||
| COPY package.json package-lock.json ./ | ||
| # --ignore-scripts: skip husky's git-hook install — no .git in the image | ||
| RUN npm ci --ignore-scripts | ||
| COPY tsconfig.json ./ | ||
| COPY src ./src | ||
| RUN npx tsc | ||
|
|
||
| # Runtime stage: dist + production node_modules, nothing else | ||
| FROM node:24-slim@sha256:cb4e8f7c443347358b7875e717c29e27bf9befc8f5a26cf18af3c3dec80e58c5 | ||
| WORKDIR /app | ||
| COPY --from=prod-deps /app/node_modules ./node_modules | ||
| COPY --from=build /app/dist ./dist | ||
| COPY package.json ./ | ||
|
|
||
| ENTRYPOINT ["node", "/app/dist/src/main.js"] | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.