aidp 0.15.1 → 0.15.2
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 +4 -4
- data/lib/aidp/cli.rb +41 -3
- data/lib/aidp/harness/provider_info.rb +1 -1
- data/lib/aidp/harness/provider_manager.rb +9 -8
- data/lib/aidp/init/doc_generator.rb +75 -10
- data/lib/aidp/init/project_analyzer.rb +154 -26
- data/lib/aidp/init/runner.rb +263 -10
- data/lib/aidp/logger.rb +11 -0
- data/lib/aidp/version.rb +1 -1
- metadata +1 -4
- data/lib/aidp/analyze/prioritizer.rb +0 -403
- data/lib/aidp/analyze/report_generator.rb +0 -582
- data/lib/aidp/cli/checkpoint_command.rb +0 -98
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '0691816c2748d6704d61bba9ed4c992f1b7d3ac8f70385ac94cb199916b71491'
|
4
|
+
data.tar.gz: e0a8b4a7c9ece98972565635894d7312c4253c2adb3341192c7622e8e28d3a12
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0d3ffce4b6aa8913658862e4412c165c4f20e8164845267d9d85c574b340d4140e63abbae359c47387255081c2738cbdaae215c159e1aeb4d36e7a84b5405dfc
|
7
|
+
data.tar.gz: d2a809ea1b79bbe97e8adfe90903ecb610851837d7b744da208c20e74574346e18e4e3a25ebb136f702fb2501cf5855f1aac0d70d31b6be618c0843a51d5d90f
|
data/lib/aidp/cli.rb
CHANGED
@@ -353,7 +353,7 @@ module Aidp
|
|
353
353
|
when "mcp" then run_mcp_command(args)
|
354
354
|
when "issue" then run_issue_command(args)
|
355
355
|
when "config" then run_config_command(args)
|
356
|
-
when "init" then run_init_command
|
356
|
+
when "init" then run_init_command(args)
|
357
357
|
when "watch" then run_watch_command(args)
|
358
358
|
else
|
359
359
|
display_message("Unknown command: #{cmd}", type: :info)
|
@@ -1015,11 +1015,49 @@ module Aidp
|
|
1015
1015
|
wizard.run
|
1016
1016
|
end
|
1017
1017
|
|
1018
|
-
def run_init_command
|
1019
|
-
|
1018
|
+
def run_init_command(args = [])
|
1019
|
+
options = {}
|
1020
|
+
|
1021
|
+
until args.empty?
|
1022
|
+
token = args.shift
|
1023
|
+
case token
|
1024
|
+
when "--explain-detection"
|
1025
|
+
options[:explain_detection] = true
|
1026
|
+
when "--dry-run"
|
1027
|
+
options[:dry_run] = true
|
1028
|
+
when "--preview"
|
1029
|
+
options[:preview] = true
|
1030
|
+
when "-h", "--help"
|
1031
|
+
display_init_usage
|
1032
|
+
return
|
1033
|
+
else
|
1034
|
+
display_message("Unknown init option: #{token}", type: :error)
|
1035
|
+
display_init_usage
|
1036
|
+
return
|
1037
|
+
end
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
require_relative "init/runner"
|
1041
|
+
runner = Aidp::Init::Runner.new(Dir.pwd, prompt: TTY::Prompt.new, options: options)
|
1020
1042
|
runner.run
|
1021
1043
|
end
|
1022
1044
|
|
1045
|
+
def display_init_usage
|
1046
|
+
display_message("Usage: aidp init [options]", type: :info)
|
1047
|
+
display_message("", type: :info)
|
1048
|
+
display_message("Options:", type: :info)
|
1049
|
+
display_message(" --explain-detection Show detailed evidence for all detections", type: :info)
|
1050
|
+
display_message(" --dry-run Run analysis without generating files", type: :info)
|
1051
|
+
display_message(" --preview Show preview before writing files", type: :info)
|
1052
|
+
display_message(" -h, --help Show this help message", type: :info)
|
1053
|
+
display_message("", type: :info)
|
1054
|
+
display_message("Examples:", type: :info)
|
1055
|
+
display_message(" aidp init # Run full init workflow", type: :info)
|
1056
|
+
display_message(" aidp init --explain-detection # Show detailed detection evidence", type: :info)
|
1057
|
+
display_message(" aidp init --dry-run # Preview without writing files", type: :info)
|
1058
|
+
display_message(" aidp init --preview # Show preview before writing", type: :info)
|
1059
|
+
end
|
1060
|
+
|
1023
1061
|
def run_watch_command(args)
|
1024
1062
|
if args.empty?
|
1025
1063
|
display_message("Usage: aidp watch <issues_url> [--interval SECONDS] [--provider NAME] [--once]", type: :info)
|
@@ -71,19 +71,20 @@ module Aidp
|
|
71
71
|
|
72
72
|
# Switch to next available provider with sophisticated fallback logic
|
73
73
|
def switch_provider(reason = "manual_switch", context = {})
|
74
|
-
|
74
|
+
old_provider = current_provider
|
75
|
+
Aidp.logger.info("provider_manager", "Attempting provider switch", reason: reason, current: old_provider, **context)
|
75
76
|
|
76
77
|
# Get fallback chain for current provider
|
77
|
-
provider_fallback_chain = fallback_chain(
|
78
|
+
provider_fallback_chain = fallback_chain(old_provider)
|
78
79
|
|
79
80
|
# Find next healthy provider in fallback chain
|
80
|
-
next_provider = find_next_healthy_provider(provider_fallback_chain,
|
81
|
+
next_provider = find_next_healthy_provider(provider_fallback_chain, old_provider)
|
81
82
|
|
82
83
|
if next_provider
|
83
84
|
success = set_current_provider(next_provider, reason, context)
|
84
85
|
if success
|
85
|
-
log_provider_switch(
|
86
|
-
Aidp.logger.info("provider_manager", "Provider switched successfully", from:
|
86
|
+
log_provider_switch(old_provider, next_provider, reason, context)
|
87
|
+
Aidp.logger.info("provider_manager", "Provider switched successfully", from: old_provider, to: next_provider, reason: reason)
|
87
88
|
return next_provider
|
88
89
|
else
|
89
90
|
Aidp.logger.warn("provider_manager", "Failed to switch to provider", provider: next_provider, reason: reason)
|
@@ -96,7 +97,7 @@ module Aidp
|
|
96
97
|
if next_provider
|
97
98
|
success = set_current_provider(next_provider, reason, context)
|
98
99
|
if success
|
99
|
-
log_provider_switch(
|
100
|
+
log_provider_switch(old_provider, next_provider, reason, context)
|
100
101
|
return next_provider
|
101
102
|
end
|
102
103
|
end
|
@@ -107,14 +108,14 @@ module Aidp
|
|
107
108
|
if next_provider
|
108
109
|
success = set_current_provider(next_provider, reason, context)
|
109
110
|
if success
|
110
|
-
log_provider_switch(
|
111
|
+
log_provider_switch(old_provider, next_provider, reason, context)
|
111
112
|
return next_provider
|
112
113
|
end
|
113
114
|
end
|
114
115
|
|
115
116
|
# No providers available
|
116
117
|
log_no_providers_available(reason, context)
|
117
|
-
Aidp.logger.error("provider_manager", "No providers available for fallback", reason: reason,
|
118
|
+
Aidp.logger.error("provider_manager", "No providers available for fallback", reason: reason, provider: old_provider)
|
118
119
|
nil
|
119
120
|
end
|
120
121
|
|
@@ -32,10 +32,11 @@ module Aidp
|
|
32
32
|
|
33
33
|
def write_style_guide(analysis, preferences)
|
34
34
|
languages = format_list(analysis[:languages].keys)
|
35
|
-
frameworks
|
36
|
-
|
35
|
+
# Extract high-confidence frameworks (>= 0.7)
|
36
|
+
frameworks = extract_confident_names(analysis[:frameworks], threshold: 0.7)
|
37
|
+
test_frameworks = extract_confident_names(analysis[:test_frameworks], threshold: 0.7)
|
37
38
|
key_dirs = format_list(analysis[:key_directories])
|
38
|
-
tooling = analysis[:tooling].
|
39
|
+
tooling = extract_confident_names(analysis[:tooling], threshold: 0.7, key: :tool).map { |tool| format_tool(tool) }.sort
|
39
40
|
|
40
41
|
adoption_note = if truthy?(preferences[:adopt_new_conventions])
|
41
42
|
"This project has opted to adopt new conventions recommended by aidp init. When in doubt, prefer the rules below over legacy patterns."
|
@@ -43,14 +44,17 @@ module Aidp
|
|
43
44
|
"Retain existing conventions when they do not conflict with the guidance below."
|
44
45
|
end
|
45
46
|
|
47
|
+
frameworks_text = frameworks.empty? ? "None detected" : frameworks.join(", ")
|
48
|
+
test_frameworks_text = test_frameworks.empty? ? "Unknown" : test_frameworks.join(", ")
|
49
|
+
|
46
50
|
content = <<~GUIDE
|
47
51
|
# Project LLM Style Guide
|
48
52
|
|
49
53
|
> Generated automatically by `aidp init` on #{Time.now.utc.iso8601}.
|
50
54
|
>
|
51
55
|
> Detected languages: #{languages}
|
52
|
-
> Framework hints: #{
|
53
|
-
> Primary test frameworks: #{
|
56
|
+
> Framework hints: #{frameworks_text}
|
57
|
+
> Primary test frameworks: #{test_frameworks_text}
|
54
58
|
> Key directories: #{key_dirs.empty? ? "Standard structure" : key_dirs}
|
55
59
|
|
56
60
|
#{adoption_note}
|
@@ -115,9 +119,10 @@ module Aidp
|
|
115
119
|
|
116
120
|
def write_project_analysis(analysis)
|
117
121
|
languages = format_language_breakdown(analysis[:languages])
|
118
|
-
frameworks =
|
122
|
+
frameworks = format_framework_detection(analysis[:frameworks])
|
123
|
+
test_frameworks = format_framework_detection(analysis[:test_frameworks])
|
119
124
|
config_files = bullet_list(analysis[:config_files], default: "_No dedicated configuration files discovered_")
|
120
|
-
tooling =
|
125
|
+
tooling = format_tooling_detection(analysis[:tooling])
|
121
126
|
|
122
127
|
stats = analysis[:repo_stats]
|
123
128
|
stats_lines = [
|
@@ -146,7 +151,7 @@ module Aidp
|
|
146
151
|
#{config_files}
|
147
152
|
|
148
153
|
## Test & Quality Signals
|
149
|
-
#{
|
154
|
+
#{test_frameworks}
|
150
155
|
|
151
156
|
## Local Quality Toolchain
|
152
157
|
#{tooling}
|
@@ -175,13 +180,19 @@ module Aidp
|
|
175
180
|
"- Keep legacy style deviations documented until dedicated refactors are scheduled.\n"
|
176
181
|
end
|
177
182
|
|
183
|
+
tooling_section = if tooling.empty?
|
184
|
+
"_No linting/formatting tools detected. Consider adding RuboCop, ESLint, or Prettier based on the primary language._"
|
185
|
+
else
|
186
|
+
format_tooling_detection_table(tooling)
|
187
|
+
end
|
188
|
+
|
178
189
|
content = <<~PLAN
|
179
190
|
# Code Quality Plan
|
180
191
|
|
181
192
|
This plan captures the current tooling landscape and proposes next steps for keeping the codebase healthy. Generated by `aidp init` on #{Time.now.utc.iso8601}.
|
182
193
|
|
183
194
|
## Local Quality Toolchain
|
184
|
-
#{
|
195
|
+
#{tooling_section}
|
185
196
|
|
186
197
|
## Immediate Actions
|
187
198
|
#{proactive}#{migration}- Document onboarding steps in `docs/` to ensure future contributors follow the agreed workflow.
|
@@ -189,7 +200,7 @@ module Aidp
|
|
189
200
|
## Long-Term Improvements
|
190
201
|
- Keep the style guide in sync with real-world code changes; regenerate with `aidp init` after major rewrites.
|
191
202
|
- Automate test and lint runs via CI (detected: #{analysis.dig(:repo_stats, :has_ci_config) ? "yes" : "no"}).
|
192
|
-
- Track flaky tests or unstable tooling in `PROJECT_ANALYSIS.md` under a
|
203
|
+
- Track flaky tests or unstable tooling in `PROJECT_ANALYSIS.md` under a "Health Log" section.
|
193
204
|
|
194
205
|
---
|
195
206
|
Based on templates: `analysis/analyze_static_code.md`, `analysis/analyze_tests.md`.
|
@@ -251,6 +262,60 @@ module Aidp
|
|
251
262
|
rescue
|
252
263
|
false
|
253
264
|
end
|
265
|
+
|
266
|
+
# Extract confident names from detection results
|
267
|
+
def extract_confident_names(detections, threshold: 0.7, key: :name)
|
268
|
+
return [] if detections.nil? || detections.empty?
|
269
|
+
|
270
|
+
detections.select { |d| d[:confidence] >= threshold }.map { |d| d[key] }
|
271
|
+
end
|
272
|
+
|
273
|
+
# Format framework detection with confidence levels
|
274
|
+
def format_framework_detection(detections)
|
275
|
+
return "_None detected_" if detections.nil? || detections.empty?
|
276
|
+
|
277
|
+
lines = detections.map do |detection|
|
278
|
+
name = detection[:name]
|
279
|
+
confidence = (detection[:confidence] * 100).round
|
280
|
+
evidence = detection[:evidence].join("; ")
|
281
|
+
"- **#{name}** (#{confidence}% confidence)\n - Evidence: #{evidence}"
|
282
|
+
end
|
283
|
+
|
284
|
+
lines.join("\n")
|
285
|
+
end
|
286
|
+
|
287
|
+
# Format tooling detection with confidence levels
|
288
|
+
def format_tooling_detection(tooling)
|
289
|
+
return "_No tooling detected._" if tooling.nil? || tooling.empty?
|
290
|
+
|
291
|
+
header = "| Tool | Confidence | Evidence |\n|------|------------|----------|"
|
292
|
+
rows = tooling.map do |tool_data|
|
293
|
+
tool_name = format_tool(tool_data[:tool])
|
294
|
+
confidence = "#{(tool_data[:confidence] * 100).round}%"
|
295
|
+
evidence = tool_data[:evidence].join(", ")
|
296
|
+
"| #{tool_name} | #{confidence} | #{evidence} |"
|
297
|
+
end
|
298
|
+
|
299
|
+
([header] + rows).join("\n")
|
300
|
+
end
|
301
|
+
|
302
|
+
# Format tooling detection table for quality plan
|
303
|
+
def format_tooling_detection_table(tooling)
|
304
|
+
return "_No tooling detected._" if tooling.nil? || tooling.empty?
|
305
|
+
|
306
|
+
header = "| Tool | Evidence |\n|------|----------|"
|
307
|
+
rows = tooling.select { |t| t[:confidence] >= 0.7 }.map do |tool_data|
|
308
|
+
tool_name = format_tool(tool_data[:tool])
|
309
|
+
evidence = tool_data[:evidence].join(", ")
|
310
|
+
"| #{tool_name} | #{evidence} |"
|
311
|
+
end
|
312
|
+
|
313
|
+
if rows.empty?
|
314
|
+
"_No high-confidence tooling detected. Consider adding linting and formatting tools._"
|
315
|
+
else
|
316
|
+
([header] + rows).join("\n")
|
317
|
+
end
|
318
|
+
end
|
254
319
|
end
|
255
320
|
end
|
256
321
|
end
|
@@ -155,7 +155,9 @@ module Aidp
|
|
155
155
|
@project_dir = project_dir
|
156
156
|
end
|
157
157
|
|
158
|
-
def analyze
|
158
|
+
def analyze(options = {})
|
159
|
+
@explain_detection = options[:explain_detection] || false
|
160
|
+
|
159
161
|
{
|
160
162
|
languages: detect_languages,
|
161
163
|
frameworks: detect_frameworks,
|
@@ -185,21 +187,57 @@ module Aidp
|
|
185
187
|
end
|
186
188
|
|
187
189
|
def detect_frameworks
|
188
|
-
|
190
|
+
results = []
|
189
191
|
|
190
192
|
FRAMEWORK_HINTS.each do |framework, rules|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
193
|
+
evidence = []
|
194
|
+
confidence = 0.0
|
195
|
+
|
196
|
+
# Check for required files
|
197
|
+
matched_files = []
|
198
|
+
if rules[:files]
|
199
|
+
matched_files = rules[:files].select { |file| project_glob?(file) }
|
200
|
+
if matched_files.any?
|
201
|
+
evidence << "Found files: #{matched_files.join(", ")}"
|
202
|
+
confidence += 0.3
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Check for content patterns in specific files or across project
|
207
|
+
if rules[:contents]
|
208
|
+
rules[:contents].each do |pattern|
|
209
|
+
if matched_files.any?
|
210
|
+
# Search only in the matched files
|
211
|
+
if search_files_for_pattern(matched_files, pattern)
|
212
|
+
evidence << "Found pattern '#{pattern.inspect}' in #{matched_files.join(", ")}"
|
213
|
+
confidence += 0.7
|
214
|
+
end
|
215
|
+
elsif search_project_for_pattern(pattern)
|
216
|
+
# Search across all project files (less confident)
|
217
|
+
evidence << "Found pattern '#{pattern.inspect}' in project files"
|
218
|
+
confidence += 0.4
|
219
|
+
end
|
196
220
|
end
|
197
|
-
elsif
|
198
|
-
|
221
|
+
elsif matched_files.any?
|
222
|
+
# Files exist but no content check needed - moderately confident
|
223
|
+
confidence = 0.6
|
224
|
+
end
|
225
|
+
|
226
|
+
# Only include frameworks with evidence
|
227
|
+
if evidence.any? && confidence > 0.0
|
228
|
+
# Cap confidence at 1.0
|
229
|
+
confidence = [confidence, 1.0].min
|
230
|
+
|
231
|
+
results << {
|
232
|
+
name: framework,
|
233
|
+
confidence: confidence,
|
234
|
+
evidence: evidence
|
235
|
+
}
|
199
236
|
end
|
200
237
|
end
|
201
238
|
|
202
|
-
|
239
|
+
# Sort by confidence (descending) then name
|
240
|
+
results.sort_by { |r| [-r[:confidence], r[:name]] }
|
203
241
|
end
|
204
242
|
|
205
243
|
def detect_key_directories
|
@@ -212,28 +250,82 @@ module Aidp
|
|
212
250
|
|
213
251
|
def detect_test_frameworks
|
214
252
|
results = []
|
253
|
+
dependency_files = ["Gemfile", "Gemfile.lock", "package.json", "pyproject.toml", "requirements.txt", "go.mod", "mix.exs", "composer.json", "Cargo.toml"]
|
215
254
|
|
216
255
|
TEST_FRAMEWORK_HINTS.each do |framework, hints|
|
217
|
-
|
218
|
-
|
219
|
-
|
256
|
+
evidence = []
|
257
|
+
confidence = 0.0
|
258
|
+
|
259
|
+
# Check for test directories
|
260
|
+
if hints[:directories]
|
261
|
+
found_dirs = hints[:directories].select { |dir| Dir.exist?(File.join(project_dir, dir)) }
|
262
|
+
if found_dirs.any?
|
263
|
+
evidence << "Found directories: #{found_dirs.join(", ")}"
|
264
|
+
confidence += 0.5
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Check for dependencies in lockfiles/manifests
|
269
|
+
hints[:dependencies]&.each do |pattern|
|
270
|
+
matched_files = []
|
271
|
+
dependency_files.each do |dep_file|
|
272
|
+
path = File.join(project_dir, dep_file)
|
273
|
+
next unless File.exist?(path)
|
274
|
+
|
275
|
+
begin
|
276
|
+
if File.read(path).match?(pattern)
|
277
|
+
matched_files << dep_file
|
278
|
+
end
|
279
|
+
rescue Errno::ENOENT, ArgumentError, Encoding::InvalidByteSequenceError
|
280
|
+
next
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
if matched_files.any?
|
285
|
+
evidence << "Found dependency pattern '#{pattern.inspect}' in #{matched_files.join(", ")}"
|
286
|
+
confidence += 0.6
|
287
|
+
end
|
288
|
+
end
|
220
289
|
|
221
|
-
|
290
|
+
# Check for test files
|
291
|
+
if hints[:files]
|
292
|
+
matched_globs = hints[:files].select { |glob| project_glob?(glob) }
|
293
|
+
if matched_globs.any?
|
294
|
+
evidence << "Found test files matching: #{matched_globs.join(", ")}"
|
295
|
+
confidence += 0.4
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# Only include test frameworks with evidence
|
300
|
+
if evidence.any? && confidence > 0.0
|
301
|
+
confidence = [confidence, 1.0].min
|
302
|
+
|
303
|
+
results << {
|
304
|
+
name: framework,
|
305
|
+
confidence: confidence,
|
306
|
+
evidence: evidence
|
307
|
+
}
|
308
|
+
end
|
222
309
|
end
|
223
310
|
|
224
|
-
|
311
|
+
# Sort by confidence (descending) then name
|
312
|
+
results.sort_by { |r| [-r[:confidence], r[:name]] }
|
225
313
|
end
|
226
314
|
|
227
315
|
def detect_tooling
|
228
|
-
|
316
|
+
results = []
|
229
317
|
|
230
318
|
TOOLING_HINTS.each do |tool, indicators|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
319
|
+
evidence = []
|
320
|
+
confidence = 0.0
|
321
|
+
|
322
|
+
matched_indicators = indicators.select { |indicator| project_glob?(indicator) }
|
323
|
+
if matched_indicators.any?
|
324
|
+
evidence << "Found config files: #{matched_indicators.join(", ")}"
|
325
|
+
confidence += 0.8
|
235
326
|
end
|
236
|
-
|
327
|
+
|
328
|
+
results << {tool: tool, evidence: evidence, confidence: confidence} if evidence.any?
|
237
329
|
end
|
238
330
|
|
239
331
|
# Post-process for package.json to extract scripts referencing linters
|
@@ -242,15 +334,31 @@ module Aidp
|
|
242
334
|
begin
|
243
335
|
json = JSON.parse(File.read(package_json_path))
|
244
336
|
scripts = json.fetch("scripts", {})
|
245
|
-
|
246
|
-
|
247
|
-
|
337
|
+
|
338
|
+
[:eslint, :prettier, :jest].each do |tool|
|
339
|
+
tool_str = tool.to_s
|
340
|
+
if scripts.values.any? { |cmd| cmd.include?(tool_str) }
|
341
|
+
# Check if we already have this tool from config detection
|
342
|
+
existing = results.find { |r| r[:tool] == tool }
|
343
|
+
if existing
|
344
|
+
existing[:evidence] << "Referenced in package.json scripts"
|
345
|
+
existing[:confidence] = [existing[:confidence] + 0.3, 1.0].min
|
346
|
+
else
|
347
|
+
results << {
|
348
|
+
tool: tool,
|
349
|
+
evidence: ["Referenced in package.json scripts"],
|
350
|
+
confidence: 0.6
|
351
|
+
}
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
248
355
|
rescue JSON::ParserError
|
249
356
|
# ignore malformed package.json
|
250
357
|
end
|
251
358
|
end
|
252
359
|
|
253
|
-
|
360
|
+
# Sort by confidence (descending) then tool name
|
361
|
+
results.sort_by { |r| [-r[:confidence], r[:tool].to_s] }
|
254
362
|
end
|
255
363
|
|
256
364
|
def collect_repo_stats
|
@@ -306,13 +414,33 @@ module Aidp
|
|
306
414
|
def search_files_for_pattern(files, pattern)
|
307
415
|
files.any? do |file|
|
308
416
|
Dir.glob(File.join(project_dir, file)).any? do |path|
|
309
|
-
|
417
|
+
# Special handling for package.json - only search in dependencies
|
418
|
+
if File.basename(path) == "package.json"
|
419
|
+
check_package_json_dependency(path, pattern)
|
420
|
+
else
|
421
|
+
File.read(path).match?(pattern)
|
422
|
+
end
|
310
423
|
rescue Errno::ENOENT, Errno::EISDIR
|
311
424
|
false
|
312
425
|
end
|
313
426
|
end
|
314
427
|
end
|
315
428
|
|
429
|
+
def check_package_json_dependency(path, pattern)
|
430
|
+
json = JSON.parse(File.read(path))
|
431
|
+
deps = json.fetch("dependencies", {})
|
432
|
+
dev_deps = json.fetch("devDependencies", {})
|
433
|
+
peer_deps = json.fetch("peerDependencies", {})
|
434
|
+
|
435
|
+
all_deps = deps.keys + dev_deps.keys + peer_deps.keys
|
436
|
+
all_deps.any? { |dep| dep.match?(pattern) }
|
437
|
+
rescue JSON::ParserError, Errno::ENOENT
|
438
|
+
# Fallback to simple text search if JSON parsing fails
|
439
|
+
File.read(path).match?(pattern)
|
440
|
+
rescue
|
441
|
+
false
|
442
|
+
end
|
443
|
+
|
316
444
|
def search_project_for_pattern(pattern, limit_files: nil)
|
317
445
|
if limit_files
|
318
446
|
limit_files.any? do |file|
|