Fix PR URL arg parsing and tour file check

This commit is contained in:
Kamran Ahmed
2026-04-23 20:01:18 +01:00
parent 419e6259c2
commit 6a9f7b5795
5 changed files with 47 additions and 31 deletions
+2 -28
View File
@@ -7,7 +7,7 @@
"": {
"name": "@diffity",
"version": "0.3.0",
"license": "PolyForm-Shield-1.0.0",
"license": "MIT",
"workspaces": [
"packages/cli",
"packages/git",
@@ -596,7 +596,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -613,7 +612,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -630,7 +628,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -647,7 +644,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -664,7 +660,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -681,7 +676,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -698,7 +692,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -715,7 +708,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -732,7 +724,6 @@
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -749,7 +740,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -766,7 +756,6 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -783,7 +772,6 @@
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -800,7 +788,6 @@
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -817,7 +804,6 @@
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -834,7 +820,6 @@
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -851,7 +836,6 @@
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -868,7 +852,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -885,7 +868,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -902,7 +884,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -919,7 +900,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -936,7 +916,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -953,7 +932,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -970,7 +948,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -987,7 +964,6 @@
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1004,7 +980,6 @@
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -1021,7 +996,6 @@
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
@@ -8191,7 +8165,7 @@
"packages/cli": {
"name": "diffity",
"version": "0.9.5",
"license": "PolyForm-Shield-1.0.0",
"license": "MIT",
"dependencies": {
"better-sqlite3": "^12.8.0",
"commander": "^14.0.3",
+23 -1
View File
@@ -1,6 +1,8 @@
import { existsSync, statSync } from 'node:fs';
import { join, isAbsolute } from 'node:path';
import type { Command } from 'commander';
import pc from 'picocolors';
import { isGitRepo, getDiffFiles, resolveRef } from '@diffity/git';
import { isGitRepo, getDiffFiles, resolveRef, getRepoRoot } from '@diffity/git';
import { getCurrentSession } from './session.js';
import {
createThread,
@@ -28,6 +30,24 @@ function requireSession() {
return session;
}
function assertFileExists(filePath: string): void {
if (isAbsolute(filePath)) {
console.error(pc.red(`Error: --file must be relative to the repo root, got absolute path: ${filePath}`));
process.exit(1);
}
const abs = join(getRepoRoot(), filePath);
if (!existsSync(abs) || !statSync(abs).isFile()) {
console.error(pc.red(`Error: File not found at repo root: ${filePath}`));
console.error(pc.dim(` Checked: ${abs}`));
if (filePath.includes('..')) {
console.error(pc.dim(` Tip: consecutive dots suggest a shell variable expanded to empty.`));
console.error(pc.dim(` If your path contains "$" (e.g. "teams.$teamId.tsx"), single-quote the --file value`));
console.error(pc.dim(` or escape it: --file 'apps/routes/teams.$teamId.tsx'`));
}
process.exit(1);
}
}
function resolveThreadId(shortId: string, sessionId: string): Thread {
const thread = getThread(shortId);
if (!thread) {
@@ -120,6 +140,7 @@ Examples:
process.exit(1);
}
const session = requireSession();
assertFileExists(opts.file);
if (session.ref !== '__tree__') {
const diffFiles = getDiffFiles(session.ref);
if (!diffFiles.includes(opts.file)) {
@@ -245,6 +266,7 @@ Examples:
.option('--json', 'Output as JSON')
.action((opts) => {
requireSession();
assertFileExists(opts.file);
const endLine = opts.endLine ?? opts.line;
const step = addTourStep(opts.tour, opts.file, opts.line, endLine, opts.body, opts.annotation);
if (opts.json) {
+20
View File
@@ -80,6 +80,26 @@ range syntax (main..feature, main...feature) also work.`)
process.exit(1);
}
// When the first positional is a PR URL, `passThroughOptions()` above slurps any
// trailing flags (e.g. `diffity <url> --no-open`) into `refs` as positional args.
// Without this reparse, `refs.length === 1` would be false and the PR URL path
// would be skipped, producing a confusing "not a valid git reference" error.
if (refs.length > 1 && isGitHubPrUrl(refs[0])) {
const extras = refs.splice(1);
for (const arg of extras) {
switch (arg) {
case '--no-open': opts.open = false; break;
case '--quiet': opts.quiet = true; break;
case '--dark': opts.dark = true; break;
case '--unified': opts.unified = true; break;
case '--new': opts.new = true; break;
default:
console.error(pc.red(`Error: Unexpected argument after PR URL: ${arg}`));
process.exit(1);
}
}
}
if (refs.length === 1 && isGitHubPrUrl(refs[0])) {
const parsed = parseGitHubPrUrl(refs[0]);
if (!parsed) {
+1 -1
View File
@@ -36,7 +36,7 @@ When the argument is a **GitHub PR URL** (matching `github.com/owner/repo/pull/N
2. **If the argument is a GitHub PR URL**:
- Check `gh` is installed and authenticated: run `gh auth status`. If not authenticated, stop and ask the user to run `gh auth login`.
- Verify the current repo matches the PR's repo: run `gh repo view --json nameWithOwner -q .nameWithOwner` and confirm it matches the `owner/repo` in the URL. If it doesn't, stop and tell the user they need to be inside the PR's repository clone — diffity can't tour a PR for a repo you don't have checked out.
- Start diffity against the PR: run `{{binary}} <pr-url> --no-open` using the Bash tool with `run_in_background: true`. This checks out the PR's branch locally and starts a diff-scoped session. Wait 2 seconds, then run `{{binary}} list --json` to get the port. You do **not** also need a tree instance — the diff session supports `agent tour-*` commands.
- Start diffity against the PR: run `{{binary}} --no-open <pr-url>` using the Bash tool with `run_in_background: true`. **The `--no-open` flag must come BEFORE the URL** — commander's `passThroughOptions()` will slurp any flag that appears after the positional URL into the refs array, skipping PR handling and producing an "unknown ref" error. This command checks out the PR's branch locally and starts a diff-scoped session. Wait 2 seconds, then run `{{binary}} list --json` to get the port. You do **not** also need a tree instance — the diff session supports `agent tour-*` commands.
3. **Otherwise**, ensure a tree instance is running: run `{{binary}} list --json`.
- If no instance is running, start one: run `{{binary}} tree --no-open` using the Bash tool with `run_in_background: true`, wait 2 seconds, then run `{{binary}} list --json` to get the port.
+1 -1
View File
@@ -39,7 +39,7 @@ diffity list --json
2. **If the argument is a GitHub PR URL**:
- Check `gh` is installed and authenticated: run `gh auth status`. If not authenticated, stop and ask the user to run `gh auth login`.
- Verify the current repo matches the PR's repo: run `gh repo view --json nameWithOwner -q .nameWithOwner` and confirm it matches the `owner/repo` in the URL. If it doesn't, stop and tell the user they need to be inside the PR's repository clone — diffity can't tour a PR for a repo you don't have checked out.
- Start diffity against the PR: run `diffity <pr-url> --no-open` using the Bash tool with `run_in_background: true`. This checks out the PR's branch locally and starts a diff-scoped session. Wait 2 seconds, then run `diffity list --json` to get the port. You do **not** also need a tree instance — the diff session supports `agent tour-*` commands.
- Start diffity against the PR: run `diffity --no-open <pr-url>` using the Bash tool with `run_in_background: true`. **The `--no-open` flag must come BEFORE the URL** — commander's `passThroughOptions()` will slurp any flag that appears after the positional URL into the refs array, skipping PR handling and producing an "unknown ref" error. This command checks out the PR's branch locally and starts a diff-scoped session. Wait 2 seconds, then run `diffity list --json` to get the port. You do **not** also need a tree instance — the diff session supports `agent tour-*` commands.
3. **Otherwise**, ensure a tree instance is running: run `diffity list --json`.
- If no instance is running, start one: run `diffity tree --no-open` using the Bash tool with `run_in_background: true`, wait 2 seconds, then run `diffity list --json` to get the port.