Skip to content

Commit ab8a8e5

Browse files
authored
Walkthrough 2 (#52)
1 parent 28b1893 commit ab8a8e5

36 files changed

Lines changed: 4142 additions & 100 deletions

README.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,20 +151,25 @@ CODIFF_CLAUDE_PATH=/absolute/path/to/claude codiff --agent claude -w
151151
Claude Code rides your existing `claude` login (subscription or `ANTHROPIC_API_KEY`); run `claude`
152152
once and complete `/login` if you have not already.
153153

154-
To seed a walkthrough with the agent conversation that produced the change, install its skill from
155-
the application menu (`Install Codex Skill` or `Install Claude Code Skill`), then invoke it from the
156-
agent:
154+
To drive Codiff from your agent, install its skills from the application menu (`Install Codex Skill`
155+
or `Install Claude Code Skill`). Each action installs both bundled skills for that agent — `codiff`
156+
and `walkthrough` — so you only install once, and Codiff updates keep them current. Invoke them from
157+
the agent:
157158

158159
```text
159-
$codiff # Codex
160-
/codiff # Claude Code
160+
$codiff /codiff # open Codiff seeded with the current session
161+
$walkthrough /walkthrough # author a narrative walkthrough of the change
161162
```
162163

163-
The skill opens Codiff with `codiff -w --codex-session <id>` (or `--agent claude --claude-session
164+
`codiff` opens Codiff with `codiff -w --codex-session <id>` (or `--agent claude --claude-session
164165
<id>`). Codiff then generates its normal diff digest and runs the walkthrough prompt seeded with
165166
that session's conversation, so the walkthrough sees the original context without a lossy summary
166167
handoff.
167168

169+
`walkthrough` asks Codiff for the current authoring guide (`codiff --walkthrough-guide`), writes a
170+
narrative walkthrough JSON to a temporary file, and opens Codiff on it with `--walkthrough-file`.
171+
Because the guidance lives in Codiff, the installed skill stays a thin shim.
172+
168173
## Development
169174

170175
```bash

bin/arguments.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,17 @@ export const flagDefinitions = [
4343
name: 'walkthrough-context',
4444
type: 'string',
4545
},
46+
{
47+
argument: '<file>',
48+
description: 'Open a pre-authored narrative walkthrough JSON file.',
49+
name: 'walkthrough-file',
50+
type: 'string',
51+
},
52+
{
53+
description: 'Print the narrative walkthrough authoring guide and schema, then exit.',
54+
name: 'walkthrough-guide',
55+
type: 'boolean',
56+
},
4657
];
4758

4859
export const usageExamples = [
@@ -267,6 +278,8 @@ export const parseArguments = (args) => {
267278
let sourceCandidate = null;
268279
const walkthroughContextPath =
269280
typeof values['walkthrough-context'] === 'string' ? values['walkthrough-context'] : null;
281+
const walkthroughFilePath =
282+
typeof values['walkthrough-file'] === 'string' ? values['walkthrough-file'] : null;
270283

271284
for (let index = 0; index < positionals.length; index += 1) {
272285
const arg = positionals[index];
@@ -323,6 +336,8 @@ export const parseArguments = (args) => {
323336
requestedPath: resolve(requestedPath ?? process.cwd()),
324337
version: values.version === true,
325338
walkthrough: values.walkthrough === true,
339+
...(values['walkthrough-guide'] === true ? { walkthroughGuide: true } : {}),
326340
...(walkthroughContextPath ? { walkthroughContextPath: resolve(walkthroughContextPath) } : {}),
341+
...(walkthroughFilePath ? { walkthroughFilePath: resolve(walkthroughFilePath) } : {}),
327342
};
328343
};

bin/codiff-app

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ show_help() {
4040
printf ' %-22s%s%s%s\n' "--version, -v" "$gray" "Show version number and exit." "$reset"
4141
printf ' %-22s%s%s%s\n' "--walkthrough, -w" "$gray" "Start with an LLM-generated review walkthrough." "$reset"
4242
printf ' %-22s%s%s%s\n' "--walkthrough-context <file>" "$gray" "Seed a walkthrough with Codex conversation context JSON." "$reset"
43+
printf ' %-22s%s%s%s\n' "--walkthrough-guide" "$gray" "Print the narrative walkthrough authoring guide and schema, then exit." "$reset"
4344
printf '\n'
4445
printf '%s\n' "${blue_bold}Examples:${reset}"
4546
printf ' %-22s%s%s%s\n' "codiff" "$gray" "Review staged and unstaged changes." "$reset"
@@ -164,6 +165,18 @@ for arg in "$@"; do
164165
printf 'codiff v%s\n' "$(codiff_version)"
165166
exit 0
166167
;;
168+
--walkthrough-guide)
169+
# The guide is pure text assembled by the Node entry; run it with the
170+
# bundled Electron as Node so no separate node install is required.
171+
electron_exec="$(ls "$app_path/Contents/MacOS/" 2>/dev/null | head -n 1)"
172+
if [ -n "$electron_exec" ] && [ -f "$script_dir/codiff.js" ]; then
173+
ELECTRON_RUN_AS_NODE=1 "$app_path/Contents/MacOS/$electron_exec" \
174+
"$script_dir/codiff.js" --walkthrough-guide
175+
else
176+
cat "$script_dir/walkthrough-guide.md"
177+
fi
178+
exit 0
179+
;;
167180
--walkthrough|-w)
168181
walkthrough=1
169182
;;

bin/codiff.js

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
#!/usr/bin/env node
22

33
import { spawn } from 'node:child_process';
4-
import { existsSync } from 'node:fs';
4+
import { existsSync, readFileSync } from 'node:fs';
5+
import { createRequire } from 'node:module';
56
import { dirname, resolve } from 'node:path';
67
import { fileURLToPath } from 'node:url';
78
import electron from 'electron';
@@ -10,6 +11,17 @@ import { formatHelpText, parseArguments, resolvePullRequestUrl } from './argumen
1011

1112
const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
1213

14+
// Assemble the narrative walkthrough authoring guide: the prose, then the live
15+
// schema object serialized inline (single-sourced from the validator — no copy).
16+
const buildWalkthroughGuide = () => {
17+
const require = createRequire(import.meta.url);
18+
const { narrativeWalkthroughSchema } = require(
19+
resolve(root, 'electron/narrative-walkthrough.cjs'),
20+
);
21+
const guide = readFileSync(resolve(root, 'bin/walkthrough-guide.md'), 'utf8').trimEnd();
22+
return `${guide}\n\n\`\`\`json\n${JSON.stringify(narrativeWalkthroughSchema, null, 2)}\n\`\`\`\n`;
23+
};
24+
1325
const run = () => {
1426
const parsedArguments = parseArguments(process.argv.slice(2));
1527

@@ -23,6 +35,11 @@ const run = () => {
2335
return;
2436
}
2537

38+
if (parsedArguments.walkthroughGuide) {
39+
process.stdout.write(buildWalkthroughGuide());
40+
return;
41+
}
42+
2643
const {
2744
agentBackend,
2845
branchRef,
@@ -33,6 +50,7 @@ const run = () => {
3350
requestedPath,
3451
walkthrough,
3552
walkthroughContextPath,
53+
walkthroughFilePath,
3654
} = parsedArguments;
3755
let { pullRequestUrl } = parsedArguments;
3856

@@ -63,6 +81,7 @@ const run = () => {
6381
CODIFF_REPOSITORY_PATH: requestedPath,
6482
CODIFF_WALKTHROUGH: walkthrough ? '1' : '',
6583
CODIFF_WALKTHROUGH_CONTEXT: walkthroughContextPath ?? '',
84+
CODIFF_WALKTHROUGH_FILE: walkthroughFilePath ?? '',
6685
},
6786
stdio: 'ignore',
6887
});

bin/walkthrough-guide.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Narrative walkthrough — authoring guide
2+
3+
This is Codiff's guidance for authoring a **narrative walkthrough**: a story-shaped
4+
review of a change. Unlike Codiff's built-in walkthrough (which only orders files), a
5+
narrative walkthrough tells the _story_ of the change — ordered stops grouped into
6+
chapters, each pinned to a specific slice of the diff, with your narration and the prior
7+
conversation attached so Codiff can answer follow-up questions.
8+
9+
You — the agent — author a JSON document conforming to the schema printed at the end of
10+
this guide. Write it to a **temporary file outside the repository** (e.g. a unique path in
11+
the system temp directory) so it never clutters the working tree; pass that path to Codiff
12+
with `--walkthrough-file`. Set the document's `"$schema"` to
13+
`https://raw.githubusercontent.com/nkzw-tech/codiff/main/src/walkthrough/narrative-walkthrough.schema.json`
14+
for editor validation.
15+
16+
Default to the **staged** diff (`git diff --staged`). If the user named a target (a commit,
17+
`HEAD`, a PR, a path), use that. If nothing is staged, fall back to the working tree
18+
(`git diff`) and say so.
19+
20+
## The shape, and why it's shaped this way
21+
22+
The document separates **segments** (order-independent slices of the diff) from **orders**
23+
(reading views over them). The same segment can lead one order and sit in another order's
24+
"rest". This is what lets one changeset present as both _key-changes-first_ and
25+
_results-first_ without duplicating data.
26+
27+
- **`segments[]`** — the order-independent atoms. Each is one addressable slice of the diff:
28+
- `id` — stable within the document, e.g. `"s1"`.
29+
- `path`, `oldPath?`, `status` (`added` | `deleted` | `modified` | `renamed` | `untracked`).
30+
- `granularity``line` | `hunk` | `file`. Choose per stop: pull out a single `line` for a
31+
one-line bug, a `hunk` for a focused change, a whole `file` for a new test that reads as a
32+
spec.
33+
- `added`, `deleted` — line counts for the slice.
34+
- `anchor` — where it points into the live diff: `display` (e.g. `"src/App.tsx:311"`),
35+
optional `sectionId` (`<path>:staged` for staged diffs, `<path>:unstaged` for working-tree
36+
edits), `sectionKind`, `side` (`additions` | `deletions` | `both`), and `startLine`/`endLine`
37+
for `line`/`hunk` granularity (omit for `file`). Codiff repairs anchors against the live diff,
38+
so a missing or slightly-off `sectionId` is fine — it's pinned to a real section on load.
39+
- `title?`, `summary?` — default framing; an order's stop may override the title.
40+
- `comments?` — review comments to seed, anchored by `side` + `lineNumber` (+ optional range).
41+
42+
- **`orders[]`** — one or more reading views over the segments. Each has:
43+
- `id` (e.g. `"keys"`, `"results"`), `label`, `tagline`.
44+
- `phases[]` — named chapters, each with an `icon` (`bug` | `wrench` | `path` | `flask` |
45+
`beaker` | `doc` | `gear`) and a `blurb`.
46+
- `sequence[]` — the ordered stops. Each is `{ segmentId, phaseId, importance, prose, title? }`.
47+
- `rest[]` — files changed alongside the work but kept off the narrative path, each
48+
`{ segmentId, reason, note? }`. Group by `reason` (e.g. `Generated`, `Lockfile`, `Snapshot`,
49+
`Mechanical`).
50+
- `restLabel`, `restBlurb` — how "the rest" is presented.
51+
52+
- **`defaultOrder`** — the order id Codiff opens first. Choose `results` when the change has a
53+
strong test/snapshot/contract that previews it well; otherwise `keys`.
54+
55+
- **`context`** — a compact summary of the originating conversation (objective, decisions,
56+
risks, validation, a few key messages), so Codiff can answer questions without you. Use the
57+
`WalkthroughContext` shape (`version: 1`, `source: { type: "claude-session" | "codex-session", generatedAt }`).
58+
59+
## How to think about it
60+
61+
- Order stops by **review leverage and story**, not by file path. It is good for the arc to
62+
cross files and return to an earlier one (the bug, the fix, the refactor, the proof).
63+
- Write `prose` as the agent's voice explaining _why this matters now_. It may use inline
64+
markdown / code. Keep review comments separate (`comments[]`), not baked into prose.
65+
- Use `importance: "critical"` sparingly — only the genuine root cause or the defining test.
66+
- Push generated files, lockfiles, and snapshots into each order's `rest[]` with a `reason`. In
67+
a results-first order you may instead promote a signal-bearing one (a snapshot, a widened
68+
contract) to a lead stop.
69+
70+
- Provide **two orders** when both make sense (`keys` and `results`); one is fine for small
71+
changes. Do not invent bugs or produce review findings — describe what changed and why.
72+
- Every `segmentId` referenced by a `sequence` or `rest` entry must exist in `segments[]`, and
73+
every segment `path` should be a file in the diff. Codiff drops dangling references on load,
74+
but a clean document renders best.
75+
76+
## The schema
77+
78+
The document must conform to the following JSON schema:

claude/skills/walkthrough/SKILL.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
---
2+
name: walkthrough
3+
description: Author a narrative Codiff walkthrough JSON from the current change and open it in Codiff. Use when the user writes "/walkthrough", "make a walkthrough", "walk me through this change", or asks to review the staged work as a guided narrative in Codiff.
4+
metadata:
5+
short-description: Generate a narrative walkthrough and open Codiff
6+
---
7+
8+
# Walkthrough
9+
10+
Author a **narrative walkthrough** of the current change as a JSON document, then open
11+
Codiff pointed at it. You — the agent running this skill — write the JSON yourself, because
12+
you already hold the conversation that produced the change.
13+
14+
Codiff owns the format and the authoring guidance, so it stays current as Codiff updates.
15+
This skill is just the handoff: fetch the guide, author the document, open Codiff.
16+
17+
## Workflow
18+
19+
1. **Get the current guidance from Codiff.** It explains the data model, how to think about
20+
the document, and prints the JSON schema:
21+
22+
```bash
23+
node scripts/open-codiff.mjs --guide
24+
```
25+
26+
Follow what it says. (Everything below is just the handoff around it.)
27+
28+
2. **Pick the change.** Default to the **staged** diff (`git diff --staged`). If the user named
29+
a target (a commit, `HEAD`, a PR, a path), use that. If nothing is staged, fall back to the
30+
working tree (`git diff`) and say so.
31+
32+
3. **Author the JSON** per the guide and write it to a **temporary file outside the
33+
repository** (so it never clutters the working tree). Pick a unique absolute path in the
34+
system temp directory — e.g. `/tmp/codiff-walkthrough-<id>.json` (use `$TMPDIR` on macOS).
35+
Remember that path for the next step.
36+
37+
4. **Open Codiff** with that file:
38+
39+
```bash
40+
node scripts/open-codiff.mjs --file /tmp/codiff-walkthrough-<id>.json
41+
```
42+
43+
Forward an explicit target after the flag if the user gave one:
44+
45+
```bash
46+
node scripts/open-codiff.mjs --file /tmp/codiff-walkthrough-<id>.json HEAD
47+
node scripts/open-codiff.mjs --file /tmp/codiff-walkthrough-<id>.json /path/to/repository
48+
```
49+
50+
The launcher passes `CLAUDE_SESSION_ID` to Codiff with `--agent claude` so follow-up
51+
questions reuse this conversation. Codiff validates and repairs the document against the
52+
live diff, so anchors that drift are pinned to a real section rather than dropped.
53+
54+
Emit JSON only into the file. Do not summarize the conversation back to the user; the skill
55+
is a handoff into Codiff.

0 commit comments

Comments
 (0)