Class: Ace::Docs::Prompts::DocumentAnalysisPrompt

Inherits:
Object
  • Object
show all
Defined in:
lib/ace/docs/prompts/document_analysis_prompt.rb

Overview

Builds prompts for analyzing changes relevant to a specific document

Constant Summary collapse

10

Class Method Summary collapse

Class Method Details

.build(document, diff, since: nil, cache_dir: nil) ⇒ Hash

Build prompts for analyzing changes for a specific document

Parameters:

  • document (Document)

    The document to analyze changes for

  • diff (String, Hash)

    Either single diff string or hash of => diff_string

  • since (String) (defaults to: nil)

    Time period for the diff

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

    Optional cache directory for context.md

Returns:

  • (Hash)

    Hash with :system, :user prompts, :context_md, :diff_stats



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ace/docs/prompts/document_analysis_prompt.rb', line 20

def self.build(document, diff, since: nil, cache_dir: nil)
  # Load base instructions
  base_instructions = load_user_prompt_template

  # Handle both single diff and multi-subject diffs
  diff_files = []
  if diff.is_a?(Hash)
    # Multi-subject: save each diff with subject name
    if cache_dir && Dir.exist?(cache_dir)
      diff.each do |subject_name, diff_content|
        next if diff_content.strip.empty?  # Skip empty diffs

        diff_file_path = File.join(cache_dir, "#{subject_name}.diff")
        File.write(diff_file_path, diff_content)
        # Store absolute path for ace-bundle (it resolves relative to context.md location)
        diff_files << diff_file_path
      end
    end
  elsif cache_dir && Dir.exist?(cache_dir)
    # Single diff: backward compatible behavior
    diff_file_path = File.join(cache_dir, "repo-diff.diff")
    File.write(diff_file_path, diff)
    # Store absolute path for ace-bundle
    diff_files << diff_file_path
  end

  # Create context.md = frontmatter + instructions + scope section
  # Use relative paths for diff files
  context_md = create_context_markdown(base_instructions, document, since,
    diff_files: diff_files)

  # Save context.md to cache
  if cache_dir && Dir.exist?(cache_dir)
    File.write(File.join(cache_dir, "context.md"), context_md)
  end

  # Load via ace-bundle (returns instructions + embedded context + diffs)
  embedded_content = load_context_md(context_md, document: document, cache_dir: cache_dir) || base_instructions

  # Calculate diff stats for metadata
  all_diffs = diff.is_a?(Hash) ? diff.values.join("\n") : diff
  diff_stats = calculate_diff_stats(all_diffs)

  {
    system: load_system_prompt,
    user: embedded_content,
    context_md: context_md,
    diff_stats: diff_stats
  }
end

.build_user_prompt(document, diff, since, context: nil) ⇒ String

Build user prompt with document context and diff

Parameters:

  • document (Document)

    The document to analyze

  • diff (String)

    The git diff content

  • since (String)

    Time period

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

    Optional embedded context from ace-bundle

Returns:

  • (String)

    User prompt



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/ace/docs/prompts/document_analysis_prompt.rb', line 124

def self.build_user_prompt(document, diff, since, context: nil)
  # Extract document metadata
  doc_type = document.doc_type || "document"
  purpose = document.purpose || "(not specified)"
  doc_path = document.respond_to?(:relative_path) ? document.relative_path : document.path

  # Extract context information
  context_keywords = document.context_keywords
  context_preset = document.context_preset

  # Extract subject filters (to show what was filtered)
  subject_filters = document.subject_diff_filters

  # Build context description
  context_desc = build_context_description(context_keywords, context_preset)

  # Build filters description
  filters_desc = build_filters_description(subject_filters)

  # Build time description
  time_desc = since ? "since #{since}" : "recent changes"

  # Build context section if provided
  context_section = if context && !context.strip.empty?
    "\n## Context\n\n#{context}\n"
  else
    ""
  end

  <<~PROMPT
    ## Document Information

    **Path**: #{doc_path}
    **Type**: #{doc_type}
    **Purpose**: #{purpose}

    #{context_desc}
    #{context_section}
    ## Changes to Analyze

    The following git diff shows changes #{time_desc}.

    #{filters_desc}

    ```diff
    #{diff}
    ```
  PROMPT
end

.fallback_system_promptString

Fallback system prompt if ace-nav unavailable

Returns:

  • (String)

    Embedded system prompt



176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/ace/docs/prompts/document_analysis_prompt.rb', line 176

def self.fallback_system_prompt
  <<~PROMPT
    You are analyzing code changes to determine what needs to be updated in documentation.

    Provide a markdown report with:
    - Summary (2-3 sentences)
    - Changes Detected (organized by HIGH/MEDIUM/LOW priority)
    - Recommended Updates (specific sections with reasoning)
    - Additional Notes

    Focus on relevance to the document's purpose and be specific about what needs updating and why.
  PROMPT
end

.load_system_promptString

Load system prompt via ace-nav protocol

Returns:

  • (String)

    System prompt content



105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/ace/docs/prompts/document_analysis_prompt.rb', line 105

def self.load_system_prompt
  stdout, _, status = Open3.capture3("ace-nav", "prompt://document-analysis.system", "--content")

  if status.success?
    stdout.strip
  else
    # Fallback to embedded prompt if ace-nav fails
    fallback_system_prompt
  end
rescue
  fallback_system_prompt
end