@solaqua/gji 0.10.0 → 0.12.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 +83 -426
- package/dist/back.d.ts +3 -0
- package/dist/back.js +19 -7
- package/dist/bootstrap-output.d.ts +3 -0
- package/dist/bootstrap-output.js +27 -0
- package/dist/bootstrap-preview.d.ts +10 -0
- package/dist/bootstrap-preview.js +21 -0
- package/dist/clean.js +27 -3
- package/dist/cli.d.ts +4 -0
- package/dist/cli.js +140 -22
- package/dist/clone-failure-store.d.ts +18 -0
- package/dist/clone-failure-store.js +230 -0
- package/dist/command-runner.d.ts +6 -0
- package/dist/command-runner.js +38 -0
- package/dist/completion.js +7 -2
- package/dist/config-command.d.ts +1 -0
- package/dist/config-command.js +7 -1
- package/dist/config.d.ts +26 -1
- package/dist/config.js +110 -6
- package/dist/context-card.d.ts +1 -0
- package/dist/context-card.js +25 -0
- package/dist/dependency-bootstrap-prompt.d.ts +24 -0
- package/dist/dependency-bootstrap-prompt.js +103 -0
- package/dist/dependency-bootstrap.d.ts +129 -0
- package/dist/dependency-bootstrap.js +566 -0
- package/dist/dir-clone.d.ts +33 -0
- package/dist/dir-clone.js +505 -0
- package/dist/doctor.js +21 -0
- package/dist/done.d.ts +10 -0
- package/dist/done.js +210 -0
- package/dist/file-sync.js +71 -22
- package/dist/format-bytes.d.ts +1 -0
- package/dist/format-bytes.js +14 -0
- package/dist/fs-utils.d.ts +4 -0
- package/dist/fs-utils.js +26 -0
- package/dist/git.d.ts +5 -0
- package/dist/git.js +50 -0
- package/dist/gji-bundle.mjs +6189 -1313
- package/dist/go-resolver.d.ts +35 -0
- package/dist/go-resolver.js +185 -0
- package/dist/go.d.ts +6 -2
- package/dist/go.js +270 -62
- package/dist/hooks.d.ts +2 -1
- package/dist/hooks.js +20 -8
- package/dist/hub.d.ts +31 -0
- package/dist/hub.js +287 -0
- package/dist/init.js +30 -22
- package/dist/install-prompt.d.ts +1 -0
- package/dist/install-prompt.js +3 -25
- package/dist/ls.js +9 -1
- package/dist/navigation-output.d.ts +11 -0
- package/dist/navigation-output.js +6 -0
- package/dist/new.d.ts +14 -2
- package/dist/new.js +422 -38
- package/dist/pr-open.js +2 -10
- package/dist/pr.d.ts +6 -2
- package/dist/pr.js +135 -30
- package/dist/pull-requests.d.ts +1 -0
- package/dist/pull-requests.js +32 -3
- package/dist/remove.js +22 -0
- package/dist/repo.d.ts +4 -0
- package/dist/repo.js +61 -6
- package/dist/run-hook.js +2 -0
- package/dist/safe-destination.d.ts +15 -0
- package/dist/safe-destination.js +166 -0
- package/dist/shell-completion.js +52 -13
- package/dist/slots.d.ts +8 -0
- package/dist/slots.js +61 -0
- package/dist/status.d.ts +2 -0
- package/dist/status.js +9 -1
- package/dist/sync-directories.d.ts +33 -0
- package/dist/sync-directories.js +97 -0
- package/dist/sync-plan.d.ts +16 -0
- package/dist/sync-plan.js +128 -0
- package/dist/sync.js +7 -13
- package/dist/task-command.d.ts +9 -0
- package/dist/task-command.js +29 -0
- package/dist/task.d.ts +9 -0
- package/dist/task.js +37 -0
- package/dist/terminal-text.d.ts +4 -0
- package/dist/terminal-text.js +72 -0
- package/dist/undo.d.ts +36 -0
- package/dist/undo.js +392 -0
- package/dist/warp.d.ts +3 -1
- package/dist/warp.js +32 -125
- package/dist/worktree-bootstrap.d.ts +35 -0
- package/dist/worktree-bootstrap.js +96 -0
- package/dist/worktree-info.d.ts +6 -1
- package/dist/worktree-info.js +14 -4
- package/dist/worktree-management.d.ts +1 -0
- package/dist/worktree-management.js +23 -4
- package/dist/worktree-picker.d.ts +24 -11
- package/dist/worktree-picker.js +176 -95
- package/dist/worktree-prompts.js +1 -1
- package/dist/worktree-source.d.ts +6 -0
- package/dist/worktree-source.js +1 -0
- package/dist/worktree-sources.d.ts +4 -0
- package/dist/worktree-sources.js +77 -0
- 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 +19 -0
- package/man/man1/gji-go.1 +12 -3
- 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 +21 -3
- package/man/man1/gji-open.1 +1 -1
- package/man/man1/gji-pr.1 +7 -1
- package/man/man1/gji-remove.1 +3 -3
- 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 +16 -0
- package/man/man1/gji-undo.1 +16 -0
- package/man/man1/gji-warp.1 +3 -6
- package/man/man1/gji.1 +17 -5
- package/package.json +3 -1
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { lstat, realpath } from "node:fs/promises";
|
|
2
|
+
import { isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
3
|
+
import { validateSyncDirPattern } from "./config.js";
|
|
4
|
+
import { directorySize } from "./dir-clone.js";
|
|
5
|
+
import { isNotFoundError } from "./fs-utils.js";
|
|
6
|
+
import { inspectDestination } from "./safe-destination.js";
|
|
7
|
+
export async function prepareSyncDirectoryPlan(repoRoot, worktreePath, directories) {
|
|
8
|
+
const normalizedDirectories = directories
|
|
9
|
+
.map(validateSyncDirPattern)
|
|
10
|
+
.sort(directoryDepthAscending);
|
|
11
|
+
const plan = [];
|
|
12
|
+
for (const directory of normalizedDirectories) {
|
|
13
|
+
const destination = join(worktreePath, directory);
|
|
14
|
+
const destinationState = await inspectDestination(worktreePath, destination);
|
|
15
|
+
const destinationWasPresent = destinationState.kind === "exists";
|
|
16
|
+
const destinationWarning = destinationState.kind === "unsafe" ? destinationState.reason : undefined;
|
|
17
|
+
try {
|
|
18
|
+
const source = await resolveSyncDirectorySource(repoRoot, directory);
|
|
19
|
+
if (!source) {
|
|
20
|
+
plan.push({
|
|
21
|
+
directory,
|
|
22
|
+
destination,
|
|
23
|
+
worktreePath,
|
|
24
|
+
destinationWasPresent,
|
|
25
|
+
destinationWarning,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
else if ("warning" in source) {
|
|
29
|
+
plan.push({
|
|
30
|
+
directory,
|
|
31
|
+
destination,
|
|
32
|
+
worktreePath,
|
|
33
|
+
destinationWasPresent,
|
|
34
|
+
destinationWarning,
|
|
35
|
+
warning: source.warning,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
plan.push({
|
|
40
|
+
directory,
|
|
41
|
+
destination,
|
|
42
|
+
worktreePath,
|
|
43
|
+
destinationWasPresent,
|
|
44
|
+
destinationWarning,
|
|
45
|
+
source: source.path,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
plan.push({
|
|
51
|
+
directory,
|
|
52
|
+
destination,
|
|
53
|
+
worktreePath,
|
|
54
|
+
destinationWasPresent,
|
|
55
|
+
destinationWarning,
|
|
56
|
+
warning: `could not inspect ${directory}: ${toErrorMessage(error)}`,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return plan;
|
|
61
|
+
}
|
|
62
|
+
export async function estimateSyncDirectoryPlan(plan) {
|
|
63
|
+
const estimates = [];
|
|
64
|
+
for (const entry of plan) {
|
|
65
|
+
if (entry.destinationWasPresent ||
|
|
66
|
+
entry.destinationWarning ||
|
|
67
|
+
!entry.source ||
|
|
68
|
+
entry.warning ||
|
|
69
|
+
isCoveredByCloneAncestor(entry, plan)) {
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
estimates.push({
|
|
74
|
+
bytes: await directorySize(entry.source),
|
|
75
|
+
dir: entry.directory,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// A dry-run is informational; an unreadable source is omitted.
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return estimates;
|
|
83
|
+
}
|
|
84
|
+
export async function estimateSyncDirectories(repoRoot, worktreePath, directories) {
|
|
85
|
+
const plan = await prepareSyncDirectoryPlan(repoRoot, worktreePath, directories);
|
|
86
|
+
return estimateSyncDirectoryPlan(plan);
|
|
87
|
+
}
|
|
88
|
+
async function resolveSyncDirectorySource(repoRoot, directory) {
|
|
89
|
+
const source = join(repoRoot, directory);
|
|
90
|
+
let resolvedSource;
|
|
91
|
+
try {
|
|
92
|
+
resolvedSource = await realpath(source);
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
if (isNotFoundError(error))
|
|
96
|
+
return null;
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
if (!isPathInside(repoRoot, resolvedSource)) {
|
|
100
|
+
return {
|
|
101
|
+
warning: `source symlink resolves outside the repository (${resolvedSource})`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
const stats = await lstat(resolvedSource);
|
|
105
|
+
if (!stats.isDirectory())
|
|
106
|
+
return { warning: "source is not a directory" };
|
|
107
|
+
return { path: resolvedSource };
|
|
108
|
+
}
|
|
109
|
+
function isCoveredByCloneAncestor(entry, plan) {
|
|
110
|
+
return plan.some((candidate) => candidate !== entry &&
|
|
111
|
+
!candidate.destinationWasPresent &&
|
|
112
|
+
!candidate.destinationWarning &&
|
|
113
|
+
!!candidate.source &&
|
|
114
|
+
isPathInside(candidate.directory, entry.directory));
|
|
115
|
+
}
|
|
116
|
+
function directoryDepthAscending(left, right) {
|
|
117
|
+
return left.split(/[\\/]+/u).length - right.split(/[\\/]+/u).length;
|
|
118
|
+
}
|
|
119
|
+
function isPathInside(parent, child) {
|
|
120
|
+
const relativePath = relative(resolve(parent), resolve(child));
|
|
121
|
+
return (relativePath !== "" &&
|
|
122
|
+
!isAbsolute(relativePath) &&
|
|
123
|
+
relativePath !== ".." &&
|
|
124
|
+
!relativePath.startsWith(`..${sep}`));
|
|
125
|
+
}
|
|
126
|
+
function toErrorMessage(error) {
|
|
127
|
+
return error instanceof Error ? error.message : String(error);
|
|
128
|
+
}
|
package/dist/sync.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadEffectiveConfig } from "./config.js";
|
|
2
|
-
import { isDirtyWorktree,
|
|
2
|
+
import { isDirtyWorktree, resolveRemoteBase, runGit, } from "./git.js";
|
|
3
3
|
import { comparePaths } from "./paths.js";
|
|
4
4
|
import { detectRepository, listWorktrees } from "./repo.js";
|
|
5
5
|
export async function runSyncCommand(options) {
|
|
@@ -7,11 +7,9 @@ export async function runSyncCommand(options) {
|
|
|
7
7
|
const config = await loadEffectiveConfig(repository.repoRoot, undefined, options.stderr);
|
|
8
8
|
const worktrees = await listWorktrees(options.cwd);
|
|
9
9
|
const remote = resolveConfiguredString(config.syncRemote) ?? "origin";
|
|
10
|
-
let
|
|
10
|
+
let remoteBase;
|
|
11
11
|
try {
|
|
12
|
-
|
|
13
|
-
resolveConfiguredString(config.syncDefaultBranch) ??
|
|
14
|
-
(await resolveRemoteDefaultBranch(repository.repoRoot, remote));
|
|
12
|
+
remoteBase = await resolveRemoteBase(repository.repoRoot, remote, resolveConfiguredString(config.syncDefaultBranch) ?? undefined);
|
|
15
13
|
}
|
|
16
14
|
catch {
|
|
17
15
|
emitError(options, `Unable to reach remote '${remote}'`);
|
|
@@ -20,7 +18,7 @@ export async function runSyncCommand(options) {
|
|
|
20
18
|
}
|
|
21
19
|
return 1;
|
|
22
20
|
}
|
|
23
|
-
if (!
|
|
21
|
+
if (!remoteBase) {
|
|
24
22
|
emitError(options, "Unable to determine the default branch for sync.");
|
|
25
23
|
if (!options.json) {
|
|
26
24
|
options.stderr(`Hint: Add the remote with: git remote add ${remote} <url>\n`);
|
|
@@ -50,15 +48,11 @@ export async function runSyncCommand(options) {
|
|
|
50
48
|
}
|
|
51
49
|
const updatedWorktrees = [];
|
|
52
50
|
for (const worktree of targetWorktrees) {
|
|
53
|
-
if (worktree.branch ===
|
|
54
|
-
await runGit(worktree.path, [
|
|
55
|
-
"merge",
|
|
56
|
-
"--ff-only",
|
|
57
|
-
`${remote}/${defaultBranch}`,
|
|
58
|
-
]);
|
|
51
|
+
if (worktree.branch === remoteBase.branch) {
|
|
52
|
+
await runGit(worktree.path, ["merge", "--ff-only", remoteBase.ref]);
|
|
59
53
|
}
|
|
60
54
|
else {
|
|
61
|
-
await runGit(worktree.path, ["rebase",
|
|
55
|
+
await runGit(worktree.path, ["rebase", remoteBase.ref]);
|
|
62
56
|
}
|
|
63
57
|
updatedWorktrees.push(worktree);
|
|
64
58
|
if (!options.json) {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface TaskCommandOptions {
|
|
2
|
+
clear?: boolean;
|
|
3
|
+
cwd: string;
|
|
4
|
+
json?: boolean;
|
|
5
|
+
task?: string;
|
|
6
|
+
stderr: (chunk: string) => void;
|
|
7
|
+
stdout: (chunk: string) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function runTaskCommand(options: TaskCommandOptions): Promise<number>;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { detectRepository } from "./repo.js";
|
|
2
|
+
import { clearTask, readTask, writeTask } from "./task.js";
|
|
3
|
+
export async function runTaskCommand(options) {
|
|
4
|
+
const repository = await detectRepository(options.cwd);
|
|
5
|
+
if (options.clear && options.task !== undefined)
|
|
6
|
+
return emitTaskError(options, "--clear cannot be combined with a task");
|
|
7
|
+
if (options.clear)
|
|
8
|
+
await clearTask(repository.currentRoot);
|
|
9
|
+
else if (options.task !== undefined)
|
|
10
|
+
await writeTask(repository.currentRoot, options.task);
|
|
11
|
+
const task = await readTask(repository.currentRoot);
|
|
12
|
+
if (options.json) {
|
|
13
|
+
options.stdout(`${JSON.stringify({ task: task?.task ?? null }, null, 2)}\n`);
|
|
14
|
+
}
|
|
15
|
+
else if (task) {
|
|
16
|
+
options.stdout(`${task.task}\n`);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
options.stdout('no task set — set one: gji task "..."\n');
|
|
20
|
+
}
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
function emitTaskError(options, message) {
|
|
24
|
+
if (options.json)
|
|
25
|
+
options.stderr(`${JSON.stringify({ error: message })}\n`);
|
|
26
|
+
else
|
|
27
|
+
options.stderr(`gji task: ${message}\n`);
|
|
28
|
+
return 1;
|
|
29
|
+
}
|
package/dist/task.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type TaskSource = "manual" | "pr";
|
|
2
|
+
export interface TaskMetadata {
|
|
3
|
+
source: TaskSource;
|
|
4
|
+
task: string;
|
|
5
|
+
updatedAt: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function readTask(worktreePath: string): Promise<TaskMetadata | null>;
|
|
8
|
+
export declare function writeTask(worktreePath: string, task: string, source?: TaskSource): Promise<TaskMetadata>;
|
|
9
|
+
export declare function clearTask(worktreePath: string): Promise<void>;
|
package/dist/task.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFile, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { worktreeGitDir } from "./repo.js";
|
|
4
|
+
export async function readTask(worktreePath) {
|
|
5
|
+
try {
|
|
6
|
+
const parsed = JSON.parse(await readFile(await taskPath(worktreePath), "utf8"));
|
|
7
|
+
return isTaskMetadata(parsed) ? parsed : null;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export async function writeTask(worktreePath, task, source = "manual") {
|
|
14
|
+
const metadata = { source, task, updatedAt: Date.now() };
|
|
15
|
+
const path = await taskPath(worktreePath);
|
|
16
|
+
await writeFile(path, `${JSON.stringify(metadata, null, 2)}\n`, "utf8");
|
|
17
|
+
return metadata;
|
|
18
|
+
}
|
|
19
|
+
export async function clearTask(worktreePath) {
|
|
20
|
+
try {
|
|
21
|
+
await unlink(await taskPath(worktreePath));
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// Missing task metadata is already the desired state.
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function taskPath(worktreePath) {
|
|
28
|
+
return join(await worktreeGitDir(worktreePath), "gji-task.json");
|
|
29
|
+
}
|
|
30
|
+
function isTaskMetadata(value) {
|
|
31
|
+
if (typeof value !== "object" || value === null)
|
|
32
|
+
return false;
|
|
33
|
+
const candidate = value;
|
|
34
|
+
return (typeof candidate.task === "string" &&
|
|
35
|
+
(candidate.source === "manual" || candidate.source === "pr") &&
|
|
36
|
+
typeof candidate.updatedAt === "number");
|
|
37
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function sanitizeTerminalText(value: string): string;
|
|
2
|
+
export declare function middleEllipsize(value: string, maxLength: number): string;
|
|
3
|
+
export declare function startEllipsize(value: string, maxLength: number): string;
|
|
4
|
+
export declare function terminalWidth(value: string): number;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export function sanitizeTerminalText(value) {
|
|
2
|
+
return Array.from(value, (character) => {
|
|
3
|
+
const codePoint = character.codePointAt(0) ?? 0;
|
|
4
|
+
return codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f)
|
|
5
|
+
? " "
|
|
6
|
+
: character;
|
|
7
|
+
}).join("");
|
|
8
|
+
}
|
|
9
|
+
export function middleEllipsize(value, maxLength) {
|
|
10
|
+
if (terminalWidth(value) <= maxLength)
|
|
11
|
+
return value;
|
|
12
|
+
if (maxLength <= 1)
|
|
13
|
+
return "…";
|
|
14
|
+
const keep = maxLength - 1;
|
|
15
|
+
const start = takeTerminalColumns(value, Math.ceil(keep / 2), "start");
|
|
16
|
+
const end = takeTerminalColumns(value, Math.floor(keep / 2), "end");
|
|
17
|
+
return `${start}…${end}`;
|
|
18
|
+
}
|
|
19
|
+
export function startEllipsize(value, maxLength) {
|
|
20
|
+
if (terminalWidth(value) <= maxLength)
|
|
21
|
+
return value;
|
|
22
|
+
if (maxLength <= 1)
|
|
23
|
+
return "…";
|
|
24
|
+
return `…${takeTerminalColumns(value, maxLength - 1, "end")}`;
|
|
25
|
+
}
|
|
26
|
+
export function terminalWidth(value) {
|
|
27
|
+
let width = 0;
|
|
28
|
+
for (const character of Array.from(value)) {
|
|
29
|
+
width += characterTerminalWidth(character);
|
|
30
|
+
}
|
|
31
|
+
return width;
|
|
32
|
+
}
|
|
33
|
+
function takeTerminalColumns(value, maxWidth, direction) {
|
|
34
|
+
const characters = direction === "start" ? Array.from(value) : Array.from(value).reverse();
|
|
35
|
+
const kept = [];
|
|
36
|
+
let width = 0;
|
|
37
|
+
for (const character of characters) {
|
|
38
|
+
const characterWidth = characterTerminalWidth(character);
|
|
39
|
+
if (width + characterWidth > maxWidth)
|
|
40
|
+
break;
|
|
41
|
+
kept.push(character);
|
|
42
|
+
width += characterWidth;
|
|
43
|
+
}
|
|
44
|
+
return direction === "start" ? kept.join("") : kept.reverse().join("");
|
|
45
|
+
}
|
|
46
|
+
function characterTerminalWidth(character) {
|
|
47
|
+
const codePoint = character.codePointAt(0);
|
|
48
|
+
if (codePoint === undefined)
|
|
49
|
+
return 0;
|
|
50
|
+
if (isZeroWidthCodePoint(codePoint) || /\p{Mark}/u.test(character))
|
|
51
|
+
return 0;
|
|
52
|
+
return isWideCodePoint(codePoint) ? 2 : 1;
|
|
53
|
+
}
|
|
54
|
+
function isZeroWidthCodePoint(codePoint) {
|
|
55
|
+
return (codePoint === 0 ||
|
|
56
|
+
codePoint === 0x200d ||
|
|
57
|
+
(codePoint >= 0xfe00 && codePoint <= 0xfe0f));
|
|
58
|
+
}
|
|
59
|
+
function isWideCodePoint(codePoint) {
|
|
60
|
+
return ((codePoint >= 0x1100 && codePoint <= 0x115f) ||
|
|
61
|
+
codePoint === 0x2329 ||
|
|
62
|
+
codePoint === 0x232a ||
|
|
63
|
+
(codePoint >= 0x2e80 && codePoint <= 0xa4cf) ||
|
|
64
|
+
(codePoint >= 0xac00 && codePoint <= 0xd7a3) ||
|
|
65
|
+
(codePoint >= 0xf900 && codePoint <= 0xfaff) ||
|
|
66
|
+
(codePoint >= 0xfe10 && codePoint <= 0xfe19) ||
|
|
67
|
+
(codePoint >= 0xfe30 && codePoint <= 0xfe6f) ||
|
|
68
|
+
(codePoint >= 0xff00 && codePoint <= 0xff60) ||
|
|
69
|
+
(codePoint >= 0xffe0 && codePoint <= 0xffe6) ||
|
|
70
|
+
(codePoint >= 0x1f300 && codePoint <= 0x1faff) ||
|
|
71
|
+
(codePoint >= 0x20000 && codePoint <= 0x3fffd));
|
|
72
|
+
}
|
package/dist/undo.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type WorktreeEntry } from "./repo.js";
|
|
2
|
+
export type UndoOperation = "remove" | "clean" | "done";
|
|
3
|
+
export interface UndoEntry {
|
|
4
|
+
branch: string | null;
|
|
5
|
+
headSha: string;
|
|
6
|
+
path: string;
|
|
7
|
+
upstream: string | null;
|
|
8
|
+
wasDirty: boolean;
|
|
9
|
+
}
|
|
10
|
+
export interface UndoRecord {
|
|
11
|
+
id: string;
|
|
12
|
+
op: UndoOperation;
|
|
13
|
+
repoRoot: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
entries: UndoEntry[];
|
|
16
|
+
}
|
|
17
|
+
export interface UndoRestoreResult {
|
|
18
|
+
restored: UndoEntry[];
|
|
19
|
+
failed: Array<UndoEntry & {
|
|
20
|
+
message: string;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export interface UndoCommandOptions {
|
|
24
|
+
cwd: string;
|
|
25
|
+
id?: string;
|
|
26
|
+
list?: boolean;
|
|
27
|
+
json?: boolean;
|
|
28
|
+
stderr: (chunk: string) => void;
|
|
29
|
+
stdout: (chunk: string) => void;
|
|
30
|
+
}
|
|
31
|
+
export declare function undoLogPath(home?: string): string;
|
|
32
|
+
export declare function loadUndoRecords(home?: string): Promise<UndoRecord[]>;
|
|
33
|
+
export declare function recordUndoOperation(op: UndoOperation, repoRoot: string, worktrees: WorktreeEntry[], home?: string): Promise<UndoRecord | null>;
|
|
34
|
+
export declare function finalizeUndoOperation(record: UndoRecord, removedWorktrees: WorktreeEntry[], home?: string): Promise<void>;
|
|
35
|
+
export declare function restoreUndoRecord(record: UndoRecord, home?: string): Promise<UndoRestoreResult>;
|
|
36
|
+
export declare function runUndoCommand(options: UndoCommandOptions): Promise<number>;
|