@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
@@ -295,6 +295,7 @@ function registerCommands(program) {
295
295
  .description("interactively prune linked worktrees")
296
296
  .option("-f, --force", "bypass prompts, force-remove dirty worktrees, and force-delete unmerged branches")
297
297
  .option("--stale", "only target clean worktrees whose upstream is gone and branch is merged into the default branch")
298
+ .option("--path <pattern>", "only target worktrees under a directory or matching a path glob (repeatable)", collectOption)
298
299
  .option("--dry-run", "show what would be deleted without removing anything")
299
300
  .option("--json", "emit JSON on success or error instead of human-readable output")
300
301
  .action(notImplemented("clean"));
@@ -638,6 +639,7 @@ function attachCommandActions(program, options) {
638
639
  dryRun: commandOptions.dryRun,
639
640
  force: commandOptions.force,
640
641
  json: commandOptions.json,
642
+ paths: commandOptions.path,
641
643
  stale: commandOptions.stale,
642
644
  runtime: options.dependencies,
643
645
  stderr: options.stderr,
@@ -752,6 +754,9 @@ function requireCommand(parent, name) {
752
754
  }
753
755
  return command;
754
756
  }
757
+ function collectOption(value, previous = []) {
758
+ return [...previous, value];
759
+ }
755
760
  function notImplemented(commandName) {
756
761
  return () => {
757
762
  throw new Error(`'${commandName}' is not implemented yet.`);
@@ -1 +1 @@
1
- export declare function mapWithConcurrency<Input, Output>(items: readonly Input[], limit: number, mapper: (item: Input) => Promise<Output>): Promise<Output[]>;
1
+ export declare function mapWithConcurrency<Input, Output>(items: readonly Input[], limit: number, mapper: (item: Input) => Promise<Output>, signal?: AbortSignal): Promise<Output[]>;
@@ -1,8 +1,9 @@
1
- export async function mapWithConcurrency(items, limit, mapper) {
1
+ export async function mapWithConcurrency(items, limit, mapper, signal) {
2
2
  const results = new Array(items.length);
3
3
  let nextIndex = 0;
4
4
  async function readNext() {
5
5
  for (;;) {
6
+ throwIfAborted(signal);
6
7
  const index = nextIndex;
7
8
  nextIndex += 1;
8
9
  if (index >= items.length)
@@ -13,3 +14,7 @@ export async function mapWithConcurrency(items, limit, mapper) {
13
14
  await Promise.all(Array.from({ length: Math.min(limit, items.length) }, () => readNext()));
14
15
  return results;
15
16
  }
17
+ function throwIfAborted(signal) {
18
+ if (signal?.aborted)
19
+ throw new Error("Operation cancelled");
20
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Builds a predicate that matches worktree paths against any of the absolute
3
+ * patterns. A pattern without glob characters matches that directory and
4
+ * anything inside it. A pattern with `*` (within one segment), `**` (across
5
+ * segments), or `?` (one character) must match the whole path. A backslash
6
+ * makes the next character literal, so `\*` and `\?` match themselves. No
7
+ * patterns match every path.
8
+ */
9
+ export declare function createWorktreePathFilter(patterns: readonly string[]): (worktreePath: string) => boolean;
10
+ /** Returns the index of the first unescaped glob character, or -1. */
11
+ export declare function findGlobIndex(pattern: string): number;
12
+ export declare function escapePathPattern(path: string): string;
13
+ export declare function unescapePathPattern(pattern: string): string;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Builds a predicate that matches worktree paths against any of the absolute
3
+ * patterns. A pattern without glob characters matches that directory and
4
+ * anything inside it. A pattern with `*` (within one segment), `**` (across
5
+ * segments), or `?` (one character) must match the whole path. A backslash
6
+ * makes the next character literal, so `\*` and `\?` match themselves. No
7
+ * patterns match every path.
8
+ */
9
+ export function createWorktreePathFilter(patterns) {
10
+ const matchers = patterns.map(createPathMatcher);
11
+ if (matchers.length === 0)
12
+ return () => true;
13
+ return (worktreePath) => matchers.some((matches) => matches(worktreePath));
14
+ }
15
+ /** Returns the index of the first unescaped glob character, or -1. */
16
+ export function findGlobIndex(pattern) {
17
+ for (let index = 0; index < pattern.length; index += 1) {
18
+ const character = pattern[index];
19
+ if (character === "\\")
20
+ index += 1;
21
+ else if (character === "*" || character === "?")
22
+ return index;
23
+ }
24
+ return -1;
25
+ }
26
+ export function escapePathPattern(path) {
27
+ return path.replace(/[\\*?]/g, "\\$&");
28
+ }
29
+ export function unescapePathPattern(pattern) {
30
+ return pattern.replace(/\\(.)/g, "$1");
31
+ }
32
+ function createPathMatcher(pattern) {
33
+ if (findGlobIndex(pattern) !== -1) {
34
+ const regExp = globToRegExp(pattern);
35
+ return (path) => regExp.test(path);
36
+ }
37
+ const literal = unescapePathPattern(pattern);
38
+ const prefix = literal === "/" ? "/" : `${literal}/`;
39
+ return (path) => path === literal || path.startsWith(prefix);
40
+ }
41
+ function globToRegExp(pattern) {
42
+ let source = "";
43
+ for (let index = 0; index < pattern.length; index += 1) {
44
+ let character = pattern[index];
45
+ if (character === "*") {
46
+ if (pattern[index + 1] === "*") {
47
+ index += 1;
48
+ if (pattern[index + 1] === "/") {
49
+ index += 1;
50
+ source += "(?:.*/)?";
51
+ }
52
+ else {
53
+ source += ".*";
54
+ }
55
+ }
56
+ else {
57
+ source += "[^/]*";
58
+ }
59
+ }
60
+ else if (character === "?") {
61
+ source += "[^/]";
62
+ }
63
+ else {
64
+ if (character === "\\" && index + 1 < pattern.length) {
65
+ index += 1;
66
+ character = pattern[index];
67
+ }
68
+ source += character.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
69
+ }
70
+ }
71
+ return new RegExp(`^${source}$`);
72
+ }
@@ -1,2 +1,3 @@
1
1
  export declare function resolveWorktreePath(repoRoot: string, branch: string, basePath?: string): string;
2
2
  export declare function validateBranchName(name: string): string | null;
3
+ export declare function expandTildeInPath(path: string): string;
@@ -49,7 +49,7 @@ export function validateBranchName(name) {
49
49
  }
50
50
  return null;
51
51
  }
52
- function expandTildeInPath(path) {
52
+ export function expandTildeInPath(path) {
53
53
  if (path === "~")
54
54
  return homedir();
55
55
  if (path.startsWith("~/"))