@solaqua/gji 0.12.5 → 0.14.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} +50 -46
- package/dist/application/worktree/read-models.d.ts +31 -0
- package/dist/application/worktree/sources.d.ts +16 -0
- package/dist/application/worktree/sources.js +86 -0
- 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} +6 -3
- package/dist/cli/commands/clean.js +555 -0
- 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} +8 -2
- package/dist/{go.js → cli/commands/go.js} +68 -48
- 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} +26 -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} +25 -13
- 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 +20 -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 +8756 -7666
- 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} +4 -62
- 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} +16 -17
- package/dist/{worktree-picker.js → presentation/worktree/picker.js} +30 -184
- 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/clean.js +0 -313
- 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-sources.js +0 -77
- /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
package/README.md
CHANGED
|
@@ -22,6 +22,18 @@ directory. For repeatable workflows, you can still provide a branch directly:
|
|
|
22
22
|
gji new feature/payment-refactor
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
If you prefer to reuse the current worktree, create a branch in place and
|
|
26
|
+
refresh the configured default branch first:
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
gji new --branch-only feature/payment-refactor
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
This creates the new branch from the latest configured remote default branch
|
|
33
|
+
without creating another worktree or installing dependencies. The current
|
|
34
|
+
worktree must be clean; use `--no-fetch` to create from the local default
|
|
35
|
+
branch as-is.
|
|
36
|
+
|
|
25
37
|
## The problem
|
|
26
38
|
|
|
27
39
|
You are halfway through a feature when someone asks you to review a pull
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { RepositoryContext } from "../../domain/repository/context.js";
|
|
2
|
+
import type { WorktreeSource } from "../../domain/worktree/source.js";
|
|
3
|
+
import type { WorktreeEntry } from "../../domain/worktree/types.js";
|
|
4
|
+
import type { PullRequestInfo, PullRequestPort } from "../../ports/pull-requests.js";
|
|
5
|
+
import type { RepositoryContextPort, WorktreePort } from "../../ports/repository.js";
|
|
6
|
+
import type { WorktreeInfo } from "./read-models.js";
|
|
7
|
+
export interface LinkedWorktreeContext {
|
|
8
|
+
linkedWorktrees: WorktreeEntry[];
|
|
9
|
+
repository: RepositoryContext;
|
|
10
|
+
}
|
|
11
|
+
export type WorktreeMetadataMode = "full" | "fast";
|
|
12
|
+
export interface HydratedWorktree {
|
|
13
|
+
info: WorktreeInfo;
|
|
14
|
+
lastUsedTimestamp: number | null;
|
|
15
|
+
pullRequests: PullRequestInfo[];
|
|
16
|
+
source: WorktreeSource;
|
|
17
|
+
}
|
|
18
|
+
export interface WorktreeCatalogDependencies {
|
|
19
|
+
loadHistory: () => Promise<ReadonlyArray<{
|
|
20
|
+
path: string;
|
|
21
|
+
timestamp: number;
|
|
22
|
+
}>>;
|
|
23
|
+
readTask: (worktreePath: string) => Promise<{
|
|
24
|
+
task: string;
|
|
25
|
+
} | null>;
|
|
26
|
+
readWorktreeInfos: (worktrees: WorktreeEntry[]) => Promise<WorktreeInfo[]>;
|
|
27
|
+
queryPullRequests?: PullRequestPort["listOpenPullRequests"];
|
|
28
|
+
queryRepositoryPullRequests?: PullRequestPort["listOpenPullRequestsForRepository"];
|
|
29
|
+
}
|
|
30
|
+
export declare function loadWorktreeCatalog(sources: WorktreeSource[], metadata: WorktreeMetadataMode, dependencies: WorktreeCatalogDependencies): Promise<HydratedWorktree[]>;
|
|
31
|
+
export declare function loadLinkedWorktrees(cwd: string, repositoryPort: RepositoryContextPort & WorktreePort): Promise<LinkedWorktreeContext>;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { mapWithConcurrency } from "../../domain/shared/concurrency.js";
|
|
2
|
+
const MAX_PULL_REQUEST_REPOSITORY_QUERY_CONCURRENCY = 4;
|
|
3
|
+
const MAX_TASK_READ_CONCURRENCY = 8;
|
|
4
|
+
export async function loadWorktreeCatalog(sources, metadata, dependencies) {
|
|
5
|
+
const includeMetadata = metadata === "full";
|
|
6
|
+
const repositoryPullRequests = includeMetadata && dependencies.queryRepositoryPullRequests !== undefined
|
|
7
|
+
? readRepositoryPullRequests(sources, dependencies.queryRepositoryPullRequests)
|
|
8
|
+
: Promise.resolve(null);
|
|
9
|
+
const [history, infos, pullRequestsByRepository] = await Promise.all([
|
|
10
|
+
dependencies.loadHistory(),
|
|
11
|
+
includeMetadata
|
|
12
|
+
? dependencies.readWorktreeInfos(sources.map((source) => source.worktree))
|
|
13
|
+
: mapWithConcurrency(sources, MAX_TASK_READ_CONCURRENCY, async (source) => createUnhydratedWorktreeInfo(source.worktree, dependencies.readTask)),
|
|
14
|
+
repositoryPullRequests,
|
|
15
|
+
]);
|
|
16
|
+
const pullRequests = includeMetadata
|
|
17
|
+
? await Promise.all(sources.map((source) => readSourcePullRequests(source, dependencies.queryPullRequests, pullRequestsByRepository)))
|
|
18
|
+
: sources.map(() => []);
|
|
19
|
+
const historyByPath = new Map(history.map((entry) => [entry.path, entry]));
|
|
20
|
+
return sources.map((source, index) => ({
|
|
21
|
+
info: infos[index],
|
|
22
|
+
lastUsedTimestamp: historyByPath.get(source.worktree.path)?.timestamp ?? null,
|
|
23
|
+
pullRequests: pullRequests[index],
|
|
24
|
+
source,
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
export async function loadLinkedWorktrees(cwd, repositoryPort) {
|
|
28
|
+
const repository = await repositoryPort.detectRepository(cwd);
|
|
29
|
+
const linkedWorktrees = (await repositoryPort.listWorktrees(cwd)).filter((worktree) => worktree.path !== repository.repoRoot);
|
|
30
|
+
return {
|
|
31
|
+
linkedWorktrees,
|
|
32
|
+
repository,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
async function createUnhydratedWorktreeInfo(worktree, readTask) {
|
|
36
|
+
let task = null;
|
|
37
|
+
try {
|
|
38
|
+
task = (await readTask(worktree.path))?.task ?? null;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Task metadata is optional picker decoration.
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
...worktree,
|
|
45
|
+
lastCommitTimestamp: null,
|
|
46
|
+
slot: null,
|
|
47
|
+
status: "unknown",
|
|
48
|
+
task,
|
|
49
|
+
upstream: { kind: "unknown" },
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
async function readSourcePullRequests(source, queryPullRequests, pullRequestsByRepository) {
|
|
53
|
+
if (source.repoRoot === undefined || source.worktree.branch === null) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
if (pullRequestsByRepository !== null) {
|
|
57
|
+
return sortPullRequests(pullRequestsByRepository
|
|
58
|
+
.get(source.repoRoot)
|
|
59
|
+
?.filter((pullRequest) => pullRequest.sourceBranch === source.worktree.branch));
|
|
60
|
+
}
|
|
61
|
+
if (queryPullRequests === undefined)
|
|
62
|
+
return [];
|
|
63
|
+
try {
|
|
64
|
+
return sortPullRequests(await queryPullRequests(source.repoRoot, source.worktree.branch));
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function readRepositoryPullRequests(sources, queryPullRequests) {
|
|
71
|
+
const repoRoots = [
|
|
72
|
+
...new Set(sources.flatMap((source) => source.repoRoot === undefined || source.worktree.branch === null
|
|
73
|
+
? []
|
|
74
|
+
: [source.repoRoot])),
|
|
75
|
+
];
|
|
76
|
+
const results = await mapWithConcurrency(repoRoots, MAX_PULL_REQUEST_REPOSITORY_QUERY_CONCURRENCY, async (repoRoot) => {
|
|
77
|
+
try {
|
|
78
|
+
return [repoRoot, sortPullRequests(await queryPullRequests(repoRoot))];
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return [repoRoot, []];
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
return new Map(results);
|
|
85
|
+
}
|
|
86
|
+
function sortPullRequests(pullRequests) {
|
|
87
|
+
return [...(pullRequests ?? [])].sort((left, right) => left.number - right.number);
|
|
88
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { WorktreeEntry } from "../../domain/worktree/types.js";
|
|
2
|
+
import type { WorktreeInfo } from "./read-models.js";
|
|
3
|
+
export interface ContextCardDependencies {
|
|
4
|
+
listWorktrees: (cwd: string) => Promise<WorktreeEntry[]>;
|
|
5
|
+
readTask: (worktreePath: string) => Promise<{
|
|
6
|
+
task: string;
|
|
7
|
+
} | null>;
|
|
8
|
+
readWorktreeInfo: (worktree: WorktreeEntry) => Promise<WorktreeInfo>;
|
|
9
|
+
}
|
|
10
|
+
export interface ContextCardModel {
|
|
11
|
+
info: WorktreeInfo;
|
|
12
|
+
task: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function loadContextCardModel(worktreePath: string, dependencies: ContextCardDependencies): Promise<ContextCardModel | null>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export async function loadContextCardModel(worktreePath, dependencies) {
|
|
2
|
+
const worktree = (await dependencies.listWorktrees(worktreePath)).find((entry) => entry.path === worktreePath);
|
|
3
|
+
if (!worktree)
|
|
4
|
+
return null;
|
|
5
|
+
const task = await dependencies.readTask(worktreePath);
|
|
6
|
+
if (!task)
|
|
7
|
+
return null;
|
|
8
|
+
return {
|
|
9
|
+
info: await dependencies.readWorktreeInfo(worktree),
|
|
10
|
+
task: task.task,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import type { WorktreeSource } from "
|
|
3
|
-
|
|
1
|
+
import type { RepositoryContext } from "../../domain/repository/context.js";
|
|
2
|
+
import type { WorktreeSource } from "../../domain/worktree/source.js";
|
|
3
|
+
import type { ConfigPort } from "../../ports/config.js";
|
|
4
|
+
import type { RepositoryContextPort, RepositoryRefPort, RepositoryRegistryPort, WorktreePort } from "../../ports/repository.js";
|
|
5
|
+
type GoBranchResolutionBase = {
|
|
4
6
|
kind: "existing";
|
|
5
7
|
source: WorktreeSource;
|
|
6
8
|
} | {
|
|
@@ -26,10 +28,16 @@ export type GoBranchResolution = {
|
|
|
26
28
|
kind: "error";
|
|
27
29
|
message: string;
|
|
28
30
|
};
|
|
31
|
+
export type GoBranchResolution = GoBranchResolutionBase & {
|
|
32
|
+
configWarnings?: readonly string[];
|
|
33
|
+
};
|
|
29
34
|
export declare function resolveGoBranch(options: {
|
|
30
35
|
branch: string;
|
|
31
|
-
configStderr?: (chunk: string) => void;
|
|
32
36
|
cwd: string;
|
|
33
37
|
currentSources: WorktreeSource[];
|
|
34
38
|
repository: RepositoryContext | null;
|
|
39
|
+
repositoryPort: RepositoryRefPort;
|
|
40
|
+
sourceDependencies: RepositoryContextPort & RepositoryRegistryPort & WorktreePort;
|
|
41
|
+
configPort: ConfigPort;
|
|
35
42
|
}): Promise<GoBranchResolution>;
|
|
43
|
+
export {};
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { parsePrInput } from "
|
|
3
|
-
import {
|
|
4
|
-
import { resolveExactWorktreeQueryMatches, resolveWorktreeQuery, resolveWorktreeQueryMatches, } from "./worktree-picker.js";
|
|
5
|
-
import { listRegisteredWorktreeSources } from "./worktree-sources.js";
|
|
1
|
+
import { resolveExactWorktreeQueryMatches, resolveWorktreeQuery, resolveWorktreeQueryMatches, } from "../../domain/worktree/matching.js";
|
|
2
|
+
import { parsePrInput } from "../../domain/worktree/pr-reference.js";
|
|
3
|
+
import { listRegisteredWorktreeSources } from "./sources.js";
|
|
6
4
|
export async function resolveGoBranch(options) {
|
|
7
|
-
const { branch,
|
|
5
|
+
const { branch, configPort, cwd, currentSources, repository, repositoryPort, sourceDependencies, } = options;
|
|
8
6
|
const pullRequestNumber = parsePrInput(branch);
|
|
9
7
|
const exactCurrentMatches = resolveExistingExactWorktreeMatches(currentSources, branch, false);
|
|
10
8
|
if (exactCurrentMatches.length === 1) {
|
|
11
9
|
return { kind: "existing", source: exactCurrentMatches[0] };
|
|
12
10
|
}
|
|
13
11
|
const localBranch = repository
|
|
14
|
-
? await hasLocalBranch(repository.repoRoot, branch)
|
|
12
|
+
? await repositoryPort.hasLocalBranch(repository.repoRoot, branch)
|
|
15
13
|
: false;
|
|
16
14
|
if (!repository && pullRequestNumber !== null) {
|
|
17
15
|
return {
|
|
@@ -25,11 +23,9 @@ export async function resolveGoBranch(options) {
|
|
|
25
23
|
const configWarnings = [];
|
|
26
24
|
if (repository) {
|
|
27
25
|
try {
|
|
28
|
-
const config = await loadEffectiveConfig(repository.repoRoot, undefined,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
remote = resolveConfigString(config, "syncRemote") ?? "origin";
|
|
32
|
-
remoteBranch = await hasRemoteBranch(repository.repoRoot, remote, branch);
|
|
26
|
+
const config = await configPort.loadEffectiveConfig(repository.repoRoot, undefined, (warning) => configWarnings.push(warning));
|
|
27
|
+
remote = configPort.resolveConfigString(config, "syncRemote") ?? "origin";
|
|
28
|
+
remoteBranch = await repositoryPort.hasRemoteBranch(repository.repoRoot, remote, branch);
|
|
33
29
|
}
|
|
34
30
|
catch (error) {
|
|
35
31
|
configError = {
|
|
@@ -38,14 +34,8 @@ export async function resolveGoBranch(options) {
|
|
|
38
34
|
};
|
|
39
35
|
}
|
|
40
36
|
}
|
|
41
|
-
const flushConfigWarnings = () => {
|
|
42
|
-
if (configStderr === undefined)
|
|
43
|
-
return;
|
|
44
|
-
for (const warning of configWarnings)
|
|
45
|
-
configStderr(warning);
|
|
46
|
-
};
|
|
47
37
|
const pullRequestBelongsToRepository = repository && pullRequestNumber !== null
|
|
48
|
-
? await isPullRequestForRepository(repository.repoRoot, branch)
|
|
38
|
+
? await isPullRequestForRepository(repository.repoRoot, branch, repositoryPort)
|
|
49
39
|
: true;
|
|
50
40
|
if (pullRequestNumber !== null &&
|
|
51
41
|
!localBranch &&
|
|
@@ -53,60 +43,74 @@ export async function resolveGoBranch(options) {
|
|
|
53
43
|
pullRequestBelongsToRepository) {
|
|
54
44
|
const pullRequestMatches = resolveExistingExactWorktreeMatches(currentSources, branch, true);
|
|
55
45
|
if (pullRequestMatches.length === 1) {
|
|
56
|
-
return { kind: "existing", source: pullRequestMatches[0] };
|
|
46
|
+
return finish({ kind: "existing", source: pullRequestMatches[0] }, false);
|
|
57
47
|
}
|
|
58
48
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
49
|
+
const registered = await listRegisteredWorktreeSources({
|
|
50
|
+
cwd,
|
|
51
|
+
repositoryPort: sourceDependencies,
|
|
62
52
|
});
|
|
53
|
+
const registeredSources = registered.sources;
|
|
54
|
+
const skippedRegisteredRepos = registered.skipped.length;
|
|
63
55
|
const crossRepoSources = registeredSources.filter((source) => source.repoRoot !== repository?.repoRoot);
|
|
64
|
-
const crossMatches = await resolveExistingWorktreeMatches(crossRepoSources, branch, (!localBranch && !remoteBranch) || isPullRequestUrl(branch));
|
|
56
|
+
const crossMatches = await resolveExistingWorktreeMatches(crossRepoSources, branch, (!localBranch && !remoteBranch) || isPullRequestUrl(branch), repositoryPort);
|
|
65
57
|
if (crossMatches.length === 1) {
|
|
66
|
-
return { kind: "existing", source: crossMatches[0] };
|
|
58
|
+
return finish({ kind: "existing", source: crossMatches[0] }, false);
|
|
67
59
|
}
|
|
68
60
|
if (crossMatches.length > 1) {
|
|
69
|
-
return { kind: "ambiguous", matches: crossMatches };
|
|
61
|
+
return finish({ kind: "ambiguous", matches: crossMatches }, false);
|
|
70
62
|
}
|
|
71
63
|
if (!repository) {
|
|
72
|
-
return registeredSources.length === 0
|
|
64
|
+
return finish(registeredSources.length === 0
|
|
73
65
|
? {
|
|
74
66
|
kind: "no-repository",
|
|
75
67
|
staleRegisteredRepos: skippedRegisteredRepos > 0,
|
|
76
68
|
}
|
|
77
|
-
: {
|
|
69
|
+
: {
|
|
70
|
+
kind: "no-match",
|
|
71
|
+
staleRegisteredRepos: skippedRegisteredRepos > 0,
|
|
72
|
+
});
|
|
78
73
|
}
|
|
79
74
|
if (localBranch) {
|
|
80
75
|
if (configError)
|
|
81
|
-
return configError;
|
|
82
|
-
|
|
83
|
-
return { kind: "create", repository, branch, mode: "checkout" };
|
|
76
|
+
return finish(configError);
|
|
77
|
+
return finish({ kind: "create", repository, branch, mode: "checkout" });
|
|
84
78
|
}
|
|
85
79
|
if (remoteBranch) {
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
return finish({
|
|
81
|
+
kind: "create",
|
|
82
|
+
repository,
|
|
83
|
+
branch,
|
|
84
|
+
mode: "track",
|
|
85
|
+
remote,
|
|
86
|
+
});
|
|
88
87
|
}
|
|
89
88
|
const localMatch = !pullRequestNumber || !isPullRequestUrl(branch)
|
|
90
89
|
? resolveWorktreeQuery(currentSources, branch)
|
|
91
90
|
: null;
|
|
92
91
|
if (localMatch) {
|
|
93
|
-
return { kind: "existing", source: localMatch };
|
|
92
|
+
return finish({ kind: "existing", source: localMatch });
|
|
94
93
|
}
|
|
95
94
|
if (configError)
|
|
96
|
-
return configError;
|
|
95
|
+
return finish(configError);
|
|
97
96
|
if (pullRequestNumber !== null && !pullRequestBelongsToRepository) {
|
|
98
|
-
|
|
99
|
-
return {
|
|
97
|
+
return finish({
|
|
100
98
|
kind: "error",
|
|
101
99
|
message: "PR URL does not belong to this repository; run gji go from the matching checkout",
|
|
102
|
-
};
|
|
100
|
+
});
|
|
103
101
|
}
|
|
104
102
|
if (pullRequestNumber !== null) {
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
return finish({ kind: "pull-request", repository, input: branch });
|
|
104
|
+
}
|
|
105
|
+
return finish({
|
|
106
|
+
kind: "no-match",
|
|
107
|
+
staleRegisteredRepos: skippedRegisteredRepos > 0,
|
|
108
|
+
});
|
|
109
|
+
function finish(resolution, includeWarnings = true) {
|
|
110
|
+
return !includeWarnings || configWarnings.length === 0
|
|
111
|
+
? resolution
|
|
112
|
+
: { ...resolution, configWarnings };
|
|
107
113
|
}
|
|
108
|
-
flushConfigWarnings();
|
|
109
|
-
return { kind: "no-match", staleRegisteredRepos: skippedRegisteredRepos > 0 };
|
|
110
114
|
}
|
|
111
115
|
function resolveExistingExactWorktreeMatches(sources, query, allowPullRequestFallback) {
|
|
112
116
|
const exactMatches = resolveExactWorktreeQueryMatches(sources, query);
|
|
@@ -117,7 +121,7 @@ function resolveExistingExactWorktreeMatches(sources, query, allowPullRequestFal
|
|
|
117
121
|
? []
|
|
118
122
|
: resolveExactWorktreeQueryMatches(sources, `pr/${pullRequestNumber}`);
|
|
119
123
|
}
|
|
120
|
-
async function resolveExistingWorktreeMatches(sources, query, allowPullRequestFallback) {
|
|
124
|
+
async function resolveExistingWorktreeMatches(sources, query, allowPullRequestFallback, repositoryPort) {
|
|
121
125
|
const exactMatches = resolveExactWorktreeQueryMatches(sources, query);
|
|
122
126
|
if (exactMatches.length > 0)
|
|
123
127
|
return exactMatches;
|
|
@@ -132,7 +136,7 @@ async function resolveExistingWorktreeMatches(sources, query, allowPullRequestFa
|
|
|
132
136
|
const repoRoot = source.repoRoot ?? source.worktree.path;
|
|
133
137
|
let ownership = ownershipByRepository.get(repoRoot);
|
|
134
138
|
if (ownership === undefined) {
|
|
135
|
-
ownership = isPullRequestForRepository(repoRoot, query);
|
|
139
|
+
ownership = isPullRequestForRepository(repoRoot, query, repositoryPort);
|
|
136
140
|
ownershipByRepository.set(repoRoot, ownership);
|
|
137
141
|
}
|
|
138
142
|
return (await ownership) ? source : null;
|
|
@@ -146,7 +150,7 @@ async function resolveExistingWorktreeMatches(sources, query, allowPullRequestFa
|
|
|
146
150
|
function isPullRequestUrl(input) {
|
|
147
151
|
return /^[a-z][a-z\d+.-]*:\/\//i.test(input);
|
|
148
152
|
}
|
|
149
|
-
async function isPullRequestForRepository(repoRoot, input) {
|
|
153
|
+
async function isPullRequestForRepository(repoRoot, input, repositoryPort) {
|
|
150
154
|
if (/^\d+$/.test(input) || /^#\d+$/.test(input))
|
|
151
155
|
return true;
|
|
152
156
|
let pullRequestUrl;
|
|
@@ -156,7 +160,7 @@ async function isPullRequestForRepository(repoRoot, input) {
|
|
|
156
160
|
catch {
|
|
157
161
|
return true;
|
|
158
162
|
}
|
|
159
|
-
const remoteUrl = await getRepositoryRemoteUrl(repoRoot, "origin");
|
|
163
|
+
const remoteUrl = await repositoryPort.getRepositoryRemoteUrl(repoRoot, "origin");
|
|
160
164
|
if (!remoteUrl)
|
|
161
165
|
return true;
|
|
162
166
|
return (normalizeRepositoryUrl(remoteUrl) ===
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { WorktreeEntry } from "../../domain/worktree/types.js";
|
|
2
|
+
export type WorktreeStatus = "clean" | "dirty" | "unknown";
|
|
3
|
+
export type UpstreamState = {
|
|
4
|
+
kind: "detached";
|
|
5
|
+
} | {
|
|
6
|
+
kind: "no-upstream";
|
|
7
|
+
} | {
|
|
8
|
+
kind: "stale";
|
|
9
|
+
} | {
|
|
10
|
+
kind: "tracked";
|
|
11
|
+
ahead: number;
|
|
12
|
+
behind: number;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "unknown";
|
|
15
|
+
};
|
|
16
|
+
export interface WorktreeInfo extends WorktreeEntry {
|
|
17
|
+
lastCommitTimestamp: number | null;
|
|
18
|
+
slot: number | null;
|
|
19
|
+
status: WorktreeStatus;
|
|
20
|
+
task: string | null;
|
|
21
|
+
upstream: UpstreamState;
|
|
22
|
+
}
|
|
23
|
+
export interface SerializedWorktreeInfo {
|
|
24
|
+
branch: string | null;
|
|
25
|
+
lastCommitTimestamp: number | null;
|
|
26
|
+
path: string;
|
|
27
|
+
slot: number | null;
|
|
28
|
+
status: WorktreeStatus;
|
|
29
|
+
task: string | null;
|
|
30
|
+
upstream: UpstreamState;
|
|
31
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { RepoRegistryEntry } from "../../domain/repository/registry.js";
|
|
2
|
+
import type { WorktreeSource } from "../../domain/worktree/source.js";
|
|
3
|
+
import type { RepositoryContextPort, RepositoryRegistryPort, WorktreePort } from "../../ports/repository.js";
|
|
4
|
+
export type WorktreeSourceDependencies = RepositoryContextPort & RepositoryRegistryPort & WorktreePort;
|
|
5
|
+
export interface WorktreeSourceDiscovery {
|
|
6
|
+
skipped: RepoRegistryEntry[];
|
|
7
|
+
sources: WorktreeSource[];
|
|
8
|
+
}
|
|
9
|
+
export interface ListWorktreeSourcesOptions {
|
|
10
|
+
cwd: string;
|
|
11
|
+
repositoryPort: WorktreeSourceDependencies;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
}
|
|
14
|
+
export declare function listRegisteredWorktreeSources(options: ListWorktreeSourcesOptions): Promise<WorktreeSourceDiscovery>;
|
|
15
|
+
export declare function listDiscoverableWorktreeSources(options: ListWorktreeSourcesOptions): Promise<WorktreeSourceDiscovery>;
|
|
16
|
+
export declare function deduplicateWorktreeSources(sources: WorktreeSource[]): WorktreeSource[];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { mapWithConcurrency } from "../../domain/shared/concurrency.js";
|
|
2
|
+
const MAX_REPOSITORY_DISCOVERY_CONCURRENCY = 4;
|
|
3
|
+
export async function listRegisteredWorktreeSources(options) {
|
|
4
|
+
const { cwd, repositoryPort, signal } = options;
|
|
5
|
+
throwIfAborted(signal);
|
|
6
|
+
const registry = await repositoryPort.loadRegistry();
|
|
7
|
+
const currentRoot = await repositoryPort
|
|
8
|
+
.detectRepository(cwd)
|
|
9
|
+
.then((repository) => repository.currentRoot)
|
|
10
|
+
.catch(() => null);
|
|
11
|
+
throwIfAborted(signal);
|
|
12
|
+
const results = await mapWithConcurrency(registry, MAX_REPOSITORY_DISCOVERY_CONCURRENCY, async (entry) => {
|
|
13
|
+
throwIfAborted(signal);
|
|
14
|
+
try {
|
|
15
|
+
const worktrees = await repositoryPort.listWorktrees(entry.path);
|
|
16
|
+
return { entry, skipped: false, worktrees };
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return { entry, skipped: true, worktrees: [] };
|
|
20
|
+
}
|
|
21
|
+
}, signal);
|
|
22
|
+
const allItems = [];
|
|
23
|
+
const skipped = [];
|
|
24
|
+
for (const result of results) {
|
|
25
|
+
if (result.skipped) {
|
|
26
|
+
skipped.push(result.entry);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
const { entry, worktrees } = result;
|
|
30
|
+
for (const worktree of worktrees) {
|
|
31
|
+
allItems.push({
|
|
32
|
+
repoRoot: entry.path,
|
|
33
|
+
repoName: entry.name,
|
|
34
|
+
worktree: {
|
|
35
|
+
...worktree,
|
|
36
|
+
isCurrent: currentRoot !== null && worktree.path === currentRoot,
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { skipped, sources: allItems };
|
|
42
|
+
}
|
|
43
|
+
export async function listDiscoverableWorktreeSources(options) {
|
|
44
|
+
const { cwd, repositoryPort } = options;
|
|
45
|
+
const currentRepository = await repositoryPort
|
|
46
|
+
.detectRepository(cwd)
|
|
47
|
+
.catch(() => null);
|
|
48
|
+
const registered = await listRegisteredWorktreeSources(options);
|
|
49
|
+
if (currentRepository === null) {
|
|
50
|
+
return {
|
|
51
|
+
skipped: registered.skipped,
|
|
52
|
+
sources: deduplicateWorktreeSources(registered.sources),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
let currentSources = [];
|
|
56
|
+
try {
|
|
57
|
+
currentSources = (await repositoryPort.listWorktrees(cwd)).map((worktree) => ({
|
|
58
|
+
repoName: currentRepository.repoName,
|
|
59
|
+
repoRoot: currentRepository.repoRoot,
|
|
60
|
+
worktree,
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Registered repositories remain discoverable when the current checkout is transiently unavailable.
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
skipped: registered.skipped,
|
|
68
|
+
sources: deduplicateWorktreeSources([
|
|
69
|
+
...currentSources,
|
|
70
|
+
...registered.sources,
|
|
71
|
+
]),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function deduplicateWorktreeSources(sources) {
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
return sources.filter((source) => {
|
|
77
|
+
if (seen.has(source.worktree.path))
|
|
78
|
+
return false;
|
|
79
|
+
seen.add(source.worktree.path);
|
|
80
|
+
return true;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function throwIfAborted(signal) {
|
|
84
|
+
if (signal?.aborted)
|
|
85
|
+
throw new Error("Operation cancelled");
|
|
86
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { HistoryEntry } from "../../ports/history.js";
|
|
2
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
2
3
|
export declare const BACK_OUTPUT_FILE_ENV = "GJI_BACK_OUTPUT_FILE";
|
|
3
4
|
export interface BackCommandOptions {
|
|
4
5
|
cwd: string;
|
|
@@ -8,6 +9,7 @@ export interface BackCommandOptions {
|
|
|
8
9
|
commandName?: string;
|
|
9
10
|
outputEnv?: string;
|
|
10
11
|
print?: boolean;
|
|
12
|
+
runtime?: CliRuntime<"config" | "historyStore" | "hooks" | "repositoryContext">;
|
|
11
13
|
stderr: (chunk: string) => void;
|
|
12
14
|
stdout: (chunk: string) => void;
|
|
13
15
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { access } from "node:fs/promises";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { createNavigationRepository, createNavigationTarget, } from "./navigation-output.js";
|
|
7
|
-
import { detectRepository } from "./repo.js";
|
|
8
|
-
import { writeShellOutput } from "./shell-handoff.js";
|
|
3
|
+
import { writeShellOutput } from "../../presentation/shell/handoff.js";
|
|
4
|
+
import { createNavigationRepository, createNavigationTarget, } from "../../presentation/terminal/navigation.js";
|
|
5
|
+
import { defaultCliDependencies } from "../dependencies.js";
|
|
9
6
|
export const BACK_OUTPUT_FILE_ENV = "GJI_BACK_OUTPUT_FILE";
|
|
10
7
|
export async function runBackCommand(options) {
|
|
8
|
+
const runtime = options.runtime ?? defaultCliDependencies;
|
|
9
|
+
const { loadEffectiveConfig } = runtime.config;
|
|
10
|
+
const { appendHistory, loadHistory } = runtime.historyStore;
|
|
11
|
+
const { extractHooks, runHook } = runtime.hooks;
|
|
12
|
+
const { detectRepository } = runtime.repositoryContext;
|
|
11
13
|
const history = await loadHistory(options.home);
|
|
12
14
|
const steps = options.n ?? 1;
|
|
13
15
|
if (steps < 1) {
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import {
|
|
1
|
+
import { type WorktreeCatalogDependencies } from "../../application/worktree/catalog.js";
|
|
2
|
+
import type { WorktreeEntry } from "../../domain/worktree/types.js";
|
|
3
|
+
import { type WorktreePromptEntry, type WorktreePromptScope } from "../../presentation/worktree/picker.js";
|
|
4
|
+
import { type CliRuntime } from "../dependencies.js";
|
|
3
5
|
export interface CleanCommandOptions {
|
|
4
6
|
cwd: string;
|
|
5
7
|
dryRun?: boolean;
|
|
6
8
|
force?: boolean;
|
|
7
9
|
json?: boolean;
|
|
8
10
|
stale?: boolean;
|
|
11
|
+
runtime?: CliRuntime<"git" | "config" | "configStore" | "slots" | "repositoryContext" | "repositoryRegistry" | "worktrees" | "worktreeInfo" | "worktreeLifecycle" | "worktreeCatalog">;
|
|
9
12
|
stderr: (chunk: string) => void;
|
|
10
13
|
stdout: (chunk: string) => void;
|
|
11
14
|
}
|
|
@@ -13,7 +16,7 @@ export interface CleanCommandDependencies {
|
|
|
13
16
|
confirmForceDeleteBranch: (branch: string) => Promise<boolean>;
|
|
14
17
|
confirmForceRemoveWorktree: (worktreePath: string) => Promise<boolean>;
|
|
15
18
|
confirmRemoval: (worktrees: WorktreeEntry[]) => Promise<boolean>;
|
|
16
|
-
promptForWorktrees: (worktrees: WorktreePromptEntry[]) => Promise<string[] | null>;
|
|
19
|
+
promptForWorktrees: (worktrees: WorktreePromptEntry[], scope?: WorktreePromptScope, catalog?: WorktreeCatalogDependencies) => Promise<string[] | null>;
|
|
17
20
|
}
|
|
18
21
|
export declare function createCleanCommand(dependencies?: Partial<CleanCommandDependencies>): (options: CleanCommandOptions) => Promise<number>;
|
|
19
22
|
export declare const runCleanCommand: (options: CleanCommandOptions) => Promise<number>;
|