@dreki-gg/pi-code-reviewer 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -30,10 +30,28 @@ This creates:
|
|
|
30
30
|
/review --lens quality,ux # Multiple lenses
|
|
31
31
|
/review --base main # Diff against a branch
|
|
32
32
|
/review --staged # Only staged changes
|
|
33
|
+
/review --repo ../project-pr35 # Review a worktree / sibling repo
|
|
33
34
|
/review-lenses # List available lenses
|
|
34
35
|
```
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
### Reviewing worktrees & other repos (`--repo`)
|
|
38
|
+
|
|
39
|
+
By default `/review` runs git against the Pi session directory. Pass `--repo <dir>`
|
|
40
|
+
(alias `--cwd <dir>`) to target a different directory — a git worktree (sibling or
|
|
41
|
+
nested) or another repo — without leaving the session:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
/review --repo /path/to/worktree --base HEAD~1 --lens code-quality,architecture
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- The path is resolved relative to the session directory and validated as a git
|
|
48
|
+
work tree (relative paths like `../project-pr35` work).
|
|
49
|
+
- `.code-review.json`, lenses, and recorded rejections all resolve relative to the
|
|
50
|
+
override directory.
|
|
51
|
+
- The session directory itself is left unchanged — only git/config/lens resolution
|
|
52
|
+
is redirected.
|
|
53
|
+
|
|
54
|
+
The `code_review` tool exposes the same override via its optional `cwd` parameter.
|
|
37
55
|
|
|
38
56
|
## How the review runs (Bugbot-style pipeline)
|
|
39
57
|
|
|
@@ -7,6 +7,7 @@ import { Type } from 'typebox';
|
|
|
7
7
|
import { loadConfig, getLensDir } from '../config';
|
|
8
8
|
import { collectDiff, getChangedFiles } from '../diff';
|
|
9
9
|
import { discoverLenses, getLensContent } from '../lenses';
|
|
10
|
+
import { resolveRepoCwd } from '../resolve-cwd';
|
|
10
11
|
import { resolveModelPlan } from '../model-plan';
|
|
11
12
|
import { runPipeline } from '../passes';
|
|
12
13
|
import {
|
|
@@ -72,10 +73,29 @@ export function registerReviewTool(pi: ExtensionAPI) {
|
|
|
72
73
|
description: 'Review only staged changes instead of all working directory changes.',
|
|
73
74
|
}),
|
|
74
75
|
),
|
|
76
|
+
cwd: Type.Optional(
|
|
77
|
+
Type.String({
|
|
78
|
+
description:
|
|
79
|
+
'Override directory for git/config/lens resolution (e.g. a worktree or sibling repo). Resolved relative to the session directory and validated as a git work tree. The session directory is left unchanged.',
|
|
80
|
+
}),
|
|
81
|
+
),
|
|
75
82
|
}),
|
|
76
83
|
|
|
77
84
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
78
|
-
|
|
85
|
+
let cwd: string;
|
|
86
|
+
try {
|
|
87
|
+
cwd = await resolveRepoCwd(pi, ctx.cwd, params.cwd);
|
|
88
|
+
} catch (cause) {
|
|
89
|
+
return {
|
|
90
|
+
content: [
|
|
91
|
+
{
|
|
92
|
+
type: 'text',
|
|
93
|
+
text: `cwd "${params.cwd}" is not a git work tree (${cause instanceof Error ? cause.message : String(cause)}).`,
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
details: {},
|
|
97
|
+
};
|
|
98
|
+
}
|
|
79
99
|
const config = await loadConfig(cwd);
|
|
80
100
|
const lensDir = getLensDir(cwd, config);
|
|
81
101
|
const available = await discoverLenses(lensDir);
|
|
@@ -14,13 +14,29 @@ import {
|
|
|
14
14
|
runTools,
|
|
15
15
|
} from '../reviewer';
|
|
16
16
|
import { parseReviewArgs } from '../parse-args';
|
|
17
|
+
import { resolveRepoCwd } from '../resolve-cwd';
|
|
17
18
|
|
|
18
19
|
export function registerReviewCommand(pi: ExtensionAPI) {
|
|
19
20
|
pi.registerCommand('review', {
|
|
20
21
|
description:
|
|
21
|
-
'Run a multi-lens code review on working directory changes. Usage: /review [--lens name,...] [--base ref] [--staged]',
|
|
22
|
+
'Run a multi-lens code review on working directory changes. Usage: /review [--lens name,...] [--base ref] [--staged] [--repo dir]',
|
|
22
23
|
handler: async (args, ctx) => {
|
|
23
|
-
const
|
|
24
|
+
const parsed = parseReviewArgs(args ?? '');
|
|
25
|
+
|
|
26
|
+
// Override directory for git/config/lens resolution (worktrees, sibling
|
|
27
|
+
// repos). Resolved relative to the session CWD and validated; the session
|
|
28
|
+
// CWD itself is left unchanged.
|
|
29
|
+
let cwd: string;
|
|
30
|
+
try {
|
|
31
|
+
cwd = await resolveRepoCwd(pi, ctx.cwd, parsed.repo);
|
|
32
|
+
} catch (cause) {
|
|
33
|
+
ctx.ui.notify(
|
|
34
|
+
`--repo ${parsed.repo} is not a git work tree (${cause instanceof Error ? cause.message : String(cause)})`,
|
|
35
|
+
'error',
|
|
36
|
+
);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
24
40
|
const config = await loadConfig(cwd);
|
|
25
41
|
const lensDir = getLensDir(cwd, config);
|
|
26
42
|
const available = await discoverLenses(lensDir);
|
|
@@ -33,7 +49,6 @@ export function registerReviewCommand(pi: ExtensionAPI) {
|
|
|
33
49
|
return;
|
|
34
50
|
}
|
|
35
51
|
|
|
36
|
-
const parsed = parseReviewArgs(args ?? '');
|
|
37
52
|
const lensNames = resolveLensNames(parsed.lenses, config.defaultLenses, available, (msg) =>
|
|
38
53
|
ctx.ui.notify(msg, 'warning'),
|
|
39
54
|
);
|
|
@@ -3,10 +3,13 @@ export function parseReviewArgs(args: string): {
|
|
|
3
3
|
lenses: string[];
|
|
4
4
|
base?: string;
|
|
5
5
|
staged: boolean;
|
|
6
|
+
/** Override directory for git operations (--repo, alias --cwd). */
|
|
7
|
+
repo?: string;
|
|
6
8
|
} {
|
|
7
9
|
const lenses: string[] = [];
|
|
8
10
|
let base: string | undefined;
|
|
9
11
|
let staged = false;
|
|
12
|
+
let repo: string | undefined;
|
|
10
13
|
|
|
11
14
|
const parts = args.split(/\s+/).filter(Boolean);
|
|
12
15
|
|
|
@@ -18,8 +21,10 @@ export function parseReviewArgs(args: string): {
|
|
|
18
21
|
base = parts[++i];
|
|
19
22
|
} else if (part === '--staged') {
|
|
20
23
|
staged = true;
|
|
24
|
+
} else if ((part === '--repo' || part === '--cwd') && i + 1 < parts.length) {
|
|
25
|
+
repo = parts[++i];
|
|
21
26
|
}
|
|
22
27
|
}
|
|
23
28
|
|
|
24
|
-
return { lenses, base, staged };
|
|
29
|
+
return { lenses, base, staged, repo };
|
|
25
30
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the directory git operations should target.
|
|
3
|
+
*
|
|
4
|
+
* `/review` and the `code_review` tool default to the Pi session CWD, but a
|
|
5
|
+
* `--repo` / `--cwd` override (or tool `cwd` param) lets a worktree or sibling
|
|
6
|
+
* repo be reviewed from the same session. The override is resolved relative to
|
|
7
|
+
* the session CWD (so relative worktree paths like `../project-pr35` work) and
|
|
8
|
+
* validated with a single `git rev-parse --is-inside-work-tree` — that one call
|
|
9
|
+
* rejects both a missing directory and a non-git path. The session CWD itself is
|
|
10
|
+
* never mutated; this only redirects git/config/lens/rejections resolution.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ExtensionAPI } from '@earendil-works/pi-coding-agent';
|
|
14
|
+
import { Effect } from 'effect';
|
|
15
|
+
import { resolve } from 'node:path';
|
|
16
|
+
|
|
17
|
+
import { Executor, makeExecutorService } from './effects/exec';
|
|
18
|
+
import type { ExecError } from './errors';
|
|
19
|
+
|
|
20
|
+
const GIT_TIMEOUT_MS = 30_000;
|
|
21
|
+
|
|
22
|
+
export function resolveRepoCwdEffect(
|
|
23
|
+
sessionCwd: string,
|
|
24
|
+
override?: string,
|
|
25
|
+
): Effect.Effect<string, ExecError, Executor> {
|
|
26
|
+
return Effect.gen(function* () {
|
|
27
|
+
if (!override) return sessionCwd;
|
|
28
|
+
const resolved = resolve(sessionCwd, override);
|
|
29
|
+
const executor = yield* Executor;
|
|
30
|
+
// Throws ExecError when the path is missing or not a git work tree; the
|
|
31
|
+
// caller maps that to a user-facing notice rather than silently falling
|
|
32
|
+
// back to the session CWD.
|
|
33
|
+
yield* executor.exec('git', ['rev-parse', '--is-inside-work-tree'], {
|
|
34
|
+
cwd: resolved,
|
|
35
|
+
timeout: GIT_TIMEOUT_MS,
|
|
36
|
+
});
|
|
37
|
+
return resolved;
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Promise wrapper: resolve + validate the repo cwd with a live Executor from `pi`. */
|
|
42
|
+
export function resolveRepoCwd(
|
|
43
|
+
pi: Pick<ExtensionAPI, 'exec'>,
|
|
44
|
+
sessionCwd: string,
|
|
45
|
+
override?: string,
|
|
46
|
+
): Promise<string> {
|
|
47
|
+
return Effect.runPromise(
|
|
48
|
+
resolveRepoCwdEffect(sessionCwd, override).pipe(
|
|
49
|
+
Effect.provideService(Executor, makeExecutorService(pi)),
|
|
50
|
+
),
|
|
51
|
+
);
|
|
52
|
+
}
|