Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .agents/agents/code-architecture-reviewer.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ color: blue
You are an expert software engineer specializing in code review and system architecture analysis. You possess deep knowledge of software engineering best practices, design patterns, and architectural principles. Your expertise spans the full technology stack of this project: TypeScript, React 19, Tailwind 4, shadcn/ui, Drizzle ORM on better-sqlite3, Commander, Vite, and Node.js (ESM, Node 20+).

You have comprehensive understanding of:
- The project's purpose: a local-only CLI (`stage-cli`) that serves a chapter-style code-review UI from `127.0.0.1`
- The project's purpose: a local-only CLI (`stagereview`) that serves a chapter-style code-review UI from `127.0.0.1`
- How the CLI, the local HTTP server, the Drizzle/SQLite layer, and the Vite/React web UI interact
- The established coding standards and patterns documented in `AGENTS.md`
- The testing strategy in `TESTING.md`
Expand Down Expand Up @@ -45,7 +45,7 @@ When reviewing code, you will:
4. **Assess Architectural Fit**:
- Evaluate which workspace package the code belongs in: `packages/cli` (CLI/server), `packages/web` (React UI), or `packages/types` (wire-format types shared between them)
- Check for proper separation of concerns: routes in `packages/cli/src/routes/`, DB code in `packages/cli/src/db/`, ingestion schemas in `packages/cli/src/schema.ts`
- Ensure module boundaries are respected — `packages/web` and `packages/cli` may depend on `@stage-cli/types`, but never on each other
- Ensure module boundaries are respected — `packages/web` and `packages/cli` may depend on `@stagereview/types`, but never on each other
- Validate that shared wire-format types live in `packages/types`, not duplicated across the CLI and web packages

5. **Review Specific Technologies**:
Expand Down
4 changes: 2 additions & 2 deletions .claude/agents/auto-error-resolver.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ pnpm typecheck # runs tsc --noEmit across every workspace package

This is a pnpm workspace with three packages, each with its own `tsconfig.json`:
- **CLI** (`packages/cli`): `pnpm --filter stagereview typecheck`
- **Types** (`packages/types`): `pnpm --filter @stage-cli/types typecheck`
- **Web UI** (`packages/web`): `pnpm --filter @stage-cli/web typecheck`
- **Types** (`packages/types`): `pnpm --filter @stagereview/types typecheck`
- **Web UI** (`packages/web`): `pnpm --filter @stagereview/web typecheck`

`pnpm typecheck` from the workspace root runs all three (`pnpm -r typecheck`). If a hook has saved a command at `~/.claude/tsc-cache/*/tsc-commands.txt`, prefer that.

Expand Down
14 changes: 7 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ Components land under `packages/web/src/components/ui/` per `packages/web/compon

## Architecture

**pnpm workspace.** Three packages with real boundaries — no path-alias indirection. The published unit is `packages/cli` (npm name `stagereview`, binary `stage-cli`); the rest are private workspace deps that get inlined at build time.
**pnpm workspace.** Three packages with real boundaries — no path-alias indirection. The published unit is `packages/cli` (npm name `stagereview`, binary `stagereview`); the rest are private workspace deps that get inlined at build time.

```
pnpm-workspace.yaml # packages: ["packages/*"]
packages/
cli/ # stagereview — published npm package
src/ # CLI + local HTTP server (Node, ESM)
index.ts # CLI entry (Commander)
show.ts # `stage-cli show <path>` implementation
show.ts # `stagereview show <path>` implementation
server.ts # Plain Node http server with regex-compiled routes
routes/ # API route handlers (one file per resource)
runs/ # Chapter run import + processing
Expand All @@ -52,12 +52,12 @@ packages/
__tests__/ # Vitest tests
drizzle/ # Generated SQL migrations + meta journal
drizzle.config.ts # Drizzle Kit config
tsdown.config.ts # CLI bundler config (inlines @stage-cli/types)
types/ # @stage-cli/types (private, TS-native)
tsdown.config.ts # CLI bundler config (inlines @stagereview/types)
types/ # @stagereview/types (private, TS-native)
src/chapters.ts # Wire-format chapter/key-change schemas + shared HunkReference/LineRef
src/view-state.ts # Wire-format view-state schema
src/index.ts # Barrel re-export
web/ # @stage-cli/web (private) — built into ../cli/web-dist
web/ # @stagereview/web (private) — built into ../cli/web-dist
src/components/ # UI components (shadcn/ui under components/ui/)
src/lib/ # Frontend utilities + tests
src/routes/ # SPA route components
Expand Down Expand Up @@ -87,7 +87,7 @@ Plain Node `http` server bound to `127.0.0.1`. Route patterns use `:name` placeh

### Shared Types (`packages/types/`)

Wire-format types shared between the CLI's HTTP routes and the SPA. The package exports `.ts` source directly (no compile step) — `tsdown` and `vite` resolve TypeScript natively. The CLI bundle inlines this package via `deps.alwaysBundle` in `tsdown.config.ts`, so the published tarball never has a runtime require for `@stage-cli/types`.
Wire-format types shared between the CLI's HTTP routes and the SPA. The package exports `.ts` source directly (no compile step) — `tsdown` and `vite` resolve TypeScript natively. The CLI bundle inlines this package via `deps.alwaysBundle` in `tsdown.config.ts`, so the published tarball never has a runtime require for `@stagereview/types`.

Building blocks like `HunkReference`, `LineRef`, and `DIFF_SIDE` live here; the strict ingestion schema (`ChaptersFileSchema`) stays in `packages/cli/src/schema.ts` and re-exports them.

Expand Down Expand Up @@ -120,7 +120,7 @@ A `pre-commit` hook (husky + lint-staged) runs `biome check --write` against sta

## Package Naming

The published npm package is `stagereview` (lives in `packages/cli`); the CLI binary is `stage-cli`. Internal workspace packages use the `@stage-cli/*` scope (`@stage-cli/types`, `@stage-cli/web`) — they are private and never published.
The published npm package is `stagereview` (lives in `packages/cli`); the CLI binary is also `stagereview`. Internal workspace packages use the `@stagereview/*` scope (`@stagereview/types`, `@stagereview/web`) — they are private and never published.

## Testing

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Chapter-style code review against your local git branch. Run it from your AI cod
npm install -g stagereview
```

This installs the `stage-cli` command.
This installs the `stagereview` command.

Then add the skill to your agent:

Expand All @@ -29,7 +29,7 @@ This breaks your branch's diff into reviewable "chapters".
To open a generated chapters file directly:

```bash
stage-cli show path/to/chapters.json
stagereview show path/to/chapters.json
```

## What it does
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"node": ">=20"
},
"scripts": {
"build": "pnpm --filter @stage-cli/web build && pnpm --filter stagereview build",
"dev:web": "pnpm --filter @stage-cli/web dev",
"build": "pnpm --filter @stagereview/web build && pnpm --filter stagereview build",
"dev:web": "pnpm --filter @stagereview/web dev",
"test": "vitest run",
"lint": "biome check .",
"lint:fix": "biome check --write .",
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"type": "module",
"main": "./dist/index.js",
"bin": {
"stage-cli": "./dist/index.js"
"stagereview": "./dist/index.js"
Comment thread
dastratakos marked this conversation as resolved.
},
"files": [
"dist",
Expand All @@ -53,7 +53,7 @@
"zod": "^4.3.6"
},
"devDependencies": {
"@stage-cli/types": "workspace:*",
"@stagereview/types": "workspace:*",
"@types/better-sqlite3": "^7.6.13",
"@types/node": "^25.6.0",
"drizzle-kit": "^0.31.10",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/db/schema/chapter-run.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Prologue } from "@stage-cli/types/prologue";
import type { Prologue } from "@stagereview/types/prologue";
import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
import { SCOPE_KIND, WORKING_TREE_REF } from "../../schema.js";
import { baseColumns } from "./columns.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { show } from "./show.js";

const program = new Command();

program.name("stage-cli").description("Chapter-style code review against your local git branch.");
program.name("stagereview").description("Chapter-style code review against your local git branch.");

program
.command("show")
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/routes/runs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Chapter, ChapterRun, KeyChange } from "@stage-cli/types/chapters";
import type { Chapter, ChapterRun, KeyChange } from "@stagereview/types/chapters";
import { asc, eq, inArray } from "drizzle-orm";
import type { StageDb } from "../db/client.js";
import { chapter, chapterRun, keyChange } from "../db/schema/index.js";
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/routes/view-state.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FileViewBodySchema } from "@stage-cli/types/view-state";
import { FileViewBodySchema } from "@stagereview/types/view-state";
import { and, eq, inArray } from "drizzle-orm";
import type { StageDb } from "../db/client.js";
import { LOCAL_USER_ID } from "../db/local-user.js";
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { hunkReferenceSchema, lineRefSchema } from "@stage-cli/types/chapters";
import { PrologueSchema } from "@stage-cli/types/prologue";
import { hunkReferenceSchema, lineRefSchema } from "@stagereview/types/chapters";
import { PrologueSchema } from "@stagereview/types/prologue";
import { z } from "zod";

export type { DiffSide, HunkReference, LineRef } from "@stage-cli/types/chapters";
export { DIFF_SIDE, hunkReferenceSchema, lineRefSchema } from "@stage-cli/types/chapters";
export type { DiffSide, HunkReference, LineRef } from "@stagereview/types/chapters";
export { DIFF_SIDE, hunkReferenceSchema, lineRefSchema } from "@stagereview/types/chapters";

export const SCOPE_KIND = {
COMMITTED: "committed",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ export default defineConfig({
outExtensions: () => ({ js: ".js" }),
// Inline workspace deps — they won't exist in the published package's
// node_modules, so the bundle has to carry their source.
deps: { alwaysBundle: [/^@stage-cli\//] },
deps: { alwaysBundle: [/^@stagereview\//] },
});
2 changes: 1 addition & 1 deletion packages/types/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@stage-cli/types",
"name": "@stagereview/types",
"version": "0.1.0",
"private": true,
"description": "Shared wire-format types for the stage-cli server and SPA.",
Expand Down
4 changes: 2 additions & 2 deletions packages/web/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "@stage-cli/web",
"name": "@stagereview/web",
"version": "0.1.0",
"private": true,
"description": "stage-cli SPA bundled into the published CLI's web-dist/.",
Expand All @@ -19,7 +19,7 @@
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tooltip": "^1.2.8",
"@stage-cli/types": "workspace:*",
"@stagereview/types": "workspace:*",
"@tanstack/react-query": "^5.100.7",
"@tanstack/react-router": "^1.169.1",
"class-variance-authority": "^0.7.1",
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function NoRunSelected() {
<h1 className="font-semibold text-lg">No run selected</h1>
<p className="mt-2 text-muted-foreground text-sm">
The URL is missing a <code>/runs/&lt;runId&gt;</code> path. Open the app via{" "}
<code>stage-cli show &lt;path&gt;</code>.
<code>stagereview show &lt;path&gt;</code>.
</p>
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/components/chapter/chapter-navigator.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Chapter } from "@stage-cli/types/chapters";
import type { Chapter } from "@stagereview/types/chapters";
import { Link } from "@tanstack/react-router";
import {
Check,
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/components/chapter/chapter-side-panel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Chapter } from "@stage-cli/types/chapters";
import type { Chapter } from "@stagereview/types/chapters";
import { useCallback, useEffect, useRef, useState } from "react";
import { LineCounts } from "@/components/shared/line-counts";
import { Markdown } from "@/components/ui/markdown";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/components/chapter/chapter-summary.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Chapter } from "@stage-cli/types/chapters";
import type { Chapter } from "@stagereview/types/chapters";
import { Checkbox } from "@/components/ui/checkbox";
import { Markdown } from "@/components/ui/markdown";
import { cn } from "@/lib/utils";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { HunkReference, LineRef } from "@stage-cli/types/chapters";
import type { HunkReference, LineRef } from "@stagereview/types/chapters";
import type { ReactNode } from "react";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { sortLineRefsByChapterOrder } from "@/lib/sort-line-refs";
Expand Down
4 changes: 2 additions & 2 deletions packages/web/src/components/prologue/prologue-section.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { FocusArea, FocusAreaSeverity, Prologue } from "@stage-cli/types/prologue";
import { FOCUS_AREA_SEVERITY } from "@stage-cli/types/prologue";
import type { FocusArea, FocusAreaSeverity, Prologue } from "@stagereview/types/prologue";
import { FOCUS_AREA_SEVERITY } from "@stagereview/types/prologue";
import { Link } from "@tanstack/react-router";
import { AlertTriangle } from "lucide-react";
import { cn } from "@/lib/utils";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/lib/__tests__/fixtures.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// __tests__/ as a sibling of the test files so the cross-file scope rule
// (per-file mock budget) stays obvious.

import type { ViewState } from "@stage-cli/types/view-state";
import type { ViewState } from "@stagereview/types/view-state";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import type { ReactElement, ReactNode } from "react";
import { vi } from "vitest";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @vitest-environment happy-dom

import type { ViewState } from "@stage-cli/types/view-state";
import type { ViewState } from "@stagereview/types/view-state";
import { act, renderHook, waitFor } from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { useViewState, viewStateQueryKey } from "../use-view-state";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/lib/chapter-context.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Chapter } from "@stage-cli/types/chapters";
import type { Chapter } from "@stagereview/types/chapters";
import { createContext, type ReactNode, use, useMemo } from "react";
import { filterFilesForChapter } from "./filter-files-for-chapter";
import { useChapters } from "./use-chapters";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/lib/filter-files-for-chapter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getSingularPatch } from "@pierre/diffs";
import type { HunkReference } from "@stage-cli/types/chapters";
import type { HunkReference } from "@stagereview/types/chapters";
import type { FileDiffEntry } from "./parse-diff";
import { fileDiffToPullRequestFile } from "./parse-diff";

Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/lib/format-chapter-markdown.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Chapter } from "@stage-cli/types/chapters";
import type { Chapter } from "@stagereview/types/chapters";
import { FILE_STATUS, type PullRequestFile } from "./diff-types";

interface ChapterFileInput {
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/lib/line-refs-by-file.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { LineRef } from "@stage-cli/types/chapters";
import type { LineRef } from "@stagereview/types/chapters";
import type { AnnotatedLineRef } from "./diff-types";

// `externalId` (not `id`) because that's what view-state, the side panel, and
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/lib/sort-line-refs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { HunkReference, LineRef } from "@stage-cli/types/chapters";
import type { HunkReference, LineRef } from "@stagereview/types/chapters";
import { DIFF_SIDE } from "./diff-types";

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/lib/use-chapters.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ChaptersResponse, ChaptersResponseSchema } from "@stage-cli/types/chapters";
import { type ChaptersResponse, ChaptersResponseSchema } from "@stagereview/types/chapters";
import { skipToken, useQuery } from "@tanstack/react-query";
import { jsonFetch } from "@/lib/use-view-state";

Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/lib/use-view-state.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ViewState, ViewStateSchema } from "@stage-cli/types/view-state";
import { type ViewState, ViewStateSchema } from "@stagereview/types/view-state";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { useMemo } from "react";

Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/routes/chapter-detail-page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Chapter, LineRef } from "@stage-cli/types/chapters";
import type { Chapter, LineRef } from "@stagereview/types/chapters";
import { Link, useNavigate } from "@tanstack/react-router";
import { useCallback, useMemo, useRef, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/routes/chapters-index-page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Chapter, HunkReference } from "@stage-cli/types/chapters";
import type { Chapter, HunkReference } from "@stagereview/types/chapters";
import { Link } from "@tanstack/react-router";
import { ChevronRight, Circle, CircleCheck } from "lucide-react";
import { useCallback, useEffect, useMemo, useState } from "react";
Expand Down
4 changes: 2 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions skills/stage-chapters/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ user-invocable: true

# stage-chapters

Generates a Stage chapter run for the current local git branch and opens it in a browser. The skill detects the base ref, computes the diff, generates chapters from it, writes a JSON file matching the `stage-cli` schema, and hands the file to `stage-cli show` to launch the SPA.
Generates a Stage chapter run for the current local git branch and opens it in a browser. The skill detects the base ref, computes the diff, generates chapters from it, writes a JSON file matching the `stagereview` schema, and hands the file to `stagereview show` to launch the SPA.

## Prerequisites

Run these checks before any other work. If either fails, stop with the error message — do not continue.

1. **`stage-cli` is installed.** Run `which stage-cli`. If it exits non-zero, instruct the user:
1. **`stagereview` is installed.** Run `which stagereview`. If it exits non-zero, instruct the user:

```
stage-cli is not installed. Run:
stagereview is not installed. Run:

npm install -g stagereview

Expand Down Expand Up @@ -244,11 +244,11 @@ Compute a unique temp path. The trailing `XXXXXX` (with no suffix after) is requ
TMPFILE=$(mktemp "${TMPDIR:-/tmp}/stage-chapters.XXXXXX")
```

`stage-cli show` reads JSON regardless of file extension, so the missing `.json` suffix is fine.
`stagereview show` reads JSON regardless of file extension, so the missing `.json` suffix is fine.

The `${TMPDIR:-/tmp}` fallback matters on macOS, where `os.tmpdir()` resolves to `/var/folders/...` but `$TMPDIR` is not always set in every shell. Avoid `date +%s%N` — the `%N` (nanoseconds) format is a GNU extension and on macOS BSD `date` it emits a literal `N`, breaking uniqueness.

Write a JSON file at `"$TMPFILE"` matching the shape below. The file must validate against `ChaptersFileSchema` in `packages/cli/src/schema.ts`; mismatched fields will be rejected by `stage-cli show`.
Write a JSON file at `"$TMPFILE"` matching the shape below. The file must validate against `ChaptersFileSchema` in `packages/cli/src/schema.ts`; mismatched fields will be rejected by `stagereview show`.

High-level shape:

Expand Down Expand Up @@ -344,12 +344,12 @@ Field rules:

## Step 6 — Display generated chapters

Hand the file to `stage-cli`:
Hand the file to `stagereview`:

```bash
stage-cli show "$TMPFILE"
stagereview show "$TMPFILE"
```

`stage-cli show` validates the JSON, inserts a new `chapter_run` plus chapters and key changes into the local SQLite database, boots a loopback HTTP server, and opens the browser to the new run. The command stays running and serves the SPA until the user kills it with Ctrl+C — invoke it as the final command in the workflow rather than expecting it to print a value and exit.
`stagereview show` validates the JSON, inserts a new `chapter_run` plus chapters and key changes into the local SQLite database, boots a loopback HTTP server, and opens the browser to the new run. The command stays running and serves the SPA until the user kills it with Ctrl+C — invoke it as the final command in the workflow rather than expecting it to print a value and exit.

Do not pass a `runId` and do not call a separate `stage-cli ingest`. `show <path>` does ingestion and serving in one step.
Do not pass a `runId` and do not call a separate `stagereview ingest`. `show <path>` does ingestion and serving in one step.
Loading