Skip to content

Instantly share code, notes, and snippets.

@jonmagic
Created August 28, 2025 13:14
Show Gist options
  • Select an option

  • Save jonmagic/83445be775791b7d3a5cc6c97641dc57 to your computer and use it in GitHub Desktop.

Select an option

Save jonmagic/83445be775791b7d3a5cc6c97641dc57 to your computer and use it in GitHub Desktop.
module Commands
class Snippets
def self.usage
"snippets [options]"
end
def self.description
[
"Generate snippets for leadership based on the conversations you participated in during the designated time period.",
].join("\n")
end
def initialize(date_range: nil, model: nil)
@date_range_input = date_range
@model = model
@github_login = nil
end
attr_accessor :github_login
def run
$GLOBAL_DEBUG = true
date_range = parse_date_range
github_client = setup_github_client
require_relative "../llm_client"
self.github_login = github_client.user.login
output_dir = setup_output_dir(date_range)
activity_urls = load_or_compute(
path: File.join(output_dir, "01_activity_urls.json"),
compute: -> { fetch_activity_urls(date_range, client: github_client) }
)
raw_data = load_or_compute(
path: File.join(output_dir, "02_raw_data.json"),
compute: -> { fetch_data_for_urls(activity_urls, client: github_client) }
)
filtered_data = load_or_compute(
path: File.join(output_dir, "03_filtered_data.json"),
compute: -> { filter_data(raw_data, date_range) }
)
executive_summaries = load_or_compute(
path: File.join(output_dir, "04_executive_summaries.json"),
compute: -> { generate_executive_summaries(filtered_data) }
)
require "erb"
classifications_data = load_or_compute(
path: File.join(output_dir, "05_classifications.json"),
compute: -> { classify_data(filtered_data) }
)
grouped_data = load_or_compute(
path: File.join(output_dir, "06_grouped_data.json"),
compute: -> { build_groups_from_classified_data(classifications_data) }
)
snippets_data = load_or_compute(
path: File.join(output_dir, "07_snippets.json"),
compute: -> { generate_snippets(executive_summaries:, classifications_data:, grouped_data:) }
)
# sorted_snippets_data = load_or_compute(
# path: File.join(output_dir, "08_sorted_snippets.json"),
# compute: -> { sort_and_limit_snippets(snippets_data:) }
# )
load_or_compute(
path: File.join(output_dir, "09_snippets.md"),
compute: -> { print_snippets(snippets_data) },
json: false
)
rescue ArgumentError => e
abort("Error: #{e.message}")
end
SECTIONS = {
"Key Ships & Accomplishments" => {
header: ":ship: Key Ships & Accomplishments",
template: "key_ships_and_accomplishments.md"
},
"Risks and Themes" => {
header: ":warning: Risks and Themes",
template: "risks_and_themes.md"
},
"Blockers" => {
header: ":red_circle: Blockers",
template: "blockers.md"
},
"Ideas" => {
header: ":bulb: Ideas",
template: "ideas.md"
},
"Collaborations" => {
header: ":fist_right: :fist_left: Collaborations",
template: "collaborations.md"
},
"Shoutouts" => {
header: ":sparkles: Shoutouts",
template: "shoutouts.md"
}
}.freeze
IMPACT_RANK = {
"high" => 1,
"medium" => 2,
"low" => 3
}.freeze
private def llm_client
@llm_client ||= @model ? LlmClient[@model] : LlmClient.default
end
private def parse_date_range
require_relative "../date_range"
DateRange.new(@date_range_input || "#{Date.today - 7}..#{Date.today}")
end
private def setup_github_client
require_relative "../github_client"
GithubClient.client
end
private def setup_output_dir(date_range)
dir = File.join(Dir.pwd, "projects", "snippets", "#{date_range.start_date}-to-#{date_range.end_date}")
if File.directory?(dir)
puts "Directory already exists: #{dir}"
else
require "fileutils"
FileUtils.mkdir_p(dir)
puts "Created directory: #{dir}"
end
dir
end
private def fetch_activity_urls(date_range, client:)
puts "Fetching activity URLs..."
require_relative "../github_search"
query = "created:#{date_range.start_date}..#{date_range.end_date}"
role_types = ["author", "commenter", "reviewer", "assignee", "mention", "involves"]
activity_urls = []
if query !~ /\b(?:author|commenter|reviewer|assignee|mention|involves):\S+/
role_types.each do |role|
modified_query = "#{query} #{role}:#{github_login}"
activity_urls += GithubSearch.new(query: modified_query, client: client).fetch_data.map { |url, _| url }
end
else
activity_urls = GithubSearch.new(query: query, client: client).fetch_data.map { |url, _| url }
end
activity_urls
end
private def fetch_data_for_urls(activity_urls, client:)
require_relative "../github_data_fetcher"
skipped_urls = []
results = activity_urls.each_with_object({}) do |url, data|
puts "Fetching data for #{url}"
begin
result = GithubDataFetcher.new(url: url, client:).fetch_to_hash
result[:source_url] = url
data[url] = result
rescue Octokit::NotFound => e
puts "Warning: Skipping #{url} as it appears to be moved or deleted (404 error)"
puts " Error: #{e.message}"
skipped_urls << url
rescue => e
puts "Error fetching data for #{url}: #{e.message}"
puts e.backtrace.join("\n") if $GLOBAL_DEBUG
skipped_urls << url
end
end
if skipped_urls.any?
puts "Skipped #{skipped_urls.size} URLs due to errors:"
skipped_urls.each { |url| puts " - #{url}" }
end
puts "Fetched data for #{results.size} URLs successfully!"
results
end
private def filter_data(raw_data, date_range)
start_date = Time.parse(date_range.start_date)
filtered = raw_data.select do |url, record|
begin
authored_by_me = record["author"] == github_login
merged_by_me = record["merged"] && record["merged_by"] == github_login
commented_by_me = record["comments"]&.any? { |c| c["actor"] == github_login }
recent_comments_by_me = record["comments"]&.count { |c|
c["actor"] == github_login &&
Time.parse(c["created_at"]) >= start_date
} || 0
reviewed_by_me = record["reviews"]&.any? { |r| r["actor"] == github_login }
non_assign_event = record["events"]&.any? do |e|
e["actor"] == github_login && e["event_type"] == "committed"
end
# Parse dates for age checking
created_at = Time.parse(record["created_at"])
closed_at = record["closed_at"] ? Time.parse(record["closed_at"]) : nil
six_months_ago = Time.now - (6 * 30 * 24 * 60 * 60) # approximate 6 months in seconds
is_housekeeping = created_at < six_months_ago &&
closed_at &&
closed_at >= start_date &&
!merged_by_me &&
recent_comments_by_me <= 1 &&
!reviewed_by_me
# Keep if not old housekeeping and has meaningful interaction
!is_housekeeping && ((authored_by_me && merged_by_me) || commented_by_me || reviewed_by_me || non_assign_event)
rescue => e
puts "Error filtering record from #{url}: #{e.message}"
false
end
end
if filtered.keys.count == 0
puts "Exiting because there are no records to process after filtering"
exit
end
puts "Filtered down to #{filtered.keys.count} records"
filtered
end
private def generate_executive_summaries(filtered_data)
puts "Generating executive summaries..."
system_prompt = File.read(File.join(Dir.pwd, "prompts/executive_summary.md"))
filtered_data.each_with_object({}) do |(url, record), results|
puts "Generating executive summary for #{url}..."
record = right_size_record(record)
attempts = 0
begin
prompt = JSON.pretty_generate(record)
llm_result = llm_client.result(prompt, system_prompt:)
results[url] = llm_result
rescue => e
attempts += 1
if attempts < 3
sleep(5 * attempts)
retry
else
puts "Warning: Failed to generate executive summary for #{url}"
results[url] = ""
end
end
results[url]
end
end
private def classify_data(filtered_data, retries: 3, delay: 5)
filtered_data.each_with_object({}) do |(url, record), results|
puts "Classifying record from #{url}..."
record = right_size_record(record)
attempts = 0
begin
system_prompt = [
"You are an AI assistant that processes JSON data describing GitHub work items (issues, pull requests, and discussions). You will output metadata about the data in valid JSON.",
"Here is context to help you understand the data and the task:",
File.read(File.join(Dir.pwd, "prompts/github_mission_semester_priorities.md")),
File.read(File.join(Dir.pwd, "prompts/who_are_safety_integrity.md")),
File.read(File.join(Dir.pwd, "prompts/who_is_#{github_login}.md")),
File.read(File.join(Dir.pwd, "prompts/snippets/sections.md")),
File.read(File.join(Dir.pwd, "prompts/classify_data.md")),
].join("\n\n")
prompt = JSON.pretty_generate(record)
llm_result = llm_client.result(prompt, json: true, system_prompt:)
results[url] = JSON.parse(llm_result)
rescue => e
attempts += 1
if attempts < retries
sleep(delay * attempts)
retry
else
puts "Warning: Failed to classify #{record["source_url"]}"
puts "Error: #{e.message}"
results[url] = {}
end
end
results[url]
end
end
private def right_size_record(record, max_size: 3000)
# Step 1: Remove the "events" array
record.delete("events")
# Step 2: Filter comments and review_comments
["comments", "review_comments"].each do |key|
next unless record[key].is_a?(Array)
record[key].reject! do |comment|
actor = comment["actor"]
actor == "hubot" || actor.end_with?("[bot]")
end
end
# Step 3: Serialize to JSON and check size
serialized = record.to_json
if (serialized.bytesize / 4.0).ceil > max_size
# Step 4: Remove "diff" key if size is too large
record.delete("diff")
end
record
end
private def build_groups_from_classified_data(classifications_data, retries: 3, delay: 5)
puts "Grouping data..."
attempts = 0
filtered_classification_data = classifications_data.transform_values do |details|
{
"summary" => details["summary"],
"xrefs" => details["xrefs"]
}
end
begin
system_prompt = File.read(File.join(Dir.pwd, "prompts/group_data.md"))
prompt = JSON.pretty_generate(filtered_classification_data)
llm_result = llm_client.result(prompt, system_prompt:)
llm_result = llm_result.gsub(/^```json/, "").gsub(/```$/, "")
result = JSON.parse(llm_result)
result = result.is_a?(Array) ? result : []
original_urls = filtered_classification_data.keys.to_set
grouped_urls = result.flat_map { |group| group["urls"] }.to_set
missing_urls = original_urls - grouped_urls
unless missing_urls.empty?
puts "Warning: Some URLs were not included in any group:"
missing_urls.each { |url| puts " - #{url}" }
end
return result
rescue => e
attempts += 1
if attempts < retries
sleep(delay * attempts)
retry
else
puts "Warning: Failed to create groupings"
puts "Error: #{e.message}"
{}
end
end
end
private def generate_snippets(executive_summaries:, classifications_data:, grouped_data:)
puts "Generating snippets..."
snippets = SECTIONS.each_with_object({}) do |(section_name, section), result|
result[section_name] = []
end
grouped_data.each_with_index do |group, group_index|
SECTIONS.each do |section_name, section|
items = group["urls"].each_with_object({}) do |url, result|
next unless executive_summaries.key?(url)
executive_summary = executive_summaries[url]
if executive_summary.nil? || executive_summary.empty?
puts "Warning: No executive summary for #{url}"
next
end
metadata = classifications_data[url]
result[url] = { "executive_summary" => executive_summary, "metadata" => metadata }
end
# Generate a snippet for the current section and group
snippet = generate_snippet(section_name, items, retries: 3, delay: 5)
if snippet.nil? || snippet.empty?
puts "Warning: No snippet generated for #{section_name} and group #{group["name"]}"
next
end
# Add snippet number (Group #) at the beginning of each snippet
snippet_with_number = "[G#{group_index + 1}] #{snippet.gsub(/\n+/, " ")}"
snippets[section_name] << snippet_with_number
end
end
snippets
end
private def generate_snippet(section, items, retries: 3, delay: 5)
attempts = 0
begin
system_prompt = [
"You are an AI assistant that processes JSON data describing my GitHub work items (issues, pull requests, and discussions) and the relationships between that data to generate snippets for leadership.",
"I am #{github_login} and these are my snippets and you should focus on the WE and not the I. For example, use 'We shipped X' instead of 'I shipped X' or 'led by @#{github_login}'.",
"Here is context to help you understand the data and the task:",
File.read(File.join(Dir.pwd, "prompts/github_mission_semester_priorities.md")),
File.read(File.join(Dir.pwd, "prompts/who_is_#{github_login}.md")),
File.read(File.join(Dir.pwd, "prompts/snippets/#{SECTIONS[section][:template]}")),
]
prompt = JSON.pretty_generate(items)
llm_client.result(prompt, system_prompt: system_prompt.join("\n\n"))
rescue
attempts += 1
if attempts < retries
sleep(delay * attempts)
retry
else
puts "Warning: Failed to generate snippet"
nil
end
end
end
private def sort_and_limit_snippets(snippets_data:, retries: 3, delay: 5)
puts "Sorting and limiting snippets..."
attempts = 0
begin
system_prompt = File.read(File.join(Dir.pwd, "prompts/snippets/sort_and_limit.md"))
prompt = JSON.pretty_generate(snippets_data)
llm_result = llm_client.result(prompt, system_prompt:)
result = JSON.parse(llm_result)
result
rescue => e
attempts += 1
if attempts < retries
sleep(delay * attempts)
retry
else
puts "Warning: Failed to sort and limit snippets"
puts "Error: #{e.message}"
{}
end
end
end
private def print_snippets(snippets_data)
puts "Generated Snippets for #{github_login}"
output = []
SECTIONS.each do |section_name, section|
next unless snippets_data[section_name].any?
output << "### #{section[:header]}"
snippets_data[section_name].each do |snippet|
output << "- #{snippet}"
end
output << ""
end
output.join("\n")
end
private def load_or_compute(path:, compute:, json: true)
if File.exist?(path)
puts "Found existing file at #{path}, skipping computation..."
begin
data = File.read(path)
result = json ? JSON.parse(data) : data
puts "Successfully loaded data from #{path}"
return result
rescue JSON::ParserError => e
puts "Error parsing JSON from #{path}: #{e.message}"
puts "Falling back to computation..."
rescue => e
puts "Error loading from #{path}: #{e.message}"
puts "Falling back to computation..."
end
end
begin
result = compute.call
if json
result = JSON.pretty_generate(result)
end
File.open(path, "w") do |f|
f.write(result)
end
puts "Generated file at #{path} successfully!"
json ? JSON.parse(result) : result
rescue => e
puts "Error during computation or file writing: #{e.message}"
puts e.backtrace.join("\n")
raise
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment