|
| 1 | +#!/usr/bin/env python3 |
| 2 | +""" |
| 3 | +Generate markdown index table from consolidated prompts JSON file. |
| 4 | +
|
| 5 | +This script creates an index.md file from the consolidated_prompts.json file, |
| 6 | +ensuring consistency and tracking growth metrics over time. |
| 7 | +""" |
| 8 | + |
| 9 | +import json |
| 10 | +import os |
| 11 | +import argparse |
| 12 | +from pathlib import Path |
| 13 | +from datetime import datetime |
| 14 | +import hashlib |
| 15 | + |
| 16 | + |
| 17 | +def load_growth_history(history_file): |
| 18 | + """Load existing growth history.""" |
| 19 | + if os.path.exists(history_file): |
| 20 | + with open(history_file, 'r', encoding='utf-8') as f: |
| 21 | + return json.load(f) |
| 22 | + return {"entries": []} |
| 23 | + |
| 24 | + |
| 25 | +def save_growth_history(history_file, history): |
| 26 | + """Save growth history.""" |
| 27 | + with open(history_file, 'w', encoding='utf-8') as f: |
| 28 | + json.dump(history, f, indent=2, default=str) |
| 29 | + |
| 30 | + |
| 31 | +def update_growth_history(history_file, prompt_count): |
| 32 | + """Update growth history with current count.""" |
| 33 | + history = load_growth_history(history_file) |
| 34 | + current_date = datetime.now().strftime('%Y-%m-%d') |
| 35 | + |
| 36 | + # Check if we already have an entry for today |
| 37 | + today_entry = None |
| 38 | + for entry in history["entries"]: |
| 39 | + if entry["date"] == current_date: |
| 40 | + today_entry = entry |
| 41 | + break |
| 42 | + |
| 43 | + if today_entry: |
| 44 | + # Update today's count |
| 45 | + today_entry["count"] = prompt_count |
| 46 | + today_entry["updated"] = datetime.now().isoformat() |
| 47 | + else: |
| 48 | + # Add new entry |
| 49 | + history["entries"].append({ |
| 50 | + "date": current_date, |
| 51 | + "count": prompt_count, |
| 52 | + "updated": datetime.now().isoformat() |
| 53 | + }) |
| 54 | + |
| 55 | + # Keep only last 365 days |
| 56 | + history["entries"] = history["entries"][-365:] |
| 57 | + |
| 58 | + save_growth_history(history_file, history) |
| 59 | + return history |
| 60 | + |
| 61 | + |
| 62 | +def generate_growth_chart(history): |
| 63 | + """Generate a simple text-based growth chart.""" |
| 64 | + if len(history["entries"]) < 2: |
| 65 | + return "📈 Growth tracking started - check back soon for trends!" |
| 66 | + |
| 67 | + entries = history["entries"] |
| 68 | + latest = entries[-1] |
| 69 | + previous = entries[-2] if len(entries) > 1 else entries[0] |
| 70 | + |
| 71 | + # Calculate growth |
| 72 | + growth = latest["count"] - previous["count"] |
| 73 | + growth_sign = "📈" if growth > 0 else "📊" if growth == 0 else "📉" |
| 74 | + |
| 75 | + # Get some historical points |
| 76 | + chart_data = [] |
| 77 | + if len(entries) >= 30: |
| 78 | + # Show last 30 days |
| 79 | + chart_data = entries[-30:] |
| 80 | + period = "30 days" |
| 81 | + elif len(entries) >= 7: |
| 82 | + # Show last 7 days |
| 83 | + chart_data = entries[-7:] |
| 84 | + period = "7 days" |
| 85 | + else: |
| 86 | + chart_data = entries |
| 87 | + period = f"{len(entries)} days" |
| 88 | + |
| 89 | + # Simple sparkline-style representation |
| 90 | + counts = [entry["count"] for entry in chart_data] |
| 91 | + min_count = min(counts) |
| 92 | + max_count = max(counts) |
| 93 | + |
| 94 | + if max_count == min_count: |
| 95 | + sparkline = "▬" * len(counts) |
| 96 | + else: |
| 97 | + # Normalize to 0-7 range for simple bar chart |
| 98 | + normalized = [] |
| 99 | + for count in counts: |
| 100 | + norm = int(((count - min_count) / (max_count - min_count)) * 7) |
| 101 | + normalized.append(norm) |
| 102 | + |
| 103 | + # Convert to simple bar chart |
| 104 | + bars = ["▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"] |
| 105 | + sparkline = "".join(bars[n] for n in normalized) |
| 106 | + |
| 107 | + return f"""📊 **Growth Metrics** ({period}): {sparkline} |
| 108 | +- Current: **{latest['count']} prompts** |
| 109 | +- Change: {growth_sign} **{abs(growth)} prompts** since {previous['date']} |
| 110 | +- Range: {min_count} - {max_count} prompts""" |
| 111 | + |
| 112 | + |
| 113 | +def generate_index_from_consolidated(consolidated_file, output_file=None, growth_history_file=None): |
| 114 | + """Generate markdown index table from consolidated JSON file.""" |
| 115 | + |
| 116 | + # Load consolidated prompts |
| 117 | + try: |
| 118 | + with open(consolidated_file, 'r', encoding='utf-8') as f: |
| 119 | + consolidated_data = json.load(f) |
| 120 | + except Exception as e: |
| 121 | + print(f"❌ Error loading consolidated file: {e}") |
| 122 | + return None |
| 123 | + |
| 124 | + # Handle different consolidated file structures |
| 125 | + if isinstance(consolidated_data, list): |
| 126 | + # Old format: direct list of prompts |
| 127 | + prompts_data = consolidated_data |
| 128 | + elif isinstance(consolidated_data, dict) and 'prompts' in consolidated_data: |
| 129 | + # New format: object with metadata and prompts |
| 130 | + prompts_data = consolidated_data['prompts'] |
| 131 | + else: |
| 132 | + print(f"❌ Error: Unexpected consolidated file structure: {type(consolidated_data)}") |
| 133 | + print(f"Available keys: {list(consolidated_data.keys()) if isinstance(consolidated_data, dict) else 'Not a dict'}") |
| 134 | + return None |
| 135 | + |
| 136 | + if not isinstance(prompts_data, list): |
| 137 | + print(f"❌ Error: Expected list of prompts, got {type(prompts_data)}") |
| 138 | + return None |
| 139 | + |
| 140 | + # Filter valid prompts (those with required fields) |
| 141 | + valid_prompts = [] |
| 142 | + for prompt in prompts_data: |
| 143 | + if isinstance(prompt, dict): |
| 144 | + agent_name = (prompt.get('agentname') or prompt.get('agent-name', '')).strip() |
| 145 | + description = (prompt.get('description') or prompt.get('agent-description', '')).strip() |
| 146 | + |
| 147 | + if agent_name and description: |
| 148 | + # Create relative link to JSON file |
| 149 | + filename = prompt.get('filename', '') |
| 150 | + if filename: |
| 151 | + relative_link = f"system-prompts/json/{filename}" |
| 152 | + else: |
| 153 | + # Fallback: try to construct filename from agent name |
| 154 | + safe_name = agent_name.replace(' ', '').replace('/', '_').replace('\\', '_') |
| 155 | + relative_link = f"system-prompts/json/{safe_name}.json" |
| 156 | + |
| 157 | + # Get ChatGPT link if available |
| 158 | + chatgpt_link = prompt.get('chatgptlink') or prompt.get('chatgpt-url', '') |
| 159 | + |
| 160 | + valid_prompts.append({ |
| 161 | + 'agent_name': agent_name, |
| 162 | + 'description': description, |
| 163 | + 'link': relative_link, |
| 164 | + 'chatgpt_link': chatgpt_link |
| 165 | + }) |
| 166 | + |
| 167 | + # Sort prompts alphabetically by agent name |
| 168 | + valid_prompts.sort(key=lambda x: x['agent_name'].lower()) |
| 169 | + |
| 170 | + # Update growth history |
| 171 | + if growth_history_file: |
| 172 | + history = update_growth_history(growth_history_file, len(valid_prompts)) |
| 173 | + growth_chart = generate_growth_chart(history) |
| 174 | + else: |
| 175 | + growth_chart = f"📊 **{len(valid_prompts)} system prompts** available" |
| 176 | + |
| 177 | + # Generate markdown content |
| 178 | + current_date = datetime.now().strftime('%Y-%m-%d') |
| 179 | + current_time = datetime.now().strftime('%H:%M UTC') |
| 180 | + |
| 181 | + markdown_content = f"""# System Prompt Library Index |
| 182 | +
|
| 183 | +{growth_chart} |
| 184 | +
|
| 185 | +*Last updated: {current_date} at {current_time} | Generated from consolidated_prompts.json* |
| 186 | +
|
| 187 | +| Agent Name | Description | CustomGPT | |
| 188 | +|------------|-------------|----------| |
| 189 | +""" |
| 190 | + |
| 191 | + for prompt in valid_prompts: |
| 192 | + # Escape pipe characters in content to avoid breaking the table |
| 193 | + agent_name = prompt['agent_name'].replace('|', '\\|') |
| 194 | + description = prompt['description'].replace('|', '\\|') |
| 195 | + |
| 196 | + # Truncate description if too long |
| 197 | + if len(description) > 150: |
| 198 | + description = description[:147] + "..." |
| 199 | + |
| 200 | + # Create agent name with link to JSON file |
| 201 | + agent_name_with_link = f"[{agent_name}]({prompt['link']})" |
| 202 | + |
| 203 | + # Add CustomGPT badge if available |
| 204 | + customgpt_column = "" |
| 205 | + if prompt['chatgpt_link']: |
| 206 | + customgpt_column = f"[]({prompt['chatgpt_link']})" |
| 207 | + |
| 208 | + markdown_content += f"| {agent_name_with_link} | {description} | {customgpt_column} |\n" |
| 209 | + |
| 210 | + # Write the index file |
| 211 | + with open(output_file, 'w', encoding='utf-8') as f: |
| 212 | + f.write(markdown_content) |
| 213 | + |
| 214 | + print(f"\n✅ Index generated successfully from consolidated file!") |
| 215 | + print(f"📊 Total valid prompts: {len(valid_prompts)}") |
| 216 | + print(f"📁 Output file: {output_file}") |
| 217 | + print(f"📈 Growth tracking: {'enabled' if growth_history_file else 'disabled'}") |
| 218 | + |
| 219 | + return output_file |
| 220 | + |
| 221 | + |
| 222 | +def main(): |
| 223 | + """Main function with command line argument parsing.""" |
| 224 | + script_dir = Path(__file__).parent |
| 225 | + repo_root = script_dir.parent |
| 226 | + |
| 227 | + parser = argparse.ArgumentParser( |
| 228 | + description="Generate markdown index table from consolidated prompts JSON file" |
| 229 | + ) |
| 230 | + parser.add_argument( |
| 231 | + "--consolidated-file", |
| 232 | + default=str(repo_root / "consolidated_prompts.json"), |
| 233 | + help="Consolidated prompts JSON file (default: ../consolidated_prompts.json)" |
| 234 | + ) |
| 235 | + parser.add_argument( |
| 236 | + "--output", |
| 237 | + default=str(repo_root / "index.md"), |
| 238 | + help="Output file name (default: ../index.md)" |
| 239 | + ) |
| 240 | + parser.add_argument( |
| 241 | + "--growth-history", |
| 242 | + default=str(repo_root / "growth_history.json"), |
| 243 | + help="Growth history file (default: ../growth_history.json)" |
| 244 | + ) |
| 245 | + parser.add_argument( |
| 246 | + "--no-growth-tracking", |
| 247 | + action="store_true", |
| 248 | + help="Disable growth tracking" |
| 249 | + ) |
| 250 | + |
| 251 | + args = parser.parse_args() |
| 252 | + |
| 253 | + # Check if consolidated file exists |
| 254 | + if not os.path.exists(args.consolidated_file): |
| 255 | + print(f"❌ Error: Consolidated file '{args.consolidated_file}' not found!") |
| 256 | + return 1 |
| 257 | + |
| 258 | + growth_file = None if args.no_growth_tracking else args.growth_history |
| 259 | + |
| 260 | + try: |
| 261 | + generate_index_from_consolidated(args.consolidated_file, args.output, growth_file) |
| 262 | + return 0 |
| 263 | + except Exception as e: |
| 264 | + print(f"❌ Error generating index: {e}") |
| 265 | + return 1 |
| 266 | + |
| 267 | + |
| 268 | +if __name__ == "__main__": |
| 269 | + exit(main()) |
0 commit comments