aidp 0.49.10 → 0.49.12

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: 2edfd145fd098c930d76688336659919ad8fa5a4df4cb9f5c3aa5434a60d425f
4
- data.tar.gz: c5a1a71bd5adb17cd850484964f11750e7d10e7d5c05e9aa366c89f700794d31
3
+ metadata.gz: 7aebc32c94a99c769c32718c0d1831888e9d7e9e16eb2968f64e9fd8e1d04b23
4
+ data.tar.gz: 10380f1bfa01c5154d3aa19be32a67b8e757a66c27b1e8bb128769787357d194
5
5
  SHA512:
6
- metadata.gz: 21667ac8aae592117e34e738b74eb3b01c2f941a9a5672f54ae6e1549bff56289e5bf62e568caf6a08b3a492d17db22e01ba4d28e1a2944e2259d6214a83a2b4
7
- data.tar.gz: d4f213b4e236003cccbdb5dbbb5dd41be6e9ce4448cdbda52900a0f651a6939d1801cfe201d3ef35b9ed2218caa04f8aa130e8d5479e3eeccf4cefdda2be615b
6
+ metadata.gz: e5b4b81a0662bbc8e5d6b2a4ecdbc0104f14cef44898cd01c219ed1a09ae53218afa89cb5178acc2a4361eee3d0ff1081fd32cdf225a4df3862168081b3140d5
7
+ data.tar.gz: '02699b7be327242d960f268f972b6fd38984d251878bca63b2075dceeae3383ac3ca82a42f182755da16263754e5a701581a5d6eeb3436980df7ff4c1c85384f'
@@ -342,12 +342,13 @@ module Aidp
342
342
  return 0
343
343
  end
344
344
 
345
- # Run RSpec with timeout protection
346
- cmd = "bundle exec rspec #{rspec_path} --format failures"
347
- @prompt.say("$ #{cmd}\n")
345
+ # Run RSpec with timeout protection, passing arguments without shell
346
+ # interpretation of the library-provided project path
347
+ command_args = ["bundle", "exec", "rspec", rspec_path, "--format", "failures"]
348
+ @prompt.say("$ #{command_args.join(" ")}\n")
348
349
  @prompt.say("(timeout: #{AUDIT_TIMEOUT_SECONDS / 60} minutes)\n")
349
350
 
350
- exit_status = run_with_timeout(cmd, AUDIT_TIMEOUT_SECONDS)
351
+ exit_status = run_with_timeout(command_args, AUDIT_TIMEOUT_SECONDS)
351
352
 
352
353
  case exit_status
353
354
  when 0
@@ -365,11 +366,11 @@ module Aidp
365
366
  private
366
367
 
367
368
  # Run a command with timeout protection
368
- # @param cmd [String] The command to execute
369
+ # @param command_args [Array<String>] The command and its arguments
369
370
  # @param timeout [Integer] Timeout in seconds
370
371
  # @return [Integer, Symbol] Exit status or :timeout
371
- def run_with_timeout(cmd, timeout)
372
- pid = spawn(cmd)
372
+ def run_with_timeout(command_args, timeout)
373
+ pid = Process.spawn(*command_args)
373
374
  start_time = Time.now
374
375
 
375
376
  loop do
@@ -3,6 +3,7 @@
3
3
  require "yaml"
4
4
  require "time"
5
5
  require "json"
6
+ require "open3"
6
7
  require "aidp/rescue_logging"
7
8
 
8
9
  module Aidp
@@ -183,7 +184,8 @@ module Aidp
183
184
  def run_rubocop_check
184
185
  # Run rubocop and parse output to get a quality score
185
186
  # This is a simplified version - could be enhanced
186
- result = `cd #{@project_dir} && rubocop --format json 2>/dev/null`
187
+ stdout, _stderr, _status = Open3.capture3("rubocop", "--format", "json", chdir: @project_dir)
188
+ result = stdout
187
189
  return nil if result.empty?
188
190
 
189
191
  data = JSON.parse(result)
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "tty-prompt"
4
4
  require "tty-spinner"
5
+ require "open3"
5
6
  require_relative "async_work_loop_runner"
6
7
  require_relative "repl_macros"
7
8
  require_relative "../rescue_logging"
@@ -307,9 +308,9 @@ module Aidp
307
308
  }
308
309
  end
309
310
 
310
- # Execute reset
311
- output = `git reset --hard HEAD~#{count} 2>&1`
312
- success = $?.success?
311
+ # Execute reset without shell interpretation of the rollback count
312
+ output, status = Open3.capture2e("git", "reset", "--hard", "HEAD~#{count}")
313
+ success = status.success?
313
314
 
314
315
  {
315
316
  success: success,
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "open3"
3
4
  require_relative "prompt_manager"
4
5
  require_relative "prompt_evaluator"
5
6
  require_relative "checkpoint"
@@ -2359,8 +2360,9 @@ module Aidp
2359
2360
 
2360
2361
  stats = {}
2361
2362
  files.each do |file|
2362
- # Use git diff to get line counts
2363
- output = `git diff --numstat HEAD -- "#{file}" 2>/dev/null`.strip
2363
+ # Use git diff to get line counts without shell interpretation of file names
2364
+ stdout, _stderr, _status = Open3.capture3("git", "diff", "--numstat", "HEAD", "--", file.to_s)
2365
+ output = stdout.strip
2364
2366
  next if output.empty?
2365
2367
 
2366
2368
  parts = output.split("\t")
@@ -64,27 +64,27 @@ module Aidp
64
64
  test_commands = detect_test_commands
65
65
  return true if test_commands.empty?
66
66
 
67
- test_commands.any? do |cmd|
68
- system("cd #{@project_dir} && #{cmd} > /dev/null 2>&1")
69
- end
67
+ test_commands.any? { |cmd| run_project_command(cmd) }
70
68
  end
71
69
 
72
70
  def linting_clean?
73
71
  lint_commands = detect_lint_commands
74
72
  return true if lint_commands.empty?
75
73
 
76
- lint_commands.all? do |cmd|
77
- system("cd #{@project_dir} && #{cmd} > /dev/null 2>&1")
78
- end
74
+ lint_commands.all? { |cmd| run_project_command(cmd) }
79
75
  end
80
76
 
81
77
  def build_successful?
82
78
  build_commands = detect_build_commands
83
79
  return true if build_commands.empty?
84
80
 
85
- build_commands.any? do |cmd|
86
- system("cd #{@project_dir} && #{cmd} > /dev/null 2>&1")
87
- end
81
+ build_commands.any? { |cmd| run_project_command(cmd) }
82
+ end
83
+
84
+ # Run a detected command in the project directory without building a
85
+ # shell command string from the library-provided project path
86
+ def run_project_command(cmd)
87
+ system(cmd, chdir: @project_dir, out: File::NULL, err: File::NULL)
88
88
  end
89
89
 
90
90
  def documentation_complete?
@@ -357,9 +357,9 @@ module Aidp
357
357
 
358
358
  next unless tool_command
359
359
 
360
- # Check if command exists
360
+ # Check if command exists without shell interpretation
361
361
  begin
362
- unless system("which #{tool_command} > /dev/null 2>&1")
362
+ unless system("which", tool_command, out: File::NULL, err: File::NULL)
363
363
  warnings << " ⚠️ #{format_tool(tool_data[:tool])} detected but command '#{tool_command}' not found in PATH"
364
364
  end
365
365
  rescue IOError
@@ -1,6 +1,5 @@
1
1
  require "json"
2
2
  require "fileutils"
3
- require "shellwords"
4
3
 
5
4
  module Aidp
6
5
  # Manages worktrees specifically for Pull Request branches
@@ -151,17 +150,18 @@ module Aidp
151
150
  branch == "* #{base_branch}"
152
151
  end
153
152
 
154
- # Enhance branch tracking and fetching
153
+ # Enhance branch tracking and fetching; argv execution keeps the
154
+ # library-provided branch name from being interpreted by a shell
155
155
  unless base_branch_exists
156
156
  # Try multiple fetch strategies
157
157
  fetch_commands = [
158
- "git fetch origin #{base_branch}:#{base_branch} 2>/dev/null",
159
- "git fetch origin 2>/dev/null",
160
- "git fetch --all 2>/dev/null"
158
+ ["git", "fetch", "origin", "#{base_branch}:#{base_branch}"],
159
+ ["git", "fetch", "origin"],
160
+ ["git", "fetch", "--all"]
161
161
  ]
162
162
 
163
- fetch_commands.each do |fetch_cmd|
164
- @shell_executor.system(fetch_cmd)
163
+ fetch_commands.each do |fetch_args|
164
+ @shell_executor.system(*fetch_args, err: File::NULL)
165
165
  branch_list_output = `git branch -a`.split("\n").map(&:strip)
166
166
  base_branch_exists = branch_list_output.any? do |branch|
167
167
  branch.end_with?("/#{base_branch}", "remotes/origin/#{base_branch}") ||
@@ -174,14 +174,15 @@ module Aidp
174
174
 
175
175
  raise ArgumentError, "Base branch '#{base_branch}' does not exist in the repository" unless base_branch_exists
176
176
 
177
- # Robust worktree creation with enhanced error handling and logging
178
- worktree_create_command = "git worktree add #{Shellwords.escape(worktree_path)} -b #{Shellwords.escape(head_branch)} #{Shellwords.escape(base_branch)}"
179
- unless @shell_executor.system(worktree_create_command)
177
+ # Robust worktree creation with enhanced error handling and logging;
178
+ # argv execution keeps library-provided branches and paths literal
179
+ worktree_create_args = ["git", "worktree", "add", worktree_path, "-b", head_branch, base_branch]
180
+ unless @shell_executor.system(*worktree_create_args)
180
181
  error_details = {
181
182
  pr_number: pr_number,
182
183
  base_branch: base_branch,
183
184
  head_branch: head_branch,
184
- command: worktree_create_command
185
+ command: worktree_create_args.join(" ")
185
186
  }
186
187
  Aidp.log_error(
187
188
  "pr_worktree_manager", "worktree_creation_failed",
@@ -377,9 +378,9 @@ module Aidp
377
378
  end
378
379
  end
379
380
 
380
- # Stage only successfully modified files
381
+ # Stage only successfully modified files without shell interpretation
381
382
  unless successful_files.empty?
382
- @shell_executor.system("git add #{successful_files.map { |f| Shellwords.escape(f) }.join(" ")}")
383
+ @shell_executor.system("git", "add", "--", *successful_files)
383
384
  end
384
385
  end
385
386
 
@@ -428,7 +429,7 @@ module Aidp
428
429
 
429
430
  Dir.chdir(worktree_path) do
430
431
  # Check staged changes with more robust capture
431
- staged_changes_output = @shell_executor.run("git diff --staged --name-only").strip
432
+ staged_changes_output = @shell_executor.run_argv("git", "diff", "--staged", "--name-only").stdout.strip
432
433
 
433
434
  if !staged_changes_output.empty?
434
435
  push_result[:git_actions][:staged_changes] = true
@@ -493,8 +494,10 @@ module Aidp
493
494
  existing_worktree = @worktrees[pr_number.to_s]
494
495
  return false unless existing_worktree
495
496
 
496
- # Remove git worktree
497
- @shell_executor.system("git worktree remove #{existing_worktree["path"]}") if File.exist?(existing_worktree["path"])
497
+ # Remove git worktree without shell interpretation of registry data
498
+ if File.exist?(existing_worktree["path"])
499
+ @shell_executor.system("git", "worktree", "remove", existing_worktree["path"])
500
+ end
498
501
 
499
502
  # Remove from registry and save
500
503
  @worktrees.delete(pr_number.to_s)
@@ -3,8 +3,9 @@
3
3
  module Aidp
4
4
  # Shell command executor wrapper for testability
5
5
  #
6
- # Provides two modes of execution:
7
- # 1. `run(command)` - Captures output silently via backticks
6
+ # Commands are always executed in argument form so shell
7
+ # metacharacters in any value are treated as literal data:
8
+ # 1. `run_argv(*command)` - Captures output without shell interpretation
8
9
  # 2. `system(*args)` - Wraps Kernel.system() with optional output suppression
9
10
  #
10
11
  # In tests, set `ShellExecutor.suppress_output = true` to suppress all
@@ -51,14 +52,6 @@ module Aidp
51
52
  end
52
53
  end
53
54
 
54
- # Run a command and capture its output
55
- #
56
- # @param command [String] The shell command to run
57
- # @return [String] The command's stdout output
58
- def run(command)
59
- `#{command}`
60
- end
61
-
62
55
  # Run a command without shell interpretation and capture its output
63
56
  #
64
57
  # The command and its arguments are passed directly to the operating
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.10"
4
+ VERSION = "0.49.12"
5
5
  end
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.10
4
+ version: 0.49.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan