@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,15 +1,10 @@
|
|
|
1
1
|
import { env, platform, stdin, stdout } from "node:process";
|
|
2
2
|
import { isCancel, Prompt } from "@clack/core";
|
|
3
3
|
import { spinner } from "@clack/prompts";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { readTask } from "./task.js";
|
|
7
|
-
import { middleEllipsize, sanitizeTerminalText, startEllipsize, terminalWidth, } from "./terminal-text.js";
|
|
8
|
-
import { readWorktreeInfos, } from "./worktree-info.js";
|
|
9
|
-
const MAX_PULL_REQUEST_REPOSITORY_QUERY_CONCURRENCY = 4;
|
|
10
|
-
const MAX_TASK_READ_CONCURRENCY = 8;
|
|
4
|
+
import { loadWorktreeCatalog, } from "../../application/worktree/catalog.js";
|
|
5
|
+
import { middleEllipsize, sanitizeTerminalText, startEllipsize, terminalWidth, } from "../terminal/text.js";
|
|
11
6
|
export async function buildWorktreePromptEntries(sources, dependencies = {}) {
|
|
12
|
-
const loading = stdin.isTTY === true && stdout.isTTY === true
|
|
7
|
+
const loading = stdin.isTTY === true && stdout.isTTY === true && stdin.isRaw !== true
|
|
13
8
|
? spinner({ indicator: "timer" })
|
|
14
9
|
: null;
|
|
15
10
|
loading?.start("Loading worktrees");
|
|
@@ -22,91 +17,14 @@ export async function buildWorktreePromptEntries(sources, dependencies = {}) {
|
|
|
22
17
|
}
|
|
23
18
|
async function buildWorktreePromptEntriesWithoutLoading(sources, dependencies) {
|
|
24
19
|
const metadataMode = dependencies.metadata ?? "full";
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const queryPullRequests = dependencies.queryPullRequests ?? pullRequestQuery.listOpenPullRequests;
|
|
28
|
-
const queryRepositoryPullRequests = includeMetadata
|
|
29
|
-
? (dependencies.queryRepositoryPullRequests ??
|
|
30
|
-
(dependencies.queryPullRequests === undefined
|
|
31
|
-
? pullRequestQuery.listOpenPullRequestsForRepository
|
|
32
|
-
: undefined))
|
|
33
|
-
: undefined;
|
|
34
|
-
const repositoryPullRequests = queryRepositoryPullRequests === undefined
|
|
35
|
-
? Promise.resolve(null)
|
|
36
|
-
: readRepositoryPullRequests(sources, queryRepositoryPullRequests);
|
|
37
|
-
const [history, infos, pullRequestsByRepository] = await Promise.all([
|
|
38
|
-
loadHistory(),
|
|
39
|
-
includeMetadata
|
|
40
|
-
? readWorktreeInfos(sources.map((source) => source.worktree))
|
|
41
|
-
: mapWithConcurrency(sources, MAX_TASK_READ_CONCURRENCY, (source) => createUnhydratedWorktreeInfo(source.worktree)),
|
|
42
|
-
repositoryPullRequests,
|
|
43
|
-
]);
|
|
44
|
-
const pullRequests = includeMetadata
|
|
45
|
-
? await Promise.all(sources.map((source) => readSourcePullRequests(source, queryPullRequests, pullRequestsByRepository)))
|
|
46
|
-
: sources.map(() => []);
|
|
47
|
-
const historyByPath = new Map(history.map((entry) => [entry.path, entry]));
|
|
48
|
-
const entries = sources.map((source, index) => buildWorktreePromptEntry(source, infos[index], pullRequests[index], historyByPath.get(source.worktree.path)?.timestamp ?? null, Date.now()));
|
|
20
|
+
const hydrated = await loadWorktreeCatalog(sources, metadataMode, dependencies.catalog ?? missingCatalogDependencies());
|
|
21
|
+
const entries = hydrated.map(({ info, lastUsedTimestamp, pullRequests, source }) => buildWorktreePromptEntry(source, info, pullRequests, lastUsedTimestamp, Date.now()));
|
|
49
22
|
return entries
|
|
50
23
|
.sort(comparePromptEntries)
|
|
51
24
|
.map(({ lastActivityTimestamp: _lastActivityTimestamp, ...entry }) => entry);
|
|
52
25
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
task = (await readTask(worktree.path))?.task ?? null;
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
// Task metadata is optional picker decoration.
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
...worktree,
|
|
63
|
-
lastCommitTimestamp: null,
|
|
64
|
-
slot: null,
|
|
65
|
-
status: "unknown",
|
|
66
|
-
task,
|
|
67
|
-
upstream: { kind: "unknown" },
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
export function resolveWorktreeQuery(sources, query) {
|
|
71
|
-
return resolveWorktreeQueryMatches(sources, query)[0] ?? null;
|
|
72
|
-
}
|
|
73
|
-
export function resolveWorktreeQueryMatches(sources, query) {
|
|
74
|
-
const normalizedQuery = normalizeQuery(query);
|
|
75
|
-
if (normalizedQuery === null)
|
|
76
|
-
return [];
|
|
77
|
-
const matches = findWorktreePromptSourceMatches(sources, normalizedQuery);
|
|
78
|
-
if (isAmbiguousRepoOnlyQuery(matches, normalizedQuery))
|
|
79
|
-
return [];
|
|
80
|
-
const bestScore = matches[0]?.matchScore;
|
|
81
|
-
return matches
|
|
82
|
-
.filter((match) => match.matchScore === bestScore)
|
|
83
|
-
.map((match) => match.source);
|
|
84
|
-
}
|
|
85
|
-
export function resolveExactWorktreeQueryMatches(sources, query) {
|
|
86
|
-
const normalizedQuery = normalizeQuery(query);
|
|
87
|
-
if (normalizedQuery === null)
|
|
88
|
-
return [];
|
|
89
|
-
return sources.filter((source) => scoreWorktreeMatch({
|
|
90
|
-
...source.worktree,
|
|
91
|
-
repoName: source.repoName,
|
|
92
|
-
}, normalizedQuery) === 1000);
|
|
93
|
-
}
|
|
94
|
-
function findWorktreePromptSourceMatches(sources, normalizedQuery) {
|
|
95
|
-
return sources
|
|
96
|
-
.flatMap((source) => {
|
|
97
|
-
const matchScore = scoreWorktreeMatch({
|
|
98
|
-
...source.worktree,
|
|
99
|
-
repoName: source.repoName,
|
|
100
|
-
}, normalizedQuery);
|
|
101
|
-
return matchScore === null ? [] : [{ matchScore, source }];
|
|
102
|
-
})
|
|
103
|
-
.sort(compareQueryMatches);
|
|
104
|
-
}
|
|
105
|
-
function isAmbiguousRepoOnlyQuery(matches, query) {
|
|
106
|
-
if (matches[0]?.matchScore === 1000)
|
|
107
|
-
return false;
|
|
108
|
-
return (matches.filter((match) => match.source.repoName.toLowerCase() === query)
|
|
109
|
-
.length > 1);
|
|
26
|
+
function missingCatalogDependencies() {
|
|
27
|
+
throw new Error("Worktree picker hydration must be supplied by the application composition root");
|
|
110
28
|
}
|
|
111
29
|
export async function promptForSingleWorktree(message, worktrees, io = {}) {
|
|
112
30
|
const choice = await runSearchablePrompt({
|
|
@@ -129,18 +47,6 @@ export async function promptForMultipleWorktrees(message, worktrees, io = {}) {
|
|
|
129
47
|
});
|
|
130
48
|
return Array.isArray(choice) ? choice : null;
|
|
131
49
|
}
|
|
132
|
-
function compareQueryMatches(a, b) {
|
|
133
|
-
if (a.matchScore !== b.matchScore) {
|
|
134
|
-
return b.matchScore - a.matchScore;
|
|
135
|
-
}
|
|
136
|
-
if (a.source.worktree.isCurrent && !b.source.worktree.isCurrent)
|
|
137
|
-
return -1;
|
|
138
|
-
if (!a.source.worktree.isCurrent && b.source.worktree.isCurrent)
|
|
139
|
-
return 1;
|
|
140
|
-
return (a.source.repoName.localeCompare(b.source.repoName) ||
|
|
141
|
-
(a.source.worktree.branch ?? "").localeCompare(b.source.worktree.branch ?? "") ||
|
|
142
|
-
a.source.worktree.path.localeCompare(b.source.worktree.path));
|
|
143
|
-
}
|
|
144
50
|
function groupPromptEntries(worktrees) {
|
|
145
51
|
const groups = new Map();
|
|
146
52
|
for (const worktree of worktrees) {
|
|
@@ -214,6 +120,7 @@ class SearchablePrompt {
|
|
|
214
120
|
selected = new Set();
|
|
215
121
|
scope;
|
|
216
122
|
scopeTogglePending = false;
|
|
123
|
+
promptClosed = false;
|
|
217
124
|
prompt;
|
|
218
125
|
constructor(options, input, output) {
|
|
219
126
|
this.options = options;
|
|
@@ -244,6 +151,8 @@ class SearchablePrompt {
|
|
|
244
151
|
this.cancel(prompt);
|
|
245
152
|
return;
|
|
246
153
|
}
|
|
154
|
+
if (this.scopeTogglePending)
|
|
155
|
+
return;
|
|
247
156
|
if (action === "escape") {
|
|
248
157
|
this.handleEscapeKey(prompt);
|
|
249
158
|
return;
|
|
@@ -256,12 +165,12 @@ class SearchablePrompt {
|
|
|
256
165
|
void this.toggleScope(prompt);
|
|
257
166
|
return;
|
|
258
167
|
}
|
|
259
|
-
if (this.searchActive && this.handleSearchKey(character, key
|
|
168
|
+
if (this.searchActive && this.handleSearchKey(character, key)) {
|
|
260
169
|
this.syncValue();
|
|
261
170
|
renderPrompt(prompt);
|
|
262
171
|
return;
|
|
263
172
|
}
|
|
264
|
-
if (character
|
|
173
|
+
if (isSearchToggleKey(character, key) && !this.searchActive) {
|
|
265
174
|
this.searchActive = true;
|
|
266
175
|
this.query = "";
|
|
267
176
|
this.cursor = this.firstSelectableIndex();
|
|
@@ -281,6 +190,8 @@ class SearchablePrompt {
|
|
|
281
190
|
renderPrompt(prompt);
|
|
282
191
|
try {
|
|
283
192
|
const nextScope = await currentScope.toggle();
|
|
193
|
+
if (this.promptClosed)
|
|
194
|
+
return;
|
|
284
195
|
this.entries = nextScope.entries.map(buildSearchableWorktreeEntry);
|
|
285
196
|
this.scope = {
|
|
286
197
|
label: nextScope.label,
|
|
@@ -292,13 +203,16 @@ class SearchablePrompt {
|
|
|
292
203
|
this.syncValue();
|
|
293
204
|
}
|
|
294
205
|
catch (error) {
|
|
206
|
+
if (this.promptClosed)
|
|
207
|
+
return;
|
|
295
208
|
prompt.error =
|
|
296
209
|
error instanceof Error ? error.message : "Could not load worktrees";
|
|
297
210
|
prompt.state = "error";
|
|
298
211
|
}
|
|
299
212
|
finally {
|
|
300
213
|
this.scopeTogglePending = false;
|
|
301
|
-
|
|
214
|
+
if (!this.promptClosed)
|
|
215
|
+
renderPrompt(prompt);
|
|
302
216
|
}
|
|
303
217
|
}
|
|
304
218
|
handleEscapeKey(prompt) {
|
|
@@ -313,12 +227,15 @@ class SearchablePrompt {
|
|
|
313
227
|
this.cancel(prompt);
|
|
314
228
|
}
|
|
315
229
|
cancel(prompt) {
|
|
230
|
+
this.promptClosed = true;
|
|
316
231
|
prompt.state = "cancel";
|
|
317
232
|
renderPrompt(prompt);
|
|
318
233
|
closePrompt(prompt);
|
|
319
234
|
}
|
|
320
|
-
handleSearchKey(character,
|
|
321
|
-
|
|
235
|
+
handleSearchKey(character, key) {
|
|
236
|
+
const keyName = key?.name;
|
|
237
|
+
const input = character ?? key?.sequence;
|
|
238
|
+
if (keyName === "space" || input === " ") {
|
|
322
239
|
return false;
|
|
323
240
|
}
|
|
324
241
|
if (keyName === "backspace" || keyName === "delete") {
|
|
@@ -326,8 +243,8 @@ class SearchablePrompt {
|
|
|
326
243
|
this.cursor = this.firstSelectableIndex();
|
|
327
244
|
return true;
|
|
328
245
|
}
|
|
329
|
-
if (isPrintableSearchCharacter(
|
|
330
|
-
this.query +=
|
|
246
|
+
if (isPrintableSearchCharacter(input)) {
|
|
247
|
+
this.query += input;
|
|
331
248
|
this.cursor = this.firstSelectableIndex();
|
|
332
249
|
return true;
|
|
333
250
|
}
|
|
@@ -373,6 +290,7 @@ class SearchablePrompt {
|
|
|
373
290
|
submit(prompt) {
|
|
374
291
|
const entry = this.visibleEntries()[this.cursor];
|
|
375
292
|
if (!this.options.multiple) {
|
|
293
|
+
this.promptClosed = true;
|
|
376
294
|
prompt.value = isSelectableEntry(entry) ? entry.value : null;
|
|
377
295
|
prompt.state = "submit";
|
|
378
296
|
renderPrompt(prompt);
|
|
@@ -386,6 +304,7 @@ class SearchablePrompt {
|
|
|
386
304
|
return;
|
|
387
305
|
}
|
|
388
306
|
prompt.value = [...this.selected];
|
|
307
|
+
this.promptClosed = true;
|
|
389
308
|
prompt.state = "submit";
|
|
390
309
|
renderPrompt(prompt);
|
|
391
310
|
closePrompt(prompt);
|
|
@@ -592,7 +511,7 @@ class SearchablePrompt {
|
|
|
592
511
|
return index === -1 ? 0 : index;
|
|
593
512
|
}
|
|
594
513
|
visibleEntries() {
|
|
595
|
-
const query =
|
|
514
|
+
const query = normalizePromptQuery(this.query);
|
|
596
515
|
if (query === null) {
|
|
597
516
|
return this.entries;
|
|
598
517
|
}
|
|
@@ -640,7 +559,10 @@ function onWorktreeKeypress(character, key) {
|
|
|
640
559
|
this.worktreePrompt.handleKeypress(this, character, key);
|
|
641
560
|
}
|
|
642
561
|
function isScopeToggleKey(character, key) {
|
|
643
|
-
return key?.name === "tab" || character === "\t";
|
|
562
|
+
return key?.name === "tab" || character === "\t" || key?.sequence === "\t";
|
|
563
|
+
}
|
|
564
|
+
function isSearchToggleKey(character, key) {
|
|
565
|
+
return character === "/" || key?.sequence === "/";
|
|
644
566
|
}
|
|
645
567
|
function resolvePromptAction(character, key) {
|
|
646
568
|
if ((key?.ctrl && key.name === "c") || character === "\u0003") {
|
|
@@ -924,61 +846,6 @@ function buildWorktreePromptEntry(source, info, pullRequests, lastUsedTimestamp,
|
|
|
924
846
|
task: info.task,
|
|
925
847
|
};
|
|
926
848
|
}
|
|
927
|
-
async function readSourcePullRequests(source, queryPullRequests, pullRequestsByRepository) {
|
|
928
|
-
if (source.repoRoot === undefined || source.worktree.branch === null) {
|
|
929
|
-
return [];
|
|
930
|
-
}
|
|
931
|
-
if (pullRequestsByRepository !== null) {
|
|
932
|
-
return sortSourcePullRequests(pullRequestsByRepository
|
|
933
|
-
.get(source.repoRoot)
|
|
934
|
-
?.filter((pullRequest) => pullRequest.sourceBranch === source.worktree.branch));
|
|
935
|
-
}
|
|
936
|
-
try {
|
|
937
|
-
const pullRequests = await queryPullRequests(source.repoRoot, source.worktree.branch);
|
|
938
|
-
return sortSourcePullRequests(pullRequests);
|
|
939
|
-
}
|
|
940
|
-
catch {
|
|
941
|
-
// PR metadata is optional selector decoration; preserve the worktree entry on lookup failures.
|
|
942
|
-
return [];
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
async function readRepositoryPullRequests(sources, queryPullRequests) {
|
|
946
|
-
const repoRoots = [
|
|
947
|
-
...new Set(sources.flatMap((source) => source.repoRoot === undefined || source.worktree.branch === null
|
|
948
|
-
? []
|
|
949
|
-
: [source.repoRoot])),
|
|
950
|
-
];
|
|
951
|
-
const results = await mapWithConcurrency(repoRoots, MAX_PULL_REQUEST_REPOSITORY_QUERY_CONCURRENCY, async (repoRoot) => {
|
|
952
|
-
try {
|
|
953
|
-
return [
|
|
954
|
-
repoRoot,
|
|
955
|
-
sortSourcePullRequests(await queryPullRequests(repoRoot)),
|
|
956
|
-
];
|
|
957
|
-
}
|
|
958
|
-
catch {
|
|
959
|
-
return [repoRoot, []];
|
|
960
|
-
}
|
|
961
|
-
});
|
|
962
|
-
return new Map(results);
|
|
963
|
-
}
|
|
964
|
-
async function mapWithConcurrency(items, limit, mapper) {
|
|
965
|
-
const results = new Array(items.length);
|
|
966
|
-
let nextIndex = 0;
|
|
967
|
-
async function readNext() {
|
|
968
|
-
for (;;) {
|
|
969
|
-
const index = nextIndex;
|
|
970
|
-
nextIndex += 1;
|
|
971
|
-
if (index >= items.length)
|
|
972
|
-
return;
|
|
973
|
-
results[index] = await mapper(items[index]);
|
|
974
|
-
}
|
|
975
|
-
}
|
|
976
|
-
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => readNext()));
|
|
977
|
-
return results;
|
|
978
|
-
}
|
|
979
|
-
function sortSourcePullRequests(pullRequests) {
|
|
980
|
-
return [...(pullRequests ?? [])].sort((a, b) => a.number - b.number);
|
|
981
|
-
}
|
|
982
849
|
export function formatWorktreeBranchLabel(branch, pullRequests = []) {
|
|
983
850
|
const branchLabel = branch ?? "(detached)";
|
|
984
851
|
if (pullRequests.length === 0)
|
|
@@ -1048,20 +915,6 @@ function isYesterday(timestamp, now) {
|
|
|
1048
915
|
date.getMonth() === yesterday.getMonth() &&
|
|
1049
916
|
date.getDate() === yesterday.getDate());
|
|
1050
917
|
}
|
|
1051
|
-
function buildSearchText(repoName, worktree) {
|
|
1052
|
-
return [
|
|
1053
|
-
repoName,
|
|
1054
|
-
worktree.branch ?? "detached",
|
|
1055
|
-
worktree.path,
|
|
1056
|
-
`${repoName}/${worktree.branch ?? "detached"}`,
|
|
1057
|
-
]
|
|
1058
|
-
.join(" ")
|
|
1059
|
-
.toLowerCase();
|
|
1060
|
-
}
|
|
1061
|
-
function normalizeQuery(query) {
|
|
1062
|
-
const normalized = query?.trim().toLowerCase();
|
|
1063
|
-
return normalized && normalized.length > 0 ? normalized : null;
|
|
1064
|
-
}
|
|
1065
918
|
function comparePromptEntries(a, b) {
|
|
1066
919
|
if (a.isCurrent && !b.isCurrent)
|
|
1067
920
|
return -1;
|
|
@@ -1079,18 +932,10 @@ function comparePromptEntries(a, b) {
|
|
|
1079
932
|
(a.branch ?? "").localeCompare(b.branch ?? "") ||
|
|
1080
933
|
a.path.localeCompare(b.path));
|
|
1081
934
|
}
|
|
935
|
+
function normalizePromptQuery(query) {
|
|
936
|
+
const normalized = query?.trim().toLowerCase();
|
|
937
|
+
return normalized && normalized.length > 0 ? normalized : null;
|
|
938
|
+
}
|
|
1082
939
|
function groupRank(group) {
|
|
1083
940
|
return group === "recent" ? 0 : 1;
|
|
1084
941
|
}
|
|
1085
|
-
function scoreWorktreeMatch(entry, query) {
|
|
1086
|
-
const branch = entry.branch ?? "detached";
|
|
1087
|
-
const exactCandidates = [
|
|
1088
|
-
branch,
|
|
1089
|
-
entry.path,
|
|
1090
|
-
`${entry.repoName}/${branch}`,
|
|
1091
|
-
].map((candidate) => candidate.toLowerCase());
|
|
1092
|
-
if (exactCandidates.includes(query)) {
|
|
1093
|
-
return 1000;
|
|
1094
|
-
}
|
|
1095
|
-
return buildSearchText(entry.repoName, entry).includes(query) ? 1 : null;
|
|
1096
|
-
}
|
package/man/man1/gji-back.1
CHANGED
package/man/man1/gji-clean.1
CHANGED
package/man/man1/gji-config.1
CHANGED
package/man/man1/gji-doctor.1
CHANGED
package/man/man1/gji-done.1
CHANGED
package/man/man1/gji-go.1
CHANGED
package/man/man1/gji-history.1
CHANGED
package/man/man1/gji-init.1
CHANGED
package/man/man1/gji-ls.1
CHANGED
package/man/man1/gji-new.1
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
.TH GJI\-NEW 1 "August 2026" "gji 0.
|
|
1
|
+
.TH GJI\-NEW 1 "August 2026" "gji 0.13.0" "User Commands"
|
|
2
2
|
.SH NAME
|
|
3
|
-
gji\-new \- create a
|
|
3
|
+
gji\-new \- create a branch with an optional linked worktree
|
|
4
4
|
.SH SYNOPSIS
|
|
5
5
|
.B gji new [\fIoptions\fR] [options] [branch]
|
|
6
6
|
.SH DESCRIPTION
|
|
7
|
-
create a
|
|
7
|
+
create a branch with an optional linked worktree
|
|
8
8
|
.SH BOOTSTRAP_POLICY
|
|
9
9
|
dependencyBuildCommand overrides Cargo's default cargo check. Supported lockfiles are installed automatically in new worktrees; set dependencyBootstrap to off or pass \-\-no\-install to skip setup. Sync\-file failures stop dependency installation and after\-create hooks. JSON bootstrap events include structured reasons, and failed setup reports the created worktree path.
|
|
10
10
|
|
|
@@ -13,6 +13,9 @@ Dependency bootstrap never copies dependency trees or build caches. pnpm, Yarn,
|
|
|
13
13
|
|
|
14
14
|
.SH OPTIONS
|
|
15
15
|
.TP
|
|
16
|
+
.B \-\-branch\-only
|
|
17
|
+
update the default branch and create the new branch in the current worktree
|
|
18
|
+
.TP
|
|
16
19
|
.B \-f, \-\-force
|
|
17
20
|
remove and recreate the worktree if the target path already exists
|
|
18
21
|
.TP
|
package/man/man1/gji-open.1
CHANGED
package/man/man1/gji-pr.1
CHANGED
package/man/man1/gji-remove.1
CHANGED
package/man/man1/gji-root.1
CHANGED
package/man/man1/gji-run-hook.1
CHANGED
package/man/man1/gji-status.1
CHANGED
package/man/man1/gji-sync.1
CHANGED
package/man/man1/gji-task.1
CHANGED
package/man/man1/gji-undo.1
CHANGED
package/man/man1/gji-warp.1
CHANGED
package/man/man1/gji.1
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
.TH GJI 1 "August 2026" "gji 0.
|
|
1
|
+
.TH GJI 1 "August 2026" "gji 0.13.0" "User Commands"
|
|
2
2
|
.SH NAME
|
|
3
3
|
gji \- Context switching without the mess.
|
|
4
4
|
.SH SYNOPSIS
|
|
@@ -12,7 +12,7 @@ eliminating the need for \fBgit stash\fR when switching contexts.
|
|
|
12
12
|
.SH COMMANDS
|
|
13
13
|
.TP
|
|
14
14
|
.B new [options] [branch]
|
|
15
|
-
create a
|
|
15
|
+
create a branch with an optional linked worktree
|
|
16
16
|
.TP
|
|
17
17
|
.B done [options] [branch]
|
|
18
18
|
finish the current linked worktree and return safely
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { DependencyBootstrapMode } from "./dependency-bootstrap.js";
|
|
2
|
-
import { type DependencyBootstrapPreview } from "./dependency-bootstrap.js";
|
|
3
|
-
export declare function createDependencyBootstrapPreview(mode: DependencyBootstrapMode, context: {
|
|
4
|
-
repoRoot: string;
|
|
5
|
-
currentRoot?: string;
|
|
6
|
-
worktreePath: string;
|
|
7
|
-
cargoBuildCommand?: string;
|
|
8
|
-
}): Promise<DependencyBootstrapPreview>;
|
|
9
|
-
export declare function formatDependencyBootstrapPreview(preview: DependencyBootstrapPreview | undefined): string;
|
package/dist/context-card.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function renderContextCard(worktreePath: string): Promise<string | null>;
|
package/dist/format-bytes.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function formatBytes(bytes: number | undefined): string;
|
package/dist/format-bytes.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export function formatBytes(bytes) {
|
|
2
|
-
if (bytes === undefined)
|
|
3
|
-
return "size unavailable";
|
|
4
|
-
if (bytes < 1024)
|
|
5
|
-
return `${bytes} B`;
|
|
6
|
-
const units = ["KB", "MB", "GB", "TB"];
|
|
7
|
-
let value = bytes;
|
|
8
|
-
let unit = -1;
|
|
9
|
-
while (value >= 1024 && unit < units.length - 1) {
|
|
10
|
-
value /= 1024;
|
|
11
|
-
unit += 1;
|
|
12
|
-
}
|
|
13
|
-
return `${value.toFixed(value >= 10 ? 0 : 1)} ${units[unit]}`;
|
|
14
|
-
}
|
package/dist/git.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
export interface WorktreeHealth {
|
|
2
|
-
ahead: number;
|
|
3
|
-
behind: number;
|
|
4
|
-
hasUpstream: boolean;
|
|
5
|
-
upstreamGone: boolean;
|
|
6
|
-
status: "clean" | "dirty";
|
|
7
|
-
}
|
|
8
|
-
export interface RemoteBase {
|
|
9
|
-
branch: string;
|
|
10
|
-
ref: string;
|
|
11
|
-
}
|
|
12
|
-
export declare function runGit(cwd: string, args: string[]): Promise<string>;
|
|
13
|
-
export declare function readWorktreeHealth(cwd: string): Promise<WorktreeHealth>;
|
|
14
|
-
export declare function isDirtyWorktree(cwd: string): Promise<boolean>;
|
|
15
|
-
export declare function isBranchMergedInto(cwd: string, branch: string, base?: string): Promise<boolean>;
|
|
16
|
-
export declare function resolveRemoteDefaultBranch(cwd: string, remote: string): Promise<string | null>;
|
|
17
|
-
export declare function resolveRemoteBase(cwd: string, remote: string, configuredBranch?: string): Promise<RemoteBase | null>;
|
|
18
|
-
export declare function readBranchLastCommitTimestamp(cwd: string, branch: string): Promise<number | null>;
|
package/dist/repo.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export interface RepositoryContext {
|
|
2
|
-
currentRoot: string;
|
|
3
|
-
gitCommonDir: string;
|
|
4
|
-
isWorktree: boolean;
|
|
5
|
-
repoName: string;
|
|
6
|
-
repoRoot: string;
|
|
7
|
-
}
|
|
8
|
-
export interface WorktreeEntry {
|
|
9
|
-
branch: string | null;
|
|
10
|
-
isCurrent: boolean;
|
|
11
|
-
path: string;
|
|
12
|
-
}
|
|
13
|
-
export declare function detectRepository(cwd: string): Promise<RepositoryContext>;
|
|
14
|
-
export declare function worktreeGitDir(worktreePath: string): Promise<string>;
|
|
15
|
-
export declare function resolveWorktreePath(repoRoot: string, branch: string, basePath?: string): string;
|
|
16
|
-
export declare function validateBranchName(name: string): string | null;
|
|
17
|
-
export declare function hasLocalBranch(repoRoot: string, branch: string): Promise<boolean>;
|
|
18
|
-
export declare function hasRemoteBranch(repoRoot: string, remote: string, branch: string): Promise<boolean>;
|
|
19
|
-
export declare function getRepositoryRemoteUrl(repoRoot: string, remote: string): Promise<string | null>;
|
|
20
|
-
export declare function listWorktrees(cwd: string): Promise<WorktreeEntry[]>;
|