@solaqua/gji 0.12.4 → 0.13.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 +12 -0
- package/dist/application/worktree/catalog.d.ts +31 -0
- package/dist/application/worktree/catalog.js +88 -0
- package/dist/application/worktree/context-card.d.ts +14 -0
- package/dist/application/worktree/context-card.js +12 -0
- package/dist/{go-resolver.d.ts → application/worktree/go-resolution.d.ts} +12 -4
- package/dist/{go-resolver.js → application/worktree/go-resolution.js} +46 -44
- package/dist/application/worktree/read-models.d.ts +31 -0
- package/dist/application/worktree/sources.d.ts +7 -0
- package/dist/{worktree-sources.js → application/worktree/sources.js} +12 -24
- package/dist/{back.d.ts → cli/commands/back.d.ts} +3 -1
- package/dist/{back.js → cli/commands/back.js} +8 -6
- package/dist/{clean.d.ts → cli/commands/clean.d.ts} +4 -2
- package/dist/{clean.js → cli/commands/clean.js} +32 -24
- package/dist/{completion.js → cli/commands/completion.js} +2 -2
- package/dist/{config-command.d.ts → cli/commands/config-command.d.ts} +2 -0
- package/dist/{config-command.js → cli/commands/config-command.js} +7 -5
- package/dist/{doctor.d.ts → cli/commands/doctor.d.ts} +2 -0
- package/dist/{doctor.js → cli/commands/doctor.js} +23 -25
- package/dist/{done.d.ts → cli/commands/done.d.ts} +2 -0
- package/dist/{done.js → cli/commands/done.js} +26 -23
- package/dist/{go.d.ts → cli/commands/go.d.ts} +6 -1
- package/dist/{go.js → cli/commands/go.js} +57 -41
- package/dist/{history-command.d.ts → cli/commands/history-command.d.ts} +2 -0
- package/dist/{history-command.js → cli/commands/history-command.js} +3 -1
- package/dist/{hub.d.ts → cli/commands/hub.d.ts} +7 -3
- package/dist/{hub.js → cli/commands/hub.js} +23 -33
- package/dist/{init.d.ts → cli/commands/init.d.ts} +3 -1
- package/dist/{init.js → cli/commands/init.js} +37 -34
- package/dist/{ls.d.ts → cli/commands/ls.d.ts} +4 -2
- package/dist/{ls.js → cli/commands/ls.js} +8 -5
- package/dist/{new.d.ts → cli/commands/new.d.ts} +6 -2
- package/dist/{new.js → cli/commands/new.js} +237 -109
- package/dist/{open.d.ts → cli/commands/open.d.ts} +4 -2
- package/dist/{open.js → cli/commands/open.js} +19 -12
- package/dist/{pr-open.d.ts → cli/commands/pr-open.d.ts} +4 -2
- package/dist/{pr-open.js → cli/commands/pr-open.js} +20 -18
- package/dist/{pr.d.ts → cli/commands/pr.d.ts} +5 -3
- package/dist/{pr.js → cli/commands/pr.js} +34 -41
- package/dist/{remove.d.ts → cli/commands/remove.d.ts} +4 -2
- package/dist/{remove.js → cli/commands/remove.js} +23 -17
- package/dist/{root.d.ts → cli/commands/root.d.ts} +2 -0
- package/dist/{root.js → cli/commands/root.js} +4 -2
- package/dist/{run-hook.d.ts → cli/commands/run-hook.d.ts} +2 -0
- package/dist/{run-hook.js → cli/commands/run-hook.js} +7 -4
- package/dist/{status.d.ts → cli/commands/status.d.ts} +2 -0
- package/dist/{status.js → cli/commands/status.js} +10 -7
- package/dist/{sync-files-command.d.ts → cli/commands/sync-files-command.d.ts} +2 -0
- package/dist/{sync-files-command.js → cli/commands/sync-files-command.js} +11 -9
- package/dist/{sync.d.ts → cli/commands/sync.d.ts} +2 -0
- package/dist/{sync.js → cli/commands/sync.js} +7 -4
- package/dist/{task-command.d.ts → cli/commands/task-command.d.ts} +2 -0
- package/dist/{task-command.js → cli/commands/task-command.js} +4 -2
- package/dist/{undo.d.ts → cli/commands/undo.d.ts} +8 -6
- package/dist/{undo.js → cli/commands/undo.js} +44 -34
- package/dist/{warp.d.ts → cli/commands/warp.d.ts} +5 -2
- package/dist/{warp.js → cli/commands/warp.js} +20 -10
- package/dist/cli/dependencies.d.ts +114 -0
- package/dist/cli/dependencies.js +118 -0
- package/dist/{cli.d.ts → cli/program.d.ts} +4 -2
- package/dist/{cli.js → cli/program.js} +108 -124
- package/dist/domain/repository/context.d.ts +6 -0
- package/dist/domain/repository/context.js +1 -0
- package/dist/domain/repository/registry.d.ts +5 -0
- package/dist/domain/repository/registry.js +1 -0
- package/dist/domain/shared/concurrency.d.ts +1 -0
- package/dist/domain/shared/concurrency.js +15 -0
- package/dist/domain/worktree/matching.d.ts +4 -0
- package/dist/domain/worktree/matching.js +79 -0
- package/dist/domain/worktree/policy.d.ts +2 -0
- package/dist/domain/worktree/policy.js +58 -0
- package/dist/domain/worktree/pr-reference.d.ts +1 -0
- package/dist/domain/worktree/pr-reference.js +9 -0
- package/dist/{worktree-source.d.ts → domain/worktree/source.d.ts} +1 -1
- package/dist/domain/worktree/source.js +1 -0
- package/dist/domain/worktree/types.d.ts +5 -0
- package/dist/domain/worktree/types.js +1 -0
- package/dist/gji-bundle.mjs +7100 -6265
- package/dist/index.js +3 -3
- package/dist/{dependency-bootstrap.d.ts → infrastructure/bootstrap/dependency-bootstrap.d.ts} +3 -30
- package/dist/{dependency-bootstrap.js → infrastructure/bootstrap/dependency-bootstrap.js} +4 -4
- package/dist/infrastructure/git/health.d.ts +9 -0
- package/dist/infrastructure/git/health.js +44 -0
- package/dist/infrastructure/git/refs.d.ts +6 -0
- package/dist/{git.js → infrastructure/git/refs.js} +1 -51
- package/dist/infrastructure/git/runner.d.ts +2 -0
- package/dist/infrastructure/git/runner.js +16 -0
- package/dist/{editor.d.ts → infrastructure/integrations/editor.d.ts} +2 -6
- package/dist/{pull-requests.d.ts → infrastructure/integrations/pull-requests.d.ts} +3 -11
- package/dist/{config.d.ts → infrastructure/persistence/config.d.ts} +3 -17
- package/dist/{config.js → infrastructure/persistence/config.js} +2 -17
- package/dist/{history.d.ts → infrastructure/persistence/history.d.ts} +2 -5
- package/dist/{task.js → infrastructure/persistence/task.js} +1 -1
- package/dist/infrastructure/process/command-runner.d.ts +3 -0
- package/dist/{hooks.d.ts → infrastructure/process/hooks.d.ts} +2 -12
- package/dist/infrastructure/repository/adapters.d.ts +7 -0
- package/dist/infrastructure/repository/adapters.js +23 -0
- package/dist/infrastructure/repository/context.d.ts +3 -0
- package/dist/infrastructure/repository/context.js +20 -0
- package/dist/infrastructure/repository/refs.d.ts +3 -0
- package/dist/infrastructure/repository/refs.js +37 -0
- package/dist/{repo-registry.d.ts → infrastructure/repository/registry.d.ts} +1 -5
- package/dist/{repo-registry.js → infrastructure/repository/registry.js} +1 -1
- package/dist/infrastructure/repository/worktrees.d.ts +2 -0
- package/dist/infrastructure/repository/worktrees.js +48 -0
- package/dist/{uv-validation.d.ts → infrastructure/validation/uv.d.ts} +1 -1
- package/dist/{uv-validation.js → infrastructure/validation/uv.js} +1 -1
- package/dist/infrastructure/worktree/bootstrap.d.ts +3 -0
- package/dist/{worktree-bootstrap.js → infrastructure/worktree/bootstrap.js} +7 -10
- package/dist/infrastructure/worktree/info.d.ts +6 -0
- package/dist/infrastructure/worktree/info.js +61 -0
- package/dist/{worktree-management.d.ts → infrastructure/worktree/lifecycle.d.ts} +0 -6
- package/dist/{worktree-management.js → infrastructure/worktree/lifecycle.js} +1 -9
- package/dist/ports/bootstrap.d.ts +61 -0
- package/dist/ports/bootstrap.js +1 -0
- package/dist/ports/config.d.ts +21 -0
- package/dist/ports/config.js +17 -0
- package/dist/ports/editor.d.ts +6 -0
- package/dist/ports/editor.js +1 -0
- package/dist/ports/git.d.ts +8 -0
- package/dist/ports/git.js +1 -0
- package/dist/ports/history.d.ts +5 -0
- package/dist/ports/history.js +1 -0
- package/dist/ports/hooks.d.ts +12 -0
- package/dist/ports/hooks.js +1 -0
- package/dist/{command-runner.d.ts → ports/process.d.ts} +0 -1
- package/dist/ports/process.js +1 -0
- package/dist/ports/pull-requests.d.ts +11 -0
- package/dist/ports/pull-requests.js +1 -0
- package/dist/ports/repository.d.ts +17 -0
- package/dist/ports/repository.js +1 -0
- package/dist/{bootstrap-output.d.ts → presentation/bootstrap/output.d.ts} +1 -1
- package/dist/presentation/bootstrap/preview.d.ts +2 -0
- package/dist/{bootstrap-preview.js → presentation/bootstrap/preview.js} +0 -4
- package/dist/{shell-completion.js → presentation/shell/completion.js} +5 -4
- package/dist/presentation/terminal/context-card.d.ts +2 -0
- package/dist/{context-card.js → presentation/terminal/context-card.js} +3 -12
- package/dist/presentation/worktree/format.d.ts +4 -0
- package/dist/presentation/worktree/format.js +41 -0
- package/dist/{worktree-picker.d.ts → presentation/worktree/picker.d.ts} +7 -11
- package/dist/{worktree-picker.js → presentation/worktree/picker.js} +36 -191
- package/man/man1/gji-back.1 +1 -1
- package/man/man1/gji-clean.1 +1 -1
- package/man/man1/gji-completion.1 +1 -1
- package/man/man1/gji-config.1 +1 -1
- package/man/man1/gji-doctor.1 +1 -1
- package/man/man1/gji-done.1 +1 -1
- package/man/man1/gji-go.1 +1 -1
- package/man/man1/gji-history.1 +1 -1
- package/man/man1/gji-init.1 +1 -1
- package/man/man1/gji-ls.1 +1 -1
- package/man/man1/gji-new.1 +6 -3
- package/man/man1/gji-open.1 +1 -1
- package/man/man1/gji-pr.1 +1 -1
- package/man/man1/gji-remove.1 +1 -1
- package/man/man1/gji-root.1 +1 -1
- package/man/man1/gji-run-hook.1 +1 -1
- package/man/man1/gji-status.1 +1 -1
- package/man/man1/gji-sync-files.1 +1 -1
- package/man/man1/gji-sync.1 +1 -1
- package/man/man1/gji-task.1 +1 -1
- package/man/man1/gji-undo.1 +1 -1
- package/man/man1/gji-warp.1 +1 -1
- package/man/man1/gji.1 +2 -2
- package/package.json +1 -1
- package/dist/bootstrap-preview.d.ts +0 -9
- package/dist/context-card.d.ts +0 -1
- package/dist/format-bytes.d.ts +0 -1
- package/dist/format-bytes.js +0 -14
- package/dist/git.d.ts +0 -18
- package/dist/repo.d.ts +0 -20
- package/dist/repo.js +0 -161
- package/dist/worktree-bootstrap.d.ts +0 -24
- package/dist/worktree-info.d.ts +0 -37
- package/dist/worktree-info.js +0 -123
- package/dist/worktree-sources.d.ts +0 -4
- /package/dist/{worktree-source.js → application/worktree/read-models.js} +0 -0
- /package/dist/{completion.d.ts → cli/commands/completion.d.ts} +0 -0
- /package/dist/{headless.d.ts → cli/runtime/headless.d.ts} +0 -0
- /package/dist/{headless.js → cli/runtime/headless.js} +0 -0
- /package/dist/{paths.d.ts → domain/shared/paths.d.ts} +0 -0
- /package/dist/{paths.js → domain/shared/paths.js} +0 -0
- /package/dist/{file-sync.d.ts → infrastructure/filesystem/file-sync.d.ts} +0 -0
- /package/dist/{file-sync.js → infrastructure/filesystem/file-sync.js} +0 -0
- /package/dist/{fs-utils.d.ts → infrastructure/filesystem/fs-utils.d.ts} +0 -0
- /package/dist/{fs-utils.js → infrastructure/filesystem/fs-utils.js} +0 -0
- /package/dist/{safe-destination.d.ts → infrastructure/filesystem/safe-destination.d.ts} +0 -0
- /package/dist/{safe-destination.js → infrastructure/filesystem/safe-destination.js} +0 -0
- /package/dist/{browser.d.ts → infrastructure/integrations/browser.d.ts} +0 -0
- /package/dist/{browser.js → infrastructure/integrations/browser.js} +0 -0
- /package/dist/{editor.js → infrastructure/integrations/editor.js} +0 -0
- /package/dist/{pull-requests.js → infrastructure/integrations/pull-requests.js} +0 -0
- /package/dist/{history.js → infrastructure/persistence/history.js} +0 -0
- /package/dist/{slots.d.ts → infrastructure/persistence/slots.d.ts} +0 -0
- /package/dist/{slots.js → infrastructure/persistence/slots.js} +0 -0
- /package/dist/{task.d.ts → infrastructure/persistence/task.d.ts} +0 -0
- /package/dist/{command-runner.js → infrastructure/process/command-runner.js} +0 -0
- /package/dist/{hooks.js → infrastructure/process/hooks.js} +0 -0
- /package/dist/{bootstrap-output.js → presentation/bootstrap/output.js} +0 -0
- /package/dist/{conflict.d.ts → presentation/prompts/path-conflict.d.ts} +0 -0
- /package/dist/{conflict.js → presentation/prompts/path-conflict.js} +0 -0
- /package/dist/{shell-completion.d.ts → presentation/shell/completion.d.ts} +0 -0
- /package/dist/{shell-handoff.d.ts → presentation/shell/handoff.d.ts} +0 -0
- /package/dist/{shell-handoff.js → presentation/shell/handoff.js} +0 -0
- /package/dist/{shell-setup.d.ts → presentation/shell/setup.d.ts} +0 -0
- /package/dist/{shell-setup.js → presentation/shell/setup.js} +0 -0
- /package/dist/{shell.d.ts → presentation/shell/shell.d.ts} +0 -0
- /package/dist/{shell.js → presentation/shell/shell.js} +0 -0
- /package/dist/{navigation-output.d.ts → presentation/terminal/navigation.d.ts} +0 -0
- /package/dist/{navigation-output.js → presentation/terminal/navigation.js} +0 -0
- /package/dist/{terminal-text.d.ts → presentation/terminal/text.d.ts} +0 -0
- /package/dist/{terminal-text.js → presentation/terminal/text.js} +0 -0
- /package/dist/{worktree-prompts.d.ts → presentation/worktree/prompts.d.ts} +0 -0
- /package/dist/{worktree-prompts.js → presentation/worktree/prompts.js} +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type QueryWorktreePullRequests, type WorktreePromptEntry } from "
|
|
1
|
+
import type { EditorDefinition } from "../../ports/editor.js";
|
|
2
|
+
import { type QueryWorktreePullRequests, type WorktreePromptEntry } from "../../presentation/worktree/picker.js";
|
|
3
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
3
4
|
export type { EditorDefinition };
|
|
4
5
|
export interface OpenCommandOptions {
|
|
5
6
|
branch?: string;
|
|
@@ -7,6 +8,7 @@ export interface OpenCommandOptions {
|
|
|
7
8
|
editor?: string;
|
|
8
9
|
save?: boolean;
|
|
9
10
|
select?: boolean;
|
|
11
|
+
runtime?: CliRuntime<"history" | "config" | "repositoryContext" | "worktrees" | "integrations" | "configStore" | "worktreeCatalog">;
|
|
10
12
|
stderr: (chunk: string) => void;
|
|
11
13
|
stdout: (chunk: string) => void;
|
|
12
14
|
workspace?: boolean;
|
|
@@ -3,19 +3,24 @@ import { access, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { promisify } from "node:util";
|
|
5
5
|
import { isCancel, select } from "@clack/prompts";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { detectRepository, listWorktrees } from "./repo.js";
|
|
11
|
-
import { buildWorktreePromptEntries, promptForSingleWorktree, resolveWorktreeQuery, } from "./worktree-picker.js";
|
|
6
|
+
import { resolveWorktreeQuery } from "../../domain/worktree/matching.js";
|
|
7
|
+
import { buildWorktreePromptEntries, promptForSingleWorktree, } from "../../presentation/worktree/picker.js";
|
|
8
|
+
import { defaultCliDependencies, withPullRequestQueries, } from "../dependencies.js";
|
|
9
|
+
import { isHeadless } from "../runtime/headless.js";
|
|
12
10
|
const execFileAsync = promisify(execFile);
|
|
13
11
|
export function createOpenCommand(dependencies = {}) {
|
|
14
|
-
const detectEditors = dependencies.detectEditors ?? detectInstalledEditors;
|
|
15
12
|
const promptForEditor = dependencies.promptForEditor ?? defaultPromptForEditor;
|
|
16
13
|
const promptForWorktree = dependencies.promptForWorktree ?? defaultPromptForWorktree;
|
|
17
|
-
const spawnEditor = dependencies.spawnEditor ?? defaultSpawnEditor;
|
|
18
14
|
return async function runOpenCommand(options) {
|
|
15
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
16
|
+
const { recordWorktreeUsage } = runtime.history;
|
|
17
|
+
const { detectRepository } = runtime.repositoryContext;
|
|
18
|
+
const { listWorktrees } = runtime.worktrees;
|
|
19
|
+
const { defaultSpawnEditor, EDITORS } = runtime.integrations;
|
|
20
|
+
const detectEditors = dependencies.detectEditors ?? (() => detectInstalledEditors(EDITORS));
|
|
21
|
+
const { loadEffectiveConfig, resolveConfigString } = runtime.config;
|
|
22
|
+
const { updateGlobalConfigKey } = runtime.configStore;
|
|
23
|
+
const spawnEditorWithRuntime = dependencies.spawnEditor ?? defaultSpawnEditor;
|
|
19
24
|
if (options.select && options.branch !== undefined) {
|
|
20
25
|
options.stderr("gji open: --select cannot be used with a branch\n");
|
|
21
26
|
return 1;
|
|
@@ -57,7 +62,9 @@ export function createOpenCommand(dependencies = {}) {
|
|
|
57
62
|
repoRoot: repository.repoRoot,
|
|
58
63
|
repoName: repository.repoName,
|
|
59
64
|
worktree,
|
|
60
|
-
})), {
|
|
65
|
+
})), {
|
|
66
|
+
catalog: withPullRequestQueries(runtime, dependencies.queryPullRequests),
|
|
67
|
+
});
|
|
61
68
|
const chosen = await promptForWorktree(entries);
|
|
62
69
|
if (!chosen) {
|
|
63
70
|
options.stderr("Aborted\n");
|
|
@@ -118,7 +125,7 @@ export function createOpenCommand(dependencies = {}) {
|
|
|
118
125
|
}
|
|
119
126
|
args.push(openTarget);
|
|
120
127
|
try {
|
|
121
|
-
await
|
|
128
|
+
await spawnEditorWithRuntime(editorCli, args);
|
|
122
129
|
}
|
|
123
130
|
catch (error) {
|
|
124
131
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -132,8 +139,8 @@ export function createOpenCommand(dependencies = {}) {
|
|
|
132
139
|
};
|
|
133
140
|
}
|
|
134
141
|
export const runOpenCommand = createOpenCommand();
|
|
135
|
-
async function detectInstalledEditors() {
|
|
136
|
-
const results = await Promise.all(
|
|
142
|
+
async function detectInstalledEditors(editors) {
|
|
143
|
+
const results = await Promise.all(editors.map(async (editor) => ({
|
|
137
144
|
editor,
|
|
138
145
|
available: await isCommandAvailable(editor.cli),
|
|
139
146
|
})));
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type QueryRepositoryPullRequests, type QueryWorktreePullRequests, type WorktreePromptEntry } from "
|
|
1
|
+
import type { PullRequestInfo } from "../../ports/pull-requests.js";
|
|
2
|
+
import { type QueryRepositoryPullRequests, type QueryWorktreePullRequests, type WorktreePromptEntry } from "../../presentation/worktree/picker.js";
|
|
3
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
3
4
|
export interface PrOpenCommandOptions {
|
|
4
5
|
cwd: string;
|
|
5
6
|
stderr: (chunk: string) => void;
|
|
6
7
|
stdout: (chunk: string) => void;
|
|
7
8
|
select?: boolean;
|
|
8
9
|
target?: string;
|
|
10
|
+
runtime?: CliRuntime<"pullRequests" | "repositoryContext" | "worktrees" | "integrations" | "worktreeCatalog">;
|
|
9
11
|
}
|
|
10
12
|
export interface PrOpenCommandDependencies {
|
|
11
13
|
findOpenPullRequest: (repoRoot: string, number: number) => Promise<PullRequestInfo | null>;
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { isCancel, select } from "@clack/prompts";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { buildWorktreePromptEntries, promptForSingleWorktree, resolveWorktreeQuery, } from "./worktree-picker.js";
|
|
2
|
+
import { resolveWorktreeQuery } from "../../domain/worktree/matching.js";
|
|
3
|
+
import { buildWorktreePromptEntries, promptForSingleWorktree, } from "../../presentation/worktree/picker.js";
|
|
4
|
+
import { defaultCliDependencies, withPullRequestQueries, } from "../dependencies.js";
|
|
5
|
+
import { isHeadless } from "../runtime/headless.js";
|
|
7
6
|
export function createPrOpenCommand(dependencies = {}) {
|
|
8
|
-
const query = createPullRequestQuery();
|
|
9
|
-
const findOpenPullRequest = dependencies.findOpenPullRequest ?? query.findOpenPullRequest;
|
|
10
|
-
const openInBrowser = dependencies.openBrowser ?? openBrowser;
|
|
11
7
|
const promptForPullRequest = dependencies.promptForPullRequest ?? defaultPromptForPullRequest;
|
|
12
8
|
const promptForWorktree = dependencies.promptForWorktree ?? defaultPromptForWorktree;
|
|
13
|
-
const queryPullRequests = dependencies.queryPullRequests ?? query.listOpenPullRequests;
|
|
14
|
-
const queryRepositoryPullRequests = dependencies.queryRepositoryPullRequests ??
|
|
15
|
-
(dependencies.queryPullRequests === undefined
|
|
16
|
-
? query.listOpenPullRequestsForRepository
|
|
17
|
-
: undefined);
|
|
18
9
|
return async function runPrOpenCommand(options) {
|
|
10
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
11
|
+
const query = runtime.pullRequests;
|
|
12
|
+
const { detectRepository } = runtime.repositoryContext;
|
|
13
|
+
const { listWorktrees } = runtime.worktrees;
|
|
14
|
+
const openInBrowser = dependencies.openBrowser ?? runtime.integrations.openBrowser;
|
|
15
|
+
const findOpenPullRequest = dependencies.findOpenPullRequest ?? query.findOpenPullRequest;
|
|
16
|
+
const queryPullRequests = dependencies.queryPullRequests ?? query.listOpenPullRequests;
|
|
17
|
+
const queryRepositoryPullRequests = dependencies.queryRepositoryPullRequests ??
|
|
18
|
+
(dependencies.queryPullRequests === undefined
|
|
19
|
+
? query.listOpenPullRequestsForRepository
|
|
20
|
+
: undefined);
|
|
19
21
|
if (options.select && options.target !== undefined) {
|
|
20
22
|
options.stderr("gji pr open: --select cannot be used with a target\n");
|
|
21
23
|
return 1;
|
|
@@ -32,9 +34,9 @@ export function createPrOpenCommand(dependencies = {}) {
|
|
|
32
34
|
return 1;
|
|
33
35
|
if (options.target === undefined) {
|
|
34
36
|
if (!options.select) {
|
|
35
|
-
return openCurrentWorktree(repository.repoRoot, options, queryPullRequests, promptForPullRequest, openInBrowser);
|
|
37
|
+
return openCurrentWorktree(repository.repoRoot, options, listWorktrees, queryPullRequests, promptForPullRequest, openInBrowser);
|
|
36
38
|
}
|
|
37
|
-
return openFromWorktreeSelector(repository.repoRoot, repository.repoName, options, promptForWorktree, promptForPullRequest, queryPullRequests, queryRepositoryPullRequests, openInBrowser);
|
|
39
|
+
return openFromWorktreeSelector(repository.repoRoot, repository.repoName, options, listWorktrees, promptForWorktree, promptForPullRequest, queryPullRequests, queryRepositoryPullRequests, openInBrowser, runtime);
|
|
38
40
|
}
|
|
39
41
|
const number = parsePrNumberTarget(options.target);
|
|
40
42
|
if (number !== null) {
|
|
@@ -59,7 +61,7 @@ export function createPrOpenCommand(dependencies = {}) {
|
|
|
59
61
|
};
|
|
60
62
|
}
|
|
61
63
|
export const runPrOpenCommand = createPrOpenCommand();
|
|
62
|
-
async function openCurrentWorktree(repoRoot, options, queryPullRequests, promptForPullRequest, openInBrowser) {
|
|
64
|
+
async function openCurrentWorktree(repoRoot, options, listWorktrees, queryPullRequests, promptForPullRequest, openInBrowser) {
|
|
63
65
|
const worktrees = await listWorktrees(options.cwd).catch((error) => {
|
|
64
66
|
options.stderr(`gji pr open: unable to list worktrees: ${formatError(error)}\n`);
|
|
65
67
|
return null;
|
|
@@ -91,7 +93,7 @@ async function openCurrentWorktree(repoRoot, options, queryPullRequests, promptF
|
|
|
91
93
|
}
|
|
92
94
|
return openPullRequests(pullRequests, options, promptForPullRequest, openInBrowser);
|
|
93
95
|
}
|
|
94
|
-
async function openFromWorktreeSelector(repoRoot, repoName, options, promptForWorktree, promptForPullRequest, queryPullRequests, queryRepositoryPullRequests, openInBrowser) {
|
|
96
|
+
async function openFromWorktreeSelector(repoRoot, repoName, options, listWorktrees, promptForWorktree, promptForPullRequest, queryPullRequests, queryRepositoryPullRequests, openInBrowser, runtime) {
|
|
95
97
|
const worktrees = await listWorktrees(options.cwd).catch((error) => {
|
|
96
98
|
options.stderr(`gji pr open: unable to list worktrees: ${formatError(error)}\n`);
|
|
97
99
|
return null;
|
|
@@ -128,7 +130,7 @@ async function openFromWorktreeSelector(repoRoot, repoName, options, promptForWo
|
|
|
128
130
|
}
|
|
129
131
|
const { pullRequestsByBranch, sources } = connectedWorktrees;
|
|
130
132
|
const entries = await buildWorktreePromptEntries(sources, {
|
|
131
|
-
|
|
133
|
+
catalog: withPullRequestQueries(runtime, async (_root, branch) => pullRequestsByBranch.get(branch) ?? []),
|
|
132
134
|
});
|
|
133
135
|
const connectedEntries = entries.filter((entry) => (entry.pullRequests?.length ?? 0) > 0);
|
|
134
136
|
if (connectedEntries.length === 0) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type
|
|
1
|
+
import type { CommandRunner as BootstrapCommandRunner } from "../../ports/process.js";
|
|
2
|
+
import { type PathConflictChoice } from "../../presentation/prompts/path-conflict.js";
|
|
3
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
3
4
|
export type { PathConflictChoice };
|
|
4
5
|
export interface PrCommandOptions {
|
|
5
6
|
cwd: string;
|
|
@@ -10,11 +11,12 @@ export interface PrCommandOptions {
|
|
|
10
11
|
outputEnv?: string;
|
|
11
12
|
stderr: (chunk: string) => void;
|
|
12
13
|
stdout: (chunk: string) => void;
|
|
14
|
+
runtime?: PrRuntime;
|
|
13
15
|
}
|
|
16
|
+
type PrRuntime = CliRuntime<"bootstrap" | "config" | "git" | "history" | "repositoryContext">;
|
|
14
17
|
export interface PrCommandDependencies {
|
|
15
18
|
promptForPathConflict: (path: string) => Promise<PathConflictChoice>;
|
|
16
19
|
runCommand: BootstrapCommandRunner;
|
|
17
20
|
}
|
|
18
|
-
export declare function parsePrInput(input: string): string | null;
|
|
19
21
|
export declare function createPrCommand(dependencies?: Partial<PrCommandDependencies>): (options: PrCommandOptions) => Promise<number>;
|
|
20
22
|
export declare const runPrCommand: (options: PrCommandOptions) => Promise<number>;
|
|
@@ -1,34 +1,19 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
1
|
import { mkdir } from "node:fs/promises";
|
|
3
2
|
import { dirname } from "node:path";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { pathExists, promptForPathConflict, } from "
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { detectRepository, resolveWorktreePath } from "./repo.js";
|
|
14
|
-
import { writeShellOutput } from "./shell-handoff.js";
|
|
15
|
-
import { bootstrapWorktree } from "./worktree-bootstrap.js";
|
|
16
|
-
const execFileAsync = promisify(execFile);
|
|
3
|
+
import { resolveWorktreePath } from "../../domain/worktree/policy.js";
|
|
4
|
+
import { parsePrInput } from "../../domain/worktree/pr-reference.js";
|
|
5
|
+
import { createBootstrapReporter } from "../../presentation/bootstrap/output.js";
|
|
6
|
+
import { formatDependencyBootstrapPreview } from "../../presentation/bootstrap/preview.js";
|
|
7
|
+
import { pathExists, promptForPathConflict, } from "../../presentation/prompts/path-conflict.js";
|
|
8
|
+
import { writeShellOutput } from "../../presentation/shell/handoff.js";
|
|
9
|
+
import { createNavigationRepository, createNavigationTarget, } from "../../presentation/terminal/navigation.js";
|
|
10
|
+
import { defaultCliDependencies, } from "../dependencies.js";
|
|
11
|
+
import { isHeadless } from "../runtime/headless.js";
|
|
17
12
|
const PR_OUTPUT_FILE_ENV = "GJI_PR_OUTPUT_FILE";
|
|
18
|
-
export function parsePrInput(input) {
|
|
19
|
-
if (/^\d+$/.test(input))
|
|
20
|
-
return input;
|
|
21
|
-
const hashMatch = input.match(/^#(\d+)$/);
|
|
22
|
-
if (hashMatch)
|
|
23
|
-
return hashMatch[1];
|
|
24
|
-
const urlMatch = input.match(/\/(?:pull|pull-requests|merge_requests)\/(\d+)/);
|
|
25
|
-
if (urlMatch)
|
|
26
|
-
return urlMatch[1];
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
13
|
export function createPrCommand(dependencies = {}) {
|
|
30
14
|
const prompt = dependencies.promptForPathConflict ?? promptForPathConflict;
|
|
31
15
|
return async function runPrCommand(options) {
|
|
16
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
32
17
|
const prNumber = parsePrInput(options.number);
|
|
33
18
|
if (!prNumber) {
|
|
34
19
|
const message = `Invalid PR reference: ${options.number}`;
|
|
@@ -40,10 +25,10 @@ export function createPrCommand(dependencies = {}) {
|
|
|
40
25
|
}
|
|
41
26
|
return 1;
|
|
42
27
|
}
|
|
43
|
-
const repository = await detectRepository(options.cwd);
|
|
28
|
+
const repository = await runtime.repositoryContext.detectRepository(options.cwd);
|
|
44
29
|
let config;
|
|
45
30
|
try {
|
|
46
|
-
config = await loadEffectiveConfig(repository.repoRoot, undefined, options.json ? undefined : options.stderr);
|
|
31
|
+
config = await runtime.config.loadEffectiveConfig(repository.repoRoot, undefined, options.json ? undefined : options.stderr);
|
|
47
32
|
}
|
|
48
33
|
catch (error) {
|
|
49
34
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -57,7 +42,7 @@ export function createPrCommand(dependencies = {}) {
|
|
|
57
42
|
}
|
|
58
43
|
const branchName = `pr/${prNumber}`;
|
|
59
44
|
const remoteRef = `refs/remotes/origin/pull/${prNumber}/head`;
|
|
60
|
-
const rawBasePath = resolveConfigString(config, "worktreePath");
|
|
45
|
+
const rawBasePath = runtime.config.resolveConfigString(config, "worktreePath");
|
|
61
46
|
const configuredBasePath = rawBasePath?.startsWith("/") || rawBasePath?.startsWith("~")
|
|
62
47
|
? rawBasePath
|
|
63
48
|
: undefined;
|
|
@@ -76,16 +61,16 @@ export function createPrCommand(dependencies = {}) {
|
|
|
76
61
|
}
|
|
77
62
|
const choice = await prompt(worktreePath);
|
|
78
63
|
if (choice === "reuse") {
|
|
79
|
-
await recordWorktreeUsage(worktreePath, branchName);
|
|
64
|
+
await runtime.history.recordWorktreeUsage(worktreePath, branchName);
|
|
80
65
|
await writeOutput(worktreePath, options.stdout, options.outputEnv);
|
|
81
66
|
return 0;
|
|
82
67
|
}
|
|
83
68
|
options.stderr(`Aborted because target worktree path already exists: ${worktreePath}\n`);
|
|
84
69
|
return 1;
|
|
85
70
|
}
|
|
86
|
-
const dependencyMode =
|
|
71
|
+
const dependencyMode = runtime.bootstrap.resolveMode(config.dependencyBootstrap, options.noInstall);
|
|
87
72
|
const dryRunDependencyBootstrap = options.dryRun
|
|
88
|
-
? await
|
|
73
|
+
? await runtime.bootstrap.preview(dependencyMode, {
|
|
89
74
|
currentRoot: repository.currentRoot,
|
|
90
75
|
repoRoot: repository.repoRoot,
|
|
91
76
|
cargoBuildCommand: config.dependencyBuildCommand,
|
|
@@ -108,7 +93,7 @@ export function createPrCommand(dependencies = {}) {
|
|
|
108
93
|
return 0;
|
|
109
94
|
}
|
|
110
95
|
try {
|
|
111
|
-
await fetchPullRequestRef(repository.repoRoot, options.number, prNumber, remoteRef);
|
|
96
|
+
await fetchPullRequestRef(repository.repoRoot, options.number, prNumber, remoteRef, runtime);
|
|
112
97
|
}
|
|
113
98
|
catch {
|
|
114
99
|
const message = `Failed to fetch PR #${prNumber} from origin`;
|
|
@@ -122,12 +107,12 @@ export function createPrCommand(dependencies = {}) {
|
|
|
122
107
|
return 1;
|
|
123
108
|
}
|
|
124
109
|
await mkdir(dirname(worktreePath), { recursive: true });
|
|
125
|
-
const branchAlreadyExists = await localBranchExists(repository.repoRoot, branchName);
|
|
110
|
+
const branchAlreadyExists = await localBranchExists(repository.repoRoot, branchName, runtime);
|
|
126
111
|
const worktreeArgs = branchAlreadyExists
|
|
127
112
|
? ["worktree", "add", worktreePath, branchName]
|
|
128
113
|
: ["worktree", "add", "-b", branchName, worktreePath, remoteRef];
|
|
129
|
-
await
|
|
130
|
-
const bootstrap = await bootstrapWorktree({
|
|
114
|
+
await runtime.git.runGit(repository.repoRoot, worktreeArgs);
|
|
115
|
+
const bootstrap = await runtime.bootstrap.bootstrapWorktree({
|
|
131
116
|
branch: branchName,
|
|
132
117
|
config,
|
|
133
118
|
currentRoot: repository.currentRoot,
|
|
@@ -138,7 +123,6 @@ export function createPrCommand(dependencies = {}) {
|
|
|
138
123
|
runCommand: dependencies.runCommand,
|
|
139
124
|
commandStdout: options.json ? () => undefined : options.stdout,
|
|
140
125
|
commandStderr: options.json ? () => undefined : options.stderr,
|
|
141
|
-
json: options.json,
|
|
142
126
|
worktreePath,
|
|
143
127
|
});
|
|
144
128
|
if (!bootstrap.ready) {
|
|
@@ -165,15 +149,20 @@ export function createPrCommand(dependencies = {}) {
|
|
|
165
149
|
options.stdout(`${JSON.stringify(output, null, 2)}\n`);
|
|
166
150
|
}
|
|
167
151
|
else {
|
|
168
|
-
await recordWorktreeUsage(worktreePath, branchName);
|
|
152
|
+
await runtime.history.recordWorktreeUsage(worktreePath, branchName);
|
|
169
153
|
await writeOutput(worktreePath, options.stdout, options.outputEnv);
|
|
170
154
|
}
|
|
171
155
|
return 0;
|
|
172
156
|
};
|
|
173
157
|
}
|
|
174
|
-
async function localBranchExists(repoRoot, branchName) {
|
|
158
|
+
async function localBranchExists(repoRoot, branchName, runtime) {
|
|
175
159
|
try {
|
|
176
|
-
await
|
|
160
|
+
await runtime.git.runGit(repoRoot, [
|
|
161
|
+
"show-ref",
|
|
162
|
+
"--verify",
|
|
163
|
+
"--quiet",
|
|
164
|
+
`refs/heads/${branchName}`,
|
|
165
|
+
]);
|
|
177
166
|
return true;
|
|
178
167
|
}
|
|
179
168
|
catch {
|
|
@@ -181,10 +170,14 @@ async function localBranchExists(repoRoot, branchName) {
|
|
|
181
170
|
}
|
|
182
171
|
}
|
|
183
172
|
export const runPrCommand = createPrCommand();
|
|
184
|
-
async function fetchPullRequestRef(repoRoot, input, prNumber, remoteRef) {
|
|
173
|
+
async function fetchPullRequestRef(repoRoot, input, prNumber, remoteRef, runtime) {
|
|
185
174
|
for (const sourceRef of listPullRequestSourceRefs(input, prNumber)) {
|
|
186
175
|
try {
|
|
187
|
-
await
|
|
176
|
+
await runtime.git.runGit(repoRoot, [
|
|
177
|
+
"fetch",
|
|
178
|
+
"origin",
|
|
179
|
+
`${sourceRef}:${remoteRef}`,
|
|
180
|
+
]);
|
|
188
181
|
return;
|
|
189
182
|
}
|
|
190
183
|
catch {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import type { WorktreeEntry } from "
|
|
2
|
-
import { type WorktreePromptEntry } from "
|
|
1
|
+
import type { WorktreeEntry } from "../../domain/worktree/types.js";
|
|
2
|
+
import { type WorktreePromptEntry } from "../../presentation/worktree/picker.js";
|
|
3
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
3
4
|
export interface RemoveCommandOptions {
|
|
4
5
|
branch?: string;
|
|
5
6
|
cwd: string;
|
|
6
7
|
dryRun?: boolean;
|
|
7
8
|
force?: boolean;
|
|
8
9
|
json?: boolean;
|
|
10
|
+
runtime?: CliRuntime<"git" | "config" | "configStore" | "slots" | "hooks" | "repositoryContext" | "repositoryRegistry" | "worktrees" | "worktreeLifecycle" | "worktreeCatalog">;
|
|
9
11
|
stderr: (chunk: string) => void;
|
|
10
12
|
stdout: (chunk: string) => void;
|
|
11
13
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
2
|
import { confirm, isCancel } from "@clack/prompts";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
3
|
+
import { loadLinkedWorktrees } from "../../application/worktree/catalog.js";
|
|
4
|
+
import { writeShellOutput } from "../../presentation/shell/handoff.js";
|
|
5
|
+
import { buildWorktreePromptEntries, promptForSingleWorktree, } from "../../presentation/worktree/picker.js";
|
|
6
|
+
import { defaultConfirmForceDeleteBranch, defaultConfirmForceRemoveWorktree, } from "../../presentation/worktree/prompts.js";
|
|
7
|
+
import { defaultCliDependencies } from "../dependencies.js";
|
|
8
|
+
import { isHeadless } from "../runtime/headless.js";
|
|
8
9
|
import { finalizeUndoOperation, recordUndoOperation } from "./undo.js";
|
|
9
|
-
import { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeForceRemovalError, loadLinkedWorktrees, removeWorktree, } from "./worktree-management.js";
|
|
10
|
-
import { buildWorktreePromptEntries, promptForSingleWorktree, } from "./worktree-picker.js";
|
|
11
|
-
import { defaultConfirmForceDeleteBranch, defaultConfirmForceRemoveWorktree, } from "./worktree-prompts.js";
|
|
12
10
|
const REMOVE_OUTPUT_FILE_ENV = "GJI_REMOVE_OUTPUT_FILE";
|
|
13
11
|
export function createRemoveCommand(dependencies = {}) {
|
|
14
12
|
const promptForWorktree = dependencies.promptForWorktree ?? defaultPromptForWorktree;
|
|
@@ -17,7 +15,17 @@ export function createRemoveCommand(dependencies = {}) {
|
|
|
17
15
|
defaultConfirmForceRemoveWorktree;
|
|
18
16
|
const confirmForceDeleteBranch = dependencies.confirmForceDeleteBranch ?? defaultConfirmForceDeleteBranch;
|
|
19
17
|
return async function runRemoveCommand(options) {
|
|
20
|
-
const
|
|
18
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
19
|
+
const { loadEffectiveConfig } = runtime.config;
|
|
20
|
+
const { releaseWorktreeSlot } = runtime.slots;
|
|
21
|
+
const { extractHooks, runHook } = runtime.hooks;
|
|
22
|
+
const sourceDependencies = {
|
|
23
|
+
...runtime.repositoryContext,
|
|
24
|
+
...runtime.repositoryRegistry,
|
|
25
|
+
...runtime.worktrees,
|
|
26
|
+
};
|
|
27
|
+
const { deleteBranch, forceDeleteBranch, forceRemoveWorktree, isBranchUnmergedError, isWorktreeForceRemovalError, removeWorktree, } = runtime.worktreeLifecycle;
|
|
28
|
+
const { linkedWorktrees, repository } = await loadLinkedWorktrees(options.cwd, sourceDependencies);
|
|
21
29
|
if (linkedWorktrees.length === 0) {
|
|
22
30
|
emitError(options, "No linked worktrees to finish");
|
|
23
31
|
return 1;
|
|
@@ -36,7 +44,7 @@ export function createRemoveCommand(dependencies = {}) {
|
|
|
36
44
|
(await promptForWorktree(await buildWorktreePromptEntries(linkedWorktrees.map((worktree) => ({
|
|
37
45
|
repoName: repository.repoName,
|
|
38
46
|
worktree,
|
|
39
|
-
})))));
|
|
47
|
+
})), { catalog: runtime.worktreeCatalog })));
|
|
40
48
|
if (!selection) {
|
|
41
49
|
options.stderr("Aborted\n");
|
|
42
50
|
return 1;
|
|
@@ -78,9 +86,7 @@ export function createRemoveCommand(dependencies = {}) {
|
|
|
78
86
|
const hooks = extractHooks(config);
|
|
79
87
|
let journal;
|
|
80
88
|
try {
|
|
81
|
-
journal = await recordUndoOperation("remove", repository.repoRoot, [
|
|
82
|
-
worktree,
|
|
83
|
-
]);
|
|
89
|
+
journal = await recordUndoOperation("remove", repository.repoRoot, [worktree], undefined, runtime);
|
|
84
90
|
}
|
|
85
91
|
catch (error) {
|
|
86
92
|
emitError(options, `could not write undo journal; no worktree was removed: ${toMessage(error)}`);
|
|
@@ -100,12 +106,12 @@ export function createRemoveCommand(dependencies = {}) {
|
|
|
100
106
|
}
|
|
101
107
|
catch (error) {
|
|
102
108
|
if (!isWorktreeForceRemovalError(error)) {
|
|
103
|
-
await finalizeUndoOperation(journal, []);
|
|
109
|
+
await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
104
110
|
throw error;
|
|
105
111
|
}
|
|
106
112
|
if (!options.force &&
|
|
107
113
|
!(await confirmForceRemoveWorktree(worktree.path))) {
|
|
108
|
-
await finalizeUndoOperation(journal, []);
|
|
114
|
+
await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
109
115
|
options.stderr("Aborted\n");
|
|
110
116
|
return 1;
|
|
111
117
|
}
|
|
@@ -113,12 +119,12 @@ export function createRemoveCommand(dependencies = {}) {
|
|
|
113
119
|
await forceRemoveWorktree(repository.repoRoot, worktree.path);
|
|
114
120
|
}
|
|
115
121
|
catch (forceError) {
|
|
116
|
-
await finalizeUndoOperation(journal, []);
|
|
122
|
+
await finalizeUndoOperation(journal, [], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
117
123
|
emitError(options, `Failed to remove worktree at ${worktree.path}: ${toMessage(forceError)}`);
|
|
118
124
|
return 1;
|
|
119
125
|
}
|
|
120
126
|
}
|
|
121
|
-
await finalizeUndoOperation(journal, [worktree]);
|
|
127
|
+
await finalizeUndoOperation(journal, [worktree], undefined, runtime.configStore.GLOBAL_CONFIG_DIRECTORY);
|
|
122
128
|
await releaseWorktreeSlot(worktree.path);
|
|
123
129
|
if (worktree.branch) {
|
|
124
130
|
try {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
1
2
|
export interface RootCommandOptions {
|
|
2
3
|
cwd: string;
|
|
3
4
|
print?: boolean;
|
|
5
|
+
runtime?: CliRuntime<"repositoryContext">;
|
|
4
6
|
stdout: (chunk: string) => void;
|
|
5
7
|
}
|
|
6
8
|
export declare function runRootCommand(options: RootCommandOptions): Promise<number>;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { writeShellOutput } from "../../presentation/shell/handoff.js";
|
|
2
|
+
import { defaultCliDependencies } from "../dependencies.js";
|
|
3
3
|
const ROOT_OUTPUT_FILE_ENV = "GJI_ROOT_OUTPUT_FILE";
|
|
4
4
|
export async function runRootCommand(options) {
|
|
5
|
+
const { detectRepository } = (options.runtime ?? defaultCliDependencies)
|
|
6
|
+
.repositoryContext;
|
|
5
7
|
const repository = await detectRepository(options.cwd);
|
|
6
8
|
if (!options.print && process.env[ROOT_OUTPUT_FILE_ENV]) {
|
|
7
9
|
await writeShellOutput(ROOT_OUTPUT_FILE_ENV, repository.repoRoot, options.stdout);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
1
2
|
export interface RunHookCommandOptions {
|
|
2
3
|
cwd: string;
|
|
3
4
|
hook: string;
|
|
5
|
+
runtime?: CliRuntime<"config" | "slots" | "hooks" | "repositoryContext" | "worktrees">;
|
|
4
6
|
stderr: (chunk: string) => void;
|
|
5
7
|
}
|
|
6
8
|
export declare function runHookCommand(options: RunHookCommandOptions): Promise<number>;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { extractHooks, runHook } from "./hooks.js";
|
|
3
|
-
import { detectRepository, listWorktrees } from "./repo.js";
|
|
4
|
-
import { getWorktreeSlot } from "./slots.js";
|
|
1
|
+
import { defaultCliDependencies } from "../dependencies.js";
|
|
5
2
|
const VALID_HOOKS = [
|
|
6
3
|
"after-create",
|
|
7
4
|
"after-enter",
|
|
@@ -16,6 +13,12 @@ function isValidHook(hook) {
|
|
|
16
13
|
return VALID_HOOKS.includes(hook);
|
|
17
14
|
}
|
|
18
15
|
export async function runHookCommand(options) {
|
|
16
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
17
|
+
const { loadEffectiveConfig } = runtime.config;
|
|
18
|
+
const { getWorktreeSlot } = runtime.slots;
|
|
19
|
+
const { extractHooks, runHook } = runtime.hooks;
|
|
20
|
+
const { detectRepository } = runtime.repositoryContext;
|
|
21
|
+
const { listWorktrees } = runtime.worktrees;
|
|
19
22
|
const normalized = CAMEL_ALIASES[options.hook] ?? options.hook;
|
|
20
23
|
if (!isValidHook(normalized)) {
|
|
21
24
|
options.stderr(`gji run-hook: unknown hook '${options.hook}'. Valid hooks: ${VALID_HOOKS.join(", ")}\n`);
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
1
2
|
export interface StatusCommandOptions {
|
|
2
3
|
cwd: string;
|
|
3
4
|
json?: boolean;
|
|
5
|
+
runtime?: CliRuntime<"repositoryContext" | "worktrees" | "slots" | "tasks" | "git">;
|
|
4
6
|
stdout: (chunk: string) => void;
|
|
5
7
|
}
|
|
6
8
|
interface WorktreeStatusRow {
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { detectRepository, listWorktrees } from "./repo.js";
|
|
4
|
-
import { getWorktreeSlot } from "./slots.js";
|
|
5
|
-
import { readTask } from "./task.js";
|
|
1
|
+
import { comparePaths } from "../../domain/shared/paths.js";
|
|
2
|
+
import { defaultCliDependencies, } from "../dependencies.js";
|
|
6
3
|
export async function runStatusCommand(options) {
|
|
4
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
5
|
+
const { detectRepository } = runtime.repositoryContext;
|
|
6
|
+
const { listWorktrees } = runtime.worktrees;
|
|
7
|
+
const { getWorktreeSlot } = runtime.slots;
|
|
8
|
+
const { readTask } = runtime.tasks;
|
|
9
|
+
const { readWorktreeHealth } = runtime.git;
|
|
7
10
|
const repository = await detectRepository(options.cwd);
|
|
8
11
|
const worktrees = sortWorktreesByPath(await listWorktrees(options.cwd));
|
|
9
|
-
const rows = await Promise.all(worktrees.map(async (worktree) => buildStatusRow(worktree)));
|
|
12
|
+
const rows = await Promise.all(worktrees.map(async (worktree) => buildStatusRow(worktree, readWorktreeHealth, getWorktreeSlot, readTask)));
|
|
10
13
|
if (options.json) {
|
|
11
14
|
options.stdout(`${JSON.stringify(formatStatusJson(repository.repoRoot, repository.currentRoot, rows), null, 2)}\n`);
|
|
12
15
|
return 0;
|
|
@@ -37,7 +40,7 @@ export function formatStatusJson(repoRoot, currentRoot, rows) {
|
|
|
37
40
|
worktrees: rows,
|
|
38
41
|
};
|
|
39
42
|
}
|
|
40
|
-
async function buildStatusRow(worktree) {
|
|
43
|
+
async function buildStatusRow(worktree, readWorktreeHealth, getWorktreeSlot, readTask) {
|
|
41
44
|
const [health, slot, task] = await Promise.all([
|
|
42
45
|
readWorktreeHealth(worktree.path),
|
|
43
46
|
getWorktreeSlot(worktree.path),
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
1
2
|
export interface SyncFilesCommandOptions {
|
|
2
3
|
action?: string;
|
|
3
4
|
cwd: string;
|
|
4
5
|
home?: string;
|
|
5
6
|
json?: boolean;
|
|
6
7
|
paths?: string[];
|
|
8
|
+
runtime?: CliRuntime<"filesystem" | "configStore" | "repositoryContext">;
|
|
7
9
|
stderr: (chunk: string) => void;
|
|
8
10
|
stdout: (chunk: string) => void;
|
|
9
11
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import { validateSyncFilePattern } from "./file-sync.js";
|
|
5
|
-
import { detectRepository } from "./repo.js";
|
|
3
|
+
import { defaultCliDependencies, } from "../dependencies.js";
|
|
6
4
|
export async function runSyncFilesCommand(options) {
|
|
5
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
6
|
+
const { validateSyncFilePattern } = runtime.filesystem;
|
|
7
|
+
const { loadGlobalConfig, saveGlobalConfig } = runtime.configStore;
|
|
8
|
+
const { detectRepository } = runtime.repositoryContext;
|
|
7
9
|
const repository = await detectRepository(options.cwd);
|
|
8
10
|
const home = options.home ?? homedir();
|
|
9
11
|
const loaded = await loadGlobalConfig(home);
|
|
@@ -16,22 +18,22 @@ export async function runSyncFilesCommand(options) {
|
|
|
16
18
|
return 0;
|
|
17
19
|
}
|
|
18
20
|
case "add": {
|
|
19
|
-
const paths = validatePaths(options.paths ?? [], options);
|
|
21
|
+
const paths = validatePaths(options.paths ?? [], options, validateSyncFilePattern);
|
|
20
22
|
if (!paths)
|
|
21
23
|
return 1;
|
|
22
24
|
const nextFiles = mergeSyncFiles(readSyncFiles(repoConfig), paths);
|
|
23
|
-
await saveRepoSyncFiles(loaded.config, repoEntry?.key ?? repository.repoRoot, nextFiles, home);
|
|
25
|
+
await saveRepoSyncFiles(loaded.config, repoEntry?.key ?? repository.repoRoot, nextFiles, home, saveGlobalConfig);
|
|
24
26
|
writeSyncFiles(options.stdout, nextFiles, !!options.json);
|
|
25
27
|
return 0;
|
|
26
28
|
}
|
|
27
29
|
case "remove": {
|
|
28
|
-
const paths = validatePaths(options.paths ?? [], options);
|
|
30
|
+
const paths = validatePaths(options.paths ?? [], options, validateSyncFilePattern);
|
|
29
31
|
if (!paths)
|
|
30
32
|
return 1;
|
|
31
33
|
const existingFiles = readSyncFiles(repoConfig);
|
|
32
34
|
const nextFiles = removeSyncFiles(existingFiles, paths);
|
|
33
35
|
if (repoEntry && nextFiles.length !== existingFiles.length) {
|
|
34
|
-
await saveRepoSyncFiles(loaded.config, repoEntry.key, nextFiles, home);
|
|
36
|
+
await saveRepoSyncFiles(loaded.config, repoEntry.key, nextFiles, home, saveGlobalConfig);
|
|
35
37
|
}
|
|
36
38
|
writeSyncFiles(options.stdout, nextFiles, !!options.json);
|
|
37
39
|
return 0;
|
|
@@ -68,7 +70,7 @@ function writeSyncFiles(stdout, files, json) {
|
|
|
68
70
|
}
|
|
69
71
|
stdout(`${files.join("\n")}\n`);
|
|
70
72
|
}
|
|
71
|
-
function validatePaths(paths, options) {
|
|
73
|
+
function validatePaths(paths, options, validateSyncFilePattern) {
|
|
72
74
|
if (paths.length === 0) {
|
|
73
75
|
writeError(options, "at least one path is required");
|
|
74
76
|
return null;
|
|
@@ -98,7 +100,7 @@ function removeSyncFiles(existing, removals) {
|
|
|
98
100
|
const removalSet = new Set(removals);
|
|
99
101
|
return existing.filter((path) => !removalSet.has(path));
|
|
100
102
|
}
|
|
101
|
-
async function saveRepoSyncFiles(config, repoKey, syncFiles, home) {
|
|
103
|
+
async function saveRepoSyncFiles(config, repoKey, syncFiles, home, saveGlobalConfig) {
|
|
102
104
|
const repos = isPlainObject(config.repos) ? { ...config.repos } : {};
|
|
103
105
|
const repoConfig = isPlainObject(repos[repoKey])
|
|
104
106
|
? repos[repoKey]
|