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.
@@ -12,20 +12,54 @@ module Aidp
12
12
  class Runner
13
13
  include Aidp::MessageDisplay
14
14
 
15
- def initialize(project_dir = Dir.pwd, prompt: TTY::Prompt.new, analyzer: nil, doc_generator: nil)
15
+ def initialize(project_dir = Dir.pwd, prompt: TTY::Prompt.new, analyzer: nil, doc_generator: nil, options: {})
16
16
  @project_dir = project_dir
17
17
  @prompt = prompt
18
18
  @analyzer = analyzer || ProjectAnalyzer.new(project_dir)
19
19
  @doc_generator = doc_generator || DocGenerator.new(project_dir)
20
+ @options = options
20
21
  end
21
22
 
22
23
  def run
23
24
  display_message("šŸ” Running aidp init project analysis...", type: :info)
24
- analysis = @analyzer.analyze
25
- display_summary(analysis)
25
+ analysis = @analyzer.analyze(explain_detection: @options[:explain_detection])
26
+
27
+ if @options[:explain_detection]
28
+ display_detailed_analysis(analysis)
29
+ else
30
+ display_summary(analysis)
31
+ end
32
+
33
+ # Dry run: skip preferences and generation
34
+ if @options[:dry_run]
35
+ display_message("\nšŸ” Dry run mode - no files will be written.", type: :info)
36
+ return {
37
+ analysis: analysis,
38
+ preferences: {},
39
+ generated_files: []
40
+ }
41
+ end
26
42
 
27
43
  preferences = gather_preferences
28
44
 
45
+ # Offer preview before writing
46
+ if @options[:preview] || ask_yes_no_with_context(
47
+ "Preview generated files before saving?",
48
+ context: "Shows a summary of what will be written to docs/",
49
+ default: false
50
+ )
51
+ preview_generated_docs(analysis, preferences)
52
+
53
+ unless ask_yes_no("Proceed with writing these files?", default: true)
54
+ display_message("\nāŒ Cancelled. No files were written.", type: :info)
55
+ return {
56
+ analysis: analysis,
57
+ preferences: preferences,
58
+ generated_files: []
59
+ }
60
+ end
61
+ end
62
+
29
63
  @doc_generator.generate(analysis: analysis, preferences: preferences)
30
64
 
31
65
  display_message("\nšŸ“„ Generated documentation:", type: :info)
@@ -52,24 +86,170 @@ module Aidp
52
86
  frameworks = analysis[:frameworks]
53
87
  tests = analysis[:test_frameworks]
54
88
  config_files = analysis[:config_files]
89
+ tooling = analysis[:tooling]
90
+
91
+ # Extract high-confidence frameworks (>= 0.7)
92
+ confident_frameworks = frameworks.select { |f| f[:confidence] >= 0.7 }.map { |f| f[:name] }
93
+ uncertain_frameworks = frameworks.select { |f| f[:confidence] < 0.7 }
94
+
95
+ # Extract high-confidence test frameworks (>= 0.7)
96
+ confident_tests = tests.select { |t| t[:confidence] >= 0.7 }.map { |t| t[:name] }
97
+
98
+ # Extract high-confidence tooling (>= 0.7)
99
+ confident_tooling = tooling.select { |t| t[:confidence] >= 0.7 }.map { |t| format_tool(t[:tool]) }
55
100
 
56
101
  display_message("\nšŸ“Š Repository Snapshot", type: :highlight)
57
102
  display_message(" Languages: #{languages.empty? ? "Unknown" : languages.join(", ")}", type: :info)
58
- display_message(" Frameworks: #{frameworks.empty? ? "None detected" : frameworks.join(", ")}", type: :info)
59
- display_message(" Test suites: #{tests.empty? ? "Not found" : tests.join(", ")}", type: :info)
60
- display_message(" Config files: #{config_files.empty? ? "None detected" : config_files.join(", ")}", type: :info)
103
+
104
+ if confident_frameworks.any?
105
+ display_message(" Frameworks: #{confident_frameworks.join(", ")}", type: :info)
106
+ else
107
+ display_message(" Frameworks: None confidently detected", type: :info)
108
+ end
109
+
110
+ if uncertain_frameworks.any?
111
+ uncertain_list = uncertain_frameworks.map { |f| "#{f[:name]} (#{(f[:confidence] * 100).round}%)" }.join(", ")
112
+ display_message(" Possible frameworks: #{uncertain_list}", type: :info)
113
+ end
114
+
115
+ if confident_tests.any?
116
+ display_message(" Test suites: #{confident_tests.join(", ")}", type: :info)
117
+ else
118
+ display_message(" Test suites: Not found", type: :info)
119
+ end
120
+
121
+ if confident_tooling.any?
122
+ display_message(" Quality tools: #{confident_tooling.join(", ")}", type: :info)
123
+ end
124
+
125
+ display_message(" Config files: #{config_files.empty? ? "None detected" : config_files.size} found", type: :info)
126
+ end
127
+
128
+ def display_detailed_analysis(analysis)
129
+ display_message("\nšŸ” Detailed Detection Analysis", type: :highlight)
130
+ display_message("=" * 60, type: :info)
131
+
132
+ # Languages
133
+ display_message("\nšŸ“ Languages (by file size):", type: :highlight)
134
+ if analysis[:languages].any?
135
+ total_size = analysis[:languages].values.sum
136
+ analysis[:languages].each do |lang, size|
137
+ percentage = ((size.to_f / total_size) * 100).round(1)
138
+ display_message(" • #{lang}: #{percentage}%", type: :info)
139
+ end
140
+ else
141
+ display_message(" None detected", type: :info)
142
+ end
143
+
144
+ # Frameworks
145
+ display_message("\nšŸŽÆ Frameworks:", type: :highlight)
146
+ if analysis[:frameworks].any?
147
+ analysis[:frameworks].each do |fw|
148
+ confidence_pct = (fw[:confidence] * 100).round
149
+ display_message(" • #{fw[:name]} (#{confidence_pct}% confidence):", type: :info)
150
+ fw[:evidence].each do |evidence|
151
+ display_message(" - #{evidence}", type: :info)
152
+ end
153
+ end
154
+ else
155
+ display_message(" None detected", type: :info)
156
+ end
157
+
158
+ # Test Frameworks
159
+ display_message("\n🧪 Test Frameworks:", type: :highlight)
160
+ if analysis[:test_frameworks].any?
161
+ analysis[:test_frameworks].each do |test|
162
+ confidence_pct = (test[:confidence] * 100).round
163
+ display_message(" • #{test[:name]} (#{confidence_pct}% confidence):", type: :info)
164
+ test[:evidence].each do |evidence|
165
+ display_message(" - #{evidence}", type: :info)
166
+ end
167
+ end
168
+ else
169
+ display_message(" None detected", type: :info)
170
+ end
171
+
172
+ # Tooling
173
+ display_message("\nšŸ”§ Quality Tooling:", type: :highlight)
174
+ if analysis[:tooling].any?
175
+ analysis[:tooling].each do |tool_data|
176
+ tool_name = format_tool(tool_data[:tool])
177
+ confidence_pct = (tool_data[:confidence] * 100).round
178
+ display_message(" • #{tool_name} (#{confidence_pct}% confidence):", type: :info)
179
+ tool_data[:evidence].each do |evidence|
180
+ display_message(" - #{evidence}", type: :info)
181
+ end
182
+ end
183
+ else
184
+ display_message(" None detected", type: :info)
185
+ end
186
+
187
+ # Key Directories
188
+ display_message("\nšŸ“ Key Directories:", type: :highlight)
189
+ if analysis[:key_directories].any?
190
+ analysis[:key_directories].each do |dir|
191
+ display_message(" • #{dir}", type: :info)
192
+ end
193
+ else
194
+ display_message(" None detected", type: :info)
195
+ end
196
+
197
+ # Config Files
198
+ display_message("\nāš™ļø Configuration Files:", type: :highlight)
199
+ if analysis[:config_files].any?
200
+ analysis[:config_files].each do |file|
201
+ display_message(" • #{file}", type: :info)
202
+ end
203
+ else
204
+ display_message(" None detected", type: :info)
205
+ end
206
+
207
+ # Repository Stats
208
+ display_message("\nšŸ“ˆ Repository Stats:", type: :highlight)
209
+ stats = analysis[:repo_stats]
210
+ display_message(" • Total files: #{stats[:total_files]}", type: :info)
211
+ display_message(" • Total directories: #{stats[:total_directories]}", type: :info)
212
+ display_message(" • Documentation: #{stats[:docs_present] ? "Present" : "Not found"}", type: :info)
213
+ display_message(" • CI/CD config: #{stats[:has_ci_config] ? "Present" : "Not found"}", type: :info)
214
+ display_message(" • Containerization: #{stats[:has_containerization] ? "Present" : "Not found"}", type: :info)
215
+
216
+ display_message("\n" + "=" * 60, type: :info)
217
+ end
218
+
219
+ def format_tool(tool)
220
+ tool.to_s.split("_").map(&:capitalize).join(" ")
61
221
  end
62
222
 
63
223
  def gather_preferences
64
- display_message("\nāš™ļø Customise bootstrap plans (press Enter to accept defaults):", type: :info)
224
+ display_message("\nāš™ļø Configuration Options", type: :highlight)
225
+ display_message("The following questions will help customize the generated documentation.", type: :info)
226
+ display_message("Press Enter to accept defaults shown in brackets.\n", type: :info)
65
227
 
66
228
  {
67
- adopt_new_conventions: ask_yes_no("Adopt the newly generated conventions as canonical defaults?", default: true),
68
- stricter_linters: ask_yes_no("Enforce stricter linting based on detected tools?", default: false),
69
- migrate_styles: ask_yes_no("Plan migrations to align legacy files with the new style guide?", default: false)
229
+ adopt_new_conventions: ask_yes_no_with_context(
230
+ "Make these conventions official for this repository?",
231
+ context: "This saves the detected patterns to LLM_STYLE_GUIDE.md and guides future AI-assisted work.",
232
+ default: true
233
+ ),
234
+ stricter_linters: ask_yes_no_with_context(
235
+ "Enable stricter linting rules in the quality plan?",
236
+ context: "Recommends failing CI on linting violations for better code quality.",
237
+ default: false
238
+ ),
239
+ migrate_styles: ask_yes_no_with_context(
240
+ "Plan gradual migration of legacy code to new style guide?",
241
+ context: "Adds migration tasks to CODE_QUALITY_PLAN.md for incremental improvements.",
242
+ default: false
243
+ )
70
244
  }
71
245
  end
72
246
 
247
+ def ask_yes_no_with_context(question, context:, default:)
248
+ display_message("\n#{question}", type: :info)
249
+ display_message(" ā„¹ļø #{context}", type: :info)
250
+ ask_yes_no(question, default: default)
251
+ end
252
+
73
253
  def ask_yes_no(question, default:)
74
254
  @prompt.yes?(question) do |q|
75
255
  q.default default ? "yes" : "no"
@@ -78,6 +258,79 @@ module Aidp
78
258
  # Compatibility with simplified prompts in tests (e.g. TestPrompt)
79
259
  default
80
260
  end
261
+
262
+ def preview_generated_docs(analysis, preferences)
263
+ display_message("\nšŸ“„ Preview of Generated Documentation", type: :highlight)
264
+ display_message("=" * 60, type: :info)
265
+
266
+ # LLM_STYLE_GUIDE summary
267
+ display_message("\n1. docs/LLM_STYLE_GUIDE.md", type: :highlight)
268
+ confident_frameworks = analysis[:frameworks].select { |f| f[:confidence] >= 0.7 }.map { |f| f[:name] }
269
+ display_message(" - Detected frameworks: #{confident_frameworks.any? ? confident_frameworks.join(", ") : "None"}", type: :info)
270
+ display_message(" - Adoption status: #{preferences[:adopt_new_conventions] ? "Official conventions" : "Optional reference"}", type: :info)
271
+
272
+ # PROJECT_ANALYSIS summary
273
+ display_message("\n2. docs/PROJECT_ANALYSIS.md", type: :highlight)
274
+ display_message(" - Languages: #{analysis[:languages].keys.join(", ")}", type: :info)
275
+ display_message(" - Total frameworks detected: #{analysis[:frameworks].size}", type: :info)
276
+ display_message(" - Test frameworks: #{analysis[:test_frameworks].map { |t| t[:name] }.join(", ") || "None"}", type: :info)
277
+
278
+ # CODE_QUALITY_PLAN summary
279
+ display_message("\n3. docs/CODE_QUALITY_PLAN.md", type: :highlight)
280
+ tooling_count = analysis[:tooling].count { |t| t[:confidence] >= 0.7 }
281
+ display_message(" - Quality tools detected: #{tooling_count}", type: :info)
282
+ display_message(" - Stricter linting: #{preferences[:stricter_linters] ? "Yes" : "No"}", type: :info)
283
+ display_message(" - Migration planning: #{preferences[:migrate_styles] ? "Yes" : "No"}", type: :info)
284
+
285
+ # Validation warnings
286
+ validate_tooling(analysis)
287
+
288
+ display_message("\n" + "=" * 60, type: :info)
289
+ end
290
+
291
+ def validate_tooling(analysis)
292
+ display_message("\nšŸ” Validation", type: :highlight)
293
+
294
+ warnings = []
295
+
296
+ # Check if detected tools actually exist
297
+ analysis[:tooling].select { |t| t[:confidence] >= 0.7 }.each do |tool_data|
298
+ tool_name = tool_data[:tool].to_s
299
+
300
+ # Try to find the tool command
301
+ tool_command = case tool_name
302
+ when "rubocop", "standardrb", "eslint", "prettier", "stylelint", "flake8", "black", "pytest", "jest"
303
+ tool_name
304
+ when "cargo_fmt"
305
+ "cargo"
306
+ when "gofmt"
307
+ "gofmt"
308
+ end
309
+
310
+ next unless tool_command
311
+
312
+ # Check if command exists
313
+ unless system("which #{tool_command} > /dev/null 2>&1")
314
+ warnings << " āš ļø #{format_tool(tool_data[:tool])} detected but command '#{tool_command}' not found in PATH"
315
+ end
316
+ end
317
+
318
+ # Check if test commands can be inferred
319
+ if analysis[:test_frameworks].empty?
320
+ warnings << " āš ļø No test framework detected - consider adding one for better quality assurance"
321
+ end
322
+
323
+ # Check for CI configuration
324
+ unless analysis[:repo_stats][:has_ci_config]
325
+ warnings << " ā„¹ļø No CI configuration detected - consider adding GitHub Actions or similar"
326
+ end
327
+
328
+ if warnings.any?
329
+ warnings.each { |warning| display_message(warning, type: :info) }
330
+ else
331
+ display_message(" āœ… All detected tools validated successfully", type: :success)
332
+ end
333
+ end
81
334
  end
82
335
  end
83
336
  end
data/lib/aidp/logger.rb CHANGED
@@ -104,10 +104,21 @@ module Aidp
104
104
  end
105
105
 
106
106
  def create_logger(path)
107
+ # Ensure parent directory exists before creating logger
108
+ dir = File.dirname(path)
109
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
110
+
107
111
  logger = ::Logger.new(path, @max_files, @max_size)
108
112
  logger.level = ::Logger::DEBUG # Control at write level instead
109
113
  logger.formatter = proc { |severity, datetime, progname, msg| "#{msg}\n" }
110
114
  logger
115
+ rescue => e
116
+ # Fall back to STDERR if file logging fails
117
+ warn "[AIDP Logger] Failed to create log file at #{path}: #{e.message}. Falling back to STDERR."
118
+ logger = ::Logger.new($stderr)
119
+ logger.level = ::Logger::DEBUG
120
+ logger.formatter = proc { |severity, datetime, progname, msg| "#{msg}\n" }
121
+ logger
111
122
  end
112
123
 
113
124
  def write_entry(level, component, message, metadata)
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.15.1"
4
+ VERSION = "0.15.2"
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.15.1
4
+ version: 0.15.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bart Agapinan
@@ -237,9 +237,7 @@ files:
237
237
  - lib/aidp/analyze/feature_analyzer.rb
238
238
  - lib/aidp/analyze/json_file_storage.rb
239
239
  - lib/aidp/analyze/kb_inspector.rb
240
- - lib/aidp/analyze/prioritizer.rb
241
240
  - lib/aidp/analyze/progress.rb
242
- - lib/aidp/analyze/report_generator.rb
243
241
  - lib/aidp/analyze/ruby_maat_integration.rb
244
242
  - lib/aidp/analyze/runner.rb
245
243
  - lib/aidp/analyze/seams.rb
@@ -247,7 +245,6 @@ files:
247
245
  - lib/aidp/analyze/tree_sitter_grammar_loader.rb
248
246
  - lib/aidp/analyze/tree_sitter_scan.rb
249
247
  - lib/aidp/cli.rb
250
- - lib/aidp/cli/checkpoint_command.rb
251
248
  - lib/aidp/cli/enhanced_input.rb
252
249
  - lib/aidp/cli/first_run_wizard.rb
253
250
  - lib/aidp/cli/issue_importer.rb