Skip to content

Commit c4c5360

Browse files
committed
clean up cli
1 parent 7ef2ed6 commit c4c5360

File tree

23 files changed

+241
-377
lines changed

23 files changed

+241
-377
lines changed

packages/cli/src/cli.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export async function main(): Promise<void> {
249249
await status();
250250
break;
251251
case "create":
252-
await create(parsed.args.join(" "), parsed.flags);
252+
await create(parsed.args.join(" "));
253253
break;
254254
case "modify":
255255
await modify();
Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,6 @@
1-
import { changeLabel, JJ } from "@array/core";
2-
import { cyan, dim, formatError, formatSuccess } from "../utils/output";
1+
import { printNav } from "../utils/output";
2+
import { createJJ, unwrap } from "../utils/run";
33

44
export async function bottom(): Promise<void> {
5-
const jj = new JJ({ cwd: process.cwd() });
6-
7-
const result = await jj.navigateBottom();
8-
if (!result.ok) {
9-
console.error(formatError(result.error.message));
10-
process.exit(1);
11-
}
12-
13-
const label = changeLabel(result.value.description, result.value.changeId);
14-
const desc = result.value.description || dim("(no description)");
15-
16-
console.log(formatSuccess(`Jumped to bottom: ${cyan(label)}`));
17-
console.log(` ${desc}`);
5+
printNav("Jumped to bottom", unwrap(await createJJ().navigateBottom()));
186
}
Lines changed: 15 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,25 @@
1-
import { changeLabel, filterRootChanges, JJ } from "@array/core";
2-
import { cyan, dim, formatError, formatSuccess } from "../utils/output";
1+
import { changeLabel } from "@array/core";
2+
import { cyan, dim, formatSuccess } from "../utils/output";
3+
import { createJJ, findChange, requireArg, unwrap } from "../utils/run";
34

45
export async function checkout(id: string): Promise<void> {
5-
if (!id) {
6-
console.error(formatError("Usage: arr checkout <id>"));
7-
process.exit(1);
8-
}
9-
10-
const jj = new JJ({ cwd: process.cwd() });
6+
requireArg(id, "Usage: arr checkout <id>");
7+
const jj = createJJ();
118

12-
// Handle "main" or trunk checkout - creates new empty change on main
9+
// Handle trunk checkout - creates new empty change on main
1310
if (id === "main" || id === "master" || id === "trunk") {
14-
const result = await jj.new({ parents: [id === "trunk" ? "trunk()" : id] });
15-
if (!result.ok) {
16-
console.error(formatError(result.error.message));
17-
process.exit(1);
18-
}
11+
unwrap(await jj.new({ parents: [id === "trunk" ? "trunk()" : id] }));
1912
console.log(formatSuccess(`Switched to ${cyan(id)}`));
2013
return;
2114
}
2215

23-
// Search by: description substring OR bookmark/branch name substring (case-insensitive)
24-
const escaped = id.replace(/"/g, '\\"');
25-
const revset = `description(substring-i:"${escaped}") | bookmarks(substring-i:"${escaped}")`;
26-
27-
const listResult = await jj.list({ revset });
28-
if (!listResult.ok) {
29-
console.error(formatError(`No changes matching: ${id}`));
30-
process.exit(1);
31-
}
32-
33-
const matches = filterRootChanges(listResult.value);
34-
35-
if (matches.length === 0) {
36-
console.error(formatError(`No changes matching: ${id}`));
37-
process.exit(1);
38-
}
39-
40-
if (matches.length > 1) {
41-
console.log(`Multiple matches for "${id}":\n`);
42-
for (const cs of matches) {
43-
const label = changeLabel(cs.description, cs.changeId);
44-
console.log(
45-
` ${cyan(label)}: ${cs.description || dim("(no description)")}`,
46-
);
47-
}
48-
console.log(dim("\nUse a more specific query or the full change ID."));
49-
process.exit(1);
50-
}
51-
52-
const changeset = matches[0];
53-
54-
const result = await jj.edit(changeset.changeId);
55-
if (!result.ok) {
56-
console.error(formatError(result.error.message));
57-
process.exit(1);
58-
}
59-
60-
const label = changeLabel(changeset.description, changeset.changeId);
61-
const desc = changeset.description || dim("(no description)");
16+
const change = await findChange(jj, id, { includeBookmarks: true });
17+
unwrap(await jj.edit(change.changeId));
6218

63-
console.log(formatSuccess(`Switched to ${cyan(label)}: ${desc}`));
19+
const label = changeLabel(change.description, change.changeId);
20+
console.log(
21+
formatSuccess(
22+
`Switched to ${cyan(label)}: ${change.description || dim("(no description)")}`,
23+
),
24+
);
6425
}

packages/cli/src/commands/continue.ts

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { JJ } from "@array/core";
2-
import { cyan, dim, formatError, formatSuccess, yellow } from "../utils/output";
1+
import { cyan, dim, formatSuccess, yellow } from "../utils/output";
2+
import { createJJ, unwrap } from "../utils/run";
33

44
interface ContinueFlags {
55
resolve?: boolean;
@@ -8,37 +8,24 @@ interface ContinueFlags {
88
export async function continueCommand(
99
flags: ContinueFlags = {},
1010
): Promise<void> {
11-
const jj = new JJ({ cwd: process.cwd() });
12-
13-
const status = await jj.status();
14-
if (!status.ok) {
15-
console.error(formatError(status.error.message));
16-
process.exit(1);
17-
}
18-
19-
const { workingCopy, conflicts } = status.value;
11+
const jj = createJJ();
12+
const { workingCopy, conflicts } = unwrap(await jj.status());
2013

2114
if (!workingCopy.hasConflicts && conflicts.length === 0) {
2215
console.log(formatSuccess("No conflicts to resolve"));
2316
return;
2417
}
2518

2619
if (flags.resolve) {
27-
const result = await jj.raw(["resolve"]);
28-
if (!result.ok) {
29-
console.error(formatError(result.error.message));
30-
process.exit(1);
31-
}
20+
unwrap(await jj.raw(["resolve"]));
21+
const newStatus = unwrap(await jj.status());
3222

33-
const newStatus = await jj.status();
34-
if (newStatus.ok && !newStatus.value.workingCopy.hasConflicts) {
23+
if (!newStatus.workingCopy.hasConflicts) {
3524
console.log(formatSuccess("All conflicts resolved"));
3625
} else {
3726
console.log(yellow("Some conflicts remain"));
38-
if (newStatus.ok) {
39-
for (const conflict of newStatus.value.conflicts) {
40-
console.log(dim(` ${conflict.path}`));
41-
}
27+
for (const conflict of newStatus.conflicts) {
28+
console.log(dim(` ${conflict.path}`));
4229
}
4330
}
4431
return;

packages/cli/src/commands/create.ts

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,17 @@
1-
import { changeLabel, JJ } from "@array/core";
2-
import { cyan, dim, formatError, formatSuccess } from "../utils/output";
1+
import { changeLabel } from "@array/core";
2+
import { cyan, dim, formatSuccess } from "../utils/output";
3+
import { createJJ, requireArg, unwrap } from "../utils/run";
34
import { showTip } from "../utils/tips";
45

5-
export async function create(
6-
message: string,
7-
_flags: Record<string, string | boolean>,
8-
): Promise<void> {
9-
if (!message) {
10-
console.error(formatError("Usage: arr create <description>"));
11-
console.error(dim(" Creates a change with current file modifications"));
12-
process.exit(1);
13-
}
14-
15-
const jj = new JJ({ cwd: process.cwd() });
16-
17-
// Always use commit (describe current WC + create new empty WC)
18-
// This is the intuitive behavior: "create a change with my current work"
19-
const result = await jj.create({
6+
export async function create(message: string): Promise<void> {
7+
requireArg(
208
message,
21-
all: true,
22-
});
23-
24-
if (!result.ok) {
25-
console.error(formatError(result.error.message));
26-
process.exit(1);
27-
}
9+
"Usage: arr create <description>\n Creates a change with current file modifications",
10+
);
2811

29-
const label = changeLabel(message, result.value);
12+
const changeId = unwrap(await createJJ().create({ message, all: true }));
3013

31-
console.log(formatSuccess(`Created ${cyan(label)}`));
14+
console.log(formatSuccess(`Created ${cyan(changeLabel(message, changeId))}`));
3215
console.log(` ${message}`);
3316
console.log(dim(" Now on a new empty change. Keep working!"));
3417

Lines changed: 10 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,18 @@
1-
import { changeLabel, filterRootChanges, JJ } from "@array/core";
2-
import { cyan, dim, formatError, formatSuccess, yellow } from "../utils/output";
1+
import { changeLabel } from "@array/core";
2+
import { cyan, dim, formatSuccess, yellow } from "../utils/output";
3+
import { createJJ, findChange, requireArg, unwrap } from "../utils/run";
34

45
export async function deleteChange(id: string): Promise<void> {
5-
if (!id) {
6-
console.error(formatError("Usage: arr delete <id>"));
7-
process.exit(1);
8-
}
9-
10-
const jj = new JJ({ cwd: process.cwd() });
11-
12-
// Use jj revset for searching - description substring (case-insensitive)
13-
const escaped = id.replace(/"/g, '\\"');
14-
const revset = `description(substring-i:"${escaped}")`;
15-
16-
const listResult = await jj.list({ revset });
17-
if (!listResult.ok) {
18-
console.error(formatError(`No changes matching: ${id}`));
19-
process.exit(1);
20-
}
21-
22-
const matches = filterRootChanges(listResult.value);
6+
requireArg(id, "Usage: arr delete <id>");
7+
const jj = createJJ();
238

24-
if (matches.length === 0) {
25-
console.error(formatError(`No changes matching: ${id}`));
26-
process.exit(1);
27-
}
28-
29-
if (matches.length > 1) {
30-
console.log(`Multiple matches for "${id}":\n`);
31-
for (const cs of matches) {
32-
const label = changeLabel(cs.description, cs.changeId);
33-
console.log(
34-
` ${cyan(label)}: ${cs.description || dim("(no description)")}`,
35-
);
36-
}
37-
console.log(dim("\nUse a more specific query or the full change ID."));
38-
process.exit(1);
39-
}
40-
41-
const changeId = matches[0].changeId;
42-
const result = await jj.delete(changeId);
43-
if (!result.ok) {
44-
console.error(formatError(result.error.message));
45-
process.exit(1);
46-
}
9+
const change = await findChange(jj, id);
10+
const result = unwrap(await jj.delete(change.changeId));
4711

48-
const label = changeLabel(matches[0].description, changeId);
12+
const label = changeLabel(change.description, change.changeId);
4913
console.log(formatSuccess(`Deleted change ${cyan(label)}`));
5014

51-
if (result.value.movedTo) {
52-
console.log(dim(` Moved to parent: ${yellow(result.value.movedTo)}`));
15+
if (result.movedTo) {
16+
console.log(dim(` Moved to parent: ${yellow(result.movedTo)}`));
5317
}
5418
}

packages/cli/src/commands/down.ts

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,6 @@
1-
import { changeLabel, JJ } from "@array/core";
2-
import { cyan, formatError, formatSuccess } from "../utils/output";
1+
import { printNav } from "../utils/output";
2+
import { createJJ, unwrap } from "../utils/run";
33

44
export async function down(): Promise<void> {
5-
const jj = new JJ({ cwd: process.cwd() });
6-
7-
const result = await jj.navigateDown();
8-
if (!result.ok) {
9-
console.error(formatError(result.error.message));
10-
process.exit(1);
11-
}
12-
13-
const label = changeLabel(result.value.description, result.value.changeId);
14-
15-
console.log(formatSuccess(`Moved down to ${cyan(label)}`));
16-
if (result.value.description) {
17-
console.log(` ${result.value.description}`);
18-
}
5+
printNav("Moved down", unwrap(await createJJ().navigateDown()));
196
}

packages/cli/src/commands/log.ts

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
1-
import { datePrefixedLabel, formatRelativeTime, GitHub, JJ } from "@array/core";
2-
import {
3-
cyan,
4-
dim,
5-
formatError,
6-
green,
7-
magenta,
8-
red,
9-
yellow,
10-
} from "../utils/output";
1+
import { datePrefixedLabel, formatRelativeTime, GitHub } from "@array/core";
2+
import { cyan, dim, green, magenta, red, yellow } from "../utils/output";
3+
import { createJJ, unwrap } from "../utils/run";
114

125
interface PRInfo {
136
number: number;
@@ -16,16 +9,10 @@ interface PRInfo {
169
}
1710

1811
export async function log(): Promise<void> {
19-
const jj = new JJ({ cwd: process.cwd() });
12+
const jj = createJJ();
2013
const github = new GitHub(process.cwd());
2114

22-
const result = await jj.getLog();
23-
if (!result.ok) {
24-
console.error(formatError(result.error.message));
25-
process.exit(1);
26-
}
27-
28-
const { entries, trunk, isOnTrunk } = result.value;
15+
const { entries, trunk, isOnTrunk } = unwrap(await jj.getLog());
2916

3017
if (entries.length === 0 && isOnTrunk) {
3118
console.log(dim("No changes in stack"));

0 commit comments

Comments
 (0)