Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions apps/cli/src/commands/assign.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import {
assignFiles,
assignFilesToNewWorkspace,
listUnassigned,
} from "@array/core/commands/assign";
import { cyan, dim, formatSuccess, green, message } from "../utils/output";
import { requireArg, unwrap } from "../utils/run";

export async function assign(args: string[]): Promise<void> {
if (args.length === 0) {
message("Usage: arr assign <file...> <workspace>");
message(" arr assign <file...> --new <workspace-name>");
message("");
message("Examples:");
message(" arr assign config.json agent-a");
message(" arr assign file1.txt file2.txt agent-b");
message(' arr assign "src/**/*.ts" --new refactor');
return;
}

// Check for --new flag
const newIndex = args.indexOf("--new");
const nIndex = args.indexOf("-n");
const newFlagIndex = newIndex !== -1 ? newIndex : nIndex;

if (newFlagIndex !== -1) {
// Everything before --new is files, next arg is workspace name
const files = args.slice(0, newFlagIndex);
const newWorkspaceName = args[newFlagIndex + 1];

requireArg(files[0], "Usage: arr assign <file...> --new <workspace-name>");
requireArg(
newWorkspaceName,
"Usage: arr assign <file...> --new <workspace-name>",
);

const result = unwrap(
await assignFilesToNewWorkspace(files, newWorkspaceName),
);

if (result.files.length === 1) {
message(
formatSuccess(
`Assigned ${cyan(result.files[0])} to new workspace ${green(result.to)}`,
),
);
} else {
message(
formatSuccess(
`Assigned ${result.files.length} files to new workspace ${green(result.to)}`,
),
);
for (const file of result.files) {
message(` ${cyan(file)}`);
}
}
return;
}

// Regular assign to existing workspace
// Last arg is workspace, everything else is files
if (args.length < 2) {
message("Usage: arr assign <file...> <workspace>");
return;
}

const files = args.slice(0, -1);
const targetWorkspace = args[args.length - 1];

requireArg(files[0], "Usage: arr assign <file...> <workspace>");
requireArg(targetWorkspace, "Usage: arr assign <file...> <workspace>");

const result = unwrap(await assignFiles(files, targetWorkspace));

if (result.files.length === 1) {
message(
formatSuccess(`Assigned ${cyan(result.files[0])} to ${green(result.to)}`),
);
} else {
message(
formatSuccess(
`Assigned ${result.files.length} files to ${green(result.to)}`,
),
);
for (const file of result.files) {
message(` ${cyan(file)}`);
}
}
}

export async function unassigned(
subcommand: string,
_args: string[],
): Promise<void> {
switch (subcommand) {
case "list":
case "ls": {
const result = unwrap(await listUnassigned());

if (result.files.length === 0) {
message(dim("No unassigned files"));
return;
}

message(
`${result.files.length} unassigned file${result.files.length === 1 ? "" : "s"}:`,
);
message("");

for (const file of result.files) {
message(` ${cyan(file)}`);
}

message("");
message(`Assign files: ${dim("arr assign <file...> <workspace>")}`);
break;
}

default:
message("Usage: arr unassigned <list>");
message("");
message("Subcommands:");
message(" list List files in unassigned workspace");
}
}
72 changes: 72 additions & 0 deletions apps/cli/src/commands/daemon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import {
daemonRestart,
daemonStart,
daemonStatus,
daemonStop,
} from "@array/core/commands/daemon";
import { cyan, dim, formatSuccess, green, message, red } from "../utils/output";
import { unwrap } from "../utils/run";

export async function daemon(subcommand: string): Promise<void> {
switch (subcommand) {
case "start": {
unwrap(await daemonStart());
message(formatSuccess("Daemon started"));
message(dim(" Watching workspaces for file changes"));
message(dim(" Stop with: arr daemon stop"));
break;
}

case "stop": {
unwrap(await daemonStop());
message(formatSuccess("Daemon stopped"));
break;
}

case "restart": {
unwrap(await daemonRestart());
message(formatSuccess("Daemon restarted"));
break;
}

case "status": {
const status = unwrap(await daemonStatus());
if (status.running) {
message(
`${green("●")} Daemon is ${green("running")} (PID: ${status.pid})`,
);
if (status.repos.length > 0) {
message("");
message("Watching repos:");
for (const repo of status.repos) {
message(` ${dim(repo.path)}`);
for (const ws of repo.workspaces) {
message(` └─ ${ws}`);
}
}
} else {
message("");
message(
dim("No repos registered. Use arr preview to register workspaces."),
);
}
message("");
message(`Logs: ${dim(status.logPath)}`);
} else {
message(`${red("○")} Daemon is ${dim("not running")}`);
message("");
message(`Start with: ${cyan("arr daemon start")}`);
}
break;
}

default:
message("Usage: arr daemon <start|stop|restart|status>");
message("");
message("Subcommands:");
message(" start Start the workspace sync daemon");
message(" stop Stop the daemon");
message(" restart Restart the daemon");
message(" status Check if daemon is running");
}
}
25 changes: 21 additions & 4 deletions apps/cli/src/commands/exit.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,40 @@
import { focusNone, focusStatus } from "@array/core/commands/focus";
import type { CommandMeta } from "@array/core/commands/types";
import { exitToGit } from "@array/core/git/branch";
import { getTrunk } from "@array/core/jj";
import { unwrap as coreUnwrap } from "@array/core/result";
import { COMMANDS } from "../registry";
import { arr, blank, cyan, green, hint, message } from "../utils/output";
import {
blank,
cyan,
formatSuccess,
green,
hint,
message,
} from "../utils/output";

export const meta: CommandMeta = {
name: "exit",
description: "Exit to plain git on trunk (escape hatch if you need git)",
description: "Exit focus mode, or exit to plain git if not previewing",
context: "jj",
category: "management",
};

export async function exit(): Promise<void> {
// Check if we're in focus mode
const status = await focusStatus();
if (status.ok && status.value.isFocused) {
// Exit focus mode
coreUnwrap(await focusNone());
message(formatSuccess("Exited focus mode"));
return;
}

// Not in preview - exit to git
const trunk = await getTrunk();
const result = coreUnwrap(await exitToGit(process.cwd(), trunk));

message(`${green(">")} Switched to git branch ${cyan(result.trunk)}`);
blank();
hint("You're now using plain git. Your jj changes are still safe.");
hint(`To return to arr/jj, run: ${arr(COMMANDS.init)}`);
hint("Run any arr command to return to jj.");
}
Loading