ace-git-worktree 0.21.8 → 0.22.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 (28) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/docs/usage.md +37 -3
  4. data/handbook/skills/as-git-worktree-cleanup/SKILL.md +20 -0
  5. data/handbook/workflow-instructions/git/worktree-cleanup.wf.md +63 -0
  6. data/handbook/workflow-instructions/git/worktree-create.wf.md +6 -0
  7. data/lib/ace/git/worktree/cli/commands/bootstrap.rb +43 -0
  8. data/lib/ace/git/worktree/cli/commands/cleanup.rb +45 -0
  9. data/lib/ace/git/worktree/cli/commands/config.rb +27 -4
  10. data/lib/ace/git/worktree/cli/commands/create.rb +1 -0
  11. data/lib/ace/git/worktree/cli.rb +6 -0
  12. data/lib/ace/git/worktree/commands/bootstrap_command.rb +159 -0
  13. data/lib/ace/git/worktree/commands/cleanup_command.rb +307 -0
  14. data/lib/ace/git/worktree/commands/config_command.rb +354 -181
  15. data/lib/ace/git/worktree/commands/create_command.rb +5 -0
  16. data/lib/ace/git/worktree/models/worktree_config.rb +33 -3
  17. data/lib/ace/git/worktree/molecules/bootstrap_executor.rb +99 -0
  18. data/lib/ace/git/worktree/molecules/cleanup_applier.rb +225 -0
  19. data/lib/ace/git/worktree/molecules/cleanup_pr_resolver.rb +213 -0
  20. data/lib/ace/git/worktree/molecules/cleanup_reporter.rb +396 -0
  21. data/lib/ace/git/worktree/molecules/config_loader.rb +1 -1
  22. data/lib/ace/git/worktree/molecules/task_committer.rb +55 -8
  23. data/lib/ace/git/worktree/molecules/toolchain_truster.rb +112 -0
  24. data/lib/ace/git/worktree/molecules/worktree_lister.rb +17 -1
  25. data/lib/ace/git/worktree/organisms/task_worktree_orchestrator.rb +159 -10
  26. data/lib/ace/git/worktree/organisms/worktree_manager.rb +4 -2
  27. data/lib/ace/git/worktree/version.rb +1 -1
  28. metadata +17 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dab3625d2a8e853524064fd1fbc807df3ccfdf1011a977dae3bd4938aad71beb
4
- data.tar.gz: 53e20047a0218cb1c226c3c60b84009d75aed18cf94b7221858f779567a93230
3
+ metadata.gz: 6e7afdc95769c4eaebe1974a49534a00e548a17f2519075c10738bd93ad68de5
4
+ data.tar.gz: 43294a3bb9d7bc38da9938f50e8e621ce7a432f54a39ecebbe57a752d46f56d9
5
5
  SHA512:
6
- metadata.gz: c9853df9965e9bca5a1a5c19c8259e256fa6b7134a63f7c9abe536d3bb47aa76ed5789c4904807a51bfc644c1ddb32636e37fe36d7d6ecad71cf67acd9ff0e3d
7
- data.tar.gz: 91f690e27dc37733462975066d629e30299a665d1bc8bcf116b7b99d65369c7c50b150b354c070de8328671206cc2b314e608f0a8c7c19a6548581a241d6e12d
6
+ metadata.gz: af41391d20f9555ddd1da0e6347823c4810dc2d330b38137317e5fc79f7ab1c81a17b43e54d33b666f5cc40163f1a6ea8abca031b94f508bdd4c525a1788a07c
7
+ data.tar.gz: 073f8bdaf60175babf66dfca52880d1ffaab445c92b4c264165cd7792d7e5c8bb5a6be4adcf1e42f7f561b0b7ae019d43ce7ffe0eda0d3032d4377bc14be661e
data/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.22.0] - 2026-08-13
11
+ ### Added
12
+ - Add cleanup plan application with drift guards
13
+ - Classify merged refs with GitHub PR evidence
14
+ - Report repository inventory with ancestry proof
15
+ - Run recoverable preparation phases in place
16
+ - Add bootstrap config management and provenance
17
+ - Fail closed on tracked mise trust verification
18
+ ### Fixed
19
+ - Resolve PR 316 findings and finalize cleanup proofs
20
+ - Restrict bookkeeping commits to owned paths
21
+
10
22
  ## [0.21.8] - 2026-08-12
11
23
 
12
24
  ### Technical
data/docs/usage.md CHANGED
@@ -19,14 +19,16 @@ gem install ace-git-worktree
19
19
 
20
20
  ## Command Overview
21
21
 
22
- `ace-git-worktree` ships six commands:
22
+ `ace-git-worktree` ships seven commands:
23
23
 
24
24
  * `create` for task-aware, PR-aware, and branch-based worktree creation
25
+ * `bootstrap` for rerunning preparation phases (toolchain trust and bootstrap policy) on existing worktrees
25
26
  * `list` for active worktree inventory and filtering
26
27
  * `switch` for resolving a worktree path you can `cd` into
28
+ * `cleanup` for report/apply cleanup of squash-merged branches
27
29
  * `remove` for safe worktree removal
28
30
  * `prune` for stale-reference cleanup
29
- * `config` for configuration inspection and validation
31
+ * `config` for configuration initialization, bootstrap policy settings, inspection, and validation
30
32
 
31
33
  Run `ace-git-worktree --help` for the top-level summary and examples.
32
34
 
@@ -112,7 +114,23 @@ Would remove task.081
112
114
 
113
115
  **Next steps:** Re-run either command without `--dry-run` once the preview looks correct.
114
116
 
115
- ### Scenario 4: Filter Task and Non-Task Worktrees
117
+ ### Scenario 2: Clean up merged worktrees
118
+
119
+ **Goal:** Remove worktrees and branches that have been merged to main.
120
+
121
+ **Commands:**
122
+
123
+ ```bash
124
+ # Generate a cleanup plan and review it
125
+ ace-git-worktree cleanup --target main
126
+
127
+ # Apply the reviewed plan using the digest from the report
128
+ ace-git-worktree cleanup --target main --apply --approved-digest <sha256-digest>
129
+ ```
130
+
131
+ **Outcome:** Merged worktrees, local branches, and remote-tracking branches are removed safely without dropping unmerged work.
132
+
133
+ ### Scenario 3: Switch Task Context and Validate Dependencies
116
134
 
117
135
  **Goal:** Show task-associated worktrees separately from non-task worktrees.
118
136
 
@@ -277,6 +295,22 @@ ace-git-worktree prune --cleanup-directories
277
295
  * `-v`, `--verbose` - Show verbose output
278
296
  * `-d`, `--debug` - Show debug output
279
297
 
298
+ ### `ace-git-worktree bootstrap [IDENTIFIER]`
299
+
300
+ Rerun preparation phases (toolchain trust and bootstrap policy) for an existing worktree in place.
301
+
302
+ ```bash
303
+ ace-git-worktree bootstrap 081
304
+ ace-git-worktree bootstrap t.081 --json
305
+ ```
306
+
307
+ **Options:**
308
+
309
+ * `--json` - Output format as JSON
310
+ * `-q`, `--quiet` - Suppress non-essential output
311
+ * `-v`, `--verbose` - Show verbose output
312
+ * `-d`, `--debug` - Show debug output
313
+
280
314
  ### `ace-git-worktree config [SUBCOMMAND]`
281
315
 
282
316
  Show current configuration, validate it, or list the config files in play.
@@ -0,0 +1,20 @@
1
+ ---
2
+ name: as-git-worktree-cleanup
3
+ description: Safely inventory and apply cleanup plans for squash-merged worktrees and refs
4
+ # bundle: wfi://git/worktree-cleanup
5
+ # agent: Bash
6
+ user-invocable: true
7
+ allowed-tools:
8
+ - Bash(ace-git-worktree:*)
9
+ - Bash(ace-bundle:*)
10
+ - Read
11
+ argument-hint: [--target main]
12
+ last_modified: 2026-08-13
13
+ source: ace-git-worktree
14
+ skill:
15
+ kind: workflow
16
+ execution:
17
+ workflow: wfi://git/worktree-cleanup
18
+ ---
19
+
20
+ Load and run `ace-bundle wfi://git/worktree-cleanup` in the current project, then follow the loaded workflow as the source of truth and execute it end-to-end instead of only summarizing it.
@@ -0,0 +1,63 @@
1
+ ---
2
+ title: Worktree Cleanup
3
+ description: Safely inventory and apply cleanup plans for squash-merged worktrees and refs
4
+ category: Git
5
+ tags: [git, worktree, cleanup, safely, squash-merge]
6
+ version: 1.0.0
7
+ ---
8
+
9
+ # Worktree Cleanup
10
+
11
+ The `ace-git-worktree cleanup` command safely removes stale worktrees, local branches, and remote-tracking branches that have been merged (including squash merges). It uses a two-phase report/apply model with strict ancestry and drift guards.
12
+
13
+ ## Generate Cleanup Report
14
+
15
+ First, generate a deterministic, no-mutation inventory of everything that could be cleaned up relative to a target branch:
16
+
17
+ ```bash
18
+ ace-git-worktree cleanup --target main --remote origin
19
+ ```
20
+
21
+ This will:
22
+ 1. Refresh remote evidence (unless `--offline` is passed).
23
+ 2. Find all worktrees, local refs, and remote refs.
24
+ 3. Classify each using GitHub PR evidence (exact merge, stable patch equivalence) or Git ancestry.
25
+ 4. Output an ordered plan with a **Plan Digest**.
26
+
27
+ ### Report Output Interpretation
28
+
29
+ - `✓` (Retain): Item is protected (e.g., primary checkout, dirty worktree) or its ancestry/merge status is unproven.
30
+ - `✗` (Remove): Item is safe to delete because it is fully merged into the target.
31
+
32
+ ## Apply Cleanup Plan
33
+
34
+ To execute the deletions, you must provide the exact Plan Digest from your reviewed report. This ensures that no destructive operations occur if the repository state drifts (e.g., if a worktree becomes dirty or a ref advances).
35
+
36
+ ```bash
37
+ ace-git-worktree cleanup --target main --apply --approved-digest <sha256-digest>
38
+ ```
39
+
40
+ ACE will:
41
+ 1. Re-scan the repository and rebuild the plan.
42
+ 2. Abort if the new plan's digest does not match your `--approved-digest`.
43
+ 3. Apply deletions in a fixed order (worktrees, local refs, remote refs).
44
+ 4. Re-validate dirty state and expected SHAs immediately before each deletion.
45
+ 5. Finish with a strict rescan and output the results.
46
+
47
+ ### Strict Rescan
48
+
49
+ If you want to ensure that *only* the target branch remains after cleanup, you can use the `--require-only-target` flag:
50
+
51
+ ```bash
52
+ ace-git-worktree cleanup --target main --apply --approved-digest <sha256-digest> --require-only-target
53
+ ```
54
+
55
+ If any non-target worktrees or refs are retained (e.g., because they were unproven or dirty), the command will exit with an error.
56
+
57
+ ## Advanced Usage
58
+
59
+ For automation or scripting, use the JSON format:
60
+
61
+ ```bash
62
+ ace-git-worktree cleanup --target main --format json | jq .
63
+ ```
@@ -126,6 +126,12 @@ ace-git-worktree remove feature-branch
126
126
  # Skip automatic mise trust
127
127
  ace-git-worktree create --task 081 --no-mise-trust
128
128
 
129
+ # Skip bootstrap command execution
130
+ ace-git-worktree create --task 081 --no-bootstrap
131
+
132
+ # Rerun preparation phases (toolchain trust and bootstrap policy) on existing worktree
133
+ ace-git-worktree bootstrap 081
134
+
129
135
  # Skip task status update
130
136
  ace-git-worktree create --task 081 --no-status-update
131
137
 
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/support/cli"
4
+ require_relative "shared_helpers"
5
+ require_relative "../../commands/bootstrap_command"
6
+
7
+ module Ace
8
+ module Git
9
+ module Worktree
10
+ module CLI
11
+ module Commands
12
+ class Bootstrap < Ace::Support::Cli::Command
13
+ include SharedHelpers
14
+
15
+ desc "Rerun preparation phases (toolchain trust and bootstrap policy) for an existing worktree"
16
+
17
+ example [
18
+ "081 # Rerun bootstrap for task 081",
19
+ "t.081 --json # Output retry status in JSON format"
20
+ ]
21
+
22
+ argument :identifier, required: true, desc: "Task ID, branch, or worktree identifier"
23
+
24
+ option :json, desc: "Format output as JSON", type: :boolean, aliases: []
25
+ option :verbose, desc: "Show verbose output", type: :boolean, aliases: ["-v"]
26
+ option :quiet, type: :boolean, aliases: ["-q"], desc: "Suppress non-essential output"
27
+ option :debug, type: :boolean, aliases: ["-d"], desc: "Show debug output"
28
+
29
+ def call(identifier: nil, **options)
30
+ display_config_summary("bootstrap", options)
31
+
32
+ args = []
33
+ args << identifier if identifier
34
+ args << "--json" if options[:json]
35
+
36
+ Ace::Git::Worktree::Commands::BootstrapCommand.new.run(args)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ace/support/cli"
4
+ require_relative "shared_helpers"
5
+ require_relative "../../commands/cleanup_command"
6
+
7
+ module Ace
8
+ module Git
9
+ module Worktree
10
+ module CLI
11
+ module Commands
12
+ class Cleanup < Ace::Support::Cli::Command
13
+ include SharedHelpers
14
+
15
+ desc "Report cleanup plan for merged worktrees and refs"
16
+
17
+ example [
18
+ "--target main # Report against main",
19
+ "--target main --format json # JSON output",
20
+ "--target main --offline # Skip remote refresh"
21
+ ]
22
+
23
+ option :target, desc: "Target ref for ancestry proof (required)", type: :string, aliases: []
24
+ option :remote, desc: "Remote name (default: origin)", type: :string, default: "origin", aliases: []
25
+ option :offline, desc: "Skip remote evidence refresh", type: :boolean, aliases: []
26
+ option :apply, desc: "Apply a reviewed plan", type: :boolean, aliases: []
27
+ option :approved_digest, desc: "Approved plan digest to apply", type: :string, aliases: []
28
+ option :require_only_target, desc: "Fail if final rescan has retained non-target state", type: :boolean, aliases: []
29
+ option :format, desc: "Output format (table, json)", type: :string, default: "table", aliases: []
30
+ option :quiet, type: :boolean, aliases: ["-q"], desc: "Suppress non-essential output"
31
+ option :debug, type: :boolean, aliases: ["-d"], desc: "Show debug output"
32
+
33
+ def call(**options)
34
+ display_config_summary("cleanup", options)
35
+
36
+ args = options_to_args(options)
37
+
38
+ Ace::Git::Worktree::Commands::CleanupCommand.new.run(args)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -23,11 +23,19 @@ module Ace
23
23
 
24
24
  # Accept extra positional arguments for backward compatibility
25
25
  # (e.g., "show", "validate" as positional args instead of flags)
26
- argument :subcommand, required: false, desc: "Subcommand (show, validate, files)"
26
+ argument :subcommand, required: false, desc: "Subcommand (init, set-bootstrap, show, validate, files)"
27
27
 
28
28
  option :show, desc: "Show current configuration", type: :boolean, aliases: []
29
29
  option :validate, desc: "Validate configuration", type: :boolean, aliases: []
30
30
  option :files, desc: "Show configuration file locations", type: :boolean, aliases: []
31
+ option :json, desc: "Format output as JSON", type: :boolean, aliases: []
32
+ option :bootstrap, desc: "Validate bootstrap policy", type: :boolean, aliases: []
33
+ option :command, desc: "Bootstrap command to execute", type: :string, aliases: []
34
+ option :working_dir, desc: "Working directory for bootstrap command", type: :string, aliases: []
35
+ option :timeout, desc: "Timeout in seconds for bootstrap command", type: :string, aliases: []
36
+ option :required, desc: "Require bootstrap execution to succeed", type: :boolean, aliases: []
37
+ option :advisory, desc: "Allow worktree creation when bootstrap fails", type: :boolean, aliases: []
38
+ option :env, desc: "Environment variables KEY=VALUE", type: :array, aliases: []
31
39
  option :verbose, desc: "Show verbose output", type: :boolean, aliases: ["-v"]
32
40
  option :quiet, type: :boolean, aliases: ["-q"], desc: "Suppress non-essential output"
33
41
  option :debug, type: :boolean, aliases: ["-d"], desc: "Show debug output"
@@ -35,10 +43,25 @@ module Ace
35
43
  def call(subcommand: nil, **options)
36
44
  display_config_summary("config", options)
37
45
 
38
- # Convert ace-support-cli options to args array format
39
- args = options_to_args(options)
40
- # Add subcommand as positional argument if provided
46
+ args = []
41
47
  args << subcommand if subcommand
48
+ args << "init" if options[:init]
49
+ args << "set-bootstrap" if options[:set_bootstrap]
50
+ args << "--show" if options[:show]
51
+ args << "--validate" if options[:validate]
52
+ args << "--files" if options[:files]
53
+ args << "--json" if options[:json]
54
+ args << "--bootstrap" if options[:bootstrap]
55
+ args << "--command" << options[:command] if options[:command]
56
+ args << "--working-dir" << options[:working_dir] if options[:working_dir]
57
+ args << "--timeout" << options[:timeout].to_s if options[:timeout]
58
+ args << "--required" if options[:required]
59
+ args << "--advisory" if options[:advisory]
60
+ if options[:env]
61
+ Array(options[:env]).each do |e|
62
+ args << "--env" << e
63
+ end
64
+ end
42
65
 
43
66
  Ace::Git::Worktree::Commands::ConfigCommand.new.run(args)
44
67
  end
@@ -49,6 +49,7 @@ module Ace
49
49
  option :no_push, desc: "Skip pushing task changes", type: :boolean, aliases: ["--no-push"]
50
50
  option :no_upstream, desc: "Skip pushing with upstream tracking", type: :boolean, aliases: ["--no-upstream"]
51
51
  option :no_pr, desc: "Skip creating draft PR", type: :boolean
52
+ option :no_bootstrap, desc: "Skip worktree bootstrap preparation", type: :boolean
52
53
  option :push_remote, desc: "Remote to push to", aliases: []
53
54
  option :no_auto_navigate, desc: "Stay in current directory", type: :boolean, aliases: ["--no-auto-navigate"]
54
55
  option :commit_message, desc: "Custom commit message", aliases: []
@@ -9,6 +9,8 @@ require_relative "cli/commands/switch"
9
9
  require_relative "cli/commands/remove"
10
10
  require_relative "cli/commands/prune"
11
11
  require_relative "cli/commands/config"
12
+ require_relative "cli/commands/cleanup"
13
+ require_relative "cli/commands/bootstrap"
12
14
  require "ace/core"
13
15
  require "ace/support/cli"
14
16
 
@@ -29,6 +31,8 @@ module Ace
29
31
  ["switch", "Resolve a worktree path for cd navigation"],
30
32
  ["remove", "Remove a worktree by task, branch, or path"],
31
33
  ["prune", "Prune stale/deleted worktree references"],
34
+ ["cleanup", "Report cleanup plan for merged worktrees and refs"],
35
+ ["bootstrap", "Rerun preparation phases for an existing worktree"],
32
36
  ["config", "Show and validate configuration"]
33
37
  ].freeze
34
38
 
@@ -78,6 +82,8 @@ module Ace
78
82
  register "switch", wrap_command(CLI::Commands::Switch), aliases: ["cd"]
79
83
  register "remove", wrap_command(CLI::Commands::Remove), aliases: ["rm"]
80
84
  register "prune", wrap_command(CLI::Commands::Prune), aliases: []
85
+ register "cleanup", wrap_command(CLI::Commands::Cleanup), aliases: []
86
+ register "bootstrap", wrap_command(CLI::Commands::Bootstrap), aliases: []
81
87
  register "config", wrap_command(CLI::Commands::Config), aliases: []
82
88
 
83
89
  # Version command
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require_relative "../organisms/worktree_manager"
5
+ require_relative "../molecules/toolchain_truster"
6
+ require_relative "../molecules/bootstrap_executor"
7
+ require_relative "../molecules/config_loader"
8
+
9
+ module Ace
10
+ module Git
11
+ module Worktree
12
+ module Commands
13
+ # Bootstrap command
14
+ #
15
+ # Reruns recoverable preparation phases (toolchain trust and bootstrap policy)
16
+ # for an existing worktree in place.
17
+ class BootstrapCommand
18
+ def initialize(manager: nil)
19
+ @manager = manager || Ace::Git::Worktree::Organisms::WorktreeManager.new
20
+ end
21
+
22
+ # Run bootstrap retry on existing worktree
23
+ #
24
+ # @param args [Array<String>] Arguments including identifier and optional flags
25
+ # @return [Integer] Exit code
26
+ def run(args = [])
27
+ options = parse_arguments(args)
28
+ return show_help if options[:help] || options[:identifier].nil?
29
+
30
+ worktree_info = find_target_worktree(options[:identifier])
31
+ unless worktree_info
32
+ puts "Error: Worktree matching '#{options[:identifier]}' not found."
33
+ return 1
34
+ end
35
+
36
+ wt_path = worktree_info[:path]
37
+ project_root = Dir.pwd
38
+ config = Molecules::ConfigLoader.new(project_root).load_without_validation
39
+
40
+ # Phase 1: Toolchain Trust
41
+ truster = Molecules::ToolchainTruster.new(project_root: wt_path, policy: "required")
42
+ trust_res = truster.verify_and_trust
43
+
44
+ # Phase 2: Bootstrap execution
45
+ executor = Molecules::BootstrapExecutor.new(
46
+ project_root: project_root,
47
+ worktree_path: wt_path,
48
+ bootstrap_config: config.bootstrap,
49
+ no_bootstrap: options[:no_bootstrap] || false
50
+ )
51
+ bootstrap_res = executor.run
52
+
53
+ phases = [trust_res, bootstrap_res]
54
+
55
+ req_failed = phases.any? { |p| p[:status] == "required_failed" }
56
+ adv_failed = phases.any? { |p| p[:status] == "advisory_failed" }
57
+
58
+ readiness = if req_failed
59
+ "not_ready"
60
+ elsif adv_failed
61
+ "ready_with_warning"
62
+ else
63
+ "ready"
64
+ end
65
+
66
+ result = {
67
+ identifier: options[:identifier],
68
+ worktree_path: wt_path,
69
+ branch: worktree_info[:branch],
70
+ readiness: readiness,
71
+ phases: phases,
72
+ retry_command: (readiness == "not_ready") ? "ace-git-worktree bootstrap #{options[:identifier]}" : nil
73
+ }
74
+
75
+ if options[:json]
76
+ puts JSON.pretty_generate(result)
77
+ else
78
+ puts "Worktree Preparation Retry: #{wt_path}"
79
+ puts "Readiness: #{readiness}"
80
+ puts
81
+ puts "Phases:"
82
+ phases.each do |p|
83
+ st = p[:status]
84
+ icon = case st
85
+ when "succeeded", "not_applicable" then "✓"
86
+ when "skipped" then "↷"
87
+ when "advisory_failed" then "⚠️"
88
+ else "❌"
89
+ end
90
+ puts " #{icon} #{p[:phase]}: #{st}"
91
+ puts " Command: #{p[:command]}" if p[:command]
92
+ puts " Output: #{p[:output]}" if p[:output] && !p[:output].empty? && st.include?("failed")
93
+ end
94
+ if readiness == "not_ready"
95
+ puts
96
+ puts "Retry with: ace-git-worktree bootstrap #{options[:identifier]}"
97
+ end
98
+ end
99
+
100
+ (readiness == "not_ready") ? 1 : 0
101
+ rescue => e
102
+ puts "Error: #{e.message}"
103
+ 1
104
+ end
105
+
106
+ def show_help
107
+ puts <<~HELP
108
+ ace-git-worktree bootstrap - Rerun preparation phases on existing worktree
109
+
110
+ USAGE:
111
+ ace-git-worktree bootstrap <identifier> [OPTIONS]
112
+
113
+ OPTIONS:
114
+ --json Output format as JSON
115
+ --help, -h Show this help message
116
+
117
+ EXAMPLES:
118
+ ace-git-worktree bootstrap 081
119
+ ace-git-worktree bootstrap t.8vb.t.vyz.2 --json
120
+ HELP
121
+ 0
122
+ end
123
+
124
+ private
125
+
126
+ def parse_arguments(args)
127
+ options = {identifier: nil, json: false, help: false}
128
+ i = 0
129
+ while i < args.length
130
+ arg = args[i]
131
+ case arg
132
+ when "--json"
133
+ options[:json] = true
134
+ when "--help", "-h"
135
+ options[:help] = true
136
+ when /^--/
137
+ raise ArgumentError, "Unknown option: #{arg}"
138
+ else
139
+ options[:identifier] = arg if options[:identifier].nil?
140
+ end
141
+ i += 1
142
+ end
143
+ options
144
+ end
145
+
146
+ def find_target_worktree(identifier)
147
+ worktrees = @manager.list[:worktrees] || []
148
+ worktrees.find do |wt|
149
+ wt[:path]&.end_with?("/#{identifier}") ||
150
+ wt[:path]&.include?("/t.#{identifier}") ||
151
+ wt[:branch]&.include?(identifier) ||
152
+ wt[:task_id] == identifier
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end