@solaqua/gji 0.13.0 → 0.15.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.
Files changed (45) hide show
  1. package/dist/application/worktree/go-resolution.js +5 -3
  2. package/dist/application/worktree/sources.d.ts +13 -4
  3. package/dist/application/worktree/sources.js +34 -13
  4. package/dist/cli/commands/clean.d.ts +4 -2
  5. package/dist/cli/commands/clean.js +338 -65
  6. package/dist/cli/commands/go.d.ts +2 -1
  7. package/dist/cli/commands/go.js +17 -13
  8. package/dist/cli/commands/hub.js +4 -1
  9. package/dist/cli/commands/warp.js +6 -4
  10. package/dist/cli/program.js +5 -0
  11. package/dist/domain/shared/concurrency.d.ts +1 -1
  12. package/dist/domain/shared/concurrency.js +6 -1
  13. package/dist/domain/worktree/path-filter.d.ts +13 -0
  14. package/dist/domain/worktree/path-filter.js +72 -0
  15. package/dist/domain/worktree/policy.d.ts +1 -0
  16. package/dist/domain/worktree/policy.js +1 -1
  17. package/dist/gji-bundle.mjs +746 -368
  18. package/dist/infrastructure/git/refs.js +3 -11
  19. package/dist/presentation/shell/completion.js +7 -2
  20. package/dist/presentation/worktree/picker.d.ts +9 -6
  21. package/dist/presentation/worktree/picker.js +19 -2
  22. package/man/man1/gji-back.1 +1 -1
  23. package/man/man1/gji-clean.1 +4 -1
  24. package/man/man1/gji-completion.1 +1 -1
  25. package/man/man1/gji-config.1 +1 -1
  26. package/man/man1/gji-doctor.1 +1 -1
  27. package/man/man1/gji-done.1 +1 -1
  28. package/man/man1/gji-go.1 +1 -1
  29. package/man/man1/gji-history.1 +1 -1
  30. package/man/man1/gji-init.1 +1 -1
  31. package/man/man1/gji-ls.1 +1 -1
  32. package/man/man1/gji-new.1 +1 -1
  33. package/man/man1/gji-open.1 +1 -1
  34. package/man/man1/gji-pr.1 +1 -1
  35. package/man/man1/gji-remove.1 +1 -1
  36. package/man/man1/gji-root.1 +1 -1
  37. package/man/man1/gji-run-hook.1 +1 -1
  38. package/man/man1/gji-status.1 +1 -1
  39. package/man/man1/gji-sync-files.1 +1 -1
  40. package/man/man1/gji-sync.1 +1 -1
  41. package/man/man1/gji-task.1 +1 -1
  42. package/man/man1/gji-undo.1 +1 -1
  43. package/man/man1/gji-warp.1 +1 -1
  44. package/man/man1/gji.1 +1 -1
  45. package/package.json +1 -1
@@ -46,10 +46,12 @@ export async function resolveGoBranch(options) {
46
46
  return finish({ kind: "existing", source: pullRequestMatches[0] }, false);
47
47
  }
48
48
  }
49
- let skippedRegisteredRepos = 0;
50
- const registeredSources = await listRegisteredWorktreeSources(cwd, sourceDependencies, () => {
51
- skippedRegisteredRepos++;
49
+ const registered = await listRegisteredWorktreeSources({
50
+ cwd,
51
+ repositoryPort: sourceDependencies,
52
52
  });
53
+ const registeredSources = registered.sources;
54
+ const skippedRegisteredRepos = registered.skipped.length;
53
55
  const crossRepoSources = registeredSources.filter((source) => source.repoRoot !== repository?.repoRoot);
54
56
  const crossMatches = await resolveExistingWorktreeMatches(crossRepoSources, branch, (!localBranch && !remoteBranch) || isPullRequestUrl(branch), repositoryPort);
55
57
  if (crossMatches.length === 1) {
@@ -1,7 +1,16 @@
1
1
  import type { RepoRegistryEntry } from "../../domain/repository/registry.js";
2
2
  import type { WorktreeSource } from "../../domain/worktree/source.js";
3
3
  import type { RepositoryContextPort, RepositoryRegistryPort, WorktreePort } from "../../ports/repository.js";
4
- type WorktreeSourceDependencies = RepositoryContextPort & RepositoryRegistryPort & WorktreePort;
5
- export declare function listRegisteredWorktreeSources(cwd: string, repositoryPort: WorktreeSourceDependencies, onSkipped?: (entry: RepoRegistryEntry) => void): Promise<WorktreeSource[]>;
6
- export declare function listDiscoverableWorktreeSources(cwd: string, repositoryPort: WorktreeSourceDependencies, onSkipped?: (entry: RepoRegistryEntry) => void): Promise<WorktreeSource[]>;
7
- export {};
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[];
@@ -1,25 +1,31 @@
1
1
  import { mapWithConcurrency } from "../../domain/shared/concurrency.js";
2
2
  const MAX_REPOSITORY_DISCOVERY_CONCURRENCY = 4;
3
- export async function listRegisteredWorktreeSources(cwd, repositoryPort, onSkipped) {
3
+ export async function listRegisteredWorktreeSources(options) {
4
+ const { cwd, repositoryPort, signal } = options;
5
+ throwIfAborted(signal);
4
6
  const registry = await repositoryPort.loadRegistry();
5
7
  const currentRoot = await repositoryPort
6
8
  .detectRepository(cwd)
7
9
  .then((repository) => repository.currentRoot)
8
10
  .catch(() => null);
11
+ throwIfAborted(signal);
9
12
  const results = await mapWithConcurrency(registry, MAX_REPOSITORY_DISCOVERY_CONCURRENCY, async (entry) => {
13
+ throwIfAborted(signal);
10
14
  try {
11
15
  const worktrees = await repositoryPort.listWorktrees(entry.path);
12
- return { entry, worktrees };
16
+ return { entry, skipped: false, worktrees };
13
17
  }
14
18
  catch {
15
- onSkipped?.(entry);
16
- return null;
19
+ return { entry, skipped: true, worktrees: [] };
17
20
  }
18
- });
21
+ }, signal);
19
22
  const allItems = [];
23
+ const skipped = [];
20
24
  for (const result of results) {
21
- if (result === null)
25
+ if (result.skipped) {
26
+ skipped.push(result.entry);
22
27
  continue;
28
+ }
23
29
  const { entry, worktrees } = result;
24
30
  for (const worktree of worktrees) {
25
31
  allItems.push({
@@ -32,15 +38,20 @@ export async function listRegisteredWorktreeSources(cwd, repositoryPort, onSkipp
32
38
  });
33
39
  }
34
40
  }
35
- return allItems;
41
+ return { skipped, sources: allItems };
36
42
  }
37
- export async function listDiscoverableWorktreeSources(cwd, repositoryPort, onSkipped) {
43
+ export async function listDiscoverableWorktreeSources(options) {
44
+ const { cwd, repositoryPort } = options;
38
45
  const currentRepository = await repositoryPort
39
46
  .detectRepository(cwd)
40
47
  .catch(() => null);
41
- const registeredSources = await listRegisteredWorktreeSources(cwd, repositoryPort, onSkipped);
42
- if (currentRepository === null)
43
- return dedupeSources(registeredSources);
48
+ const registered = await listRegisteredWorktreeSources(options);
49
+ if (currentRepository === null) {
50
+ return {
51
+ skipped: registered.skipped,
52
+ sources: deduplicateWorktreeSources(registered.sources),
53
+ };
54
+ }
44
55
  let currentSources = [];
45
56
  try {
46
57
  currentSources = (await repositoryPort.listWorktrees(cwd)).map((worktree) => ({
@@ -52,9 +63,15 @@ export async function listDiscoverableWorktreeSources(cwd, repositoryPort, onSki
52
63
  catch {
53
64
  // Registered repositories remain discoverable when the current checkout is transiently unavailable.
54
65
  }
55
- return dedupeSources([...currentSources, ...registeredSources]);
66
+ return {
67
+ skipped: registered.skipped,
68
+ sources: deduplicateWorktreeSources([
69
+ ...currentSources,
70
+ ...registered.sources,
71
+ ]),
72
+ };
56
73
  }
57
- function dedupeSources(sources) {
74
+ export function deduplicateWorktreeSources(sources) {
58
75
  const seen = new Set();
59
76
  return sources.filter((source) => {
60
77
  if (seen.has(source.worktree.path))
@@ -63,3 +80,7 @@ function dedupeSources(sources) {
63
80
  return true;
64
81
  });
65
82
  }
83
+ function throwIfAborted(signal) {
84
+ if (signal?.aborted)
85
+ throw new Error("Operation cancelled");
86
+ }
@@ -1,11 +1,13 @@
1
+ import { type WorktreeCatalogDependencies } from "../../application/worktree/catalog.js";
1
2
  import type { WorktreeEntry } from "../../domain/worktree/types.js";
2
- import { type WorktreePromptEntry } from "../../presentation/worktree/picker.js";
3
+ import { type WorktreePromptEntry, type WorktreePromptScope } from "../../presentation/worktree/picker.js";
3
4
  import { type CliRuntime } from "../dependencies.js";
4
5
  export interface CleanCommandOptions {
5
6
  cwd: string;
6
7
  dryRun?: boolean;
7
8
  force?: boolean;
8
9
  json?: boolean;
10
+ paths?: string[];
9
11
  stale?: boolean;
10
12
  runtime?: CliRuntime<"git" | "config" | "configStore" | "slots" | "repositoryContext" | "repositoryRegistry" | "worktrees" | "worktreeInfo" | "worktreeLifecycle" | "worktreeCatalog">;
11
13
  stderr: (chunk: string) => void;
@@ -15,7 +17,7 @@ export interface CleanCommandDependencies {
15
17
  confirmForceDeleteBranch: (branch: string) => Promise<boolean>;
16
18
  confirmForceRemoveWorktree: (worktreePath: string) => Promise<boolean>;
17
19
  confirmRemoval: (worktrees: WorktreeEntry[]) => Promise<boolean>;
18
- promptForWorktrees: (worktrees: WorktreePromptEntry[]) => Promise<string[] | null>;
20
+ promptForWorktrees: (worktrees: WorktreePromptEntry[], scope?: WorktreePromptScope, catalog?: WorktreeCatalogDependencies) => Promise<string[] | null>;
19
21
  }
20
22
  export declare function createCleanCommand(dependencies?: Partial<CleanCommandDependencies>): (options: CleanCommandOptions) => Promise<number>;
21
23
  export declare const runCleanCommand: (options: CleanCommandOptions) => Promise<number>;