aidp 0.1.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 (79) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +210 -0
  4. data/bin/aidp +5 -0
  5. data/lib/aidp/analyze/agent_personas.rb +71 -0
  6. data/lib/aidp/analyze/agent_tool_executor.rb +445 -0
  7. data/lib/aidp/analyze/data_retention_manager.rb +426 -0
  8. data/lib/aidp/analyze/database.rb +243 -0
  9. data/lib/aidp/analyze/dependencies.rb +335 -0
  10. data/lib/aidp/analyze/error_handler.rb +486 -0
  11. data/lib/aidp/analyze/export_manager.rb +425 -0
  12. data/lib/aidp/analyze/feature_analyzer.rb +397 -0
  13. data/lib/aidp/analyze/focus_guidance.rb +517 -0
  14. data/lib/aidp/analyze/incremental_analyzer.rb +543 -0
  15. data/lib/aidp/analyze/language_analysis_strategies.rb +897 -0
  16. data/lib/aidp/analyze/large_analysis_progress.rb +504 -0
  17. data/lib/aidp/analyze/memory_manager.rb +365 -0
  18. data/lib/aidp/analyze/parallel_processor.rb +460 -0
  19. data/lib/aidp/analyze/performance_optimizer.rb +694 -0
  20. data/lib/aidp/analyze/prioritizer.rb +402 -0
  21. data/lib/aidp/analyze/progress.rb +75 -0
  22. data/lib/aidp/analyze/progress_visualizer.rb +320 -0
  23. data/lib/aidp/analyze/report_generator.rb +582 -0
  24. data/lib/aidp/analyze/repository_chunker.rb +702 -0
  25. data/lib/aidp/analyze/ruby_maat_integration.rb +572 -0
  26. data/lib/aidp/analyze/runner.rb +245 -0
  27. data/lib/aidp/analyze/static_analysis_detector.rb +577 -0
  28. data/lib/aidp/analyze/steps.rb +53 -0
  29. data/lib/aidp/analyze/storage.rb +600 -0
  30. data/lib/aidp/analyze/tool_configuration.rb +456 -0
  31. data/lib/aidp/analyze/tool_modernization.rb +750 -0
  32. data/lib/aidp/execute/progress.rb +76 -0
  33. data/lib/aidp/execute/runner.rb +135 -0
  34. data/lib/aidp/execute/steps.rb +113 -0
  35. data/lib/aidp/shared/cli.rb +117 -0
  36. data/lib/aidp/shared/config.rb +35 -0
  37. data/lib/aidp/shared/project_detector.rb +119 -0
  38. data/lib/aidp/shared/providers/anthropic.rb +26 -0
  39. data/lib/aidp/shared/providers/base.rb +17 -0
  40. data/lib/aidp/shared/providers/cursor.rb +102 -0
  41. data/lib/aidp/shared/providers/gemini.rb +26 -0
  42. data/lib/aidp/shared/providers/macos_ui.rb +26 -0
  43. data/lib/aidp/shared/sync.rb +15 -0
  44. data/lib/aidp/shared/util.rb +41 -0
  45. data/lib/aidp/shared/version.rb +7 -0
  46. data/lib/aidp/shared/workspace.rb +21 -0
  47. data/lib/aidp.rb +53 -0
  48. data/templates/ANALYZE/01_REPOSITORY_ANALYSIS.md +100 -0
  49. data/templates/ANALYZE/02_ARCHITECTURE_ANALYSIS.md +151 -0
  50. data/templates/ANALYZE/03_TEST_ANALYSIS.md +182 -0
  51. data/templates/ANALYZE/04_FUNCTIONALITY_ANALYSIS.md +200 -0
  52. data/templates/ANALYZE/05_DOCUMENTATION_ANALYSIS.md +202 -0
  53. data/templates/ANALYZE/06_STATIC_ANALYSIS.md +233 -0
  54. data/templates/ANALYZE/07_REFACTORING_RECOMMENDATIONS.md +316 -0
  55. data/templates/COMMON/AGENT_BASE.md +129 -0
  56. data/templates/COMMON/CONVENTIONS.md +19 -0
  57. data/templates/COMMON/TEMPLATES/ADR_TEMPLATE.md +21 -0
  58. data/templates/COMMON/TEMPLATES/DOMAIN_CHARTER.md +27 -0
  59. data/templates/COMMON/TEMPLATES/EVENT_EXAMPLE.yaml +16 -0
  60. data/templates/COMMON/TEMPLATES/MERMAID_C4.md +46 -0
  61. data/templates/COMMON/TEMPLATES/OPENAPI_STUB.yaml +11 -0
  62. data/templates/EXECUTE/00_PRD.md +36 -0
  63. data/templates/EXECUTE/01_NFRS.md +27 -0
  64. data/templates/EXECUTE/02A_ARCH_GATE_QUESTIONS.md +13 -0
  65. data/templates/EXECUTE/02_ARCHITECTURE.md +42 -0
  66. data/templates/EXECUTE/03_ADR_FACTORY.md +22 -0
  67. data/templates/EXECUTE/04_DOMAIN_DECOMPOSITION.md +24 -0
  68. data/templates/EXECUTE/05_CONTRACTS.md +27 -0
  69. data/templates/EXECUTE/06_THREAT_MODEL.md +23 -0
  70. data/templates/EXECUTE/07_TEST_PLAN.md +24 -0
  71. data/templates/EXECUTE/08_TASKS.md +29 -0
  72. data/templates/EXECUTE/09_SCAFFOLDING_DEVEX.md +25 -0
  73. data/templates/EXECUTE/10_IMPLEMENTATION_AGENT.md +30 -0
  74. data/templates/EXECUTE/11_STATIC_ANALYSIS.md +22 -0
  75. data/templates/EXECUTE/12_OBSERVABILITY_SLOS.md +21 -0
  76. data/templates/EXECUTE/13_DELIVERY_ROLLOUT.md +21 -0
  77. data/templates/EXECUTE/14_DOCS_PORTAL.md +23 -0
  78. data/templates/EXECUTE/15_POST_RELEASE.md +25 -0
  79. metadata +301 -0
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "time"
5
+
6
+ module Aidp
7
+ module Execute
8
+ # Manages progress tracking for execute mode, isolated from analyze mode
9
+ class Progress
10
+ attr_reader :project_dir, :progress_file
11
+
12
+ def initialize(project_dir)
13
+ @project_dir = project_dir
14
+ @progress_file = File.join(project_dir, ".aidp-progress.yml")
15
+ load_progress
16
+ end
17
+
18
+ def completed_steps
19
+ @progress["completed_steps"] || []
20
+ end
21
+
22
+ def current_step
23
+ @progress["current_step"]
24
+ end
25
+
26
+ def started_at
27
+ @progress["started_at"] ? Time.parse(@progress["started_at"]) : nil
28
+ end
29
+
30
+ def step_completed?(step_name)
31
+ completed_steps.include?(step_name)
32
+ end
33
+
34
+ def mark_step_completed(step_name)
35
+ @progress["completed_steps"] ||= []
36
+ @progress["completed_steps"] << step_name unless step_completed?(step_name)
37
+ @progress["current_step"] = nil
38
+ @progress["started_at"] ||= Time.now.iso8601
39
+ save_progress
40
+ end
41
+
42
+ def mark_step_in_progress(step_name)
43
+ @progress["current_step"] = step_name
44
+ @progress["started_at"] ||= Time.now.iso8601
45
+ save_progress
46
+ end
47
+
48
+ def reset
49
+ @progress = {
50
+ "completed_steps" => [],
51
+ "current_step" => nil,
52
+ "started_at" => nil
53
+ }
54
+ save_progress
55
+ end
56
+
57
+ def next_step
58
+ Aidp::Execute::Steps::SPEC.keys.find { |step| !step_completed?(step) }
59
+ end
60
+
61
+ private
62
+
63
+ def load_progress
64
+ @progress = if File.exist?(@progress_file)
65
+ YAML.load_file(@progress_file) || {}
66
+ else
67
+ {}
68
+ end
69
+ end
70
+
71
+ def save_progress
72
+ File.write(@progress_file, @progress.to_yaml)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "yaml"
5
+ require "json"
6
+
7
+ module Aidp
8
+ module Execute
9
+ # Handles execution logic for execute mode steps
10
+ class Runner
11
+ attr_reader :project_dir, :progress
12
+
13
+ def initialize(project_dir)
14
+ @project_dir = project_dir
15
+ @progress = Aidp::Execute::Progress.new(project_dir)
16
+ end
17
+
18
+ def run_step(step_name, options = {})
19
+ raise "Step '#{step_name}' not found in execute mode steps" unless Aidp::Execute::Steps::SPEC.key?(step_name)
20
+
21
+ step_spec = Aidp::Execute::Steps::SPEC[step_name]
22
+ template_name = step_spec["templates"].first
23
+
24
+ # Load template
25
+ template = find_template(template_name)
26
+ raise "Template '#{template_name}' not found" unless template
27
+
28
+ # Compose prompt
29
+ prompt = composed_prompt(template_name, options)
30
+
31
+ # Handle error simulation for tests
32
+ if options[:simulate_error]
33
+ return {
34
+ status: "error",
35
+ error: options[:simulate_error],
36
+ step: step_name
37
+ }
38
+ end
39
+
40
+ # Execute step (mock for now)
41
+ result = {
42
+ status: "success",
43
+ step: step_name,
44
+ output_files: step_spec["outs"],
45
+ prompt: prompt
46
+ }
47
+
48
+ # Mark step as completed
49
+ @progress.mark_step_completed(step_name)
50
+
51
+ # Generate output files
52
+ generate_output_files(step_name, step_spec["outs"], result)
53
+
54
+ # Generate database export
55
+ generate_database_export
56
+
57
+ result
58
+ end
59
+
60
+ private
61
+
62
+ def find_template(template_name)
63
+ template_search_paths.each do |path|
64
+ template_file = File.join(path, template_name)
65
+ return File.read(template_file) if File.exist?(template_file)
66
+ end
67
+ nil
68
+ end
69
+
70
+ def template_search_paths
71
+ [
72
+ File.join(@project_dir, "templates"),
73
+ File.join(@project_dir, "templates", "COMMON"),
74
+ File.join(File.dirname(__FILE__), "..", "..", "..", "templates", "EXECUTE"),
75
+ File.join(File.dirname(__FILE__), "..", "..", "..", "templates", "COMMON")
76
+ ]
77
+ end
78
+
79
+ def composed_prompt(template_name, options = {})
80
+ template = find_template(template_name)
81
+ return template unless template
82
+
83
+ # Load agent base template if available
84
+ agent_base = find_template("AGENT_BASE.md")
85
+ template = "#{agent_base}\n\n#{template}" if agent_base
86
+
87
+ # Replace placeholders
88
+ options.each do |key, value|
89
+ template = template.gsub("{{#{key}}}", value.to_s)
90
+ end
91
+
92
+ template
93
+ end
94
+
95
+ def generate_output_files(step_name, output_files, result)
96
+ output_files.each do |output_file|
97
+ file_path = File.join(@project_dir, output_file)
98
+ content = generate_output_content(step_name, output_file, result)
99
+ File.write(file_path, content)
100
+ end
101
+ end
102
+
103
+ def generate_output_content(step_name, output_file, result)
104
+ case output_file
105
+ when /\.md$/
106
+ "# #{step_name} Output\n\nGenerated on #{Time.now}\n\n## Result\n\n#{result[:status]}"
107
+ when /\.json$/
108
+ result.to_json
109
+ else
110
+ "Output for #{step_name}: #{result[:status]}"
111
+ end
112
+ end
113
+
114
+ def generate_database_export
115
+ database_file = File.join(@project_dir, ".aidp.db")
116
+ require "sqlite3"
117
+
118
+ begin
119
+ db = SQLite3::Database.new(database_file)
120
+ db.execute("CREATE TABLE IF NOT EXISTS execute_results (step TEXT, status TEXT, completed_at TEXT)")
121
+
122
+ Aidp::Execute::Steps::SPEC.keys.each do |step|
123
+ if @progress.step_completed?(step)
124
+ db.execute("INSERT INTO execute_results (step, status, completed_at) VALUES (?, ?, ?)",
125
+ [step, "success", Time.now.iso8601])
126
+ end
127
+ end
128
+ rescue => e
129
+ # Log the error but don't fail the execution
130
+ puts "Warning: Database export failed: #{e.message}"
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aidp
4
+ module Execute
5
+ # Defines the steps, templates, outputs, and associated AI agents for execute mode
6
+ class Steps
7
+ SPEC = {
8
+ "00_PRD" => {
9
+ "templates" => ["00_PRD.md"],
10
+ "outs" => ["00_PRD.md"],
11
+ "gate" => false,
12
+ "agent" => "Product Manager"
13
+ },
14
+ "01_NFRS" => {
15
+ "templates" => ["01_NFRS.md"],
16
+ "outs" => ["01_NFRS.md"],
17
+ "gate" => false,
18
+ "agent" => "Architect"
19
+ },
20
+ "02_ARCHITECTURE" => {
21
+ "templates" => ["02_ARCHITECTURE.md"],
22
+ "outs" => ["02_ARCHITECTURE.md"],
23
+ "gate" => false,
24
+ "agent" => "Architect"
25
+ },
26
+ "02A_ARCH_GATE_QUESTIONS" => {
27
+ "templates" => ["02A_ARCH_GATE_QUESTIONS.md"],
28
+ "outs" => ["02A_ARCH_GATE_QUESTIONS.md"],
29
+ "gate" => true,
30
+ "agent" => "Architect"
31
+ },
32
+ "03_ADR_FACTORY" => {
33
+ "templates" => ["03_ADR_FACTORY.md"],
34
+ "outs" => ["03_ADR_FACTORY.md"],
35
+ "gate" => false,
36
+ "agent" => "Architect"
37
+ },
38
+ "04_DOMAIN_DECOMPOSITION" => {
39
+ "templates" => ["04_DOMAIN_DECOMPOSITION.md"],
40
+ "outs" => ["04_DOMAIN_DECOMPOSITION.md"],
41
+ "gate" => false,
42
+ "agent" => "Architect"
43
+ },
44
+ "05_CONTRACTS" => {
45
+ "templates" => ["05_CONTRACTS.md"],
46
+ "outs" => ["05_CONTRACTS.md"],
47
+ "gate" => false,
48
+ "agent" => "Architect"
49
+ },
50
+ "06_THREAT_MODEL" => {
51
+ "templates" => ["06_THREAT_MODEL.md"],
52
+ "outs" => ["06_THREAT_MODEL.md"],
53
+ "gate" => false,
54
+ "agent" => "Security Expert"
55
+ },
56
+ "07_TEST_PLAN" => {
57
+ "templates" => ["07_TEST_PLAN.md"],
58
+ "outs" => ["07_TEST_PLAN.md"],
59
+ "gate" => false,
60
+ "agent" => "Test Engineer"
61
+ },
62
+ "08_TASKS" => {
63
+ "templates" => ["08_TASKS.md"],
64
+ "outs" => ["08_TASKS.md"],
65
+ "gate" => false,
66
+ "agent" => "Project Manager"
67
+ },
68
+ "09_SCAFFOLDING_DEVEX" => {
69
+ "templates" => ["09_SCAFFOLDING_DEVEX.md"],
70
+ "outs" => ["09_SCAFFOLDING_DEVEX.md"],
71
+ "gate" => false,
72
+ "agent" => "DevOps Engineer"
73
+ },
74
+ "10_IMPLEMENTATION_AGENT" => {
75
+ "templates" => ["10_IMPLEMENTATION_AGENT.md"],
76
+ "outs" => ["10_IMPLEMENTATION_AGENT.md"],
77
+ "gate" => false,
78
+ "agent" => "Implementation Specialist"
79
+ },
80
+ "11_STATIC_ANALYSIS" => {
81
+ "templates" => ["11_STATIC_ANALYSIS.md"],
82
+ "outs" => ["11_STATIC_ANALYSIS.md"],
83
+ "gate" => false,
84
+ "agent" => "Code Quality Expert"
85
+ },
86
+ "12_OBSERVABILITY_SLOS" => {
87
+ "templates" => ["12_OBSERVABILITY_SLOS.md"],
88
+ "outs" => ["12_OBSERVABILITY_SLOS.md"],
89
+ "gate" => false,
90
+ "agent" => "SRE Engineer"
91
+ },
92
+ "13_DELIVERY_ROLLOUT" => {
93
+ "templates" => ["13_DELIVERY_ROLLOUT.md"],
94
+ "outs" => ["13_DELIVERY_ROLLOUT.md"],
95
+ "gate" => false,
96
+ "agent" => "DevOps Engineer"
97
+ },
98
+ "14_DOCS_PORTAL" => {
99
+ "templates" => ["14_DOCS_PORTAL.md"],
100
+ "outs" => ["14_DOCS_PORTAL.md"],
101
+ "gate" => false,
102
+ "agent" => "Technical Writer"
103
+ },
104
+ "15_POST_RELEASE" => {
105
+ "templates" => ["15_POST_RELEASE.md"],
106
+ "outs" => ["15_POST_RELEASE.md"],
107
+ "gate" => false,
108
+ "agent" => "Project Manager"
109
+ }
110
+ }.freeze
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "thor"
4
+
5
+ module Aidp
6
+ module Shared
7
+ # CLI interface for both execute and analyze modes
8
+ class CLI < Thor
9
+ desc "execute [STEP]", "Run execute mode step(s)"
10
+ option :force, type: :boolean, desc: "Force execution even if dependencies are not met"
11
+ option :rerun, type: :boolean, desc: "Re-run a completed step"
12
+ def execute(project_dir = Dir.pwd, step_name = nil, custom_options = {})
13
+ if step_name
14
+ runner = Aidp::Execute::Runner.new(project_dir)
15
+ # Merge Thor options with custom options
16
+ all_options = options.merge(custom_options)
17
+ runner.run_step(step_name, all_options)
18
+ else
19
+ puts "Available execute steps:"
20
+ Aidp::Execute::Steps::SPEC.keys.each { |step| puts " - #{step}" }
21
+ progress = Aidp::Execute::Progress.new(project_dir)
22
+ next_step = progress.next_step
23
+ {status: "success", message: "Available steps listed", next_step: next_step}
24
+ end
25
+ end
26
+
27
+ desc "analyze [STEP]", "Run analyze mode step(s)"
28
+ option :force, type: :boolean, desc: "Force execution even if dependencies are not met"
29
+ option :rerun, type: :boolean, desc: "Re-run a completed step"
30
+ def analyze(project_dir = Dir.pwd, step_name = nil, custom_options = {})
31
+ if step_name
32
+ runner = Aidp::Analyze::Runner.new(project_dir)
33
+ # Merge Thor options with custom options
34
+ all_options = options.merge(custom_options)
35
+ runner.run_step(step_name, all_options)
36
+ else
37
+ puts "Available analyze steps:"
38
+ Aidp::Analyze::Steps::SPEC.keys.each { |step| puts " - #{step}" }
39
+ progress = Aidp::Analyze::Progress.new(project_dir)
40
+ next_step = progress.next_step
41
+ {status: "success", message: "Available steps listed", next_step: next_step,
42
+ completed_steps: progress.completed_steps}
43
+ end
44
+ end
45
+
46
+ desc "analyze-approve STEP", "Approve a completed analyze gate step"
47
+ def analyze_approve(project_dir = Dir.pwd, step_name = nil)
48
+ progress = Aidp::Analyze::Progress.new(project_dir)
49
+ progress.mark_step_completed(step_name)
50
+ puts "✅ Approved analyze step: #{step_name}"
51
+ {status: "success", step: step_name}
52
+ end
53
+
54
+ desc "analyze-reset", "Reset analyze mode progress"
55
+ def analyze_reset(project_dir = Dir.pwd)
56
+ progress = Aidp::Analyze::Progress.new(project_dir)
57
+ progress.reset
58
+ puts "🔄 Reset analyze mode progress"
59
+ {status: "success", message: "Progress reset"}
60
+ end
61
+
62
+ desc "execute-approve STEP", "Approve a completed execute gate step"
63
+ def execute_approve(project_dir = Dir.pwd, step_name = nil)
64
+ progress = Aidp::Execute::Progress.new(project_dir)
65
+ progress.mark_step_completed(step_name)
66
+ puts "✅ Approved execute step: #{step_name}"
67
+ {status: "success", step: step_name}
68
+ end
69
+
70
+ desc "execute-reset", "Reset execute mode progress"
71
+ def execute_reset(project_dir = Dir.pwd)
72
+ progress = Aidp::Execute::Progress.new(project_dir)
73
+ progress.reset
74
+ puts "🔄 Reset execute mode progress"
75
+ {status: "success", message: "Progress reset"}
76
+ end
77
+
78
+ # Backward compatibility aliases
79
+ desc "approve STEP", "Approve a completed execute gate step (alias for execute-approve)"
80
+ def approve(project_dir = Dir.pwd, step_name = nil)
81
+ execute_approve(project_dir, step_name)
82
+ end
83
+
84
+ desc "reset", "Reset execute mode progress (alias for execute-reset)"
85
+ def reset(project_dir = Dir.pwd)
86
+ execute_reset(project_dir)
87
+ end
88
+
89
+ desc "status", "Show current progress for both modes"
90
+ def status
91
+ puts "\n📊 AI Dev Pipeline Status"
92
+ puts "=" * 50
93
+
94
+ # Execute mode status
95
+ execute_progress = Aidp::Execute::Progress.new(Dir.pwd)
96
+ puts "\n🔧 Execute Mode:"
97
+ Aidp::Execute::Steps::SPEC.keys.each do |step|
98
+ status = execute_progress.step_completed?(step) ? "✅" : "⏳"
99
+ puts " #{status} #{step}"
100
+ end
101
+
102
+ # Analyze mode status
103
+ analyze_progress = Aidp::Analyze::Progress.new(Dir.pwd)
104
+ puts "\n🔍 Analyze Mode:"
105
+ Aidp::Analyze::Steps::SPEC.keys.each do |step|
106
+ status = analyze_progress.step_completed?(step) ? "✅" : "⏳"
107
+ puts " #{status} #{step}"
108
+ end
109
+ end
110
+
111
+ desc "version", "Show version information"
112
+ def version
113
+ puts "Aidp version #{Aidp::Shared::VERSION}"
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Aidp
6
+ module Shared
7
+ # Configuration management for both execute and analyze modes
8
+ class Config
9
+ def self.load(project_dir = Dir.pwd)
10
+ config_file = File.join(project_dir, ".aidp.yml")
11
+ if File.exist?(config_file)
12
+ YAML.load_file(config_file) || {}
13
+ else
14
+ {}
15
+ end
16
+ end
17
+
18
+ def self.templates_root
19
+ File.join(Dir.pwd, "templates")
20
+ end
21
+
22
+ def self.analyze_templates_root
23
+ File.join(Dir.pwd, "templates", "ANALYZE")
24
+ end
25
+
26
+ def self.execute_templates_root
27
+ File.join(Dir.pwd, "templates", "EXECUTE")
28
+ end
29
+
30
+ def self.common_templates_root
31
+ File.join(Dir.pwd, "templates", "COMMON")
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "yaml"
5
+
6
+ module Aidp
7
+ module Shared
8
+ # Detects project type, language, framework, and other characteristics
9
+ class ProjectDetector
10
+ attr_reader :project_dir
11
+
12
+ def initialize(project_dir = Dir.pwd)
13
+ @project_dir = project_dir
14
+ end
15
+
16
+ def detect
17
+ {
18
+ language: detect_language,
19
+ framework: detect_framework,
20
+ build_system: detect_build_system,
21
+ package_manager: detect_package_manager,
22
+ static_analysis_tools: detect_static_analysis_tools,
23
+ test_framework: detect_test_framework,
24
+ database: detect_database,
25
+ deployment: detect_deployment
26
+ }
27
+ end
28
+
29
+ private
30
+
31
+ def detect_language
32
+ return "ruby" if File.exist?(File.join(@project_dir, "Gemfile"))
33
+ return "javascript" if File.exist?(File.join(@project_dir, "package.json"))
34
+ return "python" if File.exist?(File.join(@project_dir, "requirements.txt")) || File.exist?(File.join(@project_dir, "pyproject.toml"))
35
+ return "java" if File.exist?(File.join(@project_dir, "pom.xml")) || File.exist?(File.join(@project_dir, "build.gradle"))
36
+ return "go" if File.exist?(File.join(@project_dir, "go.mod"))
37
+ return "rust" if File.exist?(File.join(@project_dir, "Cargo.toml"))
38
+ return "csharp" if File.exist?(File.join(@project_dir, "*.csproj"))
39
+ "unknown"
40
+ end
41
+
42
+ def detect_framework
43
+ case detect_language
44
+ when "ruby"
45
+ return "rails" if File.exist?(File.join(@project_dir, "config", "application.rb"))
46
+ return "sinatra" if File.exist?(File.join(@project_dir, "app.rb")) && File.read(File.join(@project_dir, "app.rb")).include?("Sinatra")
47
+ when "javascript"
48
+ return "react" if File.exist?(File.join(@project_dir, "package.json")) && File.read(File.join(@project_dir, "package.json")).include?("react")
49
+ return "vue" if File.exist?(File.join(@project_dir, "package.json")) && File.read(File.join(@project_dir, "package.json")).include?("vue")
50
+ return "angular" if File.exist?(File.join(@project_dir, "angular.json"))
51
+ return "express" if File.exist?(File.join(@project_dir, "package.json")) && File.read(File.join(@project_dir, "package.json")).include?("express")
52
+ when "python"
53
+ return "django" if File.exist?(File.join(@project_dir, "manage.py"))
54
+ return "flask" if File.exist?(File.join(@project_dir, "app.py")) && File.read(File.join(@project_dir, "app.py")).include?("Flask")
55
+ when "java"
56
+ return "spring" if File.exist?(File.join(@project_dir, "pom.xml")) && File.read(File.join(@project_dir, "pom.xml")).include?("spring-boot")
57
+ end
58
+ "unknown"
59
+ end
60
+
61
+ def detect_build_system
62
+ return "maven" if File.exist?(File.join(@project_dir, "pom.xml"))
63
+ return "gradle" if File.exist?(File.join(@project_dir, "build.gradle"))
64
+ return "npm" if File.exist?(File.join(@project_dir, "package.json"))
65
+ return "bundler" if File.exist?(File.join(@project_dir, "Gemfile"))
66
+ return "pip" if File.exist?(File.join(@project_dir, "requirements.txt"))
67
+ return "cargo" if File.exist?(File.join(@project_dir, "Cargo.toml"))
68
+ return "go" if File.exist?(File.join(@project_dir, "go.mod"))
69
+ "unknown"
70
+ end
71
+
72
+ def detect_package_manager
73
+ detect_build_system
74
+ end
75
+
76
+ def detect_static_analysis_tools
77
+ tools = []
78
+ tools << "rubocop" if File.exist?(File.join(@project_dir, ".rubocop.yml"))
79
+ tools << "eslint" if File.exist?(File.join(@project_dir, ".eslintrc"))
80
+ tools << "flake8" if File.exist?(File.join(@project_dir, ".flake8"))
81
+ tools << "checkstyle" if File.exist?(File.join(@project_dir, "checkstyle.xml"))
82
+ tools << "clippy" if File.exist?(File.join(@project_dir, "Cargo.toml"))
83
+ tools
84
+ end
85
+
86
+ def detect_test_framework
87
+ case detect_language
88
+ when "ruby"
89
+ return "rspec" if File.exist?(File.join(@project_dir, "spec"))
90
+ return "minitest" if File.exist?(File.join(@project_dir, "test"))
91
+ when "javascript"
92
+ return "jest" if File.exist?(File.join(@project_dir, "package.json")) && File.read(File.join(@project_dir, "package.json")).include?("jest")
93
+ return "mocha" if File.exist?(File.join(@project_dir, "package.json")) && File.read(File.join(@project_dir, "package.json")).include?("mocha")
94
+ when "python"
95
+ return "pytest" if File.exist?(File.join(@project_dir, "pytest.ini"))
96
+ return "unittest" if Dir.exist?(File.join(@project_dir, "tests"))
97
+ when "java"
98
+ return "junit" if File.exist?(File.join(@project_dir, "src", "test"))
99
+ end
100
+ "unknown"
101
+ end
102
+
103
+ def detect_database
104
+ return "postgresql" if File.exist?(File.join(@project_dir, "config", "database.yml")) && File.read(File.join(@project_dir, "config", "database.yml")).include?("postgresql")
105
+ return "mysql" if File.exist?(File.join(@project_dir, "config", "database.yml")) && File.read(File.join(@project_dir, "config", "database.yml")).include?("mysql")
106
+ return "sqlite" if File.exist?(File.join(@project_dir, "config", "database.yml")) && File.read(File.join(@project_dir, "config", "database.yml")).include?("sqlite")
107
+ "unknown"
108
+ end
109
+
110
+ def detect_deployment
111
+ return "docker" if File.exist?(File.join(@project_dir, "Dockerfile"))
112
+ return "kubernetes" if File.exist?(File.join(@project_dir, "k8s")) || File.exist?(File.join(@project_dir, "kubernetes"))
113
+ return "heroku" if File.exist?(File.join(@project_dir, "Procfile"))
114
+ return "aws" if File.exist?(File.join(@project_dir, "serverless.yml")) || File.exist?(File.join(@project_dir, "template.yaml"))
115
+ "unknown"
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+
5
+ module Aidp
6
+ module Shared
7
+ module Providers
8
+ class Anthropic < Base
9
+ def self.available?
10
+ !!Aidp::Shared::Util.which("claude")
11
+ end
12
+
13
+ def name = "anthropic"
14
+
15
+ def send(prompt:, session: nil)
16
+ raise "claude CLI not available" unless self.class.available?
17
+
18
+ # Use Claude CLI for non-interactive mode
19
+ cmd = ["claude", "compose", "--prompt", prompt]
20
+ system(*cmd)
21
+ :ok
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Aidp
4
+ module Shared
5
+ module Providers
6
+ class Base
7
+ def name = raise(NotImplementedError)
8
+
9
+ # Send a composed prompt string to the provider.
10
+ # Return :ok when command completed successfully,
11
+ # Return :interactive when starting an interactive session (for gate steps),
12
+ # or return a string if we captured output and the caller should write to a file.
13
+ def send(prompt:, session: nil) = raise(NotImplementedError)
14
+ end
15
+ end
16
+ end
17
+ end