Skip to content

Commit 6284921

Browse files
committed
Use show for chapter imports
1 parent ecbe97d commit 6284921

7 files changed

Lines changed: 28 additions & 29 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ In your AI agent, run:
2626

2727
This breaks your branch's diff into reviewable "chapters".
2828

29+
To open a generated chapters file directly:
30+
31+
```bash
32+
stage-cli show path/to/chapters.json
33+
```
34+
2935
## What it does
3036

3137
- Splits a local git branch diff into logical review chapters
Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@ import fs from "node:fs/promises";
22
import os from "node:os";
33
import path from "node:path";
44
import { afterEach, beforeEach, describe, expect, it } from "vitest";
5-
import { ingest, insertChaptersFile } from "../commands/ingest.js";
65
import { closeDb, getDb } from "../db/client.js";
76
import { chapter, chapterRun, keyChange } from "../db/schema/index.js";
7+
import { importChaptersFile, insertChaptersFile } from "../runs/import-chapters.js";
88
import { makeFixture } from "./fixtures.js";
99

1010
let tmpDir: string;
1111
let dbPath: string;
1212

1313
beforeEach(async () => {
14-
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "stage-cli-ingest-"));
14+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "stage-cli-import-"));
1515
dbPath = path.join(tmpDir, "db.sqlite");
1616
closeDb();
1717
});
@@ -21,14 +21,14 @@ afterEach(async () => {
2121
await fs.rm(tmpDir, { recursive: true, force: true });
2222
});
2323

24-
describe("ingest", () => {
24+
describe("chapter import", () => {
2525
it("inserts a run, chapters, and key_changes atomically and returns the runId", async () => {
2626
const db = getDb({ dbPath });
2727
const fixture = makeFixture();
2828
const fixturePath = path.join(tmpDir, "chapters.json");
2929
await fs.writeFile(fixturePath, JSON.stringify(fixture));
3030

31-
const result = ingest(fixturePath, db);
31+
const result = importChaptersFile(fixturePath, db);
3232

3333
expect(result.runId).toMatch(/^[0-9a-f-]{36}$/);
3434
expect(result.chapterCount).toBe(1);
@@ -56,7 +56,7 @@ describe("ingest", () => {
5656
]);
5757
});
5858

59-
it("creates a new run on re-ingest of identical content (history preserved)", () => {
59+
it("creates a new run when importing identical content again (history preserved)", () => {
6060
const db = getDb({ dbPath });
6161
const fixture = makeFixture();
6262

@@ -68,7 +68,7 @@ describe("ingest", () => {
6868
expect(db.select().from(chapter).all()).toHaveLength(2);
6969
});
7070

71-
it("derives stable externalIds for key_changes across re-ingests of the same scope", () => {
71+
it("derives stable externalIds for key_changes across repeated imports of the same scope", () => {
7272
const db = getDb({ dbPath });
7373
const fixture = makeFixture();
7474

@@ -80,7 +80,7 @@ describe("ingest", () => {
8080
expect(all[0]?.externalId).toBe(all[1]?.externalId);
8181
});
8282

83-
it("derives stable chapter externalIds across re-ingests of the same scope", () => {
83+
it("derives stable chapter externalIds across repeated imports of the same scope", () => {
8484
const db = getDb({ dbPath });
8585
insertChaptersFile(db, makeFixture(), "/repo");
8686
insertChaptersFile(db, makeFixture(), "/repo");
@@ -165,7 +165,7 @@ describe("ingest", () => {
165165
}),
166166
);
167167

168-
expect(() => ingest(bad, db)).toThrow();
168+
expect(() => importChaptersFile(bad, db)).toThrow();
169169
expect(db.select().from(chapterRun).all()).toHaveLength(0);
170170
expect(db.select().from(chapter).all()).toHaveLength(0);
171171
});

src/__tests__/runs.routes.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import http from "node:http";
33
import os from "node:os";
44
import path from "node:path";
55
import { afterEach, beforeEach, describe, expect, it } from "vitest";
6-
import { insertChaptersFile } from "../commands/ingest.js";
76
import { closeDb, getDb } from "../db/client.js";
87
import { runRoutes } from "../routes/runs.js";
8+
import { insertChaptersFile } from "../runs/import-chapters.js";
99
import { LOOPBACK_HOST, type ServerHandle, startServer } from "../server.js";
1010
import { makeFixture } from "./fixtures.js";
1111

src/index.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#!/usr/bin/env node
22
import { Command } from "commander";
3-
import { ingest } from "./commands/ingest.js";
43
import { show } from "./show.js";
54

65
const program = new Command();
@@ -9,19 +8,10 @@ program.name("stage-cli").description("Chapter-style code review against your lo
98

109
program
1110
.command("show")
12-
.description("Serve the Stage CLI SPA in a local browser")
13-
.argument("[runId]", "Run ID to show (defaults to the latest run)")
14-
.action(async (runId?: string) => {
15-
await show(runId);
16-
});
17-
18-
program
19-
.command("ingest")
20-
.description("Load a chapters.json file into the local SQLite store")
21-
.argument("<path>", "Path to a chapters.json file produced by the agent")
22-
.action((jsonPath: string) => {
23-
const { runId } = ingest(jsonPath);
24-
process.stdout.write(`${runId}\n`);
11+
.description("Load a chapters.json file and open it in a local browser")
12+
.argument("[path]", "Path to a chapters.json file (defaults to the latest run)")
13+
.action(async (jsonPath?: string) => {
14+
await show(jsonPath);
2515
});
2616

2717
program.parseAsync(process.argv).catch((err) => {

src/routes/runs.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ export function runRoutes(db: StageDb): Route[] {
4646
.all();
4747

4848
const chapterIds = chapters.map((c) => c.id);
49-
// No `orderBy` — mirrors hosted stage's relations query, which surfaces key_changes
50-
// in natural query order. Both DBs return rows in insertion order in practice.
5149
const keyChanges =
5250
chapterIds.length > 0
5351
? db.select().from(keyChange).where(inArray(keyChange.chapterId, chapterIds)).all()
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@ import { getRepoRoot } from "../db/path.js";
66
import { chapter, chapterRun, keyChange } from "../db/schema/index.js";
77
import { type ChaptersFile, ChaptersFileSchema, SCOPE_KIND, type Scope } from "../schema.js";
88

9-
export interface IngestResult {
9+
export interface ImportChaptersResult {
1010
runId: string;
1111
chapterCount: number;
1212
keyChangeCount: number;
1313
}
1414

15-
export function ingest(jsonPath: string, db: StageDb = getDb()): IngestResult {
15+
export function importChaptersFile(
16+
jsonPath: string,
17+
db: StageDb = getDb(),
18+
): ImportChaptersResult {
1619
const absolute = path.resolve(jsonPath);
1720
const raw = readFileSync(absolute, "utf8");
1821
const parsed = JSON.parse(raw) as unknown;
@@ -24,7 +27,7 @@ export function insertChaptersFile(
2427
db: StageDb,
2528
file: ChaptersFile,
2629
repoRoot: string,
27-
): IngestResult {
30+
): ImportChaptersResult {
2831
return db.transaction((tx) => {
2932
const [runRow] = tx
3033
.insert(chapterRun)

src/show.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import open from "open";
33
import { closeDb, getDb } from "./db/client.js";
44
import { chapterRun } from "./db/schema/index.js";
55
import { runRoutes } from "./routes/runs.js";
6+
import { importChaptersFile } from "./runs/import-chapters.js";
67
import { LOOPBACK_HOST, startServer } from "./server.js";
78

8-
export async function show(runId?: string): Promise<void> {
9+
export async function show(jsonPath?: string): Promise<void> {
910
const db = getDb();
11+
const runId = jsonPath ? importChaptersFile(jsonPath, db).runId : undefined;
1012

1113
if (runId !== undefined) {
1214
const exists = db

0 commit comments

Comments
 (0)