aidp 0.49.8 → 0.49.10

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c15551574a43168d1adfd3157dd3f8981c17972953e7f5285b355358e7cdc288
4
- data.tar.gz: 37ef9124f109f9a615da64d86e938324bc4751322d7e32bfd39c9a93eb2f77f2
3
+ metadata.gz: 2edfd145fd098c930d76688336659919ad8fa5a4df4cb9f5c3aa5434a60d425f
4
+ data.tar.gz: c5a1a71bd5adb17cd850484964f11750e7d10e7d5c05e9aa366c89f700794d31
5
5
  SHA512:
6
- metadata.gz: b1b634b25d0aacea5def51a4e57ccc26a636fed79d144a7e6fdd8ebe248ee735abe57446e0ba98ae369dfd150cfad3abbb309dd8455be315afe7d2cd48fe1c35
7
- data.tar.gz: 1b61e3ff6de42b5008c44fe96ad786fdc5fee5ea25ccec925d754dd055ab59c87b4c218fb2af03a5ad6284f2a7fed9fc3fa4aa00873fed6cfc25028416d04768
6
+ metadata.gz: 21667ac8aae592117e34e738b74eb3b01c2f941a9a5672f54ae6e1549bff56289e5bf62e568caf6a08b3a492d17db22e01ba4d28e1a2944e2259d6214a83a2b4
7
+ data.tar.gz: d4f213b4e236003cccbdb5dbbb5dd41be6e9ce4448cdbda52900a0f651a6939d1801cfe201d3ef35b9ed2218caa04f8aa130e8d5479e3eeccf4cefdda2be615b
@@ -434,19 +434,16 @@ module Aidp
434
434
  push_result[:git_actions][:staged_changes] = true
435
435
  push_result[:changed_files] = staged_changes_output.split("\n")
436
436
 
437
- # More robust commit command with additional logging
438
- commit_message = "Changes applied via AIDP request-changes workflow for PR ##{pr_number}"
439
- commit_command = "git commit -m '#{commit_message}' 2>&1"
440
- commit_output = @shell_executor.run(commit_command).strip
437
+ # Commit staged changes without shell interpretation of library input
438
+ commit_output = commit_staged_changes(pr_number)
441
439
 
442
- if @shell_executor.success?
440
+ if commit_output.success?
443
441
  push_result[:git_actions][:committed] = true
444
442
 
445
- # Enhanced push with verbose tracking
446
- push_command = "git push origin #{head_branch} 2>&1"
447
- push_output = @shell_executor.run(push_command).strip
443
+ # Push the registry-sourced head branch without shell interpretation
444
+ push_output = push_head_branch(head_branch)
448
445
 
449
- if @shell_executor.success?
446
+ if push_output.success?
450
447
  push_result[:git_actions][:pushed] = true
451
448
  push_result[:success] = true
452
449
 
@@ -458,22 +455,22 @@ module Aidp
458
455
  )
459
456
  else
460
457
  # Detailed push error logging
461
- push_result[:errors] << "Push failed: #{push_output}"
458
+ push_result[:errors] << "Push failed: #{push_output.output.strip}"
462
459
  Aidp.log_error(
463
460
  "pr_worktree_manager", "push_changes_failed",
464
461
  pr_number: pr_number,
465
462
  branch: head_branch,
466
- error_details: push_output
463
+ error_details: push_output.output.strip
467
464
  )
468
465
  end
469
466
  else
470
467
  # Detailed commit error logging
471
- push_result[:errors] << "Commit failed: #{commit_output}"
468
+ push_result[:errors] << "Commit failed: #{commit_output.output.strip}"
472
469
  Aidp.log_error(
473
470
  "pr_worktree_manager", "commit_changes_failed",
474
471
  pr_number: pr_number,
475
472
  branch: head_branch,
476
- error_details: commit_output
473
+ error_details: commit_output.output.strip
477
474
  )
478
475
  end
479
476
  else
@@ -532,6 +529,19 @@ module Aidp
532
529
 
533
530
  private
534
531
 
532
+ # Commit currently staged changes; argv execution keeps externally
533
+ # sourced values (pr_number) from being interpreted by a shell
534
+ def commit_staged_changes(pr_number)
535
+ commit_message = "Changes applied via AIDP request-changes workflow for PR ##{pr_number}"
536
+ @shell_executor.run_argv("git", "commit", "-m", commit_message)
537
+ end
538
+
539
+ # Push the head branch; argv execution keeps registry-sourced branch
540
+ # names from being interpreted by a shell
541
+ def push_head_branch(head_branch)
542
+ @shell_executor.run_argv("git", "push", "origin", "--", head_branch)
543
+ end
544
+
535
545
  # Load the worktree registry from file
536
546
  def load_registry
537
547
  return {} unless File.exist?(worktree_registry_path)
@@ -25,6 +25,32 @@ module Aidp
25
25
  end
26
26
  self.suppress_output = false
27
27
 
28
+ # Immutable result of a shell-free command execution
29
+ class Result
30
+ attr_reader :stdout, :stderr, :exit_status
31
+
32
+ # @param stdout [String] standard output from the command
33
+ # @param stderr [String] standard error from the command
34
+ # @param exit_status [Integer] exit status code
35
+ def initialize(stdout:, stderr:, exit_status:)
36
+ @stdout = stdout.to_s.freeze
37
+ @stderr = stderr.to_s.freeze
38
+ @exit_status = exit_status || 1
39
+ freeze
40
+ end
41
+
42
+ # @return [Boolean] true if exit_status is 0
43
+ def success?
44
+ @exit_status.zero?
45
+ end
46
+
47
+ # Combined output, equivalent to shell `2>&1` redirection
48
+ # @return [String]
49
+ def output
50
+ [@stdout, @stderr].reject(&:empty?).join("\n")
51
+ end
52
+ end
53
+
28
54
  # Run a command and capture its output
29
55
  #
30
56
  # @param command [String] The shell command to run
@@ -33,11 +59,19 @@ module Aidp
33
59
  `#{command}`
34
60
  end
35
61
 
36
- # Check if the last command succeeded
62
+ # Run a command without shell interpretation and capture its output
63
+ #
64
+ # The command and its arguments are passed directly to the operating
65
+ # system, so shell metacharacters in any argument are treated as
66
+ # literal data. Prefer this over #run whenever an argument may contain
67
+ # externally supplied values.
37
68
  #
38
- # @return [Boolean] true if last command exited with status 0
39
- def success?
40
- $?.success?
69
+ # @param command [Array<String>] the command and its arguments
70
+ # @return [Result] captured stdout/stderr with the exit status
71
+ def run_argv(*command)
72
+ require "open3"
73
+ stdout, stderr, status = Open3.capture3(*command.map(&:to_s))
74
+ Result.new(stdout: stdout, stderr: stderr, exit_status: status.exitstatus)
41
75
  end
42
76
 
43
77
  # Run a command via system(), optionally suppressing output
data/lib/aidp/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Aidp
4
- VERSION = "0.49.8"
4
+ VERSION = "0.49.10"
5
5
  end
@@ -1,3 +1,4 @@
1
+ require "open3"
1
2
  require "aidp/watch/base_processor"
2
3
  require "aidp/worktree"
3
4
  require "aidp/pr_worktree_manager"
@@ -116,11 +117,12 @@ module Aidp
116
117
  # Use PRWorktreeManager's method to perform rebase and conflict resolution
117
118
  with_worktree_context(worktree_path) do
118
119
  # Fetch the latest changes
119
- @shell_executor.system("git fetch origin")
120
+ @shell_executor.system("git", "fetch", "origin")
120
121
 
121
- # Attempt to rebase
122
- rebase_command = "git rebase origin/#{base_branch}"
123
- rebase_output = @shell_executor.system(rebase_command)
122
+ # Attempt to rebase. Pass args as separate argv entries (not an
123
+ # interpolated shell string) so branch names can never be
124
+ # interpreted by a shell.
125
+ rebase_output = @shell_executor.system("git", "rebase", "origin/#{base_branch}")
124
126
 
125
127
  unless rebase_output
126
128
  # Conflict resolution using AI
@@ -134,9 +136,8 @@ module Aidp
134
136
  if resolution
135
137
  # Stage resolved files and continue rebase
136
138
  # GIT_EDITOR=true prevents editor from opening during automated rebase
137
- quoted_files = conflict_files.map { |f| "\"#{f}\"" }.join(" ")
138
- @shell_executor.system("git add #{quoted_files}")
139
- @shell_executor.system({"GIT_EDITOR" => "true"}, "git rebase --continue")
139
+ @shell_executor.system("git", "add", *conflict_files)
140
+ @shell_executor.system({"GIT_EDITOR" => "true"}, "git", "rebase", "--continue")
140
141
  else
141
142
  return false
142
143
  end
@@ -147,15 +148,19 @@ module Aidp
147
148
  end
148
149
 
149
150
  # Push the rebased branch
150
- @shell_executor.system("git push -f origin #{head_branch}")
151
+ @shell_executor.system("git", "push", "-f", "origin", head_branch)
151
152
  end
152
153
 
153
154
  true
154
155
  end
155
156
 
156
157
  def detect_conflicting_files(worktree_path)
157
- # Use git to list conflicting files
158
- `cd #{worktree_path} && git diff --name-only --diff-filter=U`.split("\n")
158
+ # Use git to list conflicting files. Argv form avoids shell
159
+ # interpretation of worktree_path or any file names in the output.
160
+ stdout, _stderr, _status = Dir.chdir(worktree_path) do
161
+ Open3.capture3("git", "diff", "--name-only", "--diff-filter=U")
162
+ end
163
+ stdout.split("\n")
159
164
  end
160
165
 
161
166
  def resolve_conflicts(worktree_path, base_branch, conflict_files)
@@ -189,8 +194,10 @@ module Aidp
189
194
 
190
195
  # Stage and continue rebase
191
196
  # GIT_EDITOR=true prevents editor from opening during automated rebase
192
- @shell_executor.system("cd #{worktree_path} && git add .")
193
- @shell_executor.system({"GIT_EDITOR" => "true"}, "cd #{worktree_path} && git rebase --continue")
197
+ Dir.chdir(worktree_path) do
198
+ @shell_executor.system("git", "add", ".")
199
+ @shell_executor.system({"GIT_EDITOR" => "true"}, "git", "rebase", "--continue")
200
+ end
194
201
  end
195
202
 
196
203
  def post_rebase_status(pr_number, rebase_result, error_detail = nil)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aidp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.49.8
4
+ version: 0.49.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan