Class: Aidp::Execute::PromptManager

Inherits:
Object
  • Object
show all
Defined in:
lib/aidp/execute/prompt_manager.rb

Overview

Manages the PROMPT.md file lifecycle for work loops Responsibilities:

  • Read/write PROMPT.md

  • Check existence

  • Archive completed prompts

  • Optionally optimize prompts using intelligent fragment selection (ZFC)

Constant Summary collapse

PROMPT_FILENAME =
"PROMPT.md"
ARCHIVE_DIR =
".aidp/prompt_archive"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(project_dir, config: nil) ⇒ PromptManager

Returns a new instance of PromptManager.



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/aidp/execute/prompt_manager.rb', line 20

def initialize(project_dir, config: nil)
  @project_dir = project_dir
  @aidp_dir = File.join(project_dir, ".aidp")
  @prompt_path = File.join(@aidp_dir, PROMPT_FILENAME)
  @archive_dir = File.join(project_dir, ARCHIVE_DIR)
  @config = config
  @optimizer = nil
  @last_optimization_stats = nil

  # Ensure .aidp directory exists
  FileUtils.mkdir_p(@aidp_dir)

  # Initialize optimizer if enabled
  if config&.respond_to?(:prompt_optimization_enabled?) && config.prompt_optimization_enabled?
    @optimizer = Aidp::PromptOptimization::Optimizer.new(
      project_dir: project_dir,
      config: config.prompt_optimization_config
    )
  end
end

Instance Attribute Details

#last_optimization_statsObject (readonly)

Returns the value of attribute last_optimization_stats.



18
19
20
# File 'lib/aidp/execute/prompt_manager.rb', line 18

def last_optimization_stats
  @last_optimization_stats
end

#optimizerObject (readonly)

Returns the value of attribute optimizer.



18
19
20
# File 'lib/aidp/execute/prompt_manager.rb', line 18

def optimizer
  @optimizer
end

Instance Method Details

#archive(step_name) ⇒ Object

Archive PROMPT.md with timestamp and step name



124
125
126
127
128
129
130
131
132
133
134
# File 'lib/aidp/execute/prompt_manager.rb', line 124

def archive(step_name)
  return unless exists?

  FileUtils.mkdir_p(@archive_dir)
  timestamp = Time.now.strftime("%Y%m%d_%H%M%S")
  archive_filename = "#{timestamp}_#{step_name}_PROMPT.md"
  archive_path = File.join(@archive_dir, archive_filename)

  FileUtils.cp(@prompt_path, archive_path)
  archive_path
end

#deleteObject

Delete PROMPT.md (typically after archiving)



137
138
139
# File 'lib/aidp/execute/prompt_manager.rb', line 137

def delete
  File.delete(@prompt_path) if exists?
end

#exists?Boolean

Check if PROMPT.md exists

Returns:

  • (Boolean)


119
120
121
# File 'lib/aidp/execute/prompt_manager.rb', line 119

def exists?
  File.exist?(@prompt_path)
end

#optimization_enabled?Boolean

Check if optimization is enabled

Returns:

  • (Boolean)

    True if optimizer is available



176
177
178
# File 'lib/aidp/execute/prompt_manager.rb', line 176

def optimization_enabled?
  !@optimizer.nil?
end

#optimization_reportString?

Get optimization report for last optimization

Returns:

  • (String, nil)

    Markdown report or nil if no optimization performed



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/aidp/execute/prompt_manager.rb', line 149

def optimization_report
  return nil unless @last_optimization_stats

  # Build report from composition result
  lines = []
  lines << "# Prompt Optimization Report"
  lines << ""
  lines << "## Statistics"
  lines << "- **Selected Fragments**: #{@last_optimization_stats.selected_count}"
  lines << "- **Excluded Fragments**: #{@last_optimization_stats.excluded_count}"
  lines << "- **Total Tokens**: #{@last_optimization_stats.total_tokens} / #{@last_optimization_stats.budget}"
  lines << "- **Budget Utilization**: #{@last_optimization_stats.budget_utilization.round(1)}%"
  lines << "- **Average Relevance Score**: #{(@last_optimization_stats.average_score * 100).round(1)}%"
  lines << ""
  lines << "## Selected Fragments"
  @last_optimization_stats.selected_fragments.each do |scored|
    fragment = scored[:fragment]
    score = scored[:score]
    lines << "- #{fragment_name(fragment)} (#{(score * 100).round(0)}%)"
  end

  lines.join("\n")
end

#optimizer_statsHash?

Get optimizer statistics

Returns:

  • (Hash, nil)

    Statistics hash or nil if optimizer not available



183
184
185
# File 'lib/aidp/execute/prompt_manager.rb', line 183

def optimizer_stats
  @optimizer&.statistics
end

#pathObject

Get the full path to PROMPT.md



142
143
144
# File 'lib/aidp/execute/prompt_manager.rb', line 142

def path
  @prompt_path
end

#readObject

Read content from PROMPT.md



113
114
115
116
# File 'lib/aidp/execute/prompt_manager.rb', line 113

def read
  return nil unless exists?
  File.read(@prompt_path)
end

#write(content, step_name: nil) ⇒ String?

Write content to PROMPT.md If optimization is enabled, stores the content but doesn’t write yet (use write_optimized instead)

Parameters:

  • content (String)

    The prompt content to write

  • step_name (String, nil) (defaults to: nil)

    Optional step name for immediate archiving

Returns:

  • (String, nil)

    Archive path if archived, nil otherwise



48
49
50
51
52
53
# File 'lib/aidp/execute/prompt_manager.rb', line 48

def write(content, step_name: nil)
  File.write(@prompt_path, content)

  # Archive immediately if step_name provided (issue #224)
  archive(step_name) if step_name
end

#write_optimized(task_context, options = {}) ⇒ Boolean

Write optimized prompt using intelligent fragment selection

Uses Zero Framework Cognition to select only the most relevant fragments from style guides, templates, and source code based on task context.

Parameters:

  • task_context (Hash)

    Context about the current task

  • options (Hash) (defaults to: {})

    Optimization options

Options Hash (task_context):

  • :task_type (Symbol)

    Type of task (:feature, :bugfix, etc.)

  • :description (String)

    Task description

  • :affected_files (Array<String>)

    Files being modified

  • :step_name (String)

    Current work loop step

  • :tags (Array<String>)

    Additional context tags

Options Hash (options):

  • :include_metadata (Boolean)

    Include debug metadata

Returns:

  • (Boolean)

    True if optimization was used, false if fallback to regular write



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/aidp/execute/prompt_manager.rb', line 69

def write_optimized(task_context, options = {})
  unless @optimizer
    Aidp.logger.warn("prompt_manager", "Optimization requested but not enabled")
    return false
  end

  begin
    # Use optimizer to build intelligent prompt
    result = @optimizer.optimize_prompt(
      task_type: task_context[:task_type],
      description: task_context[:description],
      affected_files: task_context[:affected_files] || [],
      step_name: task_context[:step_name],
      tags: task_context[:tags] || [],
      options: options
    )

    # Write optimized prompt
    result.write_to_file(@prompt_path)

    # Store statistics for inspection
    @last_optimization_stats = result.composition_result

    # Log optimization results
    Aidp.logger.info(
      "prompt_manager",
      "Optimized prompt written",
      selected_fragments: result.composition_result.selected_count,
      excluded_fragments: result.composition_result.excluded_count,
      tokens: result.estimated_tokens,
      budget_utilization: result.composition_result.budget_utilization
    )

    # Archive immediately if step_name provided (issue #224)
    archive(task_context[:step_name]) if task_context[:step_name]

    true
  rescue => e
    Aidp.logger.error("prompt_manager", "Optimization failed, using fallback", error: e.message)
    false
  end
end