ace-git-worktree 0.19.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 (61) hide show
  1. checksums.yaml +7 -0
  2. data/.ace-defaults/git/worktree.yml +250 -0
  3. data/.ace-defaults/nav/protocols/wfi-sources/ace-git-worktree.yml +19 -0
  4. data/CHANGELOG.md +957 -0
  5. data/LICENSE +21 -0
  6. data/README.md +40 -0
  7. data/Rakefile +14 -0
  8. data/docs/demo/ace-git-worktree-getting-started.gif +0 -0
  9. data/docs/demo/ace-git-worktree-getting-started.tape.yml +28 -0
  10. data/docs/demo/fixtures/README.md +3 -0
  11. data/docs/demo/fixtures/sample.txt +1 -0
  12. data/docs/getting-started.md +114 -0
  13. data/docs/handbook.md +38 -0
  14. data/docs/usage.md +334 -0
  15. data/exe/ace-git-worktree +24 -0
  16. data/handbook/agents/worktree.ag.md +189 -0
  17. data/handbook/skills/as-git-worktree/SKILL.md +27 -0
  18. data/handbook/skills/as-git-worktree-create/SKILL.md +21 -0
  19. data/handbook/skills/as-git-worktree-manage/SKILL.md +20 -0
  20. data/handbook/workflow-instructions/git/worktree-create.wf.md +262 -0
  21. data/handbook/workflow-instructions/git/worktree-manage.wf.md +384 -0
  22. data/handbook/workflow-instructions/git/worktree.wf.md +224 -0
  23. data/lib/ace/git/worktree/atoms/git_command.rb +121 -0
  24. data/lib/ace/git/worktree/atoms/path_expander.rb +189 -0
  25. data/lib/ace/git/worktree/atoms/slug_generator.rb +235 -0
  26. data/lib/ace/git/worktree/atoms/task_id_extractor.rb +91 -0
  27. data/lib/ace/git/worktree/cli/commands/config.rb +50 -0
  28. data/lib/ace/git/worktree/cli/commands/create.rb +80 -0
  29. data/lib/ace/git/worktree/cli/commands/list.rb +76 -0
  30. data/lib/ace/git/worktree/cli/commands/prune.rb +43 -0
  31. data/lib/ace/git/worktree/cli/commands/remove.rb +48 -0
  32. data/lib/ace/git/worktree/cli/commands/shared_helpers.rb +66 -0
  33. data/lib/ace/git/worktree/cli/commands/switch.rb +44 -0
  34. data/lib/ace/git/worktree/cli.rb +103 -0
  35. data/lib/ace/git/worktree/commands/config_command.rb +351 -0
  36. data/lib/ace/git/worktree/commands/create_command.rb +961 -0
  37. data/lib/ace/git/worktree/commands/list_command.rb +247 -0
  38. data/lib/ace/git/worktree/commands/prune_command.rb +260 -0
  39. data/lib/ace/git/worktree/commands/remove_command.rb +522 -0
  40. data/lib/ace/git/worktree/commands/switch_command.rb +249 -0
  41. data/lib/ace/git/worktree/configuration.rb +167 -0
  42. data/lib/ace/git/worktree/models/worktree_config.rb +502 -0
  43. data/lib/ace/git/worktree/models/worktree_info.rb +303 -0
  44. data/lib/ace/git/worktree/models/worktree_metadata.rb +294 -0
  45. data/lib/ace/git/worktree/molecules/config_loader.rb +125 -0
  46. data/lib/ace/git/worktree/molecules/current_task_linker.rb +136 -0
  47. data/lib/ace/git/worktree/molecules/hook_executor.rb +361 -0
  48. data/lib/ace/git/worktree/molecules/parent_task_resolver.rb +186 -0
  49. data/lib/ace/git/worktree/molecules/pr_creator.rb +253 -0
  50. data/lib/ace/git/worktree/molecules/task_committer.rb +329 -0
  51. data/lib/ace/git/worktree/molecules/task_fetcher.rb +244 -0
  52. data/lib/ace/git/worktree/molecules/task_pusher.rb +183 -0
  53. data/lib/ace/git/worktree/molecules/task_status_updater.rb +447 -0
  54. data/lib/ace/git/worktree/molecules/worktree_creator.rb +832 -0
  55. data/lib/ace/git/worktree/molecules/worktree_lister.rb +337 -0
  56. data/lib/ace/git/worktree/molecules/worktree_remover.rb +416 -0
  57. data/lib/ace/git/worktree/organisms/task_worktree_orchestrator.rb +906 -0
  58. data/lib/ace/git/worktree/organisms/worktree_manager.rb +714 -0
  59. data/lib/ace/git/worktree/version.rb +9 -0
  60. data/lib/ace/git/worktree.rb +215 -0
  61. metadata +218 -0
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/support/cli"
4
+ require_relative "shared_helpers"
5
+ require_relative "../../commands/list_command"
6
+
7
+ module Ace
8
+ module Git
9
+ module Worktree
10
+ module CLI
11
+ module Commands
12
+ class List < Ace::Support::Cli::Command
13
+ include SharedHelpers
14
+
15
+ desc "List all worktrees with optional filtering"
16
+
17
+ example [
18
+ " # List all worktrees",
19
+ "--show-tasks # Include task associations",
20
+ "--format json # JSON output",
21
+ "--search auth # Filter by branch pattern"
22
+ ]
23
+
24
+ option :format, desc: "Output format: table, json, simple", aliases: [], default: "table"
25
+ option :show_tasks, desc: "Include task associations", type: :boolean, aliases: ["--show-tasks"]
26
+ option :task_associated, desc: "Show only task-associated worktrees", type: :boolean, aliases: ["--task-associated"]
27
+ option :usable, desc: "Show only usable worktrees", type: :boolean, aliases: ["--usable"]
28
+ option :search, desc: "Filter by branch name pattern", aliases: []
29
+ option :quiet, type: :boolean, aliases: ["-q"], desc: "Suppress non-essential output"
30
+ option :verbose, type: :boolean, aliases: ["-v"], desc: "Show verbose output"
31
+ option :debug, type: :boolean, aliases: ["-d"], desc: "Show debug output"
32
+
33
+ def call(**options)
34
+ display_config_summary("list", options) unless options[:format] == "json"
35
+
36
+ # Keep explicit false values as --no-* flags so legacy parser receives filters.
37
+ args = list_options_to_args(options)
38
+
39
+ Ace::Git::Worktree::Commands::ListCommand.new.run(args)
40
+ end
41
+
42
+ private
43
+
44
+ def list_options_to_args(options)
45
+ args = []
46
+ args.concat(format_arg(options))
47
+ args << "--show-tasks" if options[:show_tasks]
48
+
49
+ unless options[:task_associated].nil?
50
+ args << (options[:task_associated] ? "--task-associated" : "--no-task-associated")
51
+ end
52
+
53
+ unless options[:usable].nil?
54
+ args << (options[:usable] ? "--usable" : "--no-usable")
55
+ end
56
+
57
+ if options[:search].is_a?(String) && !options[:search].empty?
58
+ args << "--search"
59
+ args << options[:search]
60
+ end
61
+
62
+ args
63
+ end
64
+
65
+ def format_arg(options)
66
+ format = options[:format]
67
+ return [] if format.nil? || format.empty?
68
+
69
+ ["--format", format]
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/support/cli"
4
+ require_relative "shared_helpers"
5
+ require_relative "../../commands/prune_command"
6
+
7
+ module Ace
8
+ module Git
9
+ module Worktree
10
+ module CLI
11
+ module Commands
12
+ class Prune < Ace::Support::Cli::Command
13
+ include SharedHelpers
14
+
15
+ desc "Clean up deleted worktrees from git metadata"
16
+
17
+ example [
18
+ " # Prune deleted worktrees",
19
+ "--dry-run # Preview what would be pruned",
20
+ "--cleanup-directories # Also remove orphaned directories"
21
+ ]
22
+
23
+ option :dry_run, desc: "Show what would be pruned", type: :boolean, aliases: ["--dry-run"]
24
+ option :cleanup_directories, desc: "Remove orphaned worktree directories", type: :boolean, aliases: ["--cleanup-directories"]
25
+ option :force, desc: "Force cleanup", type: :boolean, aliases: []
26
+ option :verbose, desc: "Show verbose output", type: :boolean, aliases: ["-v"]
27
+ option :quiet, type: :boolean, aliases: ["-q"], desc: "Suppress non-essential output"
28
+ option :debug, type: :boolean, aliases: ["-d"], desc: "Show debug output"
29
+
30
+ def call(**options)
31
+ display_config_summary("prune", options)
32
+
33
+ # Convert ace-support-cli options to args array format
34
+ args = options_to_args(options)
35
+
36
+ Ace::Git::Worktree::Commands::PruneCommand.new.run(args)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/support/cli"
4
+ require_relative "shared_helpers"
5
+ require_relative "../../commands/remove_command"
6
+
7
+ module Ace
8
+ module Git
9
+ module Worktree
10
+ module CLI
11
+ module Commands
12
+ class Remove < Ace::Support::Cli::Command
13
+ include SharedHelpers
14
+
15
+ desc "Remove a git worktree with safety checks"
16
+
17
+ example [
18
+ "--task 081 # Remove task worktree",
19
+ "feature-branch # Remove by branch name",
20
+ "--task 081 --force # Force removal with changes"
21
+ ]
22
+
23
+ argument :identifier, required: false, desc: "Worktree identifier (task ID, branch, directory, or path)"
24
+
25
+ option :task, desc: "Remove worktree for specific task", aliases: []
26
+ option :force, desc: "Force removal even with uncommitted changes", type: :boolean, aliases: []
27
+ option :keep_directory, desc: "Keep the worktree directory", type: :boolean, aliases: ["--keep-directory"]
28
+ option :delete_branch, desc: "Also delete the associated branch", type: :boolean, aliases: ["-D"]
29
+ option :dry_run, desc: "Show what would be removed", type: :boolean, aliases: ["--dry-run"]
30
+ option :quiet, type: :boolean, aliases: ["-q"], desc: "Suppress non-essential output"
31
+ option :verbose, type: :boolean, aliases: ["-v"], desc: "Show verbose output"
32
+ option :debug, type: :boolean, aliases: ["-d"], desc: "Show debug output"
33
+
34
+ def call(identifier: nil, **options)
35
+ display_config_summary("remove", options)
36
+
37
+ # Convert ace-support-cli options to args array format
38
+ args = options_to_args(options)
39
+ args << identifier if identifier
40
+
41
+ Ace::Git::Worktree::Commands::RemoveCommand.new.run(args)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/core"
4
+
5
+ module Ace
6
+ module Git
7
+ module Worktree
8
+ module CLI
9
+ module Commands
10
+ # Shared helper methods for CLI commands
11
+ #
12
+ # Include this module in CLI command classes to avoid code duplication
13
+ # of common patterns like config summary display and options conversion.
14
+ module SharedHelpers
15
+ include Ace::Support::Cli::Base
16
+
17
+ private
18
+
19
+ # Display config summary unless quiet mode is enabled
20
+ #
21
+ # @param command [String] Command name for display
22
+ # @param options [Hash] CLI options hash
23
+ def display_config_summary(command, options)
24
+ return if quiet?(options)
25
+
26
+ Ace::Core::Atoms::ConfigSummary.display(
27
+ command: command,
28
+ config: Ace::Git::Worktree.config,
29
+ defaults: {},
30
+ options: options
31
+ )
32
+ end
33
+
34
+ # Convert ace-support-cli options hash to args array format for legacy commands
35
+ #
36
+ # @param options [Hash] CLI options hash
37
+ # @return [Array<String>] Arguments array
38
+ #
39
+ # @note Boolean false values are skipped (not converted to --no-flag).
40
+ # This means there's no distinction between "not specified" and "explicitly
41
+ # set to false". If a command needs to distinguish these cases, it should
42
+ # handle the option directly rather than using this converter.
43
+ def options_to_args(options)
44
+ args = []
45
+ options.each do |key, value|
46
+ next if value.nil? || %i[quiet verbose debug].include?(key)
47
+
48
+ arg_key = key.to_s.tr("_", "-")
49
+ if value == true
50
+ args << "--#{arg_key}"
51
+ elsif value == false
52
+ # Skip boolean false options - no distinction between unset and false
53
+ next
54
+ elsif value.is_a?(String)
55
+ args << "--#{arg_key}"
56
+ args << value
57
+ end
58
+ end
59
+ args
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/support/cli"
4
+ require_relative "shared_helpers"
5
+ require_relative "../../commands/switch_command"
6
+
7
+ module Ace
8
+ module Git
9
+ module Worktree
10
+ module CLI
11
+ module Commands
12
+ class Switch < Ace::Support::Cli::Command
13
+ include SharedHelpers
14
+
15
+ desc "Switch to a worktree by returning its path"
16
+
17
+ example [
18
+ "081 # Switch by task ID",
19
+ "feature-branch # Switch by branch name",
20
+ "--list # List available worktrees"
21
+ ]
22
+
23
+ argument :identifier, required: false, desc: "Worktree identifier (task ID, branch, directory, or path)"
24
+
25
+ option :list, desc: "List available worktrees", type: :boolean, aliases: ["-l"]
26
+ option :verbose, desc: "Show verbose output", type: :boolean, aliases: ["-v"]
27
+ option :quiet, type: :boolean, aliases: ["-q"], desc: "Suppress non-essential output"
28
+ option :debug, type: :boolean, aliases: ["-d"], desc: "Show debug output"
29
+
30
+ def call(identifier: nil, **options)
31
+ display_config_summary("switch", options)
32
+
33
+ # Convert ace-support-cli options to args array format
34
+ args = options_to_args(options)
35
+ args << identifier if identifier
36
+
37
+ Ace::Git::Worktree::Commands::SwitchCommand.new.run(args)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/support/cli"
4
+
5
+ require_relative "version"
6
+ require_relative "cli/commands/create"
7
+ require_relative "cli/commands/list"
8
+ require_relative "cli/commands/switch"
9
+ require_relative "cli/commands/remove"
10
+ require_relative "cli/commands/prune"
11
+ require_relative "cli/commands/config"
12
+ require "ace/core"
13
+ require "ace/support/cli"
14
+
15
+ module Ace
16
+ module Git
17
+ module Worktree
18
+ # ace-support-cli based CLI registry for ace-git-worktree
19
+ #
20
+ # This follows the Hanami pattern with all commands in CLI::Commands:: namespace.
21
+ module CLI
22
+ extend Ace::Support::Cli::RegistryDsl
23
+
24
+ PROGRAM_NAME = "ace-git-worktree"
25
+
26
+ REGISTERED_COMMANDS = [
27
+ ["create", "Create a new worktree for task, PR, or branch"],
28
+ ["list", "List active worktrees with optional task metadata"],
29
+ ["switch", "Resolve a worktree path for cd navigation"],
30
+ ["remove", "Remove a worktree by task, branch, or path"],
31
+ ["prune", "Prune stale/deleted worktree references"],
32
+ ["config", "Show and validate configuration"]
33
+ ].freeze
34
+
35
+ HELP_EXAMPLES = [
36
+ "ace-git-worktree create --task 148 # Isolated worktree for task",
37
+ "ace-git-worktree list --show-tasks # Worktrees with task context",
38
+ "ace-git-worktree switch 148 # Get path for cd",
39
+ "ace-git-worktree prune --dry-run # Preview stale cleanup"
40
+ ].freeze
41
+
42
+ # Captured command exit code from last run.
43
+ @captured_exit_code = nil
44
+
45
+ # Start the CLI.
46
+ #
47
+ # @param args [Array<String>] Command-line arguments
48
+ # @return [Integer] Exit code (0 for success, non-zero for failure)
49
+ def self.start(args)
50
+ @captured_exit_code = nil
51
+ Ace::Support::Cli::Runner.new(self).call(args: args)
52
+ @captured_exit_code || 0
53
+ end
54
+
55
+ # Wrap a command to capture its exit code.
56
+ #
57
+ # @param command_class [Class] The command class to wrap
58
+ # @return [Class] Wrapped command class
59
+ def self.wrap_command(command_class)
60
+ wrapped = Class.new(Ace::Support::Cli::Command) do
61
+ define_method(:call) do |**kwargs|
62
+ result = command_class.new.call(**kwargs)
63
+ Ace::Git::Worktree::CLI.instance_variable_set(:@captured_exit_code, result) if result.is_a?(Integer)
64
+ result
65
+ end
66
+ end
67
+
68
+ command_class.instance_variables.each do |ivar|
69
+ wrapped.instance_variable_set(ivar, command_class.instance_variable_get(ivar))
70
+ end
71
+
72
+ wrapped
73
+ end
74
+
75
+ # Register commands (Hanami pattern: CLI::Commands::*)
76
+ register "create", wrap_command(CLI::Commands::Create), aliases: []
77
+ register "list", wrap_command(CLI::Commands::List), aliases: ["ls"]
78
+ register "switch", wrap_command(CLI::Commands::Switch), aliases: ["cd"]
79
+ register "remove", wrap_command(CLI::Commands::Remove), aliases: ["rm"]
80
+ register "prune", wrap_command(CLI::Commands::Prune), aliases: []
81
+ register "config", wrap_command(CLI::Commands::Config), aliases: []
82
+
83
+ # Version command
84
+ version_cmd = Ace::Support::Cli::VersionCommand.build(
85
+ gem_name: "ace-git-worktree",
86
+ version: Ace::Git::Worktree::VERSION
87
+ )
88
+ register "version", version_cmd
89
+ register "--version", version_cmd
90
+
91
+ help_cmd = Ace::Support::Cli::HelpCommand.build(
92
+ program_name: PROGRAM_NAME,
93
+ version: Ace::Git::Worktree::VERSION,
94
+ commands: REGISTERED_COMMANDS,
95
+ examples: HELP_EXAMPLES
96
+ )
97
+ register "help", help_cmd
98
+ register "--help", help_cmd
99
+ register "-h", help_cmd
100
+ end
101
+ end
102
+ end
103
+ end